DPF

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

commit 8e15fcf1c14fc52723503c03b84a3f97a4238aa1
parent ac3f5bce433a5ffbc083d3c62d30eefec8ffd1ec
Author: falkTX <falktx@falktx.com>
Date:   Sat, 22 Oct 2022 02:49:03 +0100

Implement CLAP multi-IO

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

Diffstat:
Mdistrho/src/DistrhoPluginCLAP.cpp | 374++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------
1 file changed, 312 insertions(+), 62 deletions(-)

diff --git a/distrho/src/DistrhoPluginCLAP.cpp b/distrho/src/DistrhoPluginCLAP.cpp @@ -36,6 +36,7 @@ #endif #include <map> +#include <vector> #include "clap/entry.h" #include "clap/plugin-factory.h" @@ -739,19 +740,24 @@ public: updateStateValueCallback), fHost(host), fOutputEvents(nullptr), - #if DISTRHO_PLUGIN_WANT_LATENCY + #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS != 0 + fUsingCV(false), + #endif + #if DISTRHO_PLUGIN_WANT_LATENCY fLatencyChanged(false), fLastKnownLatency(0), - #endif - #if DISTRHO_PLUGIN_WANT_MIDI_INPUT + #endif + #if DISTRHO_PLUGIN_WANT_MIDI_INPUT fMidiEventCount(0), - #endif + #endif fHostExtensions(host) { fCachedParameters.setup(fPlugin.getParameterCount()); + #if DISTRHO_PLUGIN_HAS_UI && DISTRHO_PLUGIN_WANT_MIDI_INPUT fNotesRingBuffer.setRingBuffer(&fNotesBuffer, true); #endif + #if DISTRHO_PLUGIN_WANT_STATE for (uint32_t i=0, count=fPlugin.getStateCount(); i<count; ++i) { @@ -759,6 +765,16 @@ public: fStateMap[dkey] = fPlugin.getStateDefaultValue(i); } #endif + + #if DISTRHO_PLUGIN_NUM_INPUTS != 0 + fillInBusInfoDetails<true>(); + #endif + #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0 + fillInBusInfoDetails<false>(); + #endif + #if DISTRHO_PLUGIN_NUM_INPUTS != 0 && DISTRHO_PLUGIN_NUM_OUTPUTS != 0 + fillInBusInfoPairs(); + #endif } // ---------------------------------------------------------------------------------------------------------------- @@ -992,25 +1008,66 @@ public: if (const uint32_t frames = process->frames_count) { - // TODO multi-port bus stuff - DISTRHO_SAFE_ASSERT_UINT_RETURN(process->audio_inputs_count == 0 || process->audio_inputs_count == 1, - process->audio_inputs_count, false); - DISTRHO_SAFE_ASSERT_UINT_RETURN(process->audio_outputs_count == 0 || process->audio_outputs_count == 1, - process->audio_outputs_count, false); - - const float** inputs = process->audio_inputs != nullptr - ? const_cast<const float**>(process->audio_inputs[0].data32) - : nullptr; - /**/ float** outputs = process->audio_outputs != nullptr - ? process->audio_outputs[0].data32 - : nullptr; + #if DISTRHO_PLUGIN_NUM_INPUTS != 0 + const float** const audioInputs = fAudioInputs; + + uint32_t in=0; + for (uint32_t i=0; i<process->audio_inputs_count; ++i) + { + const clap_audio_buffer_t& inputs(process->audio_inputs[i]); + DISTRHO_SAFE_ASSERT_CONTINUE(inputs.channel_count != 0); + + for (uint32_t j=0; j<inputs.channel_count; ++j, ++in) + audioInputs[in] = const_cast<const float*>(inputs.data32[j]); + } + + if (fUsingCV) + { + for (; in<DISTRHO_PLUGIN_NUM_INPUTS; ++in) + audioInputs[in] = nullptr; + } + else + { + DISTRHO_SAFE_ASSERT_UINT2_RETURN(in == DISTRHO_PLUGIN_NUM_INPUTS, + in, process->audio_inputs_count, false); + } + #else + constexpr const float* const audioInputs = nullptr; + #endif + + #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0 + float** const audioOutputs = fAudioOutputs; + + uint32_t out=0; + for (uint32_t i=0; i<process->audio_outputs_count; ++i) + { + const clap_audio_buffer_t& outputs(process->audio_outputs[i]); + DISTRHO_SAFE_ASSERT_CONTINUE(outputs.channel_count != 0); + + for (uint32_t j=0; j<outputs.channel_count; ++j, ++out) + audioOutputs[out] = outputs.data32[j]; + } + + if (fUsingCV) + { + for (; out<DISTRHO_PLUGIN_NUM_OUTPUTS; ++out) + audioOutputs[out] = nullptr; + } + else + { + DISTRHO_SAFE_ASSERT_UINT2_RETURN(out == DISTRHO_PLUGIN_NUM_OUTPUTS, + out, DISTRHO_PLUGIN_NUM_OUTPUTS, false); + } + #else + constexpr float* audioOutputs = nullptr; + #endif fOutputEvents = process->out_events; #if DISTRHO_PLUGIN_WANT_MIDI_INPUT - fPlugin.run(inputs, outputs, frames, fMidiEvents, fMidiEventCount); + fPlugin.run(audioInputs, audioOutputs, frames, fMidiEvents, fMidiEventCount); #else - fPlugin.run(inputs, outputs, frames); + fPlugin.run(audioInputs, audioOutputs, frames); #endif // TODO set last frame @@ -1244,6 +1301,48 @@ public: } // ---------------------------------------------------------------------------------------------------------------- + // audio ports + + #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS != 0 + template<bool isInput> + uint32_t getAudioPortCount() const noexcept + { + return (isInput ? fAudioInputBuses : fAudioOutputBuses).size(); + } + + template<bool isInput> + bool getAudioPortInfo(const uint32_t index, clap_audio_port_info_t* const info) const noexcept + { + const std::vector<BusInfo>& busInfos(isInput ? fAudioInputBuses : fAudioOutputBuses); + DISTRHO_SAFE_ASSERT_RETURN(index < busInfos.size(), false); + + const BusInfo& busInfo(busInfos[index]); + + info->id = busInfo.groupId; + DISTRHO_NAMESPACE::strncpy(info->name, busInfo.name, CLAP_NAME_SIZE); + + info->flags = busInfo.isMain ? CLAP_AUDIO_PORT_IS_MAIN : 0x0; + info->channel_count = busInfo.numChannels; + + switch (busInfo.groupId) + { + case kPortGroupMono: + info->port_type = CLAP_PORT_MONO; + break; + case kPortGroupStereo: + info->port_type = CLAP_PORT_STEREO; + break; + default: + info->port_type = nullptr; + break; + } + + info->in_place_pair = busInfo.hasPair ? busInfo.groupId : CLAP_INVALID_ID; + return true; + } + #endif + + // ---------------------------------------------------------------------------------------------------------------- // latency #if DISTRHO_PLUGIN_WANT_LATENCY @@ -1673,6 +1772,15 @@ private: const clap_host_t* const fHost; const clap_output_events_t* fOutputEvents; + #if DISTRHO_PLUGIN_NUM_INPUTS != 0 + const float* fAudioInputs[DISTRHO_PLUGIN_NUM_INPUTS]; + #endif + #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0 + float* fAudioOutputs[DISTRHO_PLUGIN_NUM_OUTPUTS]; + #endif + #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS != 0 + bool fUsingCV; + #endif #if DISTRHO_PLUGIN_WANT_LATENCY bool fLatencyChanged; uint32_t fLastKnownLatency; @@ -1719,6 +1827,170 @@ private: } fHostExtensions; // ---------------------------------------------------------------------------------------------------------------- + // helper functions for dealing with buses + + #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS != 0 + struct BusInfo { + char name[CLAP_NAME_SIZE]; + uint32_t numChannels; + bool hasPair; + bool isCV; + bool isMain; + uint32_t groupId; + }; + std::vector<BusInfo> fAudioInputBuses, fAudioOutputBuses; + + template<bool isInput> + void fillInBusInfoDetails() + { + constexpr const uint32_t numPorts = isInput ? DISTRHO_PLUGIN_NUM_INPUTS : DISTRHO_PLUGIN_NUM_OUTPUTS; + std::vector<BusInfo>& busInfos(isInput ? fAudioInputBuses : fAudioOutputBuses); + + enum { + kPortTypeNull, + kPortTypeAudio, + kPortTypeSidechain, + kPortTypeCV, + kPortTypeGroup + } lastSeenPortType = kPortTypeNull; + uint32_t lastSeenGroupId = kPortGroupNone; + uint32_t nonGroupAudioId = 0; + uint32_t nonGroupSidechainId = 0x20000000; + + for (uint32_t i=0; i<numPorts; ++i) + { + const AudioPortWithBusId& port(fPlugin.getAudioPort(isInput, i)); + + if (port.groupId != kPortGroupNone) + { + if (lastSeenPortType != kPortTypeGroup || lastSeenGroupId != port.groupId) + { + lastSeenPortType = kPortTypeGroup; + lastSeenGroupId = port.groupId; + + BusInfo busInfo = { + {}, 1, false, false, + // if this is the first port, set it as main + busInfos.empty(), + // user given group id with extra safety offset + port.groupId + 0x80000000 + }; + + const PortGroupWithId& group(fPlugin.getPortGroupById(port.groupId)); + + switch (port.groupId) + { + case kPortGroupStereo: + case kPortGroupMono: + if (busInfo.isMain) + { + DISTRHO_NAMESPACE::strncpy(busInfo.name, + isInput ? "Audio Input" : "Audio Output", CLAP_NAME_SIZE); + break; + } + // fall-through + default: + if (group.name.isNotEmpty()) + DISTRHO_NAMESPACE::strncpy(busInfo.name, group.name, CLAP_NAME_SIZE); + else + DISTRHO_NAMESPACE::strncpy(busInfo.name, port.name, CLAP_NAME_SIZE); + break; + } + + busInfos.push_back(busInfo); + } + else + { + ++busInfos.back().numChannels; + } + } + else if (port.hints & kAudioPortIsCV) + { + // TODO + lastSeenPortType = kPortTypeCV; + lastSeenGroupId = kPortGroupNone; + fUsingCV = true; + } + else if (port.hints & kAudioPortIsSidechain) + { + if (lastSeenPortType != kPortTypeSidechain) + { + lastSeenPortType = kPortTypeSidechain; + lastSeenGroupId = kPortGroupNone; + + BusInfo busInfo = { + {}, 1, false, false, + // not main + false, + // give unique id + nonGroupSidechainId++ + }; + + DISTRHO_NAMESPACE::strncpy(busInfo.name, port.name, CLAP_NAME_SIZE); + + busInfos.push_back(busInfo); + } + else + { + ++busInfos.back().numChannels; + } + } + else + { + if (lastSeenPortType != kPortTypeAudio) + { + lastSeenPortType = kPortTypeAudio; + lastSeenGroupId = kPortGroupNone; + + BusInfo busInfo = { + {}, 1, false, false, + // if this is the first port, set it as main + busInfos.empty(), + // give unique id + nonGroupAudioId++ + }; + + if (busInfo.isMain) + { + DISTRHO_NAMESPACE::strncpy(busInfo.name, + isInput ? "Audio Input" : "Audio Output", CLAP_NAME_SIZE); + } + else + { + DISTRHO_NAMESPACE::strncpy(busInfo.name, port.name, CLAP_NAME_SIZE); + } + + busInfos.push_back(busInfo); + } + else + { + ++busInfos.back().numChannels; + } + } + } + } + #endif + + #if DISTRHO_PLUGIN_NUM_INPUTS != 0 && DISTRHO_PLUGIN_NUM_OUTPUTS != 0 + void fillInBusInfoPairs() + { + const size_t numChannels = std::min(fAudioInputBuses.size(), fAudioOutputBuses.size()); + + for (size_t i=0; i<numChannels; ++i) + { + if (fAudioInputBuses[i].groupId != fAudioOutputBuses[i].groupId) + break; + if (fAudioInputBuses[i].numChannels != fAudioOutputBuses[i].numChannels) + break; + if (fAudioInputBuses[i].isMain != fAudioOutputBuses[i].isMain) + break; + + fAudioInputBuses[i].hasPair = fAudioOutputBuses[i].hasPair = true; + } + } + #endif + + // ---------------------------------------------------------------------------------------------------------------- // DPF callbacks #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT @@ -1962,61 +2234,41 @@ static const clap_plugin_timer_support_t clap_timer = { // -------------------------------------------------------------------------------------------------------------------- // plugin audio ports -static uint32_t clap_plugin_audio_ports_count(const clap_plugin_t*, const bool is_input) +#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS != 0 +static uint32_t clap_plugin_audio_ports_count(const clap_plugin_t* const plugin, const bool is_input) { - return (is_input ? DISTRHO_PLUGIN_NUM_INPUTS : DISTRHO_PLUGIN_NUM_OUTPUTS) != 0 ? 1 : 0; + PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data); + return is_input ? instance->getAudioPortCount<true>() + : instance->getAudioPortCount<false>(); } -static bool clap_plugin_audio_ports_get(const clap_plugin_t* /* const plugin */, +static bool clap_plugin_audio_ports_get(const clap_plugin_t* const plugin, const uint32_t index, const bool is_input, clap_audio_port_info_t* const info) { - const uint32_t maxPortCount = is_input ? DISTRHO_PLUGIN_NUM_INPUTS : DISTRHO_PLUGIN_NUM_OUTPUTS; - DISTRHO_SAFE_ASSERT_UINT2_RETURN(index < maxPortCount, index, maxPortCount, false); - - #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 - // PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data); - - // TODO use groups - AudioPortWithBusId& audioPort(sPlugin->getAudioPort(is_input, index)); - - info->id = index; - DISTRHO_NAMESPACE::strncpy(info->name, audioPort.name, CLAP_NAME_SIZE); - - // TODO bus stuff - info->flags = CLAP_AUDIO_PORT_IS_MAIN; - info->channel_count = maxPortCount; - - // TODO CV - // info->port_type = audioPort.hints & kAudioPortIsCV ? CLAP_PORT_CV : nullptr; - // TODO port groups - info->port_type = maxPortCount == 2 ? CLAP_PORT_STEREO : maxPortCount == 1 ? CLAP_PORT_MONO : nullptr; - - info->in_place_pair = DISTRHO_PLUGIN_NUM_INPUTS == DISTRHO_PLUGIN_NUM_OUTPUTS ? index : CLAP_INVALID_ID; - - return true; - #else - return false; - // unused - (void)info; - #endif + PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data); + return is_input ? instance->getAudioPortInfo<true>(index, info) + : instance->getAudioPortInfo<false>(index, info); } static const clap_plugin_audio_ports_t clap_plugin_audio_ports = { clap_plugin_audio_ports_count, clap_plugin_audio_ports_get }; +#endif // -------------------------------------------------------------------------------------------------------------------- -// plugin audio ports +// plugin note ports +#if DISTRHO_PLUGIN_WANT_MIDI_INPUT+DISTRHO_PLUGIN_WANT_MIDI_OUTPUT != 0 static uint32_t clap_plugin_note_ports_count(const clap_plugin_t*, const bool is_input) { return (is_input ? DISTRHO_PLUGIN_WANT_MIDI_INPUT : DISTRHO_PLUGIN_WANT_MIDI_OUTPUT) != 0 ? 1 : 0; } -static bool clap_plugin_note_ports_get(const clap_plugin_t*, uint32_t, const bool is_input, clap_note_port_info_t* const info) +static bool clap_plugin_note_ports_get(const clap_plugin_t*, uint32_t, + const bool is_input, clap_note_port_info_t* const info) { if (is_input) { @@ -2026,8 +2278,6 @@ static bool clap_plugin_note_ports_get(const clap_plugin_t*, uint32_t, const boo info->preferred_dialect = CLAP_NOTE_DIALECT_MIDI; std::strcpy(info->name, "Event/MIDI Input"); return true; - #else - return false; #endif } else @@ -2038,21 +2288,17 @@ static bool clap_plugin_note_ports_get(const clap_plugin_t*, uint32_t, const boo info->preferred_dialect = CLAP_NOTE_DIALECT_MIDI; std::strcpy(info->name, "Event/MIDI Output"); return true; - #else - return false; #endif } - #if DISTRHO_PLUGIN_WANT_MIDI_INPUT+DISTRHO_PLUGIN_WANT_MIDI_OUTPUT == 0 - // unused - (void)info; - #endif + return false; } static const clap_plugin_note_ports_t clap_plugin_note_ports = { clap_plugin_note_ports_count, clap_plugin_note_ports_get }; +#endif // -------------------------------------------------------------------------------------------------------------------- // plugin parameters @@ -2197,12 +2443,16 @@ static CLAP_ABI clap_process_status clap_plugin_process(const clap_plugin_t* con static CLAP_ABI const void* clap_plugin_get_extension(const clap_plugin_t*, const char* const id) { + if (std::strcmp(id, CLAP_EXT_PARAMS) == 0) + return &clap_plugin_params; + #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS != 0 if (std::strcmp(id, CLAP_EXT_AUDIO_PORTS) == 0) return &clap_plugin_audio_ports; + #endif + #if DISTRHO_PLUGIN_WANT_MIDI_INPUT+DISTRHO_PLUGIN_WANT_MIDI_OUTPUT != 0 if (std::strcmp(id, CLAP_EXT_NOTE_PORTS) == 0) return &clap_plugin_note_ports; - if (std::strcmp(id, CLAP_EXT_PARAMS) == 0) - return &clap_plugin_params; + #endif #if DISTRHO_PLUGIN_WANT_LATENCY if (std::strcmp(id, CLAP_EXT_LATENCY) == 0) return &clap_plugin_latency;