/* * DISTRHO Plugin Framework (DPF) * Copyright (C) 2012-2024 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this * permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ // TODO // - g_nextBundlePath vs d_nextBundlePath cleanup // - scale points to kAudioUnitParameterFlag_ValuesHaveStrings // - report latency changes #include "DistrhoPluginInternal.hpp" #include "../DistrhoPluginUtils.hpp" #if DISTRHO_PLUGIN_WANT_MIDI_INPUT # include "../extra/RingBuffer.hpp" #endif #include #include #include #include #ifndef DISTRHO_PLUGIN_BRAND_ID # error DISTRHO_PLUGIN_BRAND_ID undefined! #endif #ifndef DISTRHO_PLUGIN_UNIQUE_ID # error DISTRHO_PLUGIN_UNIQUE_ID undefined! #endif START_NAMESPACE_DISTRHO // -------------------------------------------------------------------------------------------------------------------- #ifndef __MAC_12_3 enum { kAudioUnitProperty_MIDIOutputBufferSizeHint = 66, }; #endif // -------------------------------------------------------------------------------------------------------------------- static const char* AudioUnitPropertyID2Str(const AudioUnitPropertyID prop) noexcept { switch (prop) { #define PROP(s) case s: return #s; PROP(kAudioUnitProperty_ClassInfo) PROP(kAudioUnitProperty_MakeConnection) PROP(kAudioUnitProperty_SampleRate) PROP(kAudioUnitProperty_ParameterList) PROP(kAudioUnitProperty_ParameterInfo) #if !TARGET_OS_IPHONE PROP(kAudioUnitProperty_FastDispatch) #endif PROP(kAudioUnitProperty_CPULoad) PROP(kAudioUnitProperty_StreamFormat) PROP(kAudioUnitProperty_ElementCount) PROP(kAudioUnitProperty_Latency) PROP(kAudioUnitProperty_SupportedNumChannels) PROP(kAudioUnitProperty_MaximumFramesPerSlice) PROP(kAudioUnitProperty_ParameterValueStrings) PROP(kAudioUnitProperty_AudioChannelLayout) PROP(kAudioUnitProperty_TailTime) PROP(kAudioUnitProperty_BypassEffect) PROP(kAudioUnitProperty_LastRenderError) PROP(kAudioUnitProperty_SetRenderCallback) PROP(kAudioUnitProperty_FactoryPresets) PROP(kAudioUnitProperty_RenderQuality) PROP(kAudioUnitProperty_HostCallbacks) PROP(kAudioUnitProperty_InPlaceProcessing) PROP(kAudioUnitProperty_ElementName) PROP(kAudioUnitProperty_SupportedChannelLayoutTags) PROP(kAudioUnitProperty_PresentPreset) PROP(kAudioUnitProperty_DependentParameters) PROP(kAudioUnitProperty_InputSamplesInOutput) PROP(kAudioUnitProperty_ShouldAllocateBuffer) PROP(kAudioUnitProperty_FrequencyResponse) PROP(kAudioUnitProperty_ParameterHistoryInfo) PROP(kAudioUnitProperty_NickName) PROP(kAudioUnitProperty_OfflineRender) PROP(kAudioUnitProperty_ParameterIDName) PROP(kAudioUnitProperty_ParameterStringFromValue) PROP(kAudioUnitProperty_ParameterClumpName) PROP(kAudioUnitProperty_ParameterValueFromString) PROP(kAudioUnitProperty_PresentationLatency) PROP(kAudioUnitProperty_ClassInfoFromDocument) PROP(kAudioUnitProperty_RequestViewController) PROP(kAudioUnitProperty_ParametersForOverview) PROP(kAudioUnitProperty_SupportsMPE) PROP(kAudioUnitProperty_RenderContextObserver) PROP(kAudioUnitProperty_LastRenderSampleTime) PROP(kAudioUnitProperty_LoadedOutOfProcess) #if !TARGET_OS_IPHONE PROP(kAudioUnitProperty_SetExternalBuffer) PROP(kAudioUnitProperty_GetUIComponentList) PROP(kAudioUnitProperty_CocoaUI) PROP(kAudioUnitProperty_IconLocation) PROP(kAudioUnitProperty_AUHostIdentifier) #endif PROP(kAudioUnitProperty_MIDIOutputCallbackInfo) PROP(kAudioUnitProperty_MIDIOutputCallback) PROP(kAudioUnitProperty_MIDIOutputEventListCallback) PROP(kAudioUnitProperty_AudioUnitMIDIProtocol) PROP(kAudioUnitProperty_HostMIDIProtocol) PROP(kAudioUnitProperty_MIDIOutputBufferSizeHint) PROP(kMusicDeviceProperty_DualSchedulingMode) #undef PROP // DPF specific properties #define PROPX(s) (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | (s[3] << 0) #define PROP(s) case PROPX(#s): return #s; PROP(DPFi) PROP(DPFe) PROP(DPFp) PROP(DPFn) PROP(DPFo) PROP(DPFl) PROP(DPFs) PROP(DPFa) #undef PROP #undef PROPX } return "[unknown]"; } static const char* AudioUnitScope2Str(const AudioUnitScope scope) noexcept { switch (scope) { #define SCOPE(s) case s: return #s; SCOPE(kAudioUnitScope_Global) SCOPE(kAudioUnitScope_Input) SCOPE(kAudioUnitScope_Output) SCOPE(kAudioUnitScope_Group) SCOPE(kAudioUnitScope_Part) SCOPE(kAudioUnitScope_Note) SCOPE(kAudioUnitScope_Layer) SCOPE(kAudioUnitScope_LayerItem) #undef SCOPE } return "[unknown]"; } static const char* AudioUnitSelector2Str(const SInt16 selector) noexcept { switch (selector) { #define SEL(s) case s: return #s; SEL(kAudioUnitInitializeSelect) SEL(kAudioUnitUninitializeSelect) SEL(kAudioUnitGetPropertyInfoSelect) SEL(kAudioUnitGetPropertySelect) SEL(kAudioUnitSetPropertySelect) SEL(kAudioUnitAddPropertyListenerSelect) SEL(kAudioUnitRemovePropertyListenerSelect) SEL(kAudioUnitRemovePropertyListenerWithUserDataSelect) SEL(kAudioUnitAddRenderNotifySelect) SEL(kAudioUnitRemoveRenderNotifySelect) SEL(kAudioUnitGetParameterSelect) SEL(kAudioUnitSetParameterSelect) SEL(kAudioUnitScheduleParametersSelect) SEL(kAudioUnitRenderSelect) SEL(kAudioUnitResetSelect) SEL(kAudioUnitComplexRenderSelect) SEL(kAudioUnitProcessSelect) SEL(kAudioUnitProcessMultipleSelect) SEL(kMusicDeviceMIDIEventSelect) SEL(kMusicDeviceSysExSelect) SEL(kMusicDevicePrepareInstrumentSelect) SEL(kMusicDeviceReleaseInstrumentSelect) SEL(kMusicDeviceStartNoteSelect) SEL(kMusicDeviceStopNoteSelect) SEL(kMusicDeviceMIDIEventListSelect) SEL(kAudioOutputUnitStartSelect) SEL(kAudioOutputUnitStopSelect) #undef SEL } return "[unknown]"; } #if 0 static OSStatus FastDispatchGetParameter(void*, AudioUnitParameterID, AudioUnitScope, AudioUnitElement, Float32*); static OSStatus FastDispatchSetParameter(void*, AudioUnitParameterID, AudioUnitScope, AudioUnitElement, Float32, UInt32); static OSStatus FastDispatchRender(void*, AudioUnitRenderActionFlags*, const AudioTimeStamp*, UInt32, UInt32, AudioBufferList*); #endif // -------------------------------------------------------------------------------------------------------------------- static constexpr const uint32_t kType = d_cconst(STRINGIFY(DISTRHO_PLUGIN_AU_TYPE)); static constexpr const uint32_t kSubType = d_cconst(STRINGIFY(DISTRHO_PLUGIN_UNIQUE_ID)); static constexpr const uint32_t kManufacturer = d_cconst(STRINGIFY(DISTRHO_PLUGIN_BRAND_ID)); static constexpr const uint32_t kWantedAudioFormat = kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved; // -------------------------------------------------------------------------------------------------------------------- // clang `std::max` is not constexpr compatible, we need to define our own template static inline constexpr T d_max(const T a, const T b) { return a > b ? a : b; } // -------------------------------------------------------------------------------------------------------------------- static constexpr const AUChannelInfo kChannelInfo[] = { { DISTRHO_PLUGIN_NUM_INPUTS, DISTRHO_PLUGIN_NUM_OUTPUTS }, #ifdef DISTRHO_PLUGIN_EXTRA_IO DISTRHO_PLUGIN_EXTRA_IO #endif }; #ifdef DISTRHO_PLUGIN_EXTRA_IO #if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS == 0 #error DISTRHO_PLUGIN_EXTRA_IO defined but no IO available #endif static inline bool isInputNumChannelsValid(const uint16_t numChannels) { for (uint16_t i = 0; i < ARRAY_SIZE(kChannelInfo); ++i) { if (kChannelInfo[i].inChannels == numChannels) return true; } return false; } static inline bool isOutputNumChannelsValid(const uint16_t numChannels) { for (uint16_t i = 0; i < ARRAY_SIZE(kChannelInfo); ++i) { if (kChannelInfo[i].outChannels == numChannels) return true; } return false; } static inline bool isNumChannelsComboValid(const uint16_t numInputs, const uint16_t numOutputs) { for (uint16_t i = 0; i < ARRAY_SIZE(kChannelInfo); ++i) { if (kChannelInfo[i].inChannels == numInputs && kChannelInfo[i].outChannels == numOutputs) return true; } return false; } #endif // -------------------------------------------------------------------------------------------------------------------- struct PropertyListener { AudioUnitPropertyID prop; AudioUnitPropertyListenerProc proc; void* userData; }; struct RenderListener { AURenderCallback proc; void* userData; }; typedef std::vector PropertyListeners; typedef std::vector RenderListeners; #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT // useful definitions static constexpr const uint32_t kMIDIPacketNonDataSize = sizeof(MIDIPacket) - sizeof(MIDIPacket::data); static constexpr const uint32_t kMIDIPacketListNonDataSize = sizeof(MIDIPacketList) - sizeof(MIDIPacketList::packet); // size of data used for midi events static constexpr const uint32_t kMIDIPacketListMaxDataSize = kMIDIPacketNonDataSize * kMaxMidiEvents + sizeof(Byte) * MidiEvent::kDataSize * kMaxMidiEvents; // size of midi list + data static constexpr const uint32_t kMIDIPacketListSize = kMIDIPacketListNonDataSize + kMIDIPacketListMaxDataSize; #endif // -------------------------------------------------------------------------------------------------------------------- #if ! DISTRHO_PLUGIN_WANT_MIDI_OUTPUT static constexpr const writeMidiFunc writeMidiCallback = nullptr; #endif #if ! DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST static constexpr const requestParameterValueChangeFunc requestParameterValueChangeCallback = nullptr; #endif #if ! DISTRHO_PLUGIN_WANT_STATE static constexpr const updateStateValueFunc updateStateValueCallback = nullptr; #endif #if DISTRHO_PLUGIN_WANT_TIMEPOS static constexpr const double kDefaultTicksPerBeat = 1920.0; #endif typedef std::map StringMap; // -------------------------------------------------------------------------------------------------------------------- class PluginAU { public: PluginAU(const AudioUnit component) : fPlugin(this, writeMidiCallback, requestParameterValueChangeCallback, updateStateValueCallback), fComponent(component), fLastRenderError(noErr), fPropertyListeners(), fRenderListeners(), #if DISTRHO_PLUGIN_NUM_INPUTS != 0 fInputConnectionBus(0), fInputConnectionUnit(nullptr), fSampleRateForInput(d_nextSampleRate), #ifdef DISTRHO_PLUGIN_EXTRA_IO fNumInputs(DISTRHO_PLUGIN_NUM_INPUTS), #endif #endif #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0 fSampleRateForOutput(d_nextSampleRate), #ifdef DISTRHO_PLUGIN_EXTRA_IO fNumOutputs(DISTRHO_PLUGIN_NUM_OUTPUTS), #endif #endif #if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS != 0 fAudioBufferList(nullptr), #endif fUsingRenderListeners(false), fParameterCount(fPlugin.getParameterCount()), fLastParameterValues(nullptr), fBypassParameterIndex(UINT32_MAX) #if DISTRHO_PLUGIN_WANT_MIDI_INPUT , fMidiEventCount(0) #endif #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT , fMidiOutputDataOffset(0) #endif #if DISTRHO_PLUGIN_WANT_PROGRAMS , fCurrentProgram(-1) , fLastFactoryProgram(0) , fProgramCount(fPlugin.getProgramCount()) , fFactoryPresetsData(nullptr) #endif #if DISTRHO_PLUGIN_WANT_STATE , fStateCount(fPlugin.getStateCount()) #endif { if (fParameterCount != 0) { fLastParameterValues = new float[fParameterCount]; std::memset(fLastParameterValues, 0, sizeof(float) * fParameterCount); for (uint32_t i=0; i(std::malloc(kMIDIPacketListSize))) != nullptr) std::memset(fMidiOutputPackets, 0, kMIDIPacketListSize); std::memset(&fMidiOutput, 0, sizeof(fMidiOutput)); #endif #if DISTRHO_PLUGIN_WANT_PROGRAMS if (fProgramCount != 0) { fFactoryPresetsData = new AUPreset[fProgramCount]; std::memset(fFactoryPresetsData, 0, sizeof(AUPreset) * fProgramCount); for (uint32_t i=0; ipresetNumber = 0; fFactoryPresetsData->presetName = CFSTR("Default"); } #endif fUserPresetData.presetNumber = -1; fUserPresetData.presetName = CFSTR(""); #if DISTRHO_PLUGIN_WANT_STATE for (uint32_t i=0; inumPackets = 0; #endif #if DISTRHO_PLUGIN_WANT_TIMEPOS fTimePosition.clear(); fTimePosition.bbt.ticksPerBeat = kDefaultTicksPerBeat; #endif fPlugin.activate(); return noErr; } OSStatus auUninitialize() { fPlugin.deactivateIfNeeded(); #if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS != 0 reallocAudioBufferList(false); #endif return noErr; } OSStatus auGetPropertyInfo(const AudioUnitPropertyID inProp, const AudioUnitScope inScope, const AudioUnitElement inElement, UInt32& outDataSize, Boolean& outWritable) { switch (inProp) { case kAudioUnitProperty_ClassInfo: DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); outDataSize = sizeof(CFPropertyListRef); outWritable = true; return noErr; case kAudioUnitProperty_MakeConnection: DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Input, inScope, kAudioUnitErr_InvalidScope); DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); #if DISTRHO_PLUGIN_NUM_INPUTS != 0 outDataSize = sizeof(AudioUnitConnection); outWritable = true; return noErr; #else return kAudioUnitErr_InvalidProperty; #endif case kAudioUnitProperty_SampleRate: DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); #if DISTRHO_PLUGIN_NUM_INPUTS != 0 if (inScope == kAudioUnitScope_Input) { outDataSize = sizeof(Float64); outWritable = true; return noErr; } #endif #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0 if (inScope == kAudioUnitScope_Output) { outDataSize = sizeof(Float64); outWritable = true; return noErr; } #endif return kAudioUnitErr_InvalidScope; case kAudioUnitProperty_ParameterList: DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); outDataSize = inScope == kAudioUnitScope_Global ? sizeof(AudioUnitParameterID) * fParameterCount : 0; outWritable = false; return noErr; case kAudioUnitProperty_ParameterInfo: DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement < fParameterCount, inElement, kAudioUnitErr_InvalidElement); outDataSize = sizeof(AudioUnitParameterInfo); outWritable = false; return noErr; #if 0 case kAudioUnitProperty_FastDispatch: DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); outDataSize = sizeof(void*); outWritable = false; return noErr; #endif case kAudioUnitProperty_StreamFormat: DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); #if DISTRHO_PLUGIN_NUM_INPUTS != 0 if (inScope == kAudioUnitScope_Input) { outDataSize = sizeof(AudioStreamBasicDescription); outWritable = true; return noErr; } #endif #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0 if (inScope == kAudioUnitScope_Output) { outDataSize = sizeof(AudioStreamBasicDescription); outWritable = true; return noErr; } #endif return kAudioUnitErr_InvalidScope; case kAudioUnitProperty_ElementCount: DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); outDataSize = sizeof(UInt32); outWritable = false; return noErr; case kAudioUnitProperty_Latency: DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); #if DISTRHO_PLUGIN_WANT_LATENCY if (inScope == kAudioUnitScope_Global) { outDataSize = sizeof(Float64); outWritable = false; return noErr; } #endif return kAudioUnitErr_InvalidProperty; case kAudioUnitProperty_SupportedNumChannels: DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); outDataSize = sizeof(kChannelInfo); outWritable = false; return noErr; case kAudioUnitProperty_MaximumFramesPerSlice: DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); outDataSize = sizeof(UInt32); outWritable = true; return noErr; case kAudioUnitProperty_BypassEffect: DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); if (inScope == kAudioUnitScope_Global && fBypassParameterIndex != UINT32_MAX) { outDataSize = sizeof(UInt32); outWritable = true; return noErr; } return kAudioUnitErr_InvalidProperty; case kAudioUnitProperty_LastRenderError: DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); outDataSize = sizeof(OSStatus); outWritable = false; return noErr; case kAudioUnitProperty_SetRenderCallback: DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Input, inScope, kAudioUnitErr_InvalidScope); DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); #if DISTRHO_PLUGIN_NUM_INPUTS != 0 outDataSize = sizeof(AURenderCallbackStruct); outWritable = true; return noErr; #else return kAudioUnitErr_InvalidProperty; #endif case kAudioUnitProperty_FactoryPresets: DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); #if DISTRHO_PLUGIN_WANT_PROGRAMS outDataSize = sizeof(CFArrayRef); outWritable = false; return noErr; #else return kAudioUnitErr_InvalidProperty; #endif case kAudioUnitProperty_HostCallbacks: DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); #if DISTRHO_PLUGIN_WANT_TIMEPOS outDataSize = sizeof(HostCallbackInfo); outWritable = false; return noErr; #else return kAudioUnitErr_InvalidProperty; #endif case kAudioUnitProperty_InPlaceProcessing: DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); #if DISTRHO_PLUGIN_NUM_INPUTS != 0 && DISTRHO_PLUGIN_NUM_OUTPUTS != 0 outDataSize = sizeof(UInt32); outWritable = false; return noErr; #else return kAudioUnitErr_InvalidProperty; #endif case kAudioUnitProperty_PresentPreset: DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); outDataSize = sizeof(AUPreset); outWritable = true; return noErr; case kAudioUnitProperty_CocoaUI: DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); #if DISTRHO_PLUGIN_HAS_UI outDataSize = sizeof(AudioUnitCocoaViewInfo); outWritable = false; return noErr; #else return kAudioUnitErr_InvalidProperty; #endif case kAudioUnitProperty_MIDIOutputCallbackInfo: DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT outDataSize = sizeof(CFArrayRef); outWritable = false; return noErr; #else return kAudioUnitErr_InvalidProperty; #endif case kAudioUnitProperty_MIDIOutputCallback: DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT outDataSize = sizeof(AUMIDIOutputCallbackStruct); outWritable = true; return noErr; #else return kAudioUnitErr_InvalidProperty; #endif case kAudioUnitProperty_AudioUnitMIDIProtocol: DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); // FIXME implement the event list stuff #if 0 && (DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT) outDataSize = sizeof(SInt32); outWritable = false; return noErr; #else return kAudioUnitErr_InvalidProperty; #endif case 'DPFi': DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); outDataSize = sizeof(uint16_t); outWritable = false; return noErr; case 'DPFe': DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement < fParameterCount, inElement, kAudioUnitErr_InvalidElement); outDataSize = sizeof(uint8_t); outWritable = true; return noErr; case 'DPFp': DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement < fParameterCount, inElement, kAudioUnitErr_InvalidElement); outDataSize = sizeof(float); outWritable = true; return noErr; #if DISTRHO_PLUGIN_WANT_MIDI_INPUT case 'DPFn': DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); outDataSize = sizeof(uint8_t) * 3; outWritable = true; return noErr; #endif #if DISTRHO_PLUGIN_WANT_PROGRAMS case 'DPFo': DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); outDataSize = sizeof(uint32_t); outWritable = false; return noErr; #endif #if DISTRHO_PLUGIN_WANT_STATE case 'DPFl': DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); outDataSize = sizeof(CFArrayRef); outWritable = false; return noErr; case 'DPFs': DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement < fStateCount, inElement, kAudioUnitErr_InvalidElement); outDataSize = sizeof(CFStringRef); outWritable = true; return noErr; #endif #if DISTRHO_PLUGIN_HAS_UI && DISTRHO_PLUGIN_WANT_DIRECT_ACCESS case 'DPFa': DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); outDataSize = sizeof(void*); outWritable = false; return noErr; #endif // unwanted properties case kAudioUnitProperty_CPULoad: case kAudioUnitProperty_RenderContextObserver: case kAudioUnitProperty_AudioChannelLayout: case kAudioUnitProperty_TailTime: case kAudioUnitProperty_SupportedChannelLayoutTags: case kMusicDeviceProperty_DualSchedulingMode: DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); return kAudioUnitErr_InvalidProperty; } d_stdout("TODO GetPropertyInfo(%d:%x:%s, %d:%s, %d, ...)", inProp, inProp, AudioUnitPropertyID2Str(inProp), inScope, AudioUnitScope2Str(inScope), inElement); return kAudioUnitErr_InvalidProperty; } OSStatus auGetProperty(const AudioUnitPropertyID inProp, const AudioUnitScope inScope, const AudioUnitElement inElement, void* const outData) { switch (inProp) { case kAudioUnitProperty_ClassInfo: *static_cast(outData) = retrieveClassInfo(); return noErr; case kAudioUnitProperty_SampleRate: #if DISTRHO_PLUGIN_NUM_INPUTS != 0 if (inScope == kAudioUnitScope_Input) { *static_cast(outData) = fSampleRateForInput; } else #endif #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0 if (inScope == kAudioUnitScope_Output) { *static_cast(outData) = fSampleRateForOutput; } else #endif { return kAudioUnitErr_InvalidScope; } return noErr; case kAudioUnitProperty_ParameterList: { AudioUnitParameterID* const paramList = static_cast(outData); for (uint32_t i=0; i(outData); std::memset(info, 0, sizeof(*info)); const ParameterRanges& ranges(fPlugin.getParameterRanges(inElement)); info->flags = kAudioUnitParameterFlag_IsHighResolution | kAudioUnitParameterFlag_IsReadable | kAudioUnitParameterFlag_HasCFNameString; if (fPlugin.getParameterDesignation(inElement) == kParameterDesignationBypass) { info->flags |= kAudioUnitParameterFlag_IsWritable; info->unit = kAudioUnitParameterUnit_Boolean; d_strncpy(info->name, "Bypass", sizeof(info->name)); info->cfNameString = CFSTR("Bypass"); } else { const uint32_t hints = fPlugin.getParameterHints(inElement); info->flags |= kAudioUnitParameterFlag_CFNameRelease; if (hints & kParameterIsOutput) { info->flags |= kAudioUnitParameterFlag_MeterReadOnly; } else { info->flags |= kAudioUnitParameterFlag_IsWritable; if ((hints & kParameterIsAutomatable) == 0x0) info->flags |= kAudioUnitParameterFlag_NonRealTime; } if (hints & kParameterIsBoolean) info->unit = kAudioUnitParameterUnit_Boolean; else if (hints & kParameterIsInteger) info->unit = kAudioUnitParameterUnit_Indexed; else info->unit = kAudioUnitParameterUnit_Generic; // | kAudioUnitParameterFlag_ValuesHaveStrings; const String& name(fPlugin.getParameterName(inElement)); d_strncpy(info->name, name, sizeof(info->name)); info->cfNameString = CFStringCreateWithCString(nullptr, name, kCFStringEncodingUTF8); } info->minValue = ranges.min; info->maxValue = ranges.max; info->defaultValue = ranges.def; } return noErr; #if 0 case kAudioUnitProperty_FastDispatch: switch (inElement) { case kAudioUnitGetParameterSelect: *static_cast(outData) = FastDispatchGetParameter; return noErr; case kAudioUnitSetParameterSelect: *static_cast(outData) = FastDispatchSetParameter; return noErr; case kAudioUnitRenderSelect: *static_cast(outData) = FastDispatchRender; return noErr; } d_stdout("WIP FastDispatch(%d:%x:%s)", inElement, inElement, AudioUnitPropertyID2Str(inElement)); return kAudioUnitErr_InvalidElement; #endif case kAudioUnitProperty_StreamFormat: { AudioStreamBasicDescription* const desc = static_cast(outData); std::memset(desc, 0, sizeof(*desc)); if (inElement != 0) return kAudioUnitErr_InvalidElement; #if DISTRHO_PLUGIN_NUM_INPUTS != 0 if (inScope == kAudioUnitScope_Input) { #ifdef DISTRHO_PLUGIN_EXTRA_IO desc->mChannelsPerFrame = fNumInputs; #else desc->mChannelsPerFrame = DISTRHO_PLUGIN_NUM_INPUTS; #endif } else #endif #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0 if (inScope == kAudioUnitScope_Output) { #ifdef DISTRHO_PLUGIN_EXTRA_IO desc->mChannelsPerFrame = fNumOutputs; #else desc->mChannelsPerFrame = DISTRHO_PLUGIN_NUM_OUTPUTS; #endif } else #endif { return kAudioUnitErr_InvalidScope; } desc->mFormatID = kAudioFormatLinearPCM; desc->mFormatFlags = kWantedAudioFormat; desc->mSampleRate = fPlugin.getSampleRate(); desc->mBitsPerChannel = 32; desc->mBytesPerFrame = sizeof(float); desc->mBytesPerPacket = sizeof(float); desc->mFramesPerPacket = 1; } return noErr; case kAudioUnitProperty_ElementCount: switch (inScope) { case kAudioUnitScope_Global: *static_cast(outData) = 1; break; case kAudioUnitScope_Input: *static_cast(outData) = DISTRHO_PLUGIN_NUM_INPUTS != 0 ? 1 : 0; break; case kAudioUnitScope_Output: *static_cast(outData) = DISTRHO_PLUGIN_NUM_OUTPUTS != 0 ? 1 : 0; break; default: *static_cast(outData) = 0; break; } return noErr; #if DISTRHO_PLUGIN_WANT_LATENCY case kAudioUnitProperty_Latency: *static_cast(outData) = static_cast(fPlugin.getLatency()) / fPlugin.getSampleRate(); return noErr; #endif case kAudioUnitProperty_SupportedNumChannels: std::memcpy(outData, kChannelInfo, sizeof(kChannelInfo)); return noErr; case kAudioUnitProperty_MaximumFramesPerSlice: *static_cast(outData) = fPlugin.getBufferSize(); return noErr; case kAudioUnitProperty_LastRenderError: *static_cast(outData) = fLastRenderError; fLastRenderError = noErr; return noErr; case kAudioUnitProperty_BypassEffect: *static_cast(outData) = fPlugin.getParameterValue(fBypassParameterIndex) > 0.5f ? 1 : 0; return noErr; #if DISTRHO_PLUGIN_NUM_INPUTS != 0 case kAudioUnitProperty_SetRenderCallback: std::memcpy(outData, &fInputRenderCallback, sizeof(AURenderCallbackStruct)); return noErr; #endif #if DISTRHO_PLUGIN_WANT_PROGRAMS case kAudioUnitProperty_FactoryPresets: if (const CFMutableArrayRef presetsRef = CFArrayCreateMutable(nullptr, fProgramCount, nullptr)) { for (uint32_t i=0; i(outData) = presetsRef; return noErr; } return kAudio_ParamError; #endif #if DISTRHO_PLUGIN_WANT_TIMEPOS case kAudioUnitProperty_HostCallbacks: std::memcpy(outData, &fHostCallbackInfo, sizeof(HostCallbackInfo)); return noErr; #endif #if DISTRHO_PLUGIN_NUM_INPUTS != 0 && DISTRHO_PLUGIN_NUM_OUTPUTS != 0 case kAudioUnitProperty_InPlaceProcessing: *static_cast(outData) = 1; return noErr; #endif case kAudioUnitProperty_PresentPreset: #if DISTRHO_PLUGIN_WANT_PROGRAMS if (fCurrentProgram >= 0) { std::memcpy(outData, &fFactoryPresetsData[fCurrentProgram], sizeof(AUPreset)); } else #endif { std::memcpy(outData, &fUserPresetData, sizeof(AUPreset)); } return noErr; #if DISTRHO_PLUGIN_HAS_UI case kAudioUnitProperty_CocoaUI: { AudioUnitCocoaViewInfo* const info = static_cast(outData); std::memset(info, 0, sizeof(*info)); NSString* const bundlePathString = [[NSString alloc] initWithBytes:d_nextBundlePath length:strlen(d_nextBundlePath) encoding:NSUTF8StringEncoding]; info->mCocoaAUViewBundleLocation = static_cast([[NSURL fileURLWithPath: bundlePathString] retain]); #define MACRO_STR3(a, b, c) a "_" b "_" c #define MACRO_STR2(a, b, c) MACRO_STR3(#a, #b, #c) #define MACRO_STR(a, b, c) MACRO_STR2(a, b, c) info->mCocoaAUViewClass[0] = CFSTR("CocoaAUView_" MACRO_STR(DISTRHO_PLUGIN_AU_TYPE, DISTRHO_PLUGIN_UNIQUE_ID, DISTRHO_PLUGIN_BRAND_ID)); #undef MACRO_STR #undef MACRO_STR2 #undef MACRO_STR3 [bundlePathString release]; } return noErr; #endif #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT case kAudioUnitProperty_MIDIOutputCallbackInfo: { CFStringRef refs[1] = { CFSTR("MIDI Output") }; *static_cast(outData) = CFArrayCreate(nullptr, reinterpret_cast(refs), 1, &kCFTypeArrayCallBacks); } return noErr; #endif #if DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT /* FIXME implement the event list stuff case kAudioUnitProperty_AudioUnitMIDIProtocol: *static_cast(outData) = kMIDIProtocol_1_0; return noErr; */ #endif case 'DPFp': *static_cast(outData) = fPlugin.getParameterValue(inElement); return noErr; #if DISTRHO_PLUGIN_WANT_PROGRAMS case 'DPFo': *static_cast(outData) = fLastFactoryProgram; return noErr; #endif #if DISTRHO_PLUGIN_WANT_STATE case 'DPFl': if (const CFMutableArrayRef keysRef = CFArrayCreateMutable(nullptr, fStateCount, &kCFTypeArrayCallBacks)) { for (uint32_t i=0; i(outData) = keysRef; return noErr; } return kAudio_ParamError; case 'DPFs': { const String& key(fPlugin.getStateKey(inElement)); #if DISTRHO_PLUGIN_WANT_FULL_STATE fStateMap[key] = fPlugin.getStateValue(key); #endif *static_cast(outData) = CFStringCreateWithCString(nullptr, fStateMap[key], kCFStringEncodingUTF8); } return noErr; #endif #if DISTRHO_PLUGIN_HAS_UI && DISTRHO_PLUGIN_WANT_DIRECT_ACCESS case 'DPFa': *static_cast(outData) = fPlugin.getInstancePointer(); return noErr; #endif } d_stdout("TODO GetProperty(%d:%x:%s, %d:%s, %d, ...)", inProp, inProp, AudioUnitPropertyID2Str(inProp), inScope, AudioUnitScope2Str(inScope), inElement); return kAudioUnitErr_InvalidProperty; } OSStatus auSetProperty(const AudioUnitPropertyID inProp, const AudioUnitScope inScope, const AudioUnitElement inElement, const void* const inData, const UInt32 inDataSize) { switch (inProp) { case kAudioUnitProperty_ClassInfo: DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(CFPropertyListRef), inDataSize, kAudioUnitErr_InvalidPropertyValue); { const CFPropertyListRef propList = *static_cast(inData); DISTRHO_SAFE_ASSERT_RETURN(CFGetTypeID(propList) == CFDictionaryGetTypeID(), kAudioUnitErr_InvalidPropertyValue); restoreClassInfo(static_cast(propList)); } return noErr; case kAudioUnitProperty_MakeConnection: DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Input, inScope, kAudioUnitErr_InvalidScope); DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(AudioUnitConnection), inDataSize, kAudioUnitErr_InvalidPropertyValue); #if DISTRHO_PLUGIN_NUM_INPUTS != 0 { const AudioUnitConnection conn = *static_cast(inData); if (conn.sourceAudioUnit == nullptr) { fInputConnectionBus = 0; fInputConnectionUnit = nullptr; return noErr; } AudioStreamBasicDescription desc; std::memset(&desc, 0, sizeof(desc)); UInt32 dataSize = sizeof(AudioStreamBasicDescription); if (AudioUnitGetProperty(conn.sourceAudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, conn.sourceOutputNumber, &desc, &dataSize) != noErr) return kAudioUnitErr_InvalidPropertyValue; DISTRHO_SAFE_ASSERT_INT_RETURN(desc.mFormatID == kAudioFormatLinearPCM, desc.mFormatID, kAudioUnitErr_FormatNotSupported); DISTRHO_SAFE_ASSERT_INT_RETURN(desc.mBitsPerChannel == 32, desc.mBitsPerChannel, kAudioUnitErr_FormatNotSupported); DISTRHO_SAFE_ASSERT_INT_RETURN(desc.mBytesPerFrame == sizeof(float), desc.mBytesPerFrame, kAudioUnitErr_FormatNotSupported); DISTRHO_SAFE_ASSERT_INT_RETURN(desc.mBytesPerPacket == sizeof(float), desc.mBytesPerPacket, kAudioUnitErr_FormatNotSupported); DISTRHO_SAFE_ASSERT_INT_RETURN(desc.mFramesPerPacket == 1, desc.mFramesPerPacket, kAudioUnitErr_FormatNotSupported); DISTRHO_SAFE_ASSERT_INT_RETURN(desc.mFormatFlags == kWantedAudioFormat, desc.mFormatFlags, kAudioUnitErr_FormatNotSupported); #ifdef DISTRHO_PLUGIN_EXTRA_IO DISTRHO_SAFE_ASSERT_UINT_RETURN(desc.mChannelsPerFrame == fNumInputs, desc.mChannelsPerFrame, kAudioUnitErr_FormatNotSupported); #else DISTRHO_SAFE_ASSERT_UINT_RETURN(desc.mChannelsPerFrame == DISTRHO_PLUGIN_NUM_INPUTS, desc.mChannelsPerFrame, kAudioUnitErr_FormatNotSupported); #endif fInputConnectionBus = conn.sourceOutputNumber; fInputConnectionUnit = conn.sourceAudioUnit; } return noErr; #else return kAudioUnitErr_PropertyNotInUse; #endif case kAudioUnitProperty_SampleRate: #if DISTRHO_PLUGIN_NUM_INPUTS != 0 && DISTRHO_PLUGIN_NUM_OUTPUTS != 0 DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Input || inScope == kAudioUnitScope_Output, inScope, kAudioUnitErr_InvalidScope); #elif DISTRHO_PLUGIN_NUM_OUTPUTS != 0 DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Output, inScope, kAudioUnitErr_InvalidScope); #else DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Input, inScope, kAudioUnitErr_InvalidScope); #endif DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(Float64), inDataSize, kAudioUnitErr_InvalidPropertyValue); { #if DISTRHO_PLUGIN_NUM_INPUTS != 0 || DISTRHO_PLUGIN_NUM_OUTPUTS != 0 const Float64 sampleRate = *static_cast(inData); #endif #if DISTRHO_PLUGIN_NUM_INPUTS != 0 if (inScope == kAudioUnitScope_Input) { if (d_isNotEqual(fSampleRateForInput, sampleRate)) { fSampleRateForInput = sampleRate; d_nextSampleRate = sampleRate; #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0 if (d_isEqual(fSampleRateForOutput, sampleRate)) #endif { fPlugin.setSampleRate(sampleRate, true); } notifyPropertyListeners(inProp, inScope, inElement); } return noErr; } #endif #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0 if (inScope == kAudioUnitScope_Output) { if (d_isNotEqual(fSampleRateForOutput, sampleRate)) { fSampleRateForOutput = sampleRate; d_nextSampleRate = sampleRate; #if DISTRHO_PLUGIN_NUM_INPUTS != 0 if (d_isEqual(fSampleRateForInput, sampleRate)) #endif { fPlugin.setSampleRate(sampleRate, true); } notifyPropertyListeners(inProp, inScope, inElement); } return noErr; } #endif } return kAudioUnitErr_PropertyNotInUse; case kAudioUnitProperty_StreamFormat: #if DISTRHO_PLUGIN_NUM_INPUTS != 0 && DISTRHO_PLUGIN_NUM_OUTPUTS != 0 DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Input || inScope == kAudioUnitScope_Output, inScope, kAudioUnitErr_InvalidScope); #elif DISTRHO_PLUGIN_NUM_OUTPUTS != 0 DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Output, inScope, kAudioUnitErr_InvalidScope); #else DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Input, inScope, kAudioUnitErr_InvalidScope); #endif DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(AudioStreamBasicDescription), inDataSize, kAudioUnitErr_InvalidPropertyValue); { const AudioStreamBasicDescription* const desc = static_cast(inData); if (desc->mFormatID != kAudioFormatLinearPCM) return kAudioUnitErr_FormatNotSupported; if (desc->mBitsPerChannel != 32) return kAudioUnitErr_FormatNotSupported; if (desc->mBytesPerFrame != sizeof(float)) return kAudioUnitErr_FormatNotSupported; if (desc->mBytesPerPacket != sizeof(float)) return kAudioUnitErr_FormatNotSupported; if (desc->mFramesPerPacket != 1) return kAudioUnitErr_FormatNotSupported; if (desc->mFormatFlags != kWantedAudioFormat) return kAudioUnitErr_FormatNotSupported; #ifndef DISTRHO_PLUGIN_EXTRA_IO if (desc->mChannelsPerFrame != (inScope == kAudioUnitScope_Input ? DISTRHO_PLUGIN_NUM_INPUTS : DISTRHO_PLUGIN_NUM_OUTPUTS)) return kAudioUnitErr_FormatNotSupported; #endif #if DISTRHO_PLUGIN_NUM_INPUTS != 0 if (inScope == kAudioUnitScope_Input) { bool changed = false; #ifdef DISTRHO_PLUGIN_EXTRA_IO if (! isInputNumChannelsValid(desc->mChannelsPerFrame)) return kAudioUnitErr_FormatNotSupported; if (fNumInputs != desc->mChannelsPerFrame) { changed = true; fNumInputs = desc->mChannelsPerFrame; #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0 if (isNumChannelsComboValid(fNumInputs, fNumOutputs)) #endif { fPlugin.setAudioPortIO(fNumInputs, fNumOutputs); } } #endif if (d_isNotEqual(fSampleRateForInput, desc->mSampleRate)) { changed = true; fSampleRateForInput = desc->mSampleRate; #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0 if (d_isEqual(fSampleRateForOutput, desc->mSampleRate)) #endif { fPlugin.setSampleRate(desc->mSampleRate, true); } notifyPropertyListeners(kAudioUnitProperty_SampleRate, inScope, inElement); } if (changed) notifyPropertyListeners(inProp, inScope, inElement); return noErr; } #endif #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0 if (inScope == kAudioUnitScope_Output) { bool changed = false; #ifdef DISTRHO_PLUGIN_EXTRA_IO if (! isOutputNumChannelsValid(desc->mChannelsPerFrame)) return kAudioUnitErr_FormatNotSupported; if (fNumOutputs != desc->mChannelsPerFrame) { changed = true; fNumOutputs = desc->mChannelsPerFrame; #if DISTRHO_PLUGIN_NUM_INPUTS != 0 if (isNumChannelsComboValid(fNumInputs, fNumOutputs)) #endif { fPlugin.setAudioPortIO(fNumInputs, fNumOutputs); } } #endif if (d_isNotEqual(fSampleRateForOutput, desc->mSampleRate)) { changed = true; fSampleRateForOutput = desc->mSampleRate; #if DISTRHO_PLUGIN_NUM_INPUTS != 0 if (d_isEqual(fSampleRateForInput, desc->mSampleRate)) #endif { fPlugin.setSampleRate(desc->mSampleRate, true); } notifyPropertyListeners(kAudioUnitProperty_SampleRate, inScope, inElement); } if (changed) notifyPropertyListeners(inProp, inScope, inElement); return noErr; } #endif } return kAudioUnitErr_PropertyNotInUse; case kAudioUnitProperty_MaximumFramesPerSlice: DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(UInt32), inDataSize, kAudioUnitErr_InvalidPropertyValue); { const UInt32 bufferSize = *static_cast(inData); if (fPlugin.setBufferSize(bufferSize, true)) notifyPropertyListeners(inProp, inScope, inElement); } return noErr; case kAudioUnitProperty_BypassEffect: DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(UInt32), inDataSize, kAudioUnitErr_InvalidPropertyValue); DISTRHO_SAFE_ASSERT_RETURN(fBypassParameterIndex != UINT32_MAX, kAudioUnitErr_PropertyNotInUse); { const bool bypass = *static_cast(inData) != 0; if ((fLastParameterValues[fBypassParameterIndex] > 0.5f) != bypass) { const float value = bypass ? 1.f : 0.f; fLastParameterValues[fBypassParameterIndex] = value; fPlugin.setParameterValue(fBypassParameterIndex, value); notifyPropertyListeners(inProp, inScope, inElement); } } return noErr; case kAudioUnitProperty_SetRenderCallback: DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Input, inScope, kAudioUnitErr_InvalidScope); DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(AURenderCallbackStruct), inDataSize, kAudioUnitErr_InvalidPropertyValue); #if DISTRHO_PLUGIN_NUM_INPUTS != 0 std::memcpy(&fInputRenderCallback, inData, sizeof(AURenderCallbackStruct)); return noErr; #else return kAudioUnitErr_PropertyNotInUse; #endif case kAudioUnitProperty_HostCallbacks: DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); #if DISTRHO_PLUGIN_WANT_TIMEPOS { const UInt32 usableDataSize = std::min(inDataSize, static_cast(sizeof(HostCallbackInfo))); const bool changed = std::memcmp(&fHostCallbackInfo, inData, usableDataSize) != 0; std::memcpy(&fHostCallbackInfo, inData, usableDataSize); if (sizeof(HostCallbackInfo) > usableDataSize) std::memset(&fHostCallbackInfo + usableDataSize, 0, sizeof(HostCallbackInfo) - usableDataSize); if (changed) notifyPropertyListeners(inProp, inScope, inElement); } return noErr; #else return kAudioUnitErr_PropertyNotInUse; #endif case kAudioUnitProperty_InPlaceProcessing: // nothing to do return noErr; case kAudioUnitProperty_PresentPreset: DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(AUPreset), inDataSize, kAudioUnitErr_InvalidPropertyValue); { const int32_t presetNumber = static_cast(inData)->presetNumber; #if DISTRHO_PLUGIN_WANT_PROGRAMS if (presetNumber >= 0) { if (fCurrentProgram != presetNumber) { fCurrentProgram = presetNumber; fLastFactoryProgram = presetNumber; fPlugin.loadProgram(fLastFactoryProgram); notifyPropertyListeners('DPFo', kAudioUnitScope_Global, 0); } } else { fCurrentProgram = presetNumber; CFRelease(fUserPresetData.presetName); std::memcpy(&fUserPresetData, inData, sizeof(AUPreset)); } #else DISTRHO_SAFE_ASSERT_INT_RETURN(presetNumber < 0, presetNumber, kAudioUnitErr_InvalidPropertyValue); CFRelease(fUserPresetData.presetName); std::memcpy(&fUserPresetData, inData, sizeof(AUPreset)); #endif } return noErr; case kAudioUnitProperty_MIDIOutputCallback: DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(AUMIDIOutputCallbackStruct), inDataSize, kAudioUnitErr_InvalidPropertyValue); #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT std::memcpy(&fMidiOutput, inData, sizeof(AUMIDIOutputCallbackStruct)); return noErr; #else return kAudioUnitErr_PropertyNotInUse; #endif case 'DPFi': DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(uint16_t), inDataSize, kAudioUnitErr_InvalidPropertyValue); { const uint16_t magic = *static_cast(inData); if (magic != 1337) return noErr; #if DISTRHO_PLUGIN_WANT_PROGRAMS notifyPropertyListeners('DPFo', kAudioUnitScope_Global, 0); #endif #if DISTRHO_PLUGIN_WANT_STATE for (uint32_t i=0; i(inData); if (flag != 1 && flag != 2) return noErr; AudioUnitEvent event; std::memset(&event, 0, sizeof(event)); event.mEventType = flag == 1 ? kAudioUnitEvent_BeginParameterChangeGesture : kAudioUnitEvent_EndParameterChangeGesture; event.mArgument.mParameter.mAudioUnit = fComponent; event.mArgument.mParameter.mParameterID = inElement; event.mArgument.mParameter.mScope = kAudioUnitScope_Global; AUEventListenerNotify(NULL, NULL, &event); } return noErr; case 'DPFp': DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement < fParameterCount, inElement, kAudioUnitErr_InvalidElement); DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(float), inDataSize, kAudioUnitErr_InvalidPropertyValue); { const float value = *static_cast(inData); DISTRHO_SAFE_ASSERT_RETURN(std::isfinite(value), kAudioUnitErr_InvalidParameterValue); if (d_isEqual(fLastParameterValues[inElement], value)) return noErr; fLastParameterValues[inElement] = value; fPlugin.setParameterValue(inElement, value); AudioUnitEvent event; std::memset(&event, 0, sizeof(event)); event.mEventType = kAudioUnitEvent_ParameterValueChange; event.mArgument.mParameter.mAudioUnit = fComponent; event.mArgument.mParameter.mParameterID = inElement; event.mArgument.mParameter.mScope = kAudioUnitScope_Global; AUEventListenerNotify(NULL, NULL, &event); if (fBypassParameterIndex == inElement) notifyPropertyListeners(kAudioUnitProperty_BypassEffect, kAudioUnitScope_Global, 0); } return noErr; case 'DPFn': DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(uint8_t) * 3, inDataSize, kAudioUnitErr_InvalidPropertyValue); #if DISTRHO_PLUGIN_WANT_MIDI_INPUT { const uint8_t* const midiData = static_cast(inData); if (midiData[0] == 0) return noErr; fNotesRingBuffer.writeCustomData(midiData, 3); fNotesRingBuffer.commitWrite(); } return noErr; #else return kAudioUnitErr_PropertyNotInUse; #endif case 'DPFs': DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(CFStringRef), inDataSize, kAudioUnitErr_InvalidPropertyValue); #if DISTRHO_PLUGIN_WANT_STATE DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement < fStateCount, inElement, kAudioUnitErr_InvalidElement); { const CFStringRef valueRef = *static_cast(inData); DISTRHO_SAFE_ASSERT_RETURN(valueRef != nullptr && CFGetTypeID(valueRef) == CFStringGetTypeID(), kAudioUnitErr_InvalidPropertyValue); const CFIndex valueLen = CFStringGetLength(valueRef); char* const value = static_cast(std::malloc(valueLen + 1)); DISTRHO_SAFE_ASSERT_RETURN(value != nullptr, kAudio_ParamError); DISTRHO_SAFE_ASSERT_RETURN(CFStringGetCString(valueRef, value, valueLen + 1, kCFStringEncodingUTF8), kAudioUnitErr_InvalidPropertyValue); const String& key(fPlugin.getStateKey(inElement)); // save this key as needed if (fPlugin.wantStateKey(key)) fStateMap[key] = value; fPlugin.setState(key, value); std::free(value); } return noErr; #else return kAudioUnitErr_PropertyNotInUse; #endif // unwanted properties case kMusicDeviceProperty_DualSchedulingMode: return kAudioUnitErr_PropertyNotInUse; } d_stdout("TODO SetProperty(%d:%x:%s, %d:%s, %d, %p, %u)", inProp, inProp, AudioUnitPropertyID2Str(inProp), inScope, AudioUnitScope2Str(inScope), inElement, inData, inDataSize); return kAudioUnitErr_InvalidProperty; } OSStatus auAddPropertyListener(const AudioUnitPropertyID prop, const AudioUnitPropertyListenerProc proc, void* const userData) { const PropertyListener pl = { prop, proc, userData }; fPropertyListeners.push_back(pl); return noErr; } OSStatus auRemovePropertyListener(const AudioUnitPropertyID prop, const AudioUnitPropertyListenerProc proc) { for (PropertyListeners::iterator it = fPropertyListeners.begin(); it != fPropertyListeners.end(); ++it) { const PropertyListener& pl(*it); if (pl.prop == prop && pl.proc == proc) { fPropertyListeners.erase(it); return auRemovePropertyListener(prop, proc); } } return noErr; } OSStatus auRemovePropertyListenerWithUserData(const AudioUnitPropertyID prop, const AudioUnitPropertyListenerProc proc, void* const userData) { for (PropertyListeners::iterator it = fPropertyListeners.begin(); it != fPropertyListeners.end(); ++it) { const PropertyListener& pl(*it); if (pl.prop == prop && pl.proc == proc && pl.userData == userData) { fPropertyListeners.erase(it); return auRemovePropertyListenerWithUserData(prop, proc, userData); } } return noErr; } OSStatus auAddRenderNotify(const AURenderCallback proc, void* const userData) { fUsingRenderListeners = true; const RenderListener rl = { proc, userData }; fRenderListeners.push_back(rl); return noErr; } OSStatus auRemoveRenderNotify(const AURenderCallback proc, void* const userData) { for (RenderListeners::iterator it = fRenderListeners.begin(); it != fRenderListeners.end(); ++it) { const RenderListener& rl(*it); if (rl.proc == proc && rl.userData == userData) { fRenderListeners.erase(it); return auRemoveRenderNotify(proc, userData); } } if (fRenderListeners.empty()) fUsingRenderListeners = false; return noErr; } OSStatus auGetParameter(const AudioUnitParameterID param, const AudioUnitScope scope, const AudioUnitElement elem, AudioUnitParameterValue* const value) { DISTRHO_SAFE_ASSERT_UINT_RETURN(scope == kAudioUnitScope_Global, scope, kAudioUnitErr_InvalidScope); DISTRHO_SAFE_ASSERT_UINT_RETURN(param < fParameterCount, param, kAudioUnitErr_InvalidElement); DISTRHO_SAFE_ASSERT_UINT_RETURN(elem == 0, elem, kAudioUnitErr_InvalidElement); DISTRHO_SAFE_ASSERT_RETURN(value != nullptr, kAudio_ParamError); *value = fPlugin.getParameterValue(param); return noErr; } OSStatus auSetParameter(const AudioUnitParameterID param, const AudioUnitScope scope, const AudioUnitElement elem, const AudioUnitParameterValue value, UInt32 /* bufferOffset */) { DISTRHO_SAFE_ASSERT_UINT_RETURN(scope == kAudioUnitScope_Global, scope, kAudioUnitErr_InvalidScope); DISTRHO_SAFE_ASSERT_UINT_RETURN(param < fParameterCount, param, kAudioUnitErr_InvalidElement); DISTRHO_SAFE_ASSERT_UINT_RETURN(elem == 0, elem, kAudioUnitErr_InvalidElement); DISTRHO_SAFE_ASSERT_RETURN(std::isfinite(value), kAudioUnitErr_InvalidParameterValue); if (d_isNotEqual(fLastParameterValues[param], value)) { fLastParameterValues[param] = value; fPlugin.setParameterValue(param, value); // TODO flag param only, notify listeners later on bg thread (sem_post etc) notifyPropertyListeners('DPFp', kAudioUnitScope_Global, param); if (fBypassParameterIndex == elem) notifyPropertyListeners(kAudioUnitProperty_BypassEffect, kAudioUnitScope_Global, 0); } return noErr; } OSStatus auScheduleParameters(const AudioUnitParameterEvent* const events, const UInt32 numEvents) { for (UInt32 i=0; inumPackets = 0; #endif #if DISTRHO_PLUGIN_WANT_TIMEPOS fTimePosition.clear(); fTimePosition.bbt.ticksPerBeat = kDefaultTicksPerBeat; #endif return noErr; } OSStatus auRender(const AudioUnitRenderActionFlags actionFlags, const AudioTimeStamp* const inTimeStamp, const UInt32 inBusNumber, const UInt32 inFramesToProcess, AudioBufferList* const ioData) { if ((actionFlags & kAudioUnitRenderAction_DoNotCheckRenderArgs) == 0x0) { DISTRHO_SAFE_ASSERT_RETURN(fPlugin.isActive(), kAudio_ParamError); DISTRHO_SAFE_ASSERT_UINT_RETURN(inBusNumber == 0, inBusNumber, kAudioUnitErr_InvalidElement); #if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS != 0 DISTRHO_SAFE_ASSERT_UINT_RETURN(ioData->mNumberBuffers == fAudioBufferList->mNumberBuffers, ioData->mNumberBuffers, kAudio_ParamError); #else DISTRHO_SAFE_ASSERT_UINT_RETURN(ioData->mNumberBuffers == 0, ioData->mNumberBuffers, kAudio_ParamError); #endif if (inFramesToProcess > fPlugin.getBufferSize()) { setLastRenderError(kAudioUnitErr_TooManyFramesToProcess); return kAudioUnitErr_TooManyFramesToProcess; } #if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS != 0 for (uint16_t i = 0; i < ioData->mNumberBuffers; ++i) { if (ioData->mBuffers[i].mDataByteSize != sizeof(float) * inFramesToProcess) { setLastRenderError(kAudio_ParamError); return kAudio_ParamError; } } #endif } #if DISTRHO_PLUGIN_NUM_INPUTS != 0 #ifdef DISTRHO_PLUGIN_EXTRA_IO const uint32_t numInputs = fNumInputs; #else constexpr const uint32_t numInputs = DISTRHO_PLUGIN_NUM_INPUTS; #endif const float* inputs[numInputs]; #else constexpr const float** inputs = nullptr; #endif #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0 #ifdef DISTRHO_PLUGIN_EXTRA_IO const uint32_t numOutputs = fNumOutputs; #else constexpr const uint32_t numOutputs = DISTRHO_PLUGIN_NUM_OUTPUTS; #endif float* outputs[numOutputs]; #else constexpr float** outputs = nullptr; #endif #if DISTRHO_PLUGIN_NUM_INPUTS != 0 if (fInputConnectionUnit != nullptr) { AudioUnitRenderActionFlags ioActionFlags = 0; const OSStatus err = AudioUnitRender(fInputConnectionUnit, &ioActionFlags, inTimeStamp, fInputConnectionBus, inFramesToProcess, fAudioBufferList); if (err != noErr) { setLastRenderError(err); return err; } for (uint16_t i = 0; i < numInputs; ++i) inputs[i] = static_cast(fAudioBufferList->mBuffers[i].mData); #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0 for (uint16_t i = 0; i < numOutputs; ++i) { if (ioData->mBuffers[i].mData == nullptr) ioData->mBuffers[i].mData = fAudioBufferList->mBuffers[i].mData; outputs[i] = static_cast(ioData->mBuffers[i].mData); } #endif } else if (fInputRenderCallback.inputProc != nullptr) { bool adjustDataByteSize, usingHostBuffer = true; UInt32 prevDataByteSize; for (uint16_t i = 0; i < ioData->mNumberBuffers; ++i) { if (ioData->mBuffers[i].mData == nullptr) { usingHostBuffer = false; ioData->mBuffers[i].mData = fAudioBufferList->mBuffers[i].mData; } } if (! usingHostBuffer) { prevDataByteSize = fAudioBufferList->mBuffers[0].mDataByteSize; adjustDataByteSize = prevDataByteSize != sizeof(float) * inFramesToProcess; if (adjustDataByteSize) { for (uint16_t i = 0; i < ioData->mNumberBuffers; ++i) fAudioBufferList->mBuffers[i].mDataByteSize = sizeof(float) * inFramesToProcess; } } else { adjustDataByteSize = false; } AudioUnitRenderActionFlags rActionFlags = 0; AudioBufferList* const rData = usingHostBuffer ? ioData : fAudioBufferList; const OSStatus err = fInputRenderCallback.inputProc(fInputRenderCallback.inputProcRefCon, &rActionFlags, inTimeStamp, inBusNumber, inFramesToProcess, rData); if (err != noErr) { if (adjustDataByteSize) { for (uint16_t i = 0; i < ioData->mNumberBuffers; ++i) fAudioBufferList->mBuffers[i].mDataByteSize = prevDataByteSize; } setLastRenderError(err); return err; } if (usingHostBuffer) { for (uint16_t i = 0; i < numInputs; ++i) inputs[i] = static_cast(ioData->mBuffers[i].mData); #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0 for (uint16_t i = 0; i < numOutputs; ++i) outputs[i] = static_cast(ioData->mBuffers[i].mData); #endif } else { for (uint16_t i = 0; i < numInputs; ++i) inputs[i] = static_cast(fAudioBufferList->mBuffers[i].mData); #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0 for (uint16_t i = 0; i < numOutputs; ++i) outputs[i] = static_cast(ioData->mBuffers[i].mData); #endif } } else #endif // DISTRHO_PLUGIN_NUM_INPUTS != 0 { #if DISTRHO_PLUGIN_NUM_INPUTS != 0 for (uint16_t i = 0; i < numInputs; ++i) { if (ioData->mBuffers[i].mData == nullptr) { ioData->mBuffers[i].mData = fAudioBufferList->mBuffers[i].mData; std::memset(ioData->mBuffers[i].mData, 0, sizeof(float) * inFramesToProcess); } inputs[i] = static_cast(ioData->mBuffers[i].mData); } #endif #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0 for (uint16_t i = 0; i < numOutputs; ++i) { if (ioData->mBuffers[i].mData == nullptr) ioData->mBuffers[i].mData = fAudioBufferList->mBuffers[i].mData; outputs[i] = static_cast(ioData->mBuffers[i].mData); } #endif } if (fUsingRenderListeners) { AudioUnitRenderActionFlags ioActionFlags = actionFlags | kAudioUnitRenderAction_PreRender; notifyRenderListeners(&ioActionFlags, inTimeStamp, inBusNumber, inFramesToProcess, ioData); } run(inputs, outputs, inFramesToProcess, inTimeStamp); if (fUsingRenderListeners) { AudioUnitRenderActionFlags ioActionFlags = actionFlags | kAudioUnitRenderAction_PostRender; notifyRenderListeners(&ioActionFlags, inTimeStamp, inBusNumber, inFramesToProcess, ioData); } return noErr; } #if DISTRHO_PLUGIN_WANT_MIDI_INPUT OSStatus auMIDIEvent(const UInt32 inStatus, const UInt32 inData1, const UInt32 inData2, const UInt32 inOffsetSampleFrame) { if (fMidiEventCount >= kMaxMidiEvents) return noErr; MidiEvent& midiEvent(fMidiEvents[fMidiEventCount++]); midiEvent.frame = inOffsetSampleFrame; midiEvent.data[0] = inStatus; midiEvent.data[1] = inData1; midiEvent.data[2] = inData2; switch (inStatus & 0xF0) { case 0x80: case 0x90: case 0xA0: case 0xB0: case 0xE0: midiEvent.size = 3; break; case 0xC0: case 0xD0: midiEvent.size = 2; break; case 0xF0: switch (inStatus & 0x0F) { case 0x0: case 0x4: case 0x5: case 0x7: case 0x9: case 0xD: // unsupported return kAudioUnitErr_InvalidPropertyValue; case 0x1: case 0x2: case 0x3: case 0xE: midiEvent.size = 3; break; case 0x6: case 0x8: case 0xA: case 0xB: case 0xC: case 0xF: midiEvent.size = 1; break; } break; default: // invalid d_debug("auMIDIEvent received invalid event %u %u %u %u @ %u", inStatus, inData1, inData2, inOffsetSampleFrame, fMidiEventCount); return kAudioUnitErr_InvalidPropertyValue; } #if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS == 0 // handle case of plugin having no working audio, simulate audio-side processing run(nullptr, nullptr, std::max(1u, inOffsetSampleFrame), nullptr); #endif return noErr; } OSStatus auSysEx(const UInt8* const inData, const UInt32 inLength) { if (fMidiEventCount >= kMaxMidiEvents) return noErr; MidiEvent& midiEvent(fMidiEvents[fMidiEventCount++]); midiEvent.frame = fMidiEventCount != 1 ? fMidiEvents[fMidiEventCount - 1].frame : 0; midiEvent.size = inLength; // FIXME who owns inData ?? if (inLength > MidiEvent::kDataSize) { std::memset(midiEvent.data, 0, MidiEvent::kDataSize); midiEvent.dataExt = inData; } else { std::memcpy(midiEvent.data, inData, inLength); } #if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS == 0 // handle case of plugin having no working audio, simulate audio-side processing run(nullptr, nullptr, 1, nullptr); #endif return noErr; } #endif // ---------------------------------------------------------------------------------------------------------------- private: PluginExporter fPlugin; // AU component const AudioUnit fComponent; // AUv2 related fields OSStatus fLastRenderError; PropertyListeners fPropertyListeners; RenderListeners fRenderListeners; #if DISTRHO_PLUGIN_NUM_INPUTS != 0 UInt32 fInputConnectionBus; AudioUnit fInputConnectionUnit; AURenderCallbackStruct fInputRenderCallback; Float64 fSampleRateForInput; #ifdef DISTRHO_PLUGIN_EXTRA_IO uint32_t fNumInputs; #endif #endif #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0 Float64 fSampleRateForOutput; #ifdef DISTRHO_PLUGIN_EXTRA_IO uint32_t fNumOutputs; #endif #endif #if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS != 0 AudioBufferList* fAudioBufferList; #endif bool fUsingRenderListeners; // Caching const uint32_t fParameterCount; float* fLastParameterValues; uint32_t fBypassParameterIndex; #if DISTRHO_PLUGIN_WANT_MIDI_INPUT uint32_t fMidiEventCount; MidiEvent fMidiEvents[kMaxMidiEvents]; SmallStackRingBuffer fNotesRingBuffer; #endif #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT uint32_t fMidiOutputDataOffset; MIDIPacketList* fMidiOutputPackets; AUMIDIOutputCallbackStruct fMidiOutput; #endif #if DISTRHO_PLUGIN_WANT_PROGRAMS int32_t fCurrentProgram; uint32_t fLastFactoryProgram; uint32_t fProgramCount; AUPreset* fFactoryPresetsData; #endif AUPreset fUserPresetData; #if DISTRHO_PLUGIN_WANT_STATE const uint32_t fStateCount; StringMap fStateMap; #endif #if DISTRHO_PLUGIN_WANT_TIMEPOS HostCallbackInfo fHostCallbackInfo; TimePosition fTimePosition; #endif // ---------------------------------------------------------------------------------------------------------------- void notifyPropertyListeners(const AudioUnitPropertyID prop, const AudioUnitScope scope, const AudioUnitElement elem) { for (PropertyListeners::iterator it = fPropertyListeners.begin(); it != fPropertyListeners.end(); ++it) { const PropertyListener& pl(*it); if (pl.prop == prop) pl.proc(pl.userData, fComponent, prop, scope, elem); } } void notifyRenderListeners(AudioUnitRenderActionFlags* const ioActionFlags, const AudioTimeStamp* const inTimeStamp, const UInt32 inBusNumber, const UInt32 inNumberFrames, AudioBufferList* const ioData) { for (RenderListeners::iterator it = fRenderListeners.begin(); it != fRenderListeners.end(); ++it) { const RenderListener& rl(*it); rl.proc(rl.userData, ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData); } } // ---------------------------------------------------------------------------------------------------------------- void run(const float** inputs, float** outputs, const uint32_t frames, const AudioTimeStamp* const inTimeStamp) { #if DISTRHO_PLUGIN_WANT_MIDI_INPUT if (fMidiEventCount != kMaxMidiEvents && fNotesRingBuffer.isDataAvailableForReading()) { uint8_t midiData[3]; const uint32_t frame = fMidiEventCount != 0 ? fMidiEvents[fMidiEventCount - 1].frame : 0; while (fNotesRingBuffer.isDataAvailableForReading()) { if (! fNotesRingBuffer.readCustomData(midiData, 3)) break; MidiEvent& midiEvent(fMidiEvents[fMidiEventCount++]); midiEvent.frame = frame; midiEvent.size = 3; std::memcpy(midiEvent.data, midiData, 3); if (fMidiEventCount == kMaxMidiEvents) break; } } #endif #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT fMidiOutputDataOffset = 0; fMidiOutputPackets->numPackets = 0; #endif #if DISTRHO_PLUGIN_WANT_TIMEPOS if (fHostCallbackInfo.beatAndTempoProc != nullptr || fHostCallbackInfo.musicalTimeLocationProc != nullptr || fHostCallbackInfo.transportStateProc != nullptr) { // reused values so we can check null and return value together Boolean b1 = false; Float32 f1 = 4.f; // initial value for beats per bar Float64 g1 = 0.0; Float64 g2 = 0.0; UInt32 u1 = 0; if (fHostCallbackInfo.musicalTimeLocationProc != nullptr && fHostCallbackInfo.musicalTimeLocationProc(fHostCallbackInfo.hostUserData, nullptr, &f1, &u1, nullptr) == noErr) { fTimePosition.bbt.beatsPerBar = f1; fTimePosition.bbt.beatType = u1; } else { fTimePosition.bbt.beatsPerBar = 4.f; fTimePosition.bbt.beatType = 4.f; } if (fHostCallbackInfo.beatAndTempoProc != nullptr && fHostCallbackInfo.beatAndTempoProc(fHostCallbackInfo.hostUserData, &g1, &g2) == noErr) { const double beat = static_cast(g1); fTimePosition.bbt.valid = true; fTimePosition.bbt.bar = static_cast(beat / f1) + 1; fTimePosition.bbt.beat = static_cast(std::fmod(beat, f1)) + 1; fTimePosition.bbt.tick = std::fmod(g1, 1.0) * 1920.0; fTimePosition.bbt.beatsPerMinute = g2; } else { fTimePosition.bbt.valid = false; fTimePosition.bbt.bar = 1; fTimePosition.bbt.beat = 1; fTimePosition.bbt.tick = 0.0; fTimePosition.bbt.beatsPerMinute = 120.0; } if (fHostCallbackInfo.transportStateProc != nullptr && fHostCallbackInfo.transportStateProc(fHostCallbackInfo.hostUserData, &b1, nullptr, &g1, nullptr, nullptr, nullptr) == noErr) { fTimePosition.playing = b1; fTimePosition.frame = static_cast(g1); } else { fTimePosition.playing = false; fTimePosition.frame = 0; } fTimePosition.bbt.barStartTick = kDefaultTicksPerBeat * fTimePosition.bbt.beatsPerBar * (fTimePosition.bbt.bar - 1); fPlugin.setTimePosition(fTimePosition); } #endif #if DISTRHO_PLUGIN_WANT_MIDI_INPUT fPlugin.run(inputs, outputs, frames, fMidiEvents, fMidiEventCount); fMidiEventCount = 0; #else fPlugin.run(inputs, outputs, frames); #endif #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT if (fMidiOutputPackets != nullptr && fMidiOutputPackets->numPackets != 0 && fMidiOutput.midiOutputCallback != nullptr) { fMidiOutput.midiOutputCallback(fMidiOutput.userData, inTimeStamp, 0, fMidiOutputPackets); } #else // unused (void)inTimeStamp; #endif float value; AudioUnitEvent event; std::memset(&event, 0, sizeof(event)); event.mEventType = kAudioUnitEvent_ParameterValueChange; event.mArgument.mParameter.mAudioUnit = fComponent; event.mArgument.mParameter.mScope = kAudioUnitScope_Global; for (uint32_t i=0; imNumberBuffers; ++i) delete[] static_cast(fAudioBufferList->mBuffers[i].mData); } #ifdef DISTRHO_PLUGIN_EXTRA_IO #if DISTRHO_PLUGIN_NUM_INPUTS != 0 && DISTRHO_PLUGIN_NUM_OUTPUTS != 0 const uint16_t numBuffers = std::max(fNumInputs, fNumOutputs); #elif DISTRHO_PLUGIN_NUM_INPUTS != 0 const uint16_t numBuffers = fNumInputs; #else const uint16_t numBuffers = fNumOutputs; #endif #else constexpr const uint16_t numBuffers = d_max(DISTRHO_PLUGIN_NUM_INPUTS, DISTRHO_PLUGIN_NUM_OUTPUTS); #endif const uint32_t bufferSize = fPlugin.getBufferSize(); if (! alloc) { std::free(fAudioBufferList); fAudioBufferList = nullptr; return true; } if (AudioBufferList* const abl = static_cast( std::realloc(fAudioBufferList, sizeof(uint32_t) + sizeof(AudioBuffer) * numBuffers))) { abl->mNumberBuffers = numBuffers; for (uint16_t i = 0; i < numBuffers; ++i) { abl->mBuffers[i].mNumberChannels = 1; abl->mBuffers[i].mData = new float[bufferSize]; abl->mBuffers[i].mDataByteSize = sizeof(float) * bufferSize; } fAudioBufferList = abl; return true; } std::free(fAudioBufferList); fAudioBufferList = nullptr; return false; } #endif // ---------------------------------------------------------------------------------------------------------------- CFMutableDictionaryRef retrieveClassInfo() { CFMutableDictionaryRef clsInfo = CFDictionaryCreateMutable(nullptr, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); SInt32 value; value = 0; if (const CFNumberRef num = CFNumberCreate(nullptr, kCFNumberSInt32Type, &value)) { CFDictionarySetValue(clsInfo, CFSTR(kAUPresetVersionKey), num); CFRelease(num); } value = kType; if (const CFNumberRef num = CFNumberCreate(nullptr, kCFNumberSInt32Type, &value)) { CFDictionarySetValue(clsInfo, CFSTR(kAUPresetTypeKey), num); CFRelease(num); } value = kSubType; if (const CFNumberRef num = CFNumberCreate(nullptr, kCFNumberSInt32Type, &value)) { CFDictionarySetValue(clsInfo, CFSTR(kAUPresetSubtypeKey), num); CFRelease(num); } value = kManufacturer; if (const CFNumberRef num = CFNumberCreate(nullptr, kCFNumberSInt32Type, &value)) { CFDictionarySetValue(clsInfo, CFSTR(kAUPresetManufacturerKey), num); CFRelease(num); } #if DISTRHO_PLUGIN_WANT_PROGRAMS if (fCurrentProgram >= 0) { CFDictionarySetValue(clsInfo, CFSTR(kAUPresetNameKey), fFactoryPresetsData[fCurrentProgram].presetName); } else #endif { CFDictionarySetValue(clsInfo, CFSTR(kAUPresetNameKey), fUserPresetData.presetName); } if (const CFMutableDictionaryRef data = CFDictionaryCreateMutable(nullptr, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)) { #if DISTRHO_PLUGIN_WANT_PROGRAMS const SInt32 program = fCurrentProgram; if (const CFNumberRef programRef = CFNumberCreate(nullptr, kCFNumberSInt32Type, &program)) { CFDictionarySetValue(data, CFSTR("program"), programRef); CFRelease(programRef); } #endif #if DISTRHO_PLUGIN_WANT_FULL_STATE // Update current state for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) { const String& key(cit->first); fStateMap[key] = fPlugin.getStateValue(key); } #endif #if DISTRHO_PLUGIN_WANT_STATE if (const CFMutableArrayRef statesRef = CFArrayCreateMutable(nullptr, fStateCount, &kCFTypeArrayCallBacks)) { for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) { const String& key(cit->first); const String& value(cit->second); #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS && ! DISTRHO_PLUGIN_HAS_UI bool wantStateKey = true; for (uint32_t i=0; i(&keyRef), reinterpret_cast(&valueRef), 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)) { CFArrayAppendValue(statesRef, dictRef); CFRelease(dictRef); } CFRelease(keyRef); CFRelease(valueRef); } CFDictionarySetValue(data, CFSTR("states"), statesRef); CFRelease(statesRef); } #endif if (const CFMutableArrayRef paramsRef = CFArrayCreateMutable(nullptr, fParameterCount, &kCFTypeArrayCallBacks)) { for (uint32_t i=0; i(&keyRef), reinterpret_cast(&valueRef), 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)) { CFArrayAppendValue(paramsRef, dictRef); CFRelease(dictRef); } CFRelease(keyRef); CFRelease(valueRef); } CFDictionarySetValue(data, CFSTR("params"), paramsRef); CFRelease(paramsRef); } CFDictionarySetValue(clsInfo, CFSTR(kAUPresetDataKey), data); CFRelease(data); } return clsInfo; } void restoreClassInfo(const CFDictionaryRef clsInfo) { CFDictionaryRef data = nullptr; DISTRHO_SAFE_ASSERT_RETURN(CFDictionaryGetValueIfPresent(clsInfo, CFSTR(kAUPresetDataKey), reinterpret_cast(&data)),); DISTRHO_SAFE_ASSERT_RETURN(CFGetTypeID(data) == CFDictionaryGetTypeID(),); #if DISTRHO_PLUGIN_WANT_PROGRAMS CFNumberRef programRef = nullptr; if (CFDictionaryGetValueIfPresent(data, CFSTR("program"), reinterpret_cast(&programRef)) && CFGetTypeID(programRef) == CFNumberGetTypeID()) { SInt32 program = -1; if (CFNumberGetValue(programRef, kCFNumberSInt32Type, &program)) { fCurrentProgram = program; if (program >= 0) { fLastFactoryProgram = program; fPlugin.loadProgram(fLastFactoryProgram); notifyPropertyListeners('DPFo', kAudioUnitScope_Global, 0); } notifyPropertyListeners(kAudioUnitProperty_PresentPreset, kAudioUnitScope_Global, 0); } } #endif #if DISTRHO_PLUGIN_WANT_STATE CFArrayRef statesRef = nullptr; if (CFDictionaryGetValueIfPresent(data, CFSTR("states"), reinterpret_cast(&statesRef)) && CFGetTypeID(statesRef) == CFArrayGetTypeID()) { const CFIndex numStates = CFArrayGetCount(statesRef); char* key = nullptr; char* value = nullptr; CFIndex keyLen = -1; CFIndex valueLen = -1; for (CFIndex i=0; i(CFArrayGetValueAtIndex(statesRef, i)); DISTRHO_SAFE_ASSERT_BREAK(CFGetTypeID(state) == CFDictionaryGetTypeID()); DISTRHO_SAFE_ASSERT_BREAK(CFDictionaryGetCount(state) == 1); CFStringRef keyRef = nullptr; CFStringRef valueRef = nullptr; CFDictionaryGetKeysAndValues(state, reinterpret_cast(&keyRef), reinterpret_cast(&valueRef)); DISTRHO_SAFE_ASSERT_BREAK(keyRef != nullptr && CFGetTypeID(keyRef) == CFStringGetTypeID()); DISTRHO_SAFE_ASSERT_BREAK(valueRef != nullptr && CFGetTypeID(valueRef) == CFStringGetTypeID()); const CFIndex keyRefLen = CFStringGetLength(keyRef); if (keyLen < keyRefLen) { keyLen = keyRefLen; key = static_cast(std::realloc(key, keyLen + 1)); } DISTRHO_SAFE_ASSERT_BREAK(CFStringGetCString(keyRef, key, keyLen + 1, kCFStringEncodingASCII)); if (! fPlugin.wantStateKey(key)) continue; const CFIndex valueRefLen = CFStringGetLength(valueRef); if (valueLen < valueRefLen) { valueLen = valueRefLen; value = static_cast(std::realloc(value, valueLen + 1)); } DISTRHO_SAFE_ASSERT_BREAK(CFStringGetCString(valueRef, value, valueLen + 1, kCFStringEncodingUTF8)); const String dkey(key); fStateMap[dkey] = value; fPlugin.setState(key, value); for (uint32_t j=0; j(¶msRef)) && CFGetTypeID(paramsRef) == CFArrayGetTypeID()) { const CFIndex numParams = CFArrayGetCount(paramsRef); char* symbol = nullptr; CFIndex symbolLen = -1; for (CFIndex i=0; i(CFArrayGetValueAtIndex(paramsRef, i)); DISTRHO_SAFE_ASSERT_BREAK(CFGetTypeID(param) == CFDictionaryGetTypeID()); DISTRHO_SAFE_ASSERT_BREAK(CFDictionaryGetCount(param) == 1); CFStringRef keyRef = nullptr; CFNumberRef valueRef = nullptr; CFDictionaryGetKeysAndValues(param, reinterpret_cast(&keyRef), reinterpret_cast(&valueRef)); DISTRHO_SAFE_ASSERT_BREAK(keyRef != nullptr && CFGetTypeID(keyRef) == CFStringGetTypeID()); DISTRHO_SAFE_ASSERT_BREAK(valueRef != nullptr && CFGetTypeID(valueRef) == CFNumberGetTypeID()); float value = 0.f; DISTRHO_SAFE_ASSERT_BREAK(CFNumberGetValue(valueRef, kCFNumberFloat32Type, &value)); const CFIndex keyRefLen = CFStringGetLength(keyRef); if (symbolLen < keyRefLen) { symbolLen = keyRefLen; symbol = static_cast(std::realloc(symbol, symbolLen + 1)); } DISTRHO_SAFE_ASSERT_BREAK(CFStringGetCString(keyRef, symbol, symbolLen + 1, kCFStringEncodingASCII)); for (uint32_t j=0; j= kMIDIPacketListMaxDataSize) return false; const uint8_t* const midiData = midiEvent.size > MidiEvent::kDataSize ? midiEvent.dataExt : midiEvent.data; MIDIPacket* const packet = reinterpret_cast( reinterpret_cast(fMidiOutputPackets->packet) + fMidiOutputDataOffset); packet->timeStamp = midiEvent.frame; packet->length = midiEvent.size; std::memcpy(packet->data, midiData, midiEvent.size); ++fMidiOutputPackets->numPackets; fMidiOutputDataOffset += kMIDIPacketNonDataSize + midiEvent.size; return true; } static bool writeMidiCallback(void* const ptr, const MidiEvent& midiEvent) { return static_cast(ptr)->writeMidi(midiEvent); } #endif #if DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST bool requestParameterValueChange(const uint32_t index, const float value) { AudioUnitEvent event; std::memset(&event, 0, sizeof(event)); event.mEventType = kAudioUnitEvent_ParameterValueChange; event.mArgument.mParameter.mAudioUnit = fComponent; event.mArgument.mParameter.mParameterID = index; event.mArgument.mParameter.mScope = kAudioUnitScope_Global; fLastParameterValues[index] = value; AUEventListenerNotify(NULL, NULL, &event); notifyPropertyListeners('DPFp', kAudioUnitScope_Global, index); return true; } static bool requestParameterValueChangeCallback(void* const ptr, const uint32_t index, const float value) { return static_cast(ptr)->requestParameterValueChange(index, value); } #endif #if DISTRHO_PLUGIN_WANT_STATE bool updateState(const char* const key, const char* const newValue) { fPlugin.setState(key, newValue); for (uint32_t i=0; i(ptr)->updateState(key, newValue); } #endif DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginAU) }; // -------------------------------------------------------------------------------------------------------------------- struct AudioComponentPlugInInstance { AudioComponentPlugInInterface acpi; PluginAU* plugin; AudioComponentPlugInInstance() noexcept : acpi(), plugin(nullptr) { std::memset(&acpi, 0, sizeof(acpi)); acpi.Open = Open; acpi.Close = Close; acpi.Lookup = Lookup; acpi.reserved = nullptr; } ~AudioComponentPlugInInstance() { delete plugin; } static OSStatus Open(void* const self, const AudioUnit component) { d_debug("AudioComponentPlugInInstance::Open(%p)", self); static_cast(self)->plugin = new PluginAU(component); return noErr; } static OSStatus Close(void* const self) { d_debug("AudioComponentPlugInInstance::Close(%p)", self); delete static_cast(self); return noErr; } static AudioComponentMethod Lookup(const SInt16 selector) { d_debug("AudioComponentPlugInInstance::Lookup(%3d:%s)", selector, AudioUnitSelector2Str(selector)); switch (selector) { case kAudioUnitInitializeSelect: return reinterpret_cast(Initialize); case kAudioUnitUninitializeSelect: return reinterpret_cast(Uninitialize); case kAudioUnitGetPropertyInfoSelect: return reinterpret_cast(GetPropertyInfo); case kAudioUnitGetPropertySelect: return reinterpret_cast(GetProperty); case kAudioUnitSetPropertySelect: return reinterpret_cast(SetProperty); case kAudioUnitAddPropertyListenerSelect: return reinterpret_cast(AddPropertyListener); case kAudioUnitRemovePropertyListenerSelect: return reinterpret_cast(RemovePropertyListener); case kAudioUnitRemovePropertyListenerWithUserDataSelect: return reinterpret_cast(RemovePropertyListenerWithUserData); case kAudioUnitAddRenderNotifySelect: return reinterpret_cast(AddRenderNotify); case kAudioUnitRemoveRenderNotifySelect: return reinterpret_cast(RemoveRenderNotify); case kAudioUnitGetParameterSelect: return reinterpret_cast(GetParameter); case kAudioUnitSetParameterSelect: return reinterpret_cast(SetParameter); case kAudioUnitScheduleParametersSelect: return reinterpret_cast(ScheduleParameters); case kAudioUnitRenderSelect: return reinterpret_cast(Render); /* case kAudioUnitComplexRenderSelect: return reinterpret_cast(ComplexRender); */ case kAudioUnitResetSelect: return reinterpret_cast(Reset); /* case kAudioUnitProcessSelect: return reinterpret_cast(Process); case kAudioUnitProcessMultipleSelect: return reinterpret_cast(ProcessMultiple); */ #if DISTRHO_PLUGIN_WANT_MIDI_INPUT case kMusicDeviceMIDIEventSelect: return reinterpret_cast(MIDIEvent); case kMusicDeviceSysExSelect: return reinterpret_cast(SysEx); #else case kMusicDeviceMIDIEventSelect: case kMusicDeviceSysExSelect: return nullptr; #endif } d_stdout("TODO Lookup(%3d:%s)", selector, AudioUnitSelector2Str(selector)); return nullptr; } static OSStatus Initialize(AudioComponentPlugInInstance* const self) { d_debug("AudioComponentPlugInInstance::Initialize(%p)", self); return self->plugin->auInitialize(); } static OSStatus Uninitialize(AudioComponentPlugInInstance* const self) { d_debug("AudioComponentPlugInInstance::Uninitialize(%p)", self); return self->plugin->auUninitialize(); } static OSStatus GetPropertyInfo(AudioComponentPlugInInstance* const self, const AudioUnitPropertyID inProp, const AudioUnitScope inScope, const AudioUnitElement inElement, UInt32* const outDataSize, Boolean* const outWritable) { d_debug("AudioComponentPlugInInstance::GetPropertyInfo(%p, %d:%x:%s, %d:%s, %d, ...)", self, inProp, inProp, AudioUnitPropertyID2Str(inProp), inScope, AudioUnitScope2Str(inScope), inElement); UInt32 dataSize = 0; Boolean writable = false; const OSStatus res = self->plugin->auGetPropertyInfo(inProp, inScope, inElement, dataSize, writable); if (outDataSize != nullptr) *outDataSize = dataSize; if (outWritable != nullptr) *outWritable = writable; return res; } static OSStatus GetProperty(AudioComponentPlugInInstance* const self, const AudioUnitPropertyID inProp, const AudioUnitScope inScope, const AudioUnitElement inElement, void* const outData, UInt32* const ioDataSize) { #ifdef DEBUG switch (inProp) { case kAudioUnitProperty_PresentPreset: break; default: d_debug("AudioComponentPlugInInstance::GetProperty(%p, %d:%x:%s, %d:%s, %d, ...)", self, inProp, inProp, AudioUnitPropertyID2Str(inProp), inScope, AudioUnitScope2Str(inScope), inElement); break; } #endif DISTRHO_SAFE_ASSERT_RETURN(ioDataSize != nullptr, kAudio_ParamError); Boolean writable; UInt32 outDataSize = 0; OSStatus res; if (outData == nullptr) { res = self->plugin->auGetPropertyInfo(inProp, inScope, inElement, outDataSize, writable); *ioDataSize = outDataSize; return res; } const UInt32 inDataSize = *ioDataSize; if (inDataSize == 0) return kAudio_ParamError; res = self->plugin->auGetPropertyInfo(inProp, inScope, inElement, outDataSize, writable); if (res != noErr) return res; void* outBuffer; uint8_t* tmpBuffer; if (inDataSize < outDataSize) { tmpBuffer = new uint8_t[outDataSize]; outBuffer = tmpBuffer; } else { tmpBuffer = nullptr; outBuffer = outData; } res = self->plugin->auGetProperty(inProp, inScope, inElement, outBuffer); if (res != noErr) { *ioDataSize = 0; return res; } if (tmpBuffer != nullptr) { memcpy(outData, tmpBuffer, inDataSize); delete[] tmpBuffer; } else { *ioDataSize = outDataSize; } return noErr; } static OSStatus SetProperty(AudioComponentPlugInInstance* const self, const AudioUnitPropertyID inProp, const AudioUnitScope inScope, const AudioUnitElement inElement, const void* const inData, const UInt32 inDataSize) { d_debug("AudioComponentPlugInInstance::SetProperty(%p, %d:%x:%s, %d:%s, %d, %p, %u)", self, inProp, inProp, AudioUnitPropertyID2Str(inProp), inScope, AudioUnitScope2Str(inScope), inElement, inData, inDataSize); return self->plugin->auSetProperty(inProp, inScope, inElement, inData, inDataSize); } static OSStatus AddPropertyListener(AudioComponentPlugInInstance* const self, const AudioUnitPropertyID prop, const AudioUnitPropertyListenerProc proc, void* const userData) { d_debug("AudioComponentPlugInInstance::AddPropertyListener(%p, %d:%x:%s, %p, %p)", self, prop, prop, AudioUnitPropertyID2Str(prop), proc, userData); return self->plugin->auAddPropertyListener(prop, proc, userData); } static OSStatus RemovePropertyListener(AudioComponentPlugInInstance* const self, const AudioUnitPropertyID prop, const AudioUnitPropertyListenerProc proc) { d_debug("AudioComponentPlugInInstance::RemovePropertyListener(%p, %d:%x:%s, %p)", self, prop, prop, AudioUnitPropertyID2Str(prop), proc); return self->plugin->auRemovePropertyListener(prop, proc); } static OSStatus RemovePropertyListenerWithUserData(AudioComponentPlugInInstance* const self, const AudioUnitPropertyID prop, const AudioUnitPropertyListenerProc proc, void* const userData) { d_debug("AudioComponentPlugInInstance::RemovePropertyListenerWithUserData(%p, %d:%x:%s, %p, %p)", self, prop, prop, AudioUnitPropertyID2Str(prop), proc, userData); return self->plugin->auRemovePropertyListenerWithUserData(prop, proc, userData); } static OSStatus AddRenderNotify(AudioComponentPlugInInstance* const self, const AURenderCallback proc, void* const userData) { d_debug("AudioComponentPlugInInstance::AddRenderNotify(%p, %p, %p)", self, proc, userData); return self->plugin->auAddRenderNotify(proc, userData); } static OSStatus RemoveRenderNotify(AudioComponentPlugInInstance* const self, const AURenderCallback proc, void* const userData) { d_debug("AudioComponentPlugInInstance::RemoveRenderNotify(%p, %p, %p)", self, proc, userData); return self->plugin->auRemoveRenderNotify(proc, userData); } static OSStatus GetParameter(AudioComponentPlugInInstance* const self, const AudioUnitParameterID param, const AudioUnitScope scope, const AudioUnitElement elem, AudioUnitParameterValue* const value) { d_debug("AudioComponentPlugInInstance::GetParameter(%p, %d, %d:%s, %d, %p)", self, param, scope, AudioUnitScope2Str(scope), elem, value); return self->plugin->auGetParameter(param, scope, elem, value); } static OSStatus SetParameter(AudioComponentPlugInInstance* const self, const AudioUnitParameterID param, const AudioUnitScope scope, const AudioUnitElement elem, const AudioUnitParameterValue value, const UInt32 bufferOffset) { d_debug("AudioComponentPlugInInstance::SetParameter(%p, %d %d:%s, %d, %f, %u)", self, param, scope, AudioUnitScope2Str(scope), elem, value, bufferOffset); return self->plugin->auSetParameter(param, scope, elem, value, bufferOffset); } static OSStatus ScheduleParameters(AudioComponentPlugInInstance* const self, const AudioUnitParameterEvent* const events, const UInt32 numEvents) { d_debug("AudioComponentPlugInInstance::ScheduleParameters(%p, %p, %u)", self, events, numEvents); return self->plugin->auScheduleParameters(events, numEvents); } static OSStatus Reset(AudioComponentPlugInInstance* const self, const AudioUnitScope scope, const AudioUnitElement elem) { d_debug("AudioComponentPlugInInstance::Reset(%p, %d:%s, %d)", self, scope, AudioUnitScope2Str(scope), elem); return self->plugin->auReset(scope, elem); } static OSStatus Render(AudioComponentPlugInInstance* const self, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* const inTimeStamp, const UInt32 inOutputBusNumber, const UInt32 inNumberFrames, AudioBufferList* const ioData) { const AudioUnitRenderActionFlags actionFlags = ioActionFlags != nullptr ? *ioActionFlags : 0; if ((actionFlags & kAudioUnitRenderAction_DoNotCheckRenderArgs) == 0x0) { DISTRHO_SAFE_ASSERT_RETURN(inTimeStamp != nullptr, kAudio_ParamError); DISTRHO_SAFE_ASSERT_RETURN(ioData != nullptr, kAudio_ParamError); } return self->plugin->auRender(actionFlags, inTimeStamp, inOutputBusNumber, inNumberFrames, ioData); } #if DISTRHO_PLUGIN_WANT_MIDI_INPUT static OSStatus MIDIEvent(AudioComponentPlugInInstance* const self, const UInt32 inStatus, const UInt32 inData1, const UInt32 inData2, const UInt32 inOffsetSampleFrame) { return self->plugin->auMIDIEvent(inStatus, inData1, inData2, inOffsetSampleFrame); } static OSStatus SysEx(AudioComponentPlugInInstance* const self, const UInt8* const inData, const UInt32 inLength) { return self->plugin->auSysEx(inData, inLength); } #endif DISTRHO_DECLARE_NON_COPYABLE(AudioComponentPlugInInstance) }; #if 0 static OSStatus FastDispatchGetParameter(void* const self, const AudioUnitParameterID param, const AudioUnitScope scope, const AudioUnitElement elem, Float32* const value) { d_debug("FastDispatchGetParameter(%p, %d, %d:%s, %d, %p)", self, param, scope, AudioUnitScope2Str(scope), elem, value); return static_cast(self)->plugin->auGetParameter(param, scope, elem, value); } static OSStatus FastDispatchSetParameter(void* const self, const AudioUnitParameterID param, const AudioUnitScope scope, const AudioUnitElement elem, const Float32 value, const UInt32 bufferOffset) { d_debug("FastDispatchSetParameter(%p, %d %d:%s, %d, %f, %u)", self, param, scope, AudioUnitScope2Str(scope), elem, value, bufferOffset); return static_cast(self)->plugin->auSetParameter(param, scope, elem, value, bufferOffset); } static OSStatus FastDispatchRender(void* const self, AudioUnitRenderActionFlags* const ioActionFlags, const AudioTimeStamp* const inTimeStamp, const UInt32 inBusNumber, const UInt32 inNumberFrames, AudioBufferList* const ioData) { DISTRHO_SAFE_ASSERT_RETURN(inTimeStamp != nullptr, kAudio_ParamError); DISTRHO_SAFE_ASSERT_RETURN(ioData != nullptr, kAudio_ParamError); return static_cast(self)->plugin->auRender(ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, *ioData); } #endif // -------------------------------------------------------------------------------------------------------------------- END_NAMESPACE_DISTRHO DISTRHO_PLUGIN_EXPORT void* PluginAUFactory(const AudioComponentDescription* const desc) { USE_NAMESPACE_DISTRHO DISTRHO_SAFE_ASSERT_UINT2_RETURN(desc->componentType == kType, desc->componentType, kType, nullptr); DISTRHO_SAFE_ASSERT_UINT2_RETURN(desc->componentSubType == kSubType, desc->componentSubType, kSubType, nullptr); DISTRHO_SAFE_ASSERT_UINT2_RETURN(desc->componentManufacturer == kManufacturer, desc->componentManufacturer, kManufacturer, nullptr); if (d_nextBufferSize == 0) d_nextBufferSize = 1156; if (d_isZero(d_nextSampleRate)) d_nextSampleRate = 44100.0; if (d_nextBundlePath == nullptr) { static String bundlePath; String tmpPath(getBinaryFilename()); tmpPath.truncate(tmpPath.rfind(DISTRHO_OS_SEP)); tmpPath.truncate(tmpPath.rfind(DISTRHO_OS_SEP)); if (tmpPath.endsWith(DISTRHO_OS_SEP_STR "Contents")) { tmpPath.truncate(tmpPath.rfind(DISTRHO_OS_SEP)); bundlePath = tmpPath; } else { bundlePath = "error"; } d_nextBundlePath = bundlePath.buffer(); } d_nextCanRequestParameterValueChanges = true; return new AudioComponentPlugInInstance(); } // --------------------------------------------------------------------------------------------------------------------