DPF

DISTRHO Plugin Framework
Log | Files | Refs | Submodules | README | LICENSE

commit 19cb24186bb25e9e17586e2055eaee0e6d71b3f9
parent 808d4fea316aa5fda1909a49a2341cad265b3b66
Author: falkTX <falktx@falktx.com>
Date:   Mon, 26 Feb 2024 03:30:14 +0100

Deal with more AU audio rendering details

Signed-off-by: falkTX <falktx@falktx.com>

Diffstat:
Mdistrho/src/DistrhoPluginAU.cpp | 316++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
1 file changed, 248 insertions(+), 68 deletions(-)

diff --git 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<PropertyListener> PropertyListeners; +typedef std::vector<RenderListener> 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<DPF_AU_NUM_BUFFERS; ++i) + { + fAudioBufferList.mBuffers[i].mNumberChannels = 1; + fAudioBufferList.mBuffers[i].mData = new float[bufferSize]; + fAudioBufferList.mBuffers[i].mDataByteSize = sizeof(float) * bufferSize; + } + #if DISTRHO_PLUGIN_WANT_MIDI_INPUT std::memset(&fMidiEvents, 0, sizeof(fMidiEvents)); #endif @@ -331,6 +363,9 @@ public: delete[] fLastParameterValues; CFRelease(fUserPresetData.presetName); + for (uint16_t i=0; i<DPF_AU_NUM_BUFFERS; ++i) + delete[] static_cast<float*>(fAudioBufferList.mBuffers[i].mData); + #if DISTRHO_PLUGIN_WANT_PROGRAMS for (uint32_t i=0; i<fProgramCount; ++i) CFRelease(fFactoryPresetsData[i].presetName); @@ -1037,7 +1072,7 @@ public: fPlugin.setSampleRate(sampleRate, true); } - notifyListeners(inProp, inScope, inElement); + notifyPropertyListeners(inProp, inScope, inElement); } return noErr; } @@ -1055,7 +1090,7 @@ public: fPlugin.setSampleRate(sampleRate, true); } - notifyListeners(inProp, inScope, inElement); + notifyPropertyListeners(inProp, inScope, inElement); } return noErr; } @@ -1111,8 +1146,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); } #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<const UInt32*>(inData); - if (fPlugin.setBufferSize(*static_cast<const UInt32*>(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<DPF_AU_NUM_BUFFERS; ++i) + { + delete[] static_cast<float*>(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<fStateCount; ++i) - notifyListeners('DPFs', kAudioUnitScope_Global, i); + notifyPropertyListeners('DPFs', kAudioUnitScope_Global, i); #endif for (uint32_t i=0; i<fParameterCount; ++i) - notifyListeners('DPFp', kAudioUnitScope_Global, i); + notifyPropertyListeners('DPFp', kAudioUnitScope_Global, i); } return noErr; @@ -1310,7 +1358,7 @@ public: AUEventListenerNotify(NULL, NULL, &event); if (fBypassParameterIndex == inElement) - notifyListeners(kAudioUnitProperty_BypassEffect, kAudioUnitScope_Global, 0); + notifyPropertyListeners(kAudioUnitProperty_BypassEffect, kAudioUnitScope_Global, 0); } return noErr; @@ -1382,9 +1430,6 @@ public: prop, proc, userData }; - if (fPropertyListeners.empty()) - fPropertyListeners.reserve(32); - fPropertyListeners.push_back(pl); return noErr; } @@ -1425,15 +1470,29 @@ public: OSStatus auAddRenderNotify(const AURenderCallback proc, void* const userData) { - d_stdout("WIP AddRenderNotify(%p, %p)", proc, userData); - // TODO + fUsingRenderListeners = true; + + const RenderListener rl = { + proc, userData + }; + + fRenderListeners.push_back(rl); return noErr; } OSStatus auRemoveRenderNotify(const AURenderCallback proc, void* const userData) { - d_stdout("WIP RemoveRenderNotify(%p, %p)", proc, userData); - // TODO + 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); + } + } + return noErr; } @@ -1468,10 +1527,10 @@ public: fPlugin.setParameterValue(param, value); // TODO flag param only, notify listeners later on bg thread (sem_post etc) - notifyListeners('DPFp', kAudioUnitScope_Global, param); + notifyPropertyListeners('DPFp', kAudioUnitScope_Global, param); if (fBypassParameterIndex == elem) - notifyListeners(kAudioUnitProperty_BypassEffect, kAudioUnitScope_Global, 0); + notifyPropertyListeners(kAudioUnitProperty_BypassEffect, kAudioUnitScope_Global, 0); } return noErr; @@ -1523,61 +1582,158 @@ public: return noErr; } - OSStatus auRender(AudioUnitRenderActionFlags* const ioActionFlags, + OSStatus auRender(const AudioUnitRenderActionFlags actionFlags, const AudioTimeStamp* const inTimeStamp, const UInt32 inBusNumber, const UInt32 inFramesToProcess, - AudioBufferList& ioData) + AudioBufferList* const ioData) { - DISTRHO_SAFE_ASSERT_UINT_RETURN(inBusNumber == 0, inBusNumber, kAudioUnitErr_InvalidElement); - DISTRHO_SAFE_ASSERT_UINT_RETURN(ioData.mNumberBuffers == std::max<uint>(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; i<ioData.mNumberBuffers; ++i) + if ((actionFlags & kAudioUnitRenderAction_DoNotCheckRenderArgs) == 0x0) { - AudioBuffer& buffer(ioData.mBuffers[i]); + DISTRHO_SAFE_ASSERT_UINT_RETURN(inBusNumber == 0, inBusNumber, kAudioUnitErr_InvalidElement); + DISTRHO_SAFE_ASSERT_UINT_RETURN(ioData->mNumberBuffers == 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; i<DPF_AU_NUM_BUFFERS; ++i) + { + if (ioData->mBuffers[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<DISTRHO_PLUGIN_NUM_INPUTS; ++i) - { - inputs[i] = static_cast<const float*>(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<DISTRHO_PLUGIN_NUM_OUTPUTS; ++i) - { - outputs[i] = static_cast<float*>(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; i<DPF_AU_NUM_BUFFERS; ++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<DPF_AU_NUM_BUFFERS; ++i) + fAudioBufferList.mBuffers[i].mDataByteSize = sizeof(float) * inFramesToProcess; + } + } + else + { + adjustDataByteSize = false; + } + + AudioUnitRenderActionFlags rActionFlags = 0; + AudioBufferList* const rData = usingHostBuffer ? ioData : reinterpret_cast<AudioBufferList*>(&fAudioBufferList); + const OSStatus err = fInputRenderCallback.inputProc(fInputRenderCallback.inputProcRefCon, + &rActionFlags, + inTimeStamp, + inBusNumber, + inFramesToProcess, + rData); + + if (err != noErr) + { + if (adjustDataByteSize) + { + for (uint16_t i=0; i<DPF_AU_NUM_BUFFERS; ++i) + fAudioBufferList.mBuffers[i].mDataByteSize = prevDataByteSize; + } + + setLastRenderError(err); + return err; + } + + if (usingHostBuffer) + { + #if DISTRHO_PLUGIN_NUM_INPUTS != 0 + for (uint16_t i=0; i<DISTRHO_PLUGIN_NUM_INPUTS; ++i) + inputs[i] = static_cast<const float*>(ioData->mBuffers[i].mData); + #endif + + #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0 + for (uint16_t i=0; i<DISTRHO_PLUGIN_NUM_OUTPUTS; ++i) + outputs[i] = static_cast<float*>(ioData->mBuffers[i].mData); + #endif + + } + else + { + #if DISTRHO_PLUGIN_NUM_INPUTS != 0 + for (uint16_t i=0; i<DISTRHO_PLUGIN_NUM_INPUTS; ++i) + inputs[i] = static_cast<const float*>(fAudioBufferList.mBuffers[i].mData); + #endif + + #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0 + for (uint16_t i=0; i<DISTRHO_PLUGIN_NUM_OUTPUTS; ++i) + outputs[i] = static_cast<float*>(ioData->mBuffers[i].mData); + #endif + } + } + else + { + #if DISTRHO_PLUGIN_NUM_INPUTS != 0 + for (uint16_t i=0; i<DISTRHO_PLUGIN_NUM_INPUTS; ++i) + { + if (ioData->mBuffers[i].mData == nullptr) + ioData->mBuffers[i].mData = fAudioBufferList.mBuffers[i].mData; + + inputs[i] = static_cast<const float*>(ioData->mBuffers[i].mData); + } + #endif + + #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0 + for (uint16_t i=0; i<DISTRHO_PLUGIN_NUM_OUTPUTS; ++i) + { + if (ioData->mBuffers[i].mData == nullptr) + ioData->mBuffers[i].mData = fAudioBufferList.mBuffers[i].mData; + + outputs[i] = static_cast<float*>(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