diff --git a/distrho/src/DistrhoPluginAU.cpp b/distrho/src/DistrhoPluginAU.cpp index a774f751..b02e9938 100644 --- a/distrho/src/DistrhoPluginAU.cpp +++ b/distrho/src/DistrhoPluginAU.cpp @@ -200,14 +200,33 @@ struct PropertyListener { void* userData; }; +struct RenderListener { + AURenderCallback proc; + void* userData; +}; + typedef std::vector PropertyListeners; +typedef std::vector RenderListeners; // -------------------------------------------------------------------------------------------------------------------- +#if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS == 0 +# define DPF_AU_NUM_BUFFERS 1 +#elif DISTRHO_PLUGIN_NUM_INPUTS > DISTRHO_PLUGIN_NUM_OUTPUTS +# define DPF_AU_NUM_BUFFERS DISTRHO_PLUGIN_NUM_INPUTS +#else +# define DPF_AU_NUM_BUFFERS DISTRHO_PLUGIN_NUM_OUTPUTS +#endif + +typedef struct { + UInt32 mNumberBuffers; + AudioBuffer mBuffers[DPF_AU_NUM_BUFFERS]; +} d_AudioBufferList; + typedef struct { UInt32 numPackets; MIDIPacket packets[kMaxMidiEvents]; -} MIDIPacketList; +} d_MIDIPacketList; // -------------------------------------------------------------------------------------------------------------------- @@ -233,6 +252,7 @@ public: fComponent(component), fLastRenderError(noErr), fPropertyListeners(), + fRenderListeners(), fSampleRateForInput(d_nextSampleRate), #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0 fSampleRateForOutput(d_nextSampleRate), @@ -240,6 +260,7 @@ public: #if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS == 0 fHasWorkingAudio(false), #endif + fUsingRenderListeners(false), fParameterCount(fPlugin.getParameterCount()), fLastParameterValues(nullptr), fBypassParameterIndex(UINT32_MAX) @@ -256,6 +277,8 @@ public: , fStateCount(fPlugin.getStateCount()) #endif { + const uint32_t bufferSize = fPlugin.getBufferSize(); + if (fParameterCount != 0) { fLastParameterValues = new float[fParameterCount]; @@ -274,6 +297,15 @@ public: fInputRenderCallback.inputProc = nullptr; fInputRenderCallback.inputProcRefCon = nullptr; + fAudioBufferList.mNumberBuffers = DPF_AU_NUM_BUFFERS; + + for (uint16_t i=0; i(fAudioBufferList.mBuffers[i].mData); + #if DISTRHO_PLUGIN_WANT_PROGRAMS for (uint32_t i=0; imSampleRate, true); } - notifyListeners(inProp, inScope, inElement); - notifyListeners(kAudioUnitProperty_SampleRate, inScope, inElement); + notifyPropertyListeners(inProp, inScope, inElement); + notifyPropertyListeners(kAudioUnitProperty_SampleRate, inScope, inElement); } #if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS == 0 fHasWorkingAudio = true; @@ -1132,8 +1167,8 @@ public: fPlugin.setSampleRate(desc->mSampleRate, true); } - notifyListeners(inProp, inScope, inElement); - notifyListeners(kAudioUnitProperty_SampleRate, inScope, inElement); + notifyPropertyListeners(inProp, inScope, inElement); + notifyPropertyListeners(kAudioUnitProperty_SampleRate, inScope, inElement); } return noErr; } @@ -1145,10 +1180,23 @@ public: 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(*static_cast(inData), true)) - notifyListeners(inProp, inScope, inElement); + if (fPlugin.setBufferSize(bufferSize, true)) + { + notifyPropertyListeners(inProp, inScope, inElement); + #if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS != 0 + for (uint16_t i=0; i(fAudioBufferList.mBuffers[i].mData); + fAudioBufferList.mBuffers[i].mData = new float[bufferSize]; + fAudioBufferList.mBuffers[i].mDataByteSize = sizeof(float) * bufferSize; + } + #endif + } + } return noErr; case kAudioUnitProperty_BypassEffect: @@ -1164,7 +1212,7 @@ public: const float value = bypass ? 1.f : 0.f; fLastParameterValues[fBypassParameterIndex] = value; fPlugin.setParameterValue(fBypassParameterIndex, value); - notifyListeners(inProp, inScope, inElement); + notifyPropertyListeners(inProp, inScope, inElement); } } return noErr; @@ -1190,7 +1238,7 @@ public: std::memset(&fHostCallbackInfo + usableDataSize, 0, sizeof(HostCallbackInfo) - usableDataSize); if (changed) - notifyListeners(inProp, inScope, inElement); + notifyPropertyListeners(inProp, inScope, inElement); } return noErr; #else @@ -1212,7 +1260,7 @@ public: fCurrentProgram = presetNumber; fLastFactoryProgram = presetNumber; fPlugin.loadProgram(fLastFactoryProgram); - notifyListeners('DPFo', kAudioUnitScope_Global, 0); + notifyPropertyListeners('DPFo', kAudioUnitScope_Global, 0); } } else @@ -1251,16 +1299,16 @@ public: return noErr; #if DISTRHO_PLUGIN_WANT_PROGRAMS - notifyListeners('DPFo', kAudioUnitScope_Global, 0); + notifyPropertyListeners('DPFo', kAudioUnitScope_Global, 0); #endif #if DISTRHO_PLUGIN_WANT_STATE for (uint32_t i=0; i(DISTRHO_PLUGIN_NUM_INPUTS, DISTRHO_PLUGIN_NUM_OUTPUTS), ioData.mNumberBuffers, kAudio_ParamError); - - if (inFramesToProcess > fPlugin.getBufferSize()) - { - setLastRenderError(kAudioUnitErr_TooManyFramesToProcess); - return kAudioUnitErr_TooManyFramesToProcess; - } - - for (uint i=0; imNumberBuffers == DPF_AU_NUM_BUFFERS, + ioData->mNumberBuffers, kAudio_ParamError); - // TODO there must be something more to this... - if (buffer.mData == nullptr) + if (inFramesToProcess > fPlugin.getBufferSize()) { - return noErr; + setLastRenderError(kAudioUnitErr_TooManyFramesToProcess); + return kAudioUnitErr_TooManyFramesToProcess; } - DISTRHO_SAFE_ASSERT_UINT_RETURN(buffer.mDataByteSize == inFramesToProcess * sizeof(float), buffer.mDataByteSize, kAudio_ParamError); + for (uint16_t i=0; imBuffers[i].mDataByteSize != sizeof(float) * inFramesToProcess) + { + setLastRenderError(kAudio_ParamError); + return kAudio_ParamError; + } + } } #if DISTRHO_PLUGIN_NUM_INPUTS != 0 const float* inputs[DISTRHO_PLUGIN_NUM_INPUTS]; - - for (uint16_t i=0; i(ioData.mBuffers[i].mData); - } #else constexpr const float** inputs = nullptr; #endif #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0 float* outputs[DISTRHO_PLUGIN_NUM_OUTPUTS]; - - for (uint16_t i=0; i(ioData.mBuffers[i].mData); - } #else constexpr float** outputs = nullptr; #endif + if (fInputRenderCallback.inputProc != nullptr) + { + bool adjustDataByteSize, usingHostBuffer = true; + UInt32 prevDataByteSize; + + for (uint16_t i=0; imBuffers[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(&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->mBuffers[i].mData); + #endif + + #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0 + for (uint16_t i=0; i(ioData->mBuffers[i].mData); + #endif + + } + else + { + #if DISTRHO_PLUGIN_NUM_INPUTS != 0 + for (uint16_t i=0; i(fAudioBufferList.mBuffers[i].mData); + #endif + + #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0 + for (uint16_t i=0; i(ioData->mBuffers[i].mData); + #endif + } + } + else + { + #if DISTRHO_PLUGIN_NUM_INPUTS != 0 + for (uint16_t i=0; imBuffers[i].mData == nullptr) + ioData->mBuffers[i].mData = fAudioBufferList.mBuffers[i].mData; + + inputs[i] = static_cast(ioData->mBuffers[i].mData); + } + #endif + + #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0 + for (uint16_t i=0; imBuffers[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 (ioActionFlags != nullptr) + if (fUsingRenderListeners) { - // TODO what now? + AudioUnitRenderActionFlags ioActionFlags = actionFlags | kAudioUnitRenderAction_PostRender; + notifyRenderListeners(&ioActionFlags, inTimeStamp, inBusNumber, inFramesToProcess, ioData); } return noErr; @@ -1667,14 +1823,17 @@ private: // AUv2 related fields OSStatus fLastRenderError; PropertyListeners fPropertyListeners; + RenderListeners fRenderListeners; AURenderCallbackStruct fInputRenderCallback; Float64 fSampleRateForInput; #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0 Float64 fSampleRateForOutput; #endif + d_AudioBufferList fAudioBufferList; #if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS == 0 bool fHasWorkingAudio; #endif + bool fUsingRenderListeners; // Caching const uint32_t fParameterCount; @@ -1689,7 +1848,7 @@ private: #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT AUMIDIOutputCallbackStruct fMidiOutput; - MIDIPacketList fMidiOutputPackets; + d_MIDIPacketList fMidiOutputPackets; #endif #if DISTRHO_PLUGIN_WANT_PROGRAMS @@ -1712,7 +1871,7 @@ private: // ---------------------------------------------------------------------------------------------------------------- - void notifyListeners(const AudioUnitPropertyID prop, const AudioUnitScope scope, const AudioUnitElement elem) + void notifyPropertyListeners(const AudioUnitPropertyID prop, const AudioUnitScope scope, const AudioUnitElement elem) { for (PropertyListeners::iterator it = fPropertyListeners.begin(); it != fPropertyListeners.end(); ++it) { @@ -1723,6 +1882,22 @@ private: } } + 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 @@ -1862,7 +2037,7 @@ private: // TODO flag param only, notify listeners later on bg thread (sem_post etc) event.mArgument.mParameter.mParameterID = i; AUEventListenerNotify(NULL, NULL, &event); - notifyListeners('DPFp', kAudioUnitScope_Global, i); + notifyPropertyListeners('DPFp', kAudioUnitScope_Global, i); } } } @@ -1873,7 +2048,7 @@ private: return; fLastRenderError = err; - notifyListeners(kAudioUnitProperty_LastRenderError, kAudioUnitScope_Global, 0); + notifyPropertyListeners(kAudioUnitProperty_LastRenderError, kAudioUnitScope_Global, 0); } // ---------------------------------------------------------------------------------------------------------------- @@ -2063,10 +2238,10 @@ private: { fLastFactoryProgram = program; fPlugin.loadProgram(fLastFactoryProgram); - notifyListeners('DPFo', kAudioUnitScope_Global, 0); + notifyPropertyListeners('DPFo', kAudioUnitScope_Global, 0); } - notifyListeners(kAudioUnitProperty_PresentPreset, kAudioUnitScope_Global, 0); + notifyPropertyListeners(kAudioUnitProperty_PresentPreset, kAudioUnitScope_Global, 0); } } #endif @@ -2124,7 +2299,7 @@ private: if (fPlugin.getStateKey(j) == key) { if ((fPlugin.getStateHints(i) & kStateIsOnlyForDSP) == 0x0) - notifyListeners('DPFs', kAudioUnitScope_Global, j); + notifyPropertyListeners('DPFs', kAudioUnitScope_Global, j); break; } @@ -2178,10 +2353,10 @@ private: fLastParameterValues[j] = value; fPlugin.setParameterValue(j, value); - notifyListeners('DPFp', kAudioUnitScope_Global, j); + notifyPropertyListeners('DPFp', kAudioUnitScope_Global, j); if (fBypassParameterIndex == j) - notifyListeners(kAudioUnitProperty_BypassEffect, kAudioUnitScope_Global, 0); + notifyPropertyListeners(kAudioUnitProperty_BypassEffect, kAudioUnitScope_Global, 0); break; } @@ -2230,7 +2405,7 @@ private: fLastParameterValues[index] = value; AUEventListenerNotify(NULL, NULL, &event); - notifyListeners('DPFp', kAudioUnitScope_Global, index); + notifyPropertyListeners('DPFp', kAudioUnitScope_Global, index); return true; } @@ -2253,7 +2428,7 @@ private: fStateMap[dkey] = newValue; if ((fPlugin.getStateHints(i) & kStateIsOnlyForDSP) == 0x0) - notifyListeners('DPFs', kAudioUnitScope_Global, i); + notifyPropertyListeners('DPFs', kAudioUnitScope_Global, i); return true; } @@ -2571,10 +2746,15 @@ struct AudioComponentPlugInInstance { const UInt32 inNumberFrames, AudioBufferList* const ioData) { - DISTRHO_SAFE_ASSERT_RETURN(inTimeStamp != nullptr, kAudio_ParamError); - DISTRHO_SAFE_ASSERT_RETURN(ioData != nullptr, kAudio_ParamError); + 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(ioActionFlags, inTimeStamp, inOutputBusNumber, inNumberFrames, *ioData); + return self->plugin->auRender(actionFlags, inTimeStamp, inOutputBusNumber, inNumberFrames, ioData); } #if DISTRHO_PLUGIN_WANT_MIDI_INPUT