From 1d2fef1ef050f55097f16b6bcf1f4f71efeb950c Mon Sep 17 00:00:00 2001 From: Quant1um Date: Fri, 16 Jan 2026 17:38:12 +0400 Subject: [PATCH] add 'configurable-audio-ports', 'audio-ports-config' and 'audio-ports-activation' extension support for vst3, implement the 'audio-ports-config' extension for the clap-first-example plugin remove 'audio-ports-config', change popcount64, move to util.h as suggested by @defiantnerd fix #include import --- src/clap_proxy.cpp | 22 ++- src/clap_proxy.h | 2 + src/detail/shared/util.h | 17 +++ src/wrapasvst3.cpp | 140 +++++++++++++------ tests/clap-first-example/distortion_clap.cpp | 123 +++++++++------- 5 files changed, 205 insertions(+), 99 deletions(-) diff --git a/src/clap_proxy.cpp b/src/clap_proxy.cpp index 06226d66..de9608d7 100644 --- a/src/clap_proxy.cpp +++ b/src/clap_proxy.cpp @@ -228,6 +228,7 @@ void Plugin::connectClap(const clap_plugin_t *clap) getExtension(_plugin, _ext._configurable_audio_ports, CLAP_EXT_CONFIGURABLE_AUDIO_PORTS); if (!_ext._configurable_audio_ports) getExtension(_plugin, _ext._configurable_audio_ports, CLAP_EXT_CONFIGURABLE_AUDIO_PORTS_COMPAT); + getExtension(_plugin, _ext._audio_ports_activation, CLAP_EXT_AUDIO_PORTS_ACTIVATION); getExtension(_plugin, _ext._noteports, CLAP_EXT_NOTE_PORTS); getExtension(_plugin, _ext._latency, CLAP_EXT_LATENCY); getExtension(_plugin, _ext._render, CLAP_EXT_RENDER); @@ -323,6 +324,14 @@ void Plugin::setBlockSizes(uint32_t minFrames, uint32_t maxFrames) _audioSetup.maxFrames = maxFrames; } +void Plugin::setBusActivation(bool isInput, uint32_t busIndex, bool active) +{ + if (_ext._audio_ports_activation) + { + _ext._audio_ports_activation->set_active(_plugin, isInput, busIndex, active, 32); + } +} + bool Plugin::load(const clap_istream_t *stream) const { if (_ext._state) @@ -526,23 +535,22 @@ void Plugin::param_request_flush() // [thread-safe] const void *Plugin::clapExtension(const clap_host * /*host*/, const char *extension) { + // TODO: add 'audio-ports' host-side extension if (!strcmp(extension, CLAP_EXT_LOG)) return &HostExt::log; if (!strcmp(extension, CLAP_EXT_PARAMS)) return &HostExt::params; if (!strcmp(extension, CLAP_EXT_TRACK_INFO)) return &HostExt::trackinfo; if (!strcmp(extension, CLAP_EXT_THREAD_CHECK)) return &HostExt::threadcheck; if (!strcmp(extension, CLAP_EXT_GUI)) return &HostExt::hostgui; if (!strcmp(extension, CLAP_EXT_TIMER_SUPPORT)) return &HostExt::hosttimer; -#if LIN - if (!strcmp(extension, CLAP_EXT_POSIX_FD_SUPPORT)) return &HostExt::hostposixfd; -#endif if (!strcmp(extension, CLAP_EXT_LATENCY)) return &HostExt::latency; - if (!strcmp(extension, CLAP_EXT_TAIL)) - { - return &HostExt::tail; - } + if (!strcmp(extension, CLAP_EXT_TAIL)) return &HostExt::tail; if (!strcmp(extension, CLAP_EXT_STATE)) return &HostExt::state; if (!strcmp(extension, CLAP_EXT_CONTEXT_MENU)) return &HostExt::context_menu; +#if LIN + if (!strcmp(extension, CLAP_EXT_POSIX_FD_SUPPORT)) return &HostExt::hostposixfd; +#endif + return nullptr; } diff --git a/src/clap_proxy.h b/src/clap_proxy.h index 7c8e71f3..d1c98307 100644 --- a/src/clap_proxy.h +++ b/src/clap_proxy.h @@ -107,6 +107,7 @@ struct ClapPluginExtensions const clap_plugin_state_t *_state = nullptr; const clap_plugin_params_t *_params = nullptr; const clap_plugin_audio_ports_t *_audioports = nullptr; + const clap_plugin_audio_ports_activation_t *_audio_ports_activation = nullptr; const clap_plugin_configurable_audio_ports_t *_configurable_audio_ports = nullptr; const clap_plugin_gui_t *_gui = nullptr; const clap_plugin_note_ports_t *_noteports = nullptr; @@ -171,6 +172,7 @@ class Plugin void schnick(); bool initialize(); void terminate(); + void setBusActivation(bool isInput, uint32_t busIndex, bool active); void setSampleRate(double sampleRate); double getSampleRate() const { diff --git a/src/detail/shared/util.h b/src/detail/shared/util.h index 920d296e..4ec1e078 100644 --- a/src/detail/shared/util.h +++ b/src/detail/shared/util.h @@ -1,8 +1,25 @@ #pragma once #include +#include + +#if __cplusplus >= 202002L +#include +#endif uint32_t fnv1a_keogh(const char *input); // Function to shorten a string to a 4-character string std::string ShortenString(const std::string &input); + +inline uint32_t popcount64(uint64_t x) +{ +#if __cplusplus >= 202002L + return std::popcount(x); +#else + x = x - ((x >> 1) & 0x5555555555555555ULL); + x = (x & 0x3333333333333333ULL) + ((x >> 2) & 0x3333333333333333ULL); + x = (x + (x >> 4)) & 0x0F0F0F0F0F0F0F0FULL; + return (x * 0x0101010101010101ULL) >> 56; +#endif +} diff --git a/src/wrapasvst3.cpp b/src/wrapasvst3.cpp index fb6f5396..b8b85af0 100644 --- a/src/wrapasvst3.cpp +++ b/src/wrapasvst3.cpp @@ -22,6 +22,7 @@ #include "detail/vst3/process.h" #include "detail/vst3/parameter.h" #include "detail/clap/fsutil.h" +#include "detail/shared/util.h" #include #include @@ -182,21 +183,31 @@ tresult PLUGIN_API ClapAsVst3::setActive(TBool state) if (state) { if (_active) return kResultFalse; - if (!_plugin->activate()) return kResultFalse; - _active = true; - _processAdapter = new Clap::ProcessAdapter(); - auto supportsnoteexpression = - (_expressionmap & clap_supported_note_expressions::AS_VST3_NOTE_EXPRESSION_PRESSURE); + for (auto i = 0U; i < audioInputs.size(); ++i) + { + _plugin->setBusActivation(true, i, audioInputs[i]->isActive()); + } + + for (auto i = 0U; i < audioOutputs.size(); ++i) + { + _plugin->setBusActivation(false, i, audioOutputs[i]->isActive()); + } + + if (!_plugin->activate()) return kResultFalse; _gesturedparameters.reserve(8192); - // the processAdapter needs to know a few things to intercommunicate between VST3 host and CLAP plugin. + _active = true; + _processAdapter = new Clap::ProcessAdapter(); _processAdapter->setupProcessing( _plugin->_plugin, _plugin->_ext._params, this->audioInputs, this->audioOutputs, this->_largestBlocksize, this->eventInputs.size(), this->eventOutputs.size(), parameters, - componentHandler, this, _gesturedparameters, supportsnoteexpression, + componentHandler, this, _gesturedparameters, + _expressionmap & clap_supported_note_expressions::AS_VST3_NOTE_EXPRESSION_PRESSURE, _expressionmap & clap_supported_note_expressions::AS_VST3_NOTE_EXPRESSION_TUNING); + + // do we need this still? updateAudioBusses(); if (_missedLatencyRequest) @@ -360,40 +371,67 @@ tresult PLUGIN_API ClapAsVst3::setBusArrangements(Vst::SpeakerArrangement *input auto raise = _plugin->AlwaysMainThread(); - int32_t inc = _plugin->_ext._audioports->count(_plugin->_plugin, true); - int32_t ouc = _plugin->_ext._audioports->count(_plugin->_plugin, false); - if (inc != numIns || ouc != numOuts) + // if we have configurable-audio-ports, ask the plugin to set the requested arrangement + if (_plugin->_ext._configurable_audio_ports) { - return kResultFalse; - } + std::vector requests; - for (int i = 0; i < numIns; ++i) - { - clap_audio_port_info_t info; - _plugin->_ext._audioports->get(_plugin->_plugin, i, true, &info); - Vst::SpeakerArrangement sa{0}; - for (auto c = 0U; c < info.channel_count; ++c) + for (int i = 0; i < numIns + numOuts; ++i) { - sa = (sa << 1) + 1; + clap_audio_port_configuration_request_t request; + + request.is_input = i < numIns; + request.port_index = i < numIns ? i : (i - numIns); + auto arrangement = i < numIns ? inputs[i] : outputs[i - numIns]; + + switch (arrangement) + { + case Vst::SpeakerArr::kMono: + request.channel_count = 1; + request.port_type = CLAP_PORT_MONO; + request.port_details = nullptr; + break; + case Vst::SpeakerArr::kStereo: + request.channel_count = 2; + request.port_type = CLAP_PORT_STEREO; + request.port_details = nullptr; + break; + default: + request.channel_count = popcount64(arrangement); + request.port_type = nullptr; + request.port_details = nullptr; + break; + } + + requests.push_back(request); } - if (inputs[i] != sa) + + if (_plugin->_ext._configurable_audio_ports->apply_configuration( + _plugin->_plugin, requests.data(), static_cast(requests.size()))) { - return kResultFalse; + setupAudioBusses(_plugin->_plugin, _plugin->_ext._audioports); + return super::setBusArrangements(inputs, numIns, outputs, numOuts); } } - for (int i = 0; i < numOuts; ++i) + // otherwise we just make sure that the requested arrangements matches the current layout { - clap_audio_port_info_t info; - _plugin->_ext._audioports->get(_plugin->_plugin, i, false, &info); - Vst::SpeakerArrangement sa{0}; - for (auto c = 0U; c < info.channel_count; ++c) + int32_t inc = _plugin->_ext._audioports->count(_plugin->_plugin, true); + int32_t ouc = _plugin->_ext._audioports->count(_plugin->_plugin, false); + if (inc != numIns || ouc != numOuts) return kResultFalse; + + for (int i = 0; i < numIns; ++i) { - sa = (sa << 1) + 1; + clap_audio_port_info_t info; + _plugin->_ext._audioports->get(_plugin->_plugin, i, true, &info); + if (popcount64(inputs[i]) != info.channel_count) return kResultFalse; } - if (outputs[i] != sa) + + for (int i = 0; i < numOuts; ++i) { - return kResultFalse; + clap_audio_port_info_t info; + _plugin->_ext._audioports->get(_plugin->_plugin, i, false, &info); + if (popcount64(outputs[i]) != info.channel_count) return kResultFalse; } } @@ -705,33 +743,42 @@ ARAPlugInExtensionInstancePtr PLUGIN_API ClapAsVst3::bindToDocumentControllerWit return nullptr; } -static Vst::SpeakerArrangement speakerArrFromPortType(const char *port_type) +// TODO: surround extension support +static Vst::SpeakerArrangement speakerArrFromPortType(const char *port_type, uint32_t channel_count) { - if (!port_type) return Vst::SpeakerArr::kEmpty; - static const std::pair arrangementmap[] = { - {CLAP_PORT_MONO, Vst::SpeakerArr::kMono}, - {CLAP_PORT_STEREO, Vst::SpeakerArr::kStereo}, - // {CLAP_PORT_AMBISONIC, Vst::SpeakerArr::kAmbi1stOrderACN} <- we need also CLAP_EXT_AMBISONIC - // {CLAP_PORT_SURROUND, Vst::SpeakerArr::kStereoSurround}, // add when CLAP_EXT_SURROUND is not draft anymore - // TODO: add more PortTypes to Speaker Arrangement - {nullptr, Vst::SpeakerArr::kEmpty}}; - - auto p = &arrangementmap[0]; - while (p->first) + if (!port_type) { - if (!strcmp(port_type, p->first)) + switch (channel_count) { - return p->second; + case 0: + return Vst::SpeakerArr::kEmpty; + case 1: + return Vst::SpeakerArr::kMono; + case 2: + return Vst::SpeakerArr::kStereo; + case 5: + return Vst::SpeakerArr::k50; + case 6: + return Vst::SpeakerArr::k51; + case 7: + return Vst::SpeakerArr::k70Cine; + case 8: + return Vst::SpeakerArr::k71Cine; + default: + return (1 << channel_count) - 1; // bitmask with channel_count bits set } - ++p; } + if (!strcmp(port_type, CLAP_PORT_MONO)) return Vst::SpeakerArr::kMono; + if (!strcmp(port_type, CLAP_PORT_STEREO)) return Vst::SpeakerArr::kStereo; + return Vst::SpeakerArr::kEmpty; } void ClapAsVst3::addAudioBusFrom(const clap_audio_port_info_t *info, bool is_input) { - auto spk = speakerArrFromPortType(info->port_type); + auto spk = speakerArrFromPortType(info->port_type, info->channel_count); + auto bustype = Vst::BusTypes::kMain; // actually, everything is main, except if (is_input && !(info->flags & CLAP_AUDIO_PORT_IS_MAIN)) { @@ -897,6 +944,9 @@ void ClapAsVst3::setupAudioBusses(const clap_plugin_t *plugin, const clap_plugin_audio_ports_t *audioports) { if (!audioports) return; + + removeAudioBusses(); + auto numAudioInputs = audioports->count(plugin, true); auto numAudioOutputs = audioports->count(plugin, false); diff --git a/tests/clap-first-example/distortion_clap.cpp b/tests/clap-first-example/distortion_clap.cpp index 246e84f5..b02169d9 100644 --- a/tests/clap-first-example/distortion_clap.cpp +++ b/tests/clap-first-example/distortion_clap.cpp @@ -54,6 +54,8 @@ typedef struct float drive; float mix; int32_t mode; + + uint32_t channels; } clap1st_distortion_plug; static void clap1stDist_process_event(clap1st_distortion_plug *plug, const clap_event_header_t *hdr); @@ -82,9 +84,40 @@ static const clap_plugin_track_info_t s_clap1stDist_track_info = { clap1stDist_track_info_changed, }; -///////////////////////////// -// clap_plugin_audio_ports // -///////////////////////////// +/////////////////////////////////// +// clap_configurable_audio_ports // +/////////////////////////////////// + +static bool clap1stDist_can_apply_configuration( + const clap_plugin_t *plugin, const struct clap_audio_port_configuration_request *requests, + uint32_t request_count) +{ + if (request_count != 2) return false; // one request for input, one for output + if (requests[0].is_input == requests[1].is_input) return false; + if (requests[0].port_index != 0 || requests[1].port_index != 0) return false; + if (requests[0].channel_count != requests[1].channel_count) return false; + + return true; +} + +static bool clap1stDist_apply_configuration(const clap_plugin_t *plugin, + const struct clap_audio_port_configuration_request *requests, + uint32_t request_count) +{ + if (!clap1stDist_can_apply_configuration(plugin, requests, request_count)) return false; + auto *plug = (clap1st_distortion_plug *)plugin->plugin_data; + plug->channels = requests[0].channel_count; + return true; +} + +static clap_plugin_configurable_audio_ports_t s_clap1stDist_configurable_audio_ports = { + clap1stDist_can_apply_configuration, + clap1stDist_apply_configuration, +}; + +////////////////////// +// clap_audio_ports // +////////////////////// static uint32_t clap1stDist_audio_ports_count(const clap_plugin_t *plugin, bool is_input) { @@ -95,16 +128,18 @@ static uint32_t clap1stDist_audio_ports_count(const clap_plugin_t *plugin, bool static bool clap1stDist_audio_ports_get(const clap_plugin_t *plugin, uint32_t index, bool is_input, clap_audio_port_info_t *info) { + auto *plug = (clap1st_distortion_plug *)plugin->plugin_data; + if (index > 0) return false; info->id = 0; if (is_input) snprintf(info->name, sizeof(info->name), "%s", "Stereo In"); else snprintf(info->name, sizeof(info->name), "%s", "Distorted Output"); - info->channel_count = 2; info->flags = CLAP_AUDIO_PORT_IS_MAIN; - info->port_type = CLAP_PORT_STEREO; info->in_place_pair = CLAP_INVALID_ID; + info->channel_count = plug->channels; + info->port_type = nullptr; return true; } @@ -113,9 +148,9 @@ static const clap_plugin_audio_ports_t s_clap1stDist_audio_ports = { clap1stDist_audio_ports_get, }; -////////////////// -// clap_porams // -////////////////// +///////////////// +// clap_params // +///////////////// uint32_t clap1stDist_param_count(const clap_plugin_t *plugin) { @@ -321,6 +356,7 @@ static bool clap1stDist_init(const struct clap_plugin *plugin) // Fetch host's extensions here plug->hostLog = (const clap_host_log_t *)plug->host->get_extension(plug->host, CLAP_EXT_LOG); + plug->channels = 2; // default to stereo plug->drive = 0.f; plug->mix = 0.5f; @@ -424,53 +460,45 @@ static clap_process_status clap1stDist_process(const struct clap_plugin *plugin, } } + assert(process->audio_inputs[0].channel_count == process->audio_outputs[0].channel_count); + assert(process->audio_inputs[0].channel_count == plug->channels); + /* process every samples until the next event */ - for (; i < next_ev_frame; ++i) + for (int j = 0; j < process->audio_inputs[0].channel_count; ++j) { - // fetch input samples - const float in_l = process->audio_inputs[0].data32[0][i]; - const float in_r = process->audio_inputs[0].data32[1][i]; - - float out_l, out_r; - out_l = 0; - out_r = 0; - - float tl = in_l * (1.0 + plug->drive); - float tr = in_r * (1.0 + plug->drive); - - // Obviously this is inefficient but - switch (plug->mode) + for (; i < next_ev_frame; ++i) { - case HARD: - { - tl = (tl > 1 ? 1 : tl < -1 ? -1 : tl); - tr = (tr > 1 ? 1 : tr < -1 ? -1 : tr); - } - break; - case SOFT: - { - tl = (tl > 1 ? 1 : tl < -1 ? -1 : tl); - tl = 1.5 * tl - 0.5 * tl * tl * tl; + // fetch input samples + const float in = process->audio_inputs[0].data32[j][i]; + float tl = in * (1.0 + plug->drive); - tr = (tr > 1 ? 1 : tr < -1 ? -1 : tr); - tr = 1.5 * tr - 0.5 * tr * tr * tr; - } - break; - case FOLD: + // Obviously this is inefficient but + switch (plug->mode) { - tl = sin(2.0 * 3.14159265 * tl); - tr = sin(2.0 * 3.14159265 * tr); + case HARD: + { + tl = (tl > 1 ? 1 : tl < -1 ? -1 : tl); + } + break; + case SOFT: + { + tl = (tl > 1 ? 1 : tl < -1 ? -1 : tl); + tl = 1.5 * tl - 0.5 * tl * tl * tl; + } + break; + case FOLD: + { + tl = sin(2.0 * 3.14159265 * tl); + } + break; } - break; - } - float mix = plug->mix; - out_l = mix * tl + (1.0 - mix) * in_l; - out_r = mix * tr + (1.0 - mix) * in_r; + float mix = plug->mix; + float out = mix * tl + (1.0 - mix) * in; - // store output samples - process->audio_outputs[0].data32[0][i] = out_l; - process->audio_outputs[0].data32[1][i] = out_r; + // store output samples + process->audio_outputs[0].data32[j][i] = out; + } } } @@ -480,6 +508,7 @@ static clap_process_status clap1stDist_process(const struct clap_plugin *plugin, static const void *clap1stDist_get_extension(const struct clap_plugin *plugin, const char *id) { if (!strcmp(id, CLAP_EXT_TRACK_INFO)) return &s_clap1stDist_track_info; + if (!strcmp(id, CLAP_EXT_CONFIGURABLE_AUDIO_PORTS)) return &s_clap1stDist_configurable_audio_ports; if (!strcmp(id, CLAP_EXT_AUDIO_PORTS)) return &s_clap1stDist_audio_ports; if (!strcmp(id, CLAP_EXT_PARAMS)) return &s_clap1stDist_params; if (!strcmp(id, CLAP_EXT_STATE)) return &s_clap1stDist_state;