#include "rack_au.h"
#include <AudioToolbox/AudioToolbox.h>
#include <CoreFoundation/CoreFoundation.h>
#include <cstring>
#include <cstdio>
#include <climits>
#include <new>
#include <mutex>
static std::mutex g_audio_unit_cleanup_mutex;
struct RackAUPlugin {
AudioComponentInstance audio_unit;
bool initialized;
double sample_rate;
uint32_t max_block_size;
char unique_id[64];
AudioBufferList* input_buffer_list;
AudioBufferList* output_buffer_list;
uint32_t input_channels;
uint32_t output_channels;
int64_t sample_position;
AudioUnitParameterID* parameter_ids;
AudioUnitParameterInfo* parameter_info; UInt32 parameter_count;
};
static const char* parameter_unit_to_string(AudioUnitParameterUnit unit) {
switch (unit) {
case kAudioUnitParameterUnit_Generic: return "";
case kAudioUnitParameterUnit_Indexed: return "indexed";
case kAudioUnitParameterUnit_Boolean: return "on/off";
case kAudioUnitParameterUnit_Percent: return "%";
case kAudioUnitParameterUnit_Seconds: return "s";
case kAudioUnitParameterUnit_SampleFrames: return "samples";
case kAudioUnitParameterUnit_Phase: return "°";
case kAudioUnitParameterUnit_Rate: return "rate";
case kAudioUnitParameterUnit_Hertz: return "Hz";
case kAudioUnitParameterUnit_Cents: return "cents";
case kAudioUnitParameterUnit_RelativeSemiTones: return "semitones";
case kAudioUnitParameterUnit_MIDINoteNumber: return "note";
case kAudioUnitParameterUnit_MIDIController: return "CC";
case kAudioUnitParameterUnit_Decibels: return "dB";
case kAudioUnitParameterUnit_LinearGain: return "gain";
case kAudioUnitParameterUnit_Degrees: return "°";
case kAudioUnitParameterUnit_EqualPowerCrossfade: return "xfade";
case kAudioUnitParameterUnit_MixerFaderCurve1: return "fader";
case kAudioUnitParameterUnit_Pan: return "pan";
case kAudioUnitParameterUnit_Meters: return "m";
case kAudioUnitParameterUnit_AbsoluteCents: return "cents";
case kAudioUnitParameterUnit_Octaves: return "octaves";
case kAudioUnitParameterUnit_BPM: return "BPM";
case kAudioUnitParameterUnit_Beats: return "beats";
case kAudioUnitParameterUnit_Milliseconds: return "ms";
case kAudioUnitParameterUnit_Ratio: return "ratio";
case kAudioUnitParameterUnit_CustomUnit: return "custom";
default: return "";
}
}
static OSStatus input_render_callback(
void* inRefCon,
AudioUnitRenderActionFlags* ioActionFlags,
const AudioTimeStamp* inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList* ioData
) {
RackAUPlugin* plugin = static_cast<RackAUPlugin*>(inRefCon);
if (!plugin || !plugin->input_buffer_list || !ioData) {
*ioActionFlags |= kAudioUnitRenderAction_OutputIsSilence;
return noErr;
}
if (inNumberFrames > plugin->max_block_size) {
*ioActionFlags |= kAudioUnitRenderAction_OutputIsSilence;
return kAudioUnitErr_TooManyFramesToProcess;
}
UInt32 num_channels = ioData->mNumberBuffers < plugin->input_buffer_list->mNumberBuffers
? ioData->mNumberBuffers
: plugin->input_buffer_list->mNumberBuffers;
const UInt32 required_bytes = inNumberFrames * sizeof(float);
for (UInt32 ch = 0; ch < num_channels; ch++) {
if (ioData->mBuffers[ch].mData &&
ioData->mBuffers[ch].mDataByteSize >= required_bytes &&
plugin->input_buffer_list->mBuffers[ch].mData) {
const float* src = static_cast<const float*>(plugin->input_buffer_list->mBuffers[ch].mData);
float* dest = static_cast<float*>(ioData->mBuffers[ch].mData);
memcpy(dest, src, required_bytes);
}
}
return noErr;
}
static bool parse_unique_id(const char* unique_id, AudioComponentDescription* desc) {
if (!unique_id || !desc) {
return false;
}
unsigned int type = 0, subtype = 0, manufacturer = 0;
int matched = sscanf(unique_id, "%x-%x-%x", &type, &subtype, &manufacturer);
if (matched != 3) {
return false;
}
desc->componentType = type;
desc->componentSubType = subtype;
desc->componentManufacturer = manufacturer;
desc->componentFlags = 0;
desc->componentFlagsMask = 0;
return true;
}
RackAUPlugin* rack_au_plugin_new(const char* unique_id) {
if (!unique_id) {
return nullptr;
}
RackAUPlugin* plugin = new RackAUPlugin();
plugin->audio_unit = nullptr;
plugin->initialized = false;
plugin->sample_rate = 0.0;
plugin->max_block_size = 0;
plugin->input_buffer_list = nullptr;
plugin->output_buffer_list = nullptr;
plugin->input_channels = 0;
plugin->output_channels = 0;
plugin->sample_position = 0;
plugin->parameter_ids = nullptr;
plugin->parameter_info = nullptr;
plugin->parameter_count = 0;
strncpy(plugin->unique_id, unique_id, sizeof(plugin->unique_id) - 1);
plugin->unique_id[sizeof(plugin->unique_id) - 1] = '\0';
AudioComponentDescription desc;
if (!parse_unique_id(unique_id, &desc)) {
delete plugin;
return nullptr;
}
AudioComponent component = AudioComponentFindNext(nullptr, &desc);
if (!component) {
delete plugin;
return nullptr;
}
OSStatus status;
{
std::lock_guard<std::mutex> lock(g_audio_unit_cleanup_mutex);
status = AudioComponentInstanceNew(component, &plugin->audio_unit);
}
if (status != noErr || !plugin->audio_unit) {
delete plugin;
return nullptr;
}
return plugin;
}
void rack_au_plugin_free(RackAUPlugin* plugin) {
if (!plugin) {
return;
}
if (plugin->audio_unit) {
std::lock_guard<std::mutex> lock(g_audio_unit_cleanup_mutex);
AudioUnitUninitialize(plugin->audio_unit);
AudioComponentInstanceDispose(plugin->audio_unit);
}
if (plugin->input_buffer_list) {
free(plugin->input_buffer_list);
}
if (plugin->output_buffer_list) {
free(plugin->output_buffer_list);
}
if (plugin->parameter_ids) {
free(plugin->parameter_ids);
}
if (plugin->parameter_info) {
free(plugin->parameter_info);
}
delete plugin;
}
int rack_au_plugin_initialize(RackAUPlugin* plugin, double sample_rate, uint32_t max_block_size) {
if (!plugin) {
return RACK_AU_ERROR_INVALID_PARAM;
}
if (!plugin->audio_unit) {
return RACK_AU_ERROR_NOT_INITIALIZED;
}
if (plugin->initialized) {
return RACK_AU_OK; }
plugin->sample_rate = sample_rate;
plugin->max_block_size = max_block_size;
uint32_t channels = 2;
AudioStreamBasicDescription format;
memset(&format, 0, sizeof(format));
format.mSampleRate = sample_rate;
format.mFormatID = kAudioFormatLinearPCM;
format.mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked | kAudioFormatFlagIsNonInterleaved;
format.mBitsPerChannel = 32;
format.mChannelsPerFrame = channels;
format.mFramesPerPacket = 1;
format.mBytesPerFrame = sizeof(float); format.mBytesPerPacket = format.mBytesPerFrame * format.mFramesPerPacket;
OSStatus status_input = AudioUnitSetProperty(
plugin->audio_unit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input,
0, &format,
sizeof(format)
);
OSStatus status_output = AudioUnitSetProperty(
plugin->audio_unit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Output,
0, &format,
sizeof(format)
);
if (status_input != noErr && status_output != noErr) {
}
UInt32 max_frames = max_block_size;
OSStatus status = AudioUnitSetProperty(
plugin->audio_unit,
kAudioUnitProperty_MaximumFramesPerSlice,
kAudioUnitScope_Global,
0,
&max_frames,
sizeof(max_frames)
);
if (status != noErr) {
}
AudioStreamBasicDescription actual_input_format;
UInt32 size_of_format = sizeof(actual_input_format);
OSStatus query_status = AudioUnitGetProperty(
plugin->audio_unit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input,
0,
&actual_input_format,
&size_of_format
);
uint32_t input_channels = (query_status == noErr) ? actual_input_format.mChannelsPerFrame : channels;
AudioStreamBasicDescription actual_output_format;
query_status = AudioUnitGetProperty(
plugin->audio_unit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Output,
0,
&actual_output_format,
&size_of_format
);
uint32_t output_channels = (query_status == noErr) ? actual_output_format.mChannelsPerFrame : channels;
plugin->input_channels = input_channels;
plugin->output_channels = output_channels;
size_t buffer_list_size = offsetof(AudioBufferList, mBuffers[0]) + (sizeof(AudioBuffer) * input_channels);
plugin->input_buffer_list = static_cast<AudioBufferList*>(malloc(buffer_list_size));
if (!plugin->input_buffer_list) {
return RACK_AU_ERROR_GENERIC; }
plugin->input_buffer_list->mNumberBuffers = input_channels;
for (UInt32 i = 0; i < input_channels; i++) {
plugin->input_buffer_list->mBuffers[i].mNumberChannels = 1;
plugin->input_buffer_list->mBuffers[i].mDataByteSize = 0; plugin->input_buffer_list->mBuffers[i].mData = nullptr; }
size_t output_buffer_list_size = offsetof(AudioBufferList, mBuffers[0]) + (sizeof(AudioBuffer) * output_channels);
plugin->output_buffer_list = static_cast<AudioBufferList*>(malloc(output_buffer_list_size));
if (!plugin->output_buffer_list) {
free(plugin->input_buffer_list);
plugin->input_buffer_list = nullptr;
return RACK_AU_ERROR_GENERIC; }
plugin->output_buffer_list->mNumberBuffers = output_channels;
for (UInt32 i = 0; i < output_channels; i++) {
plugin->output_buffer_list->mBuffers[i].mNumberChannels = 1;
plugin->output_buffer_list->mBuffers[i].mDataByteSize = 0; plugin->output_buffer_list->mBuffers[i].mData = nullptr; }
AURenderCallbackStruct callback;
callback.inputProc = input_render_callback;
callback.inputProcRefCon = plugin;
status = AudioUnitSetProperty(
plugin->audio_unit,
kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Input,
0,
&callback,
sizeof(callback)
);
{
std::lock_guard<std::mutex> lock(g_audio_unit_cleanup_mutex);
status = AudioUnitInitialize(plugin->audio_unit);
}
if (status != noErr) {
if (plugin->input_buffer_list) {
free(plugin->input_buffer_list);
plugin->input_buffer_list = nullptr;
}
if (plugin->output_buffer_list) {
free(plugin->output_buffer_list);
plugin->output_buffer_list = nullptr;
}
return RACK_AU_ERROR_AUDIO_UNIT + status;
}
UInt32 data_size = 0;
status = AudioUnitGetPropertyInfo(
plugin->audio_unit,
kAudioUnitProperty_ParameterList,
kAudioUnitScope_Global,
0,
&data_size,
nullptr
);
if (status == noErr && data_size > 0) {
plugin->parameter_count = data_size / sizeof(AudioUnitParameterID);
plugin->parameter_ids = static_cast<AudioUnitParameterID*>(malloc(data_size));
if (plugin->parameter_ids) {
status = AudioUnitGetProperty(
plugin->audio_unit,
kAudioUnitProperty_ParameterList,
kAudioUnitScope_Global,
0,
plugin->parameter_ids,
&data_size
);
if (status != noErr) {
free(plugin->parameter_ids);
plugin->parameter_ids = nullptr;
plugin->parameter_count = 0;
} else {
plugin->parameter_info = static_cast<AudioUnitParameterInfo*>(
malloc(plugin->parameter_count * sizeof(AudioUnitParameterInfo))
);
if (plugin->parameter_info) {
for (UInt32 i = 0; i < plugin->parameter_count; i++) {
UInt32 info_size = sizeof(AudioUnitParameterInfo);
OSStatus info_status = AudioUnitGetProperty(
plugin->audio_unit,
kAudioUnitProperty_ParameterInfo,
kAudioUnitScope_Global,
plugin->parameter_ids[i],
&plugin->parameter_info[i],
&info_size
);
if (info_status != noErr) {
free(plugin->parameter_info);
plugin->parameter_info = nullptr;
break;
}
}
}
}
} else {
plugin->parameter_count = 0;
}
}
plugin->initialized = true;
return RACK_AU_OK;
}
int rack_au_plugin_is_initialized(RackAUPlugin* plugin) {
return plugin && plugin->initialized ? 1 : 0;
}
int rack_au_plugin_reset(RackAUPlugin* plugin) {
if (!plugin || !plugin->initialized) {
return RACK_AU_ERROR_NOT_INITIALIZED;
}
OSStatus status = AudioUnitReset(plugin->audio_unit, kAudioUnitScope_Global, 0);
if (status != noErr) {
return RACK_AU_ERROR_AUDIO_UNIT + status;
}
return RACK_AU_OK;
}
int rack_au_plugin_process(
RackAUPlugin* plugin,
const float* const* inputs,
uint32_t num_input_channels,
float* const* outputs,
uint32_t num_output_channels,
uint32_t frames
) {
if (!plugin || !plugin->initialized) {
return RACK_AU_ERROR_NOT_INITIALIZED;
}
if (!inputs || !outputs || frames == 0) {
return RACK_AU_ERROR_INVALID_PARAM;
}
if (frames > plugin->max_block_size) {
return RACK_AU_ERROR_INVALID_PARAM;
}
const uint32_t byte_size = frames * sizeof(float);
for (uint32_t ch = 0; ch < num_input_channels; ch++) {
plugin->input_buffer_list->mBuffers[ch].mData = const_cast<float*>(inputs[ch]);
plugin->input_buffer_list->mBuffers[ch].mDataByteSize = byte_size;
}
for (uint32_t ch = 0; ch < num_output_channels; ch++) {
plugin->output_buffer_list->mBuffers[ch].mData = outputs[ch];
plugin->output_buffer_list->mBuffers[ch].mDataByteSize = byte_size;
}
AudioTimeStamp timestamp;
memset(×tamp, 0, sizeof(timestamp));
timestamp.mFlags = kAudioTimeStampSampleTimeValid;
timestamp.mSampleTime = plugin->sample_position;
AudioUnitRenderActionFlags flags = 0;
OSStatus status = AudioUnitRender(
plugin->audio_unit,
&flags,
×tamp,
0, frames,
plugin->output_buffer_list
);
if (status != noErr) {
return RACK_AU_ERROR_AUDIO_UNIT + status;
}
plugin->sample_position += frames;
return RACK_AU_OK;
}
int rack_au_plugin_parameter_count(RackAUPlugin* plugin) {
if (!plugin || !plugin->initialized) {
return 0;
}
return static_cast<int>(plugin->parameter_count);
}
int rack_au_plugin_get_parameter(RackAUPlugin* plugin, uint32_t index, float* value) {
if (!plugin || !plugin->initialized) {
return RACK_AU_ERROR_NOT_INITIALIZED;
}
if (!value) {
return RACK_AU_ERROR_INVALID_PARAM;
}
if (index >= plugin->parameter_count) {
return RACK_AU_ERROR_INVALID_PARAM;
}
AudioUnitParameterID param_id = plugin->parameter_ids[index];
AudioUnitParameterInfo param_info;
if (plugin->parameter_info) {
param_info = plugin->parameter_info[index];
} else {
UInt32 data_size = sizeof(param_info);
OSStatus status = AudioUnitGetProperty(
plugin->audio_unit,
kAudioUnitProperty_ParameterInfo,
kAudioUnitScope_Global,
param_id,
¶m_info,
&data_size
);
if (status != noErr) {
return RACK_AU_ERROR_AUDIO_UNIT + status;
}
}
AudioUnitParameterValue raw_value;
OSStatus status = AudioUnitGetParameter(
plugin->audio_unit,
param_id,
kAudioUnitScope_Global,
0,
&raw_value
);
if (status != noErr) {
return RACK_AU_ERROR_AUDIO_UNIT + status;
}
float min_val = param_info.minValue;
float max_val = param_info.maxValue;
float range = max_val - min_val;
if (max_val < min_val) {
*value = 0.5f;
return RACK_AU_OK;
}
const float epsilon = 1e-7f;
if (range > epsilon) {
*value = (raw_value - min_val) / range;
if (*value < 0.0f) *value = 0.0f;
if (*value > 1.0f) *value = 1.0f;
} else {
*value = 0.0f;
}
return RACK_AU_OK;
}
int rack_au_plugin_set_parameter(RackAUPlugin* plugin, uint32_t index, float value) {
if (!plugin || !plugin->initialized) {
return RACK_AU_ERROR_NOT_INITIALIZED;
}
if (index >= plugin->parameter_count) {
return RACK_AU_ERROR_INVALID_PARAM;
}
if (value < 0.0f) value = 0.0f;
if (value > 1.0f) value = 1.0f;
AudioUnitParameterID param_id = plugin->parameter_ids[index];
AudioUnitParameterInfo param_info;
if (plugin->parameter_info) {
param_info = plugin->parameter_info[index];
} else {
UInt32 data_size = sizeof(param_info);
OSStatus status = AudioUnitGetProperty(
plugin->audio_unit,
kAudioUnitProperty_ParameterInfo,
kAudioUnitScope_Global,
param_id,
¶m_info,
&data_size
);
if (status != noErr) {
return RACK_AU_ERROR_AUDIO_UNIT + status;
}
}
float min_val = param_info.minValue;
float max_val = param_info.maxValue;
float raw_value = min_val + (value * (max_val - min_val));
OSStatus status = AudioUnitSetParameter(
plugin->audio_unit,
param_id,
kAudioUnitScope_Global,
0,
raw_value,
0 );
if (status != noErr) {
return RACK_AU_ERROR_AUDIO_UNIT + status;
}
return RACK_AU_OK;
}
int rack_au_plugin_parameter_info(
RackAUPlugin* plugin,
uint32_t index,
char* name,
size_t name_size,
float* min,
float* max,
float* default_value,
char* unit,
size_t unit_size
) {
if (!plugin || !plugin->initialized) {
return RACK_AU_ERROR_NOT_INITIALIZED;
}
if (!name || !min || !max || !default_value) {
return RACK_AU_ERROR_INVALID_PARAM;
}
if (index >= plugin->parameter_count) {
return RACK_AU_ERROR_INVALID_PARAM;
}
AudioUnitParameterID param_id = plugin->parameter_ids[index];
AudioUnitParameterInfo param_info;
if (plugin->parameter_info) {
param_info = plugin->parameter_info[index];
} else {
UInt32 data_size = sizeof(param_info);
OSStatus status = AudioUnitGetProperty(
plugin->audio_unit,
kAudioUnitProperty_ParameterInfo,
kAudioUnitScope_Global,
param_id,
¶m_info,
&data_size
);
if (status != noErr) {
return RACK_AU_ERROR_AUDIO_UNIT + status;
}
}
if (param_info.cfNameString) {
CFStringGetCString(
param_info.cfNameString,
name,
name_size,
kCFStringEncodingUTF8
);
} else {
snprintf(name, name_size, "Parameter %u", param_id);
}
if (unit && unit_size > 0) {
const char* unit_str = parameter_unit_to_string(param_info.unit);
strncpy(unit, unit_str, unit_size - 1);
unit[unit_size - 1] = '\0'; }
*min = param_info.minValue;
*max = param_info.maxValue;
*default_value = param_info.defaultValue;
return RACK_AU_OK;
}
int rack_au_plugin_get_preset_count(RackAUPlugin* plugin) {
if (!plugin || !plugin->initialized) {
return 0;
}
CFArrayRef presets = nullptr;
UInt32 data_size = sizeof(presets);
OSStatus status = AudioUnitGetProperty(
plugin->audio_unit,
kAudioUnitProperty_FactoryPresets,
kAudioUnitScope_Global,
0,
&presets,
&data_size
);
if (status != noErr || !presets) {
return 0; }
CFIndex count = CFArrayGetCount(presets);
return static_cast<int>(count);
}
int rack_au_plugin_get_preset_info(
RackAUPlugin* plugin,
uint32_t index,
char* name,
size_t name_size,
int32_t* preset_number
) {
if (!plugin || !plugin->initialized) {
return RACK_AU_ERROR_NOT_INITIALIZED;
}
if (!name || !preset_number) {
return RACK_AU_ERROR_INVALID_PARAM;
}
CFArrayRef presets = nullptr;
UInt32 data_size = sizeof(presets);
OSStatus status = AudioUnitGetProperty(
plugin->audio_unit,
kAudioUnitProperty_FactoryPresets,
kAudioUnitScope_Global,
0,
&presets,
&data_size
);
if (status != noErr || !presets) {
return RACK_AU_ERROR_NOT_FOUND;
}
CFIndex count = CFArrayGetCount(presets);
if (index >= static_cast<uint32_t>(count)) {
return RACK_AU_ERROR_INVALID_PARAM;
}
const AUPreset* preset = static_cast<const AUPreset*>(
CFArrayGetValueAtIndex(presets, index)
);
if (!preset) {
return RACK_AU_ERROR_GENERIC;
}
*preset_number = preset->presetNumber;
if (preset->presetName) {
Boolean success = CFStringGetCString(
preset->presetName,
name,
name_size,
kCFStringEncodingUTF8
);
if (!success) {
snprintf(name, name_size, "Preset %d", preset->presetNumber);
}
} else {
snprintf(name, name_size, "Preset %d", preset->presetNumber);
}
return RACK_AU_OK;
}
int rack_au_plugin_load_preset(RackAUPlugin* plugin, int32_t preset_number) {
if (!plugin || !plugin->initialized) {
return RACK_AU_ERROR_NOT_INITIALIZED;
}
AUPreset preset;
preset.presetNumber = preset_number;
preset.presetName = nullptr;
OSStatus status = AudioUnitSetProperty(
plugin->audio_unit,
kAudioUnitProperty_PresentPreset,
kAudioUnitScope_Global,
0,
&preset,
sizeof(preset)
);
if (status != noErr) {
return RACK_AU_ERROR_AUDIO_UNIT + status;
}
return RACK_AU_OK;
}
int rack_au_plugin_get_state_size(RackAUPlugin* plugin) {
if (!plugin || !plugin->initialized) {
return 0;
}
CFPropertyListRef class_info = nullptr;
UInt32 data_size = sizeof(class_info);
OSStatus status = AudioUnitGetProperty(
plugin->audio_unit,
kAudioUnitProperty_ClassInfo,
kAudioUnitScope_Global,
0,
&class_info,
&data_size
);
if (status != noErr || !class_info) {
return 0;
}
CFDataRef data = CFPropertyListCreateData(
kCFAllocatorDefault,
class_info,
kCFPropertyListBinaryFormat_v1_0,
0, nullptr );
CFRelease(class_info);
if (!data) {
return 0;
}
CFIndex size = CFDataGetLength(data);
CFRelease(data);
if (size > INT_MAX || size < 0) {
return 0; }
return static_cast<int>(size);
}
int rack_au_plugin_get_state(RackAUPlugin* plugin, uint8_t* data, size_t* size) {
if (!plugin || !plugin->initialized) {
return RACK_AU_ERROR_NOT_INITIALIZED;
}
if (!data || !size) {
return RACK_AU_ERROR_INVALID_PARAM;
}
CFPropertyListRef class_info = nullptr;
UInt32 data_size = sizeof(class_info);
OSStatus status = AudioUnitGetProperty(
plugin->audio_unit,
kAudioUnitProperty_ClassInfo,
kAudioUnitScope_Global,
0,
&class_info,
&data_size
);
if (status != noErr || !class_info) {
return RACK_AU_ERROR_AUDIO_UNIT + status;
}
CFDataRef cf_data = CFPropertyListCreateData(
kCFAllocatorDefault,
class_info,
kCFPropertyListBinaryFormat_v1_0,
0, nullptr );
CFRelease(class_info);
if (!cf_data) {
return RACK_AU_ERROR_GENERIC;
}
CFIndex cf_size = CFDataGetLength(cf_data);
if (static_cast<size_t>(cf_size) > *size) {
CFRelease(cf_data);
return RACK_AU_ERROR_GENERIC; }
const UInt8* bytes = CFDataGetBytePtr(cf_data);
memcpy(data, bytes, cf_size);
*size = cf_size;
CFRelease(cf_data);
return RACK_AU_OK;
}
int rack_au_plugin_set_state(RackAUPlugin* plugin, const uint8_t* data, size_t size) {
if (!plugin || !plugin->initialized) {
return RACK_AU_ERROR_NOT_INITIALIZED;
}
if (!data || size == 0) {
return RACK_AU_ERROR_INVALID_PARAM;
}
CFDataRef cf_data = CFDataCreate(
kCFAllocatorDefault,
data,
size
);
if (!cf_data) {
return RACK_AU_ERROR_GENERIC;
}
CFPropertyListRef class_info = CFPropertyListCreateWithData(
kCFAllocatorDefault,
cf_data,
kCFPropertyListImmutable,
nullptr, nullptr );
CFRelease(cf_data);
if (!class_info) {
return RACK_AU_ERROR_GENERIC; }
OSStatus status = AudioUnitSetProperty(
plugin->audio_unit,
kAudioUnitProperty_ClassInfo,
kAudioUnitScope_Global,
0,
&class_info,
sizeof(class_info)
);
CFRelease(class_info);
if (status != noErr) {
return RACK_AU_ERROR_AUDIO_UNIT + status;
}
return RACK_AU_OK;
}
int rack_au_plugin_send_midi(
RackAUPlugin* plugin,
const RackAUMidiEvent* events,
uint32_t event_count
) {
if (!plugin || !plugin->initialized) {
return RACK_AU_ERROR_NOT_INITIALIZED;
}
if (!events && event_count > 0) {
return RACK_AU_ERROR_INVALID_PARAM;
}
if (event_count == 0) {
return RACK_AU_OK;
}
for (uint32_t i = 0; i < event_count; i++) {
const RackAUMidiEvent* event = &events[i];
uint8_t status;
if (event->status >= 0xF0) {
status = event->status;
} else {
if (event->channel > 15) {
return RACK_AU_ERROR_INVALID_PARAM;
}
status = (event->status & 0xF0) | (event->channel & 0x0F);
}
OSStatus result = MusicDeviceMIDIEvent(
plugin->audio_unit,
status,
event->data1,
event->data2,
event->sample_offset );
if (result != noErr) {
return RACK_AU_ERROR_AUDIO_UNIT + result;
}
}
return RACK_AU_OK;
}
int rack_au_plugin_get_input_channels(RackAUPlugin* plugin) {
if (!plugin || !plugin->initialized) {
return 0;
}
return static_cast<int>(plugin->input_channels);
}
int rack_au_plugin_get_output_channels(RackAUPlugin* plugin) {
if (!plugin || !plugin->initialized) {
return 0;
}
return static_cast<int>(plugin->output_channels);
}
extern "C" AudioComponentInstance rack_au_plugin_get_audio_unit(RackAUPlugin* plugin) {
if (!plugin) {
return NULL;
}
return plugin->audio_unit;
}