diff --git a/distrho/DistrhoPlugin.hpp b/distrho/DistrhoPlugin.hpp index 6f01fa2f..fc113980 100644 --- a/distrho/DistrhoPlugin.hpp +++ b/distrho/DistrhoPlugin.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2022 Filipe Coelho + * 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 @@ -373,6 +373,14 @@ protected: */ virtual void sampleRateChanged(double newSampleRate); + /** + Optional callback to inform the plugin about audio port IO changes.@n + This function will only be called when the plugin is deactivated.@n + Only used in AU (AudioUnit) format when DISTRHO_PLUGIN_EXTRA_IO is defined. + @see DISTRHO_PLUGIN_EXTRA_IO + */ + virtual void ioChanged(uint16_t numInputs, uint16_t numOutputs); + // ------------------------------------------------------------------------------------------------------- private: diff --git a/distrho/src/DistrhoPlugin.cpp b/distrho/src/DistrhoPlugin.cpp index 3436d475..e2ffadb2 100644 --- a/distrho/src/DistrhoPlugin.cpp +++ b/distrho/src/DistrhoPlugin.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2023 Filipe Coelho + * 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 @@ -246,6 +246,7 @@ void Plugin::setState(const char*, const char*) {} void Plugin::bufferSizeChanged(uint32_t) {} void Plugin::sampleRateChanged(double) {} +void Plugin::ioChanged(uint16_t, uint16_t) {} // ----------------------------------------------------------------------------------------------------------- diff --git a/distrho/src/DistrhoPluginAU.cpp b/distrho/src/DistrhoPluginAU.cpp index 21b5d252..75139b35 100644 --- a/distrho/src/DistrhoPluginAU.cpp +++ b/distrho/src/DistrhoPluginAU.cpp @@ -200,6 +200,64 @@ static constexpr const uint32_t kType = d_cconst(STRINGIFY(DISTRHO_PLUGIN_AU_TYP 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 { @@ -218,25 +276,6 @@ typedef std::vector RenderListeners; // -------------------------------------------------------------------------------------------------------------------- -#if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS != 0 -# if 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 -#else -# define DPF_AU_NUM_BUFFERS 0 -#endif - -#if DPF_AU_NUM_BUFFERS != 0 -typedef struct { - UInt32 mNumberBuffers; - AudioBuffer mBuffers[DPF_AU_NUM_BUFFERS]; -} d_AudioBufferList; -#endif - -// -------------------------------------------------------------------------------------------------------------------- - typedef struct { UInt32 numPackets; MIDIPacket packets[kMaxMidiEvents]; @@ -270,11 +309,22 @@ public: fLastRenderError(noErr), fPropertyListeners(), fRenderListeners(), - #if DISTRHO_PLUGIN_NUM_INPUTS != 0 + #if DISTRHO_PLUGIN_NUM_INPUTS != 0 + fInputConnectionBus(0), + fInputConnectionUnit(nullptr), fSampleRateForInput(d_nextSampleRate), + #ifdef DISTRHO_PLUGIN_EXTRA_IO + fNumInputs(DISTRHO_PLUGIN_NUM_INPUTS), #endif - #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0 + #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()), @@ -313,19 +363,6 @@ public: fInputRenderCallback.inputProcRefCon = nullptr; #endif - #if DPF_AU_NUM_BUFFERS != 0 - const uint32_t bufferSize = fPlugin.getBufferSize(); - - fAudioBufferList.mNumberBuffers = DPF_AU_NUM_BUFFERS; - - for (uint16_t i=0; i(fAudioBufferList.mBuffers[i].mData); + #if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS != 0 + reallocAudioBufferList(false); #endif #if DISTRHO_PLUGIN_WANT_PROGRAMS @@ -399,7 +435,16 @@ public: OSStatus auInitialize() { - fPlugin.activate(); + #if defined(DISTRHO_PLUGIN_EXTRA_IO) && DISTRHO_PLUGIN_NUM_INPUTS != 0 && DISTRHO_PLUGIN_NUM_OUTPUTS != 0 + if (! isNumChannelsComboValid(fNumInputs, fNumOutputs)) + return kAudioUnitErr_FormatNotSupported; + #endif + + #if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS != 0 + if (! reallocAudioBufferList(true)) + return kAudio_ParamError; + #endif + #if DISTRHO_PLUGIN_WANT_MIDI_INPUT fMidiEventCount = 0; #endif @@ -410,12 +455,18 @@ public: 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; } @@ -435,16 +486,20 @@ public: return noErr; case kAudioUnitProperty_MakeConnection: - DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global || inScope == kAudioUnitScope_Input, inScope, kAudioUnitErr_InvalidScope); + 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 && 0) + if (inScope == kAudioUnitScope_Input) { outDataSize = sizeof(Float64); outWritable = true; @@ -523,7 +578,7 @@ public: 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(AUChannelInfo); + outDataSize = sizeof(kChannelInfo); outWritable = false; return noErr; @@ -587,7 +642,7 @@ public: 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 + #if DISTRHO_PLUGIN_NUM_INPUTS != 0 && DISTRHO_PLUGIN_NUM_OUTPUTS != 0 outDataSize = sizeof(UInt32); outWritable = false; return noErr; @@ -853,14 +908,22 @@ public: #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 @@ -869,7 +932,7 @@ public: } desc->mFormatID = kAudioFormatLinearPCM; - desc->mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved; + desc->mFormatFlags = kWantedAudioFormat; desc->mSampleRate = fPlugin.getSampleRate(); desc->mBitsPerChannel = 32; desc->mBytesPerFrame = sizeof(float); @@ -903,7 +966,7 @@ public: #endif case kAudioUnitProperty_SupportedNumChannels: - *static_cast(outData) = { DISTRHO_PLUGIN_NUM_INPUTS, DISTRHO_PLUGIN_NUM_OUTPUTS }; + std::memcpy(outData, kChannelInfo, sizeof(kChannelInfo)); return noErr; case kAudioUnitProperty_MaximumFramesPerSlice: @@ -944,7 +1007,7 @@ public: return noErr; #endif - #if DISTRHO_PLUGIN_NUM_INPUTS != 0 + #if DISTRHO_PLUGIN_NUM_INPUTS != 0 && DISTRHO_PLUGIN_NUM_OUTPUTS != 0 case kAudioUnitProperty_InPlaceProcessing: *static_cast(outData) = 1; return noErr; @@ -1090,13 +1153,56 @@ public: return noErr; case kAudioUnitProperty_MakeConnection: - DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global || inScope == kAudioUnitScope_Input, inScope, kAudioUnitErr_InvalidScope); + 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); - d_stdout("WIP SetProperty(%d:%x:%s, %d:%s, %d, %p, %u)", - inProp, inProp, AudioUnitPropertyID2Str(inProp), inScope, AudioUnitScope2Str(inScope), inElement, inData, inDataSize); - // TODO + #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 @@ -1155,7 +1261,7 @@ public: } #endif } - return kAudioUnitErr_InvalidScope; + return kAudioUnitErr_PropertyNotInUse; case kAudioUnitProperty_StreamFormat: #if DISTRHO_PLUGIN_NUM_INPUTS != 0 && DISTRHO_PLUGIN_NUM_OUTPUTS != 0 @@ -1180,17 +1286,41 @@ public: return kAudioUnitErr_FormatNotSupported; if (desc->mFramesPerPacket != 1) return kAudioUnitErr_FormatNotSupported; - if (desc->mFormatFlags != (kAudioFormatFlagsNativeFloatPacked|kAudioFormatFlagIsNonInterleaved)) + 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 @@ -1200,9 +1330,12 @@ public: fPlugin.setSampleRate(desc->mSampleRate, true); } - notifyPropertyListeners(inProp, inScope, inElement); notifyPropertyListeners(kAudioUnitProperty_SampleRate, inScope, inElement); } + + if (changed) + notifyPropertyListeners(inProp, inScope, inElement); + return noErr; } #endif @@ -1210,8 +1343,29 @@ public: #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 @@ -1221,14 +1375,17 @@ public: fPlugin.setSampleRate(desc->mSampleRate, true); } - notifyPropertyListeners(inProp, inScope, inElement); notifyPropertyListeners(kAudioUnitProperty_SampleRate, inScope, inElement); } + + if (changed) + notifyPropertyListeners(inProp, inScope, inElement); + return noErr; } #endif } - return kAudioUnitErr_InvalidScope; + return kAudioUnitErr_PropertyNotInUse; case kAudioUnitProperty_MaximumFramesPerSlice: DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); @@ -1238,18 +1395,7 @@ public: const UInt32 bufferSize = *static_cast(inData); if (fPlugin.setBufferSize(bufferSize, true)) - { notifyPropertyListeners(inProp, inScope, inElement); - - #if DPF_AU_NUM_BUFFERS != 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; @@ -1271,14 +1417,16 @@ public: } return noErr; - #if DISTRHO_PLUGIN_NUM_INPUTS != 0 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; - #endif + #else + return kAudioUnitErr_PropertyNotInUse; + #endif case kAudioUnitProperty_HostCallbacks: DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); @@ -1470,7 +1618,7 @@ public: // unwanted properties case kMusicDeviceProperty_DualSchedulingMode: - return kAudioUnitErr_InvalidProperty; + return kAudioUnitErr_PropertyNotInUse; } d_stdout("TODO SetProperty(%d:%x:%s, %d:%s, %d, %p, %u)", @@ -1549,6 +1697,9 @@ public: } } + if (fRenderListeners.empty()) + fUsingRenderListeners = false; + return noErr; } @@ -1647,9 +1798,14 @@ public: { if ((actionFlags & kAudioUnitRenderAction_DoNotCheckRenderArgs) == 0x0) { + DISTRHO_SAFE_ASSERT_RETURN(fPlugin.isActive(), kAudio_ParamError); DISTRHO_SAFE_ASSERT_UINT_RETURN(inBusNumber == 0, inBusNumber, kAudioUnitErr_InvalidElement); - DISTRHO_SAFE_ASSERT_UINT_RETURN(ioData->mNumberBuffers == DPF_AU_NUM_BUFFERS, + #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()) { @@ -1657,8 +1813,8 @@ public: return kAudioUnitErr_TooManyFramesToProcess; } - #if DPF_AU_NUM_BUFFERS != 0 - for (uint16_t i=0; imNumberBuffers; ++i) { if (ioData->mBuffers[i].mDataByteSize != sizeof(float) * inFramesToProcess) { @@ -1669,42 +1825,81 @@ public: #endif } - #if DISTRHO_PLUGIN_NUM_INPUTS != 0 - const float* inputs[DISTRHO_PLUGIN_NUM_INPUTS]; + #if DISTRHO_PLUGIN_NUM_INPUTS != 0 + #ifdef DISTRHO_PLUGIN_EXTRA_IO + const uint32_t numInputs = fNumInputs; #else - constexpr const float** inputs = nullptr; + 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 - float* outputs[DISTRHO_PLUGIN_NUM_OUTPUTS]; + #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0 + #ifdef DISTRHO_PLUGIN_EXTRA_IO + const uint32_t numOutputs = fNumOutputs; #else - constexpr float** outputs = nullptr; + 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 (fInputRenderCallback.inputProc != nullptr) + 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; imNumberBuffers; ++i) { if (ioData->mBuffers[i].mData == nullptr) { usingHostBuffer = false; - ioData->mBuffers[i].mData = fAudioBufferList.mBuffers[i].mData; + ioData->mBuffers[i].mData = fAudioBufferList->mBuffers[i].mData; } } if (! usingHostBuffer) { - prevDataByteSize = fAudioBufferList.mBuffers[0].mDataByteSize; + prevDataByteSize = fAudioBufferList->mBuffers[0].mDataByteSize; adjustDataByteSize = prevDataByteSize != sizeof(float) * inFramesToProcess; if (adjustDataByteSize) { - for (uint16_t i=0; imNumberBuffers; ++i) + fAudioBufferList->mBuffers[i].mDataByteSize = sizeof(float) * inFramesToProcess; } } else @@ -1713,7 +1908,7 @@ public: } AudioUnitRenderActionFlags rActionFlags = 0; - AudioBufferList* const rData = usingHostBuffer ? ioData : reinterpret_cast(&fAudioBufferList); + AudioBufferList* const rData = usingHostBuffer ? ioData : fAudioBufferList; const OSStatus err = fInputRenderCallback.inputProc(fInputRenderCallback.inputProcRefCon, &rActionFlags, inTimeStamp, @@ -1725,8 +1920,8 @@ public: { if (adjustDataByteSize) { - for (uint16_t i=0; imNumberBuffers; ++i) + fAudioBufferList->mBuffers[i].mDataByteSize = prevDataByteSize; } setLastRenderError(err); @@ -1735,35 +1930,35 @@ public: if (usingHostBuffer) { - for (uint16_t i=0; i(ioData->mBuffers[i].mData); #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0 - for (uint16_t i=0; i(ioData->mBuffers[i].mData); #endif } else { - for (uint16_t i=0; i(fAudioBufferList.mBuffers[i].mData); + 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(ioData->mBuffers[i].mData); #endif } } else - #endif + #endif // DISTRHO_PLUGIN_NUM_INPUTS != 0 { #if DISTRHO_PLUGIN_NUM_INPUTS != 0 - for (uint16_t i=0; imBuffers[i].mData == nullptr) { - ioData->mBuffers[i].mData = fAudioBufferList.mBuffers[i].mData; + ioData->mBuffers[i].mData = fAudioBufferList->mBuffers[i].mData; std::memset(ioData->mBuffers[i].mData, 0, sizeof(float) * inFramesToProcess); } @@ -1772,10 +1967,10 @@ public: #endif #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0 - for (uint16_t i=0; imBuffers[i].mData == nullptr) - ioData->mBuffers[i].mData = fAudioBufferList.mBuffers[i].mData; + ioData->mBuffers[i].mData = fAudioBufferList->mBuffers[i].mData; outputs[i] = static_cast(ioData->mBuffers[i].mData); } @@ -1882,15 +2077,23 @@ private: OSStatus fLastRenderError; PropertyListeners fPropertyListeners; RenderListeners fRenderListeners; - #if DISTRHO_PLUGIN_NUM_INPUTS != 0 + #if DISTRHO_PLUGIN_NUM_INPUTS != 0 + UInt32 fInputConnectionBus; + AudioUnit fInputConnectionUnit; AURenderCallbackStruct fInputRenderCallback; Float64 fSampleRateForInput; + #ifdef DISTRHO_PLUGIN_EXTRA_IO + uint32_t fNumInputs; #endif - #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0 + #endif + #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0 Float64 fSampleRateForOutput; + #ifdef DISTRHO_PLUGIN_EXTRA_IO + uint32_t fNumOutputs; #endif - #if DPF_AU_NUM_BUFFERS != 0 - d_AudioBufferList fAudioBufferList; + #endif + #if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS != 0 + AudioBufferList* fAudioBufferList; #endif bool fUsingRenderListeners; @@ -2108,6 +2311,59 @@ private: // ---------------------------------------------------------------------------------------------------------------- + #if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS != 0 + bool reallocAudioBufferList(const bool alloc) + { + if (fAudioBufferList != nullptr) + { + for (uint16_t i = 0; i < fAudioBufferList->mNumberBuffers; ++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, @@ -2803,7 +3059,7 @@ struct AudioComponentPlugInInstance { { const AudioUnitRenderActionFlags actionFlags = ioActionFlags != nullptr ? *ioActionFlags : 0; - // if ((actionFlags & kAudioUnitRenderAction_DoNotCheckRenderArgs) == 0x0) + if ((actionFlags & kAudioUnitRenderAction_DoNotCheckRenderArgs) == 0x0) { DISTRHO_SAFE_ASSERT_RETURN(inTimeStamp != nullptr, kAudio_ParamError); DISTRHO_SAFE_ASSERT_RETURN(ioData != nullptr, kAudio_ParamError); diff --git a/distrho/src/DistrhoPluginInternal.hpp b/distrho/src/DistrhoPluginInternal.hpp index 727051da..e175f5e8 100644 --- a/distrho/src/DistrhoPluginInternal.hpp +++ b/distrho/src/DistrhoPluginInternal.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2023 Filipe Coelho + * 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 @@ -965,7 +965,7 @@ public: } } -#if DISTRHO_PLUGIN_WANT_MIDI_INPUT + #if DISTRHO_PLUGIN_WANT_MIDI_INPUT void run(const float** const inputs, float** const outputs, const uint32_t frames, const MidiEvent* const midiEvents, const uint32_t midiEventCount) { @@ -982,7 +982,7 @@ public: fPlugin->run(inputs, outputs, frames, midiEvents, midiEventCount); fData->isProcessing = false; } -#else + #else void run(const float** const inputs, float** const outputs, const uint32_t frames) { DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr,); @@ -998,7 +998,21 @@ public: fPlugin->run(inputs, outputs, frames); fData->isProcessing = false; } -#endif + #endif + + // ------------------------------------------------------------------- + + #ifdef DISTRHO_PLUGIN_TARGET_AU + void setAudioPortIO(const uint16_t numInputs, const uint16_t numOutputs) + { + DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr,); + DISTRHO_SAFE_ASSERT_RETURN(fPlugin != nullptr,); + + if (fIsActive) fPlugin->deactivate(); + fPlugin->ioChanged(numInputs, numOutputs); + if (fIsActive) fPlugin->activate(); + } + #endif // -------------------------------------------------------------------