clap

CLAP Audio Plugin API
Log | Files | Refs | README | LICENSE

commit 63d49ec3ba0ed9f94d0b6fb7b204f089a971e0ca
parent 1f428a898fca85585e4d5ee183d6c143f8a86099
Author: Alexandre BIQUE <bique.alexandre@gmail.com>
Date:   Mon, 24 May 2021 19:27:04 +0200

Work on audio ports management

Diffstat:
Mexamples/plugins/clap-entry.cc | 2+-
Mexamples/plugins/gain/gain.cc | 111+++++++++++++++++++++++++++++++++++++++++--------------------------------------
Mexamples/plugins/gain/gain.hh | 34+++++++++++++++++++---------------
Mexamples/plugins/plugin.cc | 638++++++++++++++++++++++++++++++++++++++++++-------------------------------------
Mexamples/plugins/plugin.hh | 346+++++++++++++++++++++++++++++++++++++++++--------------------------------------
5 files changed, 597 insertions(+), 534 deletions(-)

diff --git a/examples/plugins/clap-entry.cc b/examples/plugins/clap-entry.cc @@ -29,7 +29,7 @@ static void addPlugin() { } static bool clap_init(const char *plugin_path) { - addPlugin<Gain>(); + addPlugin<clap::Gain>(); return true; } diff --git a/examples/plugins/gain/gain.cc b/examples/plugins/gain/gain.cc @@ -2,59 +2,61 @@ #include "gain.hh" -const clap_plugin_descriptor *Gain::descriptor() { - static const clap_plugin_descriptor desc = { - - CLAP_VERSION, - "com.github.free-audio.clap.gain", - "gain", - "clap", - "https://github.com/free-audio/clap", - nullptr, - nullptr, - "0.1", - "example gain plugin", - "mix;gain", - CLAP_PLUGIN_AUDIO_EFFECT - - }; - return &desc; -} - -Gain::Gain(clap_host *host) : Plugin(descriptor(), host) {} - -bool Gain::activate(int sample_rate) { - channelCount_ = trackChannelCount(); - return true; -} - -void Gain::deactivate() { channelCount_ = 0; } - -clap_process_status Gain::process(const clap_process *process) { - float **in = process->audio_inputs[0].data32; - float **out = process->audio_outputs[0].data32; - - float k = 1; - for (int i = 0; i < process->frames_count; ++i) { - for (int c = 0; c < channelCount_; ++c) - out[c][i] = k * in[c][i]; +namespace clap { + const clap_plugin_descriptor *Gain::descriptor() { + static const clap_plugin_descriptor desc = { + + CLAP_VERSION, + "com.github.free-audio.clap.gain", + "gain", + "clap", + "https://github.com/free-audio/clap", + nullptr, + nullptr, + "0.1", + "example gain plugin", + "mix;gain", + CLAP_PLUGIN_AUDIO_EFFECT + + }; + return &desc; } - return CLAP_PROCESS_CONTINUE_IF_NOT_QUIET; -} - -void Gain::defineAudioPorts(std::vector<clap_audio_port_info> &inputPorts, - std::vector<clap_audio_port_info> &outputPorts) { - clap_audio_port_info info; - info.id = 0; - strncpy(info.name, "main", sizeof(info.name)); - info.is_main = true; - info.is_cv = false; - info.supports_64_bits = false; - info.supports_in_place = true; - info.channel_count = channelCount_; - info.channel_map = CLAP_CHMAP_UNSPECIFIED; - - inputPorts.push_back(info); - outputPorts.push_back(info); -} + Gain::Gain(clap_host *host) : Plugin(descriptor(), host) {} + + bool Gain::activate(int sample_rate) { + channelCount_ = trackChannelCount(); + return true; + } + + void Gain::deactivate() { channelCount_ = 0; } + + clap_process_status Gain::process(const clap_process *process) { + float **in = process->audio_inputs[0].data32; + float **out = process->audio_outputs[0].data32; + + float k = 1; + for (int i = 0; i < process->frames_count; ++i) { + for (int c = 0; c < channelCount_; ++c) + out[c][i] = k * in[c][i]; + } + + return CLAP_PROCESS_CONTINUE_IF_NOT_QUIET; + } + + void Gain::defineAudioPorts(std::vector<clap_audio_port_info> &inputPorts, + std::vector<clap_audio_port_info> &outputPorts) { + clap_audio_port_info info; + info.id = 0; + strncpy(info.name, "main", sizeof(info.name)); + info.is_main = true; + info.is_cv = false; + info.supports_64_bits = false; + info.supports_in_place = true; + info.channel_count = channelCount_; + info.channel_map = CLAP_CHMAP_UNSPECIFIED; + + inputPorts.push_back(info); + outputPorts.push_back(info); + } +} // namespace clap +\ No newline at end of file diff --git a/examples/plugins/gain/gain.hh b/examples/plugins/gain/gain.hh @@ -4,21 +4,25 @@ #include "../plugin.hh" -class Gain final : public Plugin { -public: - Gain(clap_host *host); +namespace clap { + class Gain final : public Plugin { + public: + Gain(clap_host *host); - static const clap_plugin_descriptor *descriptor(); + static const clap_plugin_descriptor *descriptor(); -protected: - bool activate(int sample_rate) override; - void deactivate() override; - clap_process_status process(const clap_process *process) override; + protected: + bool activate(int sample_rate) override; + void deactivate() override; + clap_process_status process(const clap_process *process) override; - void defineAudioPorts(std::vector<clap_audio_port_info> &inputPorts, - std::vector<clap_audio_port_info> &outputPorts) override; - bool shouldInvalidateAudioPortsDefinitionOnTrackChannelChange() const override { return true; } + void defineAudioPorts(std::vector<clap_audio_port_info> &inputPorts, + std::vector<clap_audio_port_info> &outputPorts) override; + bool shouldInvalidateAudioPortsDefinitionOnTrackChannelChange() const override { + return true; + } -private: - int channelCount_ = 0; -}; -\ No newline at end of file + private: + int channelCount_ = 0; + }; +} // namespace clap +\ No newline at end of file diff --git a/examples/plugins/plugin.cc b/examples/plugins/plugin.cc @@ -6,360 +6,402 @@ #include "plugin.hh" -Plugin::Plugin(const clap_plugin_descriptor *desc, clap_host *host) : host_(host) { - plugin_.plugin_data = this; - plugin_.desc = desc; - plugin_.init = Plugin::clapInit; - plugin_.destroy = Plugin::clapDestroy; - plugin_.extension = nullptr; - plugin_.process = nullptr; - plugin_.activate = nullptr; - plugin_.deactivate = nullptr; - plugin_.start_processing = nullptr; - plugin_.stop_processing = nullptr; -} - -///////////////////// -// CLAP Interfaces // -///////////////////// - -// clap_plugin interface -bool Plugin::clapInit(clap_plugin *plugin) { - auto &self = from(plugin); - - self.plugin_.extension = Plugin::clapExtension; - self.plugin_.process = Plugin::clapProcess; - self.plugin_.activate = Plugin::clapActivate; - self.plugin_.deactivate = Plugin::clapDeactivate; - self.plugin_.start_processing = Plugin::clapStartProcessing; - self.plugin_.stop_processing = Plugin::clapStopProcessing; - - self.initInterfaces(); - self.ensureMainThread("clap_plugin.init"); - self.initTrackInfo(); - self.defineAudioPorts(self.inputAudioPorts_, self.outputAudioPorts_); - return self.init(); -} - -void Plugin::clapDestroy(clap_plugin *plugin) { - auto &self = from(plugin); - self.ensureMainThread("clap_plugin.destroy"); - delete &from(plugin); -} - -bool Plugin::clapActivate(clap_plugin *plugin, int sample_rate) { - auto &self = from(plugin); - self.ensureMainThread("clap_plugin.activate"); - - if (self.isActive_) { - self.hostMisbehaving("Plugin was activated twice"); - - if (sample_rate != self.sampleRate_) { - std::ostringstream msg; - msg << "The plugin was activated twice and with different sample rates: " - << self.sampleRate_ << " and " << sample_rate - << ". The host must deactivate the plugin first." << std::endl - << "Simulating deactivation."; - self.hostMisbehaving(msg.str()); - clapDeactivate(plugin); - } +namespace clap { + + Plugin::Plugin(const clap_plugin_descriptor *desc, clap_host *host) : host_(host) { + plugin_.plugin_data = this; + plugin_.desc = desc; + plugin_.init = Plugin::clapInit; + plugin_.destroy = Plugin::clapDestroy; + plugin_.extension = nullptr; + plugin_.process = nullptr; + plugin_.activate = nullptr; + plugin_.deactivate = nullptr; + plugin_.start_processing = nullptr; + plugin_.stop_processing = nullptr; } - if (sample_rate <= 0) { - std::ostringstream msg; - msg << "The plugin was activated with an invalid sample rates: " << sample_rate; - self.hostMisbehaving(msg.str()); - return false; + ///////////////////// + // CLAP Interfaces // + ///////////////////// + + // clap_plugin interface + bool Plugin::clapInit(clap_plugin *plugin) { + auto &self = from(plugin); + + self.plugin_.extension = Plugin::clapExtension; + self.plugin_.process = Plugin::clapProcess; + self.plugin_.activate = Plugin::clapActivate; + self.plugin_.deactivate = Plugin::clapDeactivate; + self.plugin_.start_processing = Plugin::clapStartProcessing; + self.plugin_.stop_processing = Plugin::clapStopProcessing; + + self.initInterfaces(); + self.ensureMainThread("clap_plugin.init"); + self.initTrackInfo(); + self.defineAudioPorts(self.inputAudioPorts_, self.outputAudioPorts_); + return self.init(); + } + + void Plugin::clapDestroy(clap_plugin *plugin) { + auto &self = from(plugin); + self.ensureMainThread("clap_plugin.destroy"); + delete &from(plugin); } - assert(!self.isActive_); - assert(self.sampleRate_ == 0); + bool Plugin::clapActivate(clap_plugin *plugin, int sample_rate) { + auto &self = from(plugin); + self.ensureMainThread("clap_plugin.activate"); + + if (self.isActive_) { + self.hostMisbehaving("Plugin was activated twice"); + + if (sample_rate != self.sampleRate_) { + std::ostringstream msg; + msg << "The plugin was activated twice and with different sample rates: " + << self.sampleRate_ << " and " << sample_rate + << ". The host must deactivate the plugin first." << std::endl + << "Simulating deactivation."; + self.hostMisbehaving(msg.str()); + clapDeactivate(plugin); + } + } + + if (sample_rate <= 0) { + std::ostringstream msg; + msg << "The plugin was activated with an invalid sample rates: " << sample_rate; + self.hostMisbehaving(msg.str()); + return false; + } - if (!self.activate(sample_rate)) { assert(!self.isActive_); assert(self.sampleRate_ == 0); - return false; + + if (!self.activate(sample_rate)) { + assert(!self.isActive_); + assert(self.sampleRate_ == 0); + return false; + } + + self.isActive_ = true; + self.sampleRate_ = sample_rate; + return true; } - self.isActive_ = true; - self.sampleRate_ = sample_rate; - return true; -} + void Plugin::clapDeactivate(clap_plugin *plugin) { + auto &self = from(plugin); + self.ensureMainThread("clap_plugin.deactivate"); -void Plugin::clapDeactivate(clap_plugin *plugin) { - auto &self = from(plugin); - self.ensureMainThread("clap_plugin.deactivate"); + if (!self.isActive_) { + self.hostMisbehaving("The plugin was deactivated twice."); + return; + } + + if (self.scheduleAudioPortsUpdate_) + self.updateAudioPorts(); - if (!self.isActive_) { - self.hostMisbehaving("The plugin was deactivated twice."); - return; + self.deactivate(); } - if (self.scheduleAudioPortsUpdate_) - self.updateAudioPorts(); + bool Plugin::clapStartProcessing(clap_plugin *plugin) { + auto &self = from(plugin); + self.ensureAudioThread("clap_plugin.start_processing"); - self.deactivate(); -} + if (!self.isActive_) { + self.hostMisbehaving("Host called clap_plugin.start_processing() on a deactivated plugin"); + return false; + } -bool Plugin::clapStartProcessing(clap_plugin *plugin) { - auto &self = from(plugin); - self.ensureAudioThread("clap_plugin.start_processing"); + if (self.isProcessing_) { + self.hostMisbehaving("Host called clap_plugin.start_processing() twice"); + return true; + } - if (!self.isActive_) { - self.hostMisbehaving("Host called clap_plugin.start_processing() on a deactivated plugin"); - return false; + self.isProcessing_ = self.startProcessing(); + return self.isProcessing_; } - if (self.isProcessing_) { - self.hostMisbehaving("Host called clap_plugin.start_processing() twice"); - return true; - } + void Plugin::clapStopProcessing(clap_plugin *plugin) { + auto &self = from(plugin); + self.ensureAudioThread("clap_plugin.stop_processing"); - self.isProcessing_ = self.startProcessing(); - return self.isProcessing_; -} + if (!self.isActive_) { + self.hostMisbehaving("Host called clap_plugin.stop_processing() on a deactivated plugin"); + return; + } -void Plugin::clapStopProcessing(clap_plugin *plugin) { - auto &self = from(plugin); - self.ensureAudioThread("clap_plugin.stop_processing"); + if (!self.isProcessing_) { + self.hostMisbehaving("Host called clap_plugin.stop_processing() twice"); + return; + } - if (!self.isActive_) { - self.hostMisbehaving("Host called clap_plugin.stop_processing() on a deactivated plugin"); - return; + self.stopProcessing(); + self.isProcessing_ = false; } - if (!self.isProcessing_) { - self.hostMisbehaving("Host called clap_plugin.stop_processing() twice"); - return; + clap_process_status Plugin::clapProcess(struct clap_plugin *plugin, + const clap_process *process) { + auto &self = from(plugin); + self.ensureAudioThread("clap_plugin.process"); + + if (!self.isActive_) { + self.hostMisbehaving("Host called clap_plugin.process() on a deactivated plugin"); + return CLAP_PROCESS_ERROR; + } + + if (!self.isProcessing_) { + self.hostMisbehaving( + "Host called clap_plugin.process() without calling clap_plugin.start_processing()"); + return CLAP_PROCESS_ERROR; + } + + return self.process(process); } - self.stopProcessing(); - self.isProcessing_ = false; -} + const void *Plugin::clapExtension(struct clap_plugin *plugin, const char *id) { + auto &self = from(plugin); + self.ensureMainThread("clap_plugin.extension"); -clap_process_status Plugin::clapProcess(struct clap_plugin *plugin, const clap_process *process) { - auto &self = from(plugin); - self.ensureAudioThread("clap_plugin.process"); + if (!strcmp(id, CLAP_EXT_RENDER)) + return &self.pluginRender_; + if (!strcmp(id, CLAP_EXT_TRACK_INFO)) + return &pluginTrackInfo_; + if (!strcmp(id, CLAP_EXT_AUDIO_PORTS)) + return &pluginAudioPorts_; - if (!self.isActive_) { - self.hostMisbehaving("Host called clap_plugin.process() on a deactivated plugin"); - return CLAP_PROCESS_ERROR; + return from(plugin).extension(id); } - if (!self.isProcessing_) { - self.hostMisbehaving( - "Host called clap_plugin.process() without calling clap_plugin.start_processing()"); - return CLAP_PROCESS_ERROR; + void Plugin::clapTrackInfoChanged(clap_plugin *plugin) { + auto &self = from(plugin); + self.ensureMainThread("clap_plugin_track_info.changed"); + + if (!self.canUseTrackInfo()) { + self.hostMisbehaving("Host called clap_plugin_track_info.changed() but does not provide a " + "complete clap_host_track_info interface"); + return; + } + + clap_track_info info; + if (!self.hostTrackInfo_->get(self.host_, &info)) { + self.hasTrackInfo_ = false; + self.hostMisbehaving( + "clap_host_track_info.get() failed after calling clap_plugin_track_info.changed()"); + return; + } + + const bool didChannelChange = info.channel_count != self.trackInfo_.channel_count || + info.channel_map != self.trackInfo_.channel_map; + self.trackInfo_ = info; + self.hasTrackInfo_ = true; + + if (didChannelChange && self.canChangeAudioPorts() && + self.shouldInvalidateAudioPortsDefinitionOnTrackChannelChange()) + self.invalidateAudioPortsDefinition(); + + self.trackInfoChanged(); } - return self.process(process); -} + void Plugin::invalidateAudioPortsDefinition() { + checkMainThread(); + + if (isActive()) { + scheduleAudioPortsUpdate_ = true; + hostAudioPorts_->rescan(host_, CLAP_AUDIO_PORTS_RESCAN_ALL); + return; + } + + updateAudioPorts(); + } -const void *Plugin::clapExtension(struct clap_plugin *plugin, const char *id) { - auto &self = from(plugin); - self.ensureMainThread("clap_plugin.extension"); + void Plugin::initTrackInfo() { + checkMainThread(); - if (!strcmp(id, CLAP_EXT_RENDER)) - return &self.pluginRender_; - if (!strcmp(id, CLAP_EXT_TRACK_INFO)) - return &pluginTrackInfo_; - if (!strcmp(id, CLAP_EXT_AUDIO_PORTS)) - return &pluginAudioPorts_; + assert(!hasTrackInfo_); + if (!canUseTrackInfo()) + return; - return from(plugin).extension(id); -} + hasTrackInfo_ = hostTrackInfo_->get(host_, &trackInfo_); + } -void Plugin::clapTrackInfoChanged(clap_plugin *plugin) { - auto &self = from(plugin); - self.ensureMainThread("clap_plugin_track_info.changed"); + uint32_t Plugin::clapAudioPortsCount(clap_plugin *plugin, bool is_input) { + auto &self = from(plugin); + self.ensureMainThread("clap_plugin_audio_ports.count"); - if (!self.canUseTrackInfo()) { - self.hostMisbehaving("Host called clap_plugin_track_info.changed() but does not provide a " - "complete clap_host_track_info interface"); - return; + return is_input ? self.inputAudioPorts_.size() : self.outputAudioPorts_.size(); } - clap_track_info info; - if (!self.hostTrackInfo_->get(self.host_, &info)) { - self.hasTrackInfo_ = false; - self.hostMisbehaving( - "clap_host_track_info.get() failed after calling clap_plugin_track_info.changed()"); - return; + bool Plugin::clapAudioPortsInfo(clap_plugin * plugin, + uint32_t index, + bool is_input, + clap_audio_port_info *info) { + auto &self = from(plugin); + self.ensureMainThread("clap_plugin_audio_ports.info"); + auto count = clapAudioPortsCount(plugin, is_input); + if (index >= count) { + std::ostringstream msg; + msg << "Host called clap_plugin_audio_ports.info() with an index out of bounds: " << index + << " >= " << count; + self.hostMisbehaving(msg.str()); + return false; + } + + *info = is_input ? self.inputAudioPorts_[index] : self.outputAudioPorts_[index]; + return true; } - const bool didChannelChange = info.channel_count != self.trackInfo_.channel_count || - info.channel_map != self.trackInfo_.channel_map; - self.trackInfo_ = info; - self.hasTrackInfo_ = true; + void Plugin::updateAudioPorts() { + checkMainThread(); + assert(!isActive()); - if (didChannelChange && self.canChangeAudioPorts() && - self.shouldInvalidateAudioPortsDefinitionOnTrackChannelChange()) - self.invalidateAudioPortsDefinition(); + scheduleAudioPortsUpdate_ = false; - self.trackInfoChanged(); -} + // Get the new list of ports + std::vector<clap_audio_port_info> inputs; + std::vector<clap_audio_port_info> outputs; + defineAudioPorts(inputAudioPorts_, outputAudioPorts_); -void Plugin::invalidateAudioPortsDefinition() { - checkMainThread(); + uint32_t flags = 0; + + if (inputs.size() != inputAudioPorts_.size() || outputs.size() != outputAudioPorts_.size()) + flags = CLAP_AUDIO_PORTS_RESCAN_ALL; + else { + for (int i = 0; i < inputs.size() && !(flags & CLAP_AUDIO_PORTS_RESCAN_ALL); ++i) + flags |= compareAudioPortsInfo(inputs[i], inputAudioPorts_[i]); + for (int i = 0; i < outputs.size() && !(flags & CLAP_AUDIO_PORTS_RESCAN_ALL); ++i) + flags |= compareAudioPortsInfo(outputs[i], outputAudioPorts_[i]); + } - if (isActive()) { - scheduleAudioPortsUpdate_ = true; - hostAudioPorts_->rescan(host_, CLAP_AUDIO_PORTS_RESCAN_ALL); - return; + if (flags == 0) + return; + + // compare the list of ports to the old one + inputAudioPorts_ = inputs; + outputAudioPorts_ = outputs; + + hostAudioPorts_->rescan(host_, flags); } - updateAudioPorts(); - hostAudioPorts_->rescan(host_, CLAP_AUDIO_PORTS_RESCAN_ALL); -} + ///////////// + // Logging // + ///////////// + void Plugin::log(clap_log_severity severity, const char *msg) const { + if (canUseHostLog()) + hostLog_->log(host_, severity, msg); + else + std::clog << msg << std::endl; + } -void Plugin::initTrackInfo() { - checkMainThread(); + void Plugin::hostMisbehaving(const char *msg) { log(CLAP_LOG_HOST_MISBEHAVING, msg); } - assert(!hasTrackInfo_); - if (!canUseTrackInfo()) - return; + ///////////////////////////////// + // Interface consistency check // + ///////////////////////////////// - hasTrackInfo_ = hostTrackInfo_->get(host_, &trackInfo_); -} + bool Plugin::canUseHostLog() const noexcept { return hostLog_ && hostLog_->log; } -uint32_t Plugin::clapAudioPortsCount(clap_plugin *plugin, bool is_input) { - auto &self = from(plugin); - self.ensureMainThread("clap_plugin_audio_ports.count"); + bool Plugin::canUseThreadCheck() const noexcept { + return hostThreadCheck_ && hostThreadCheck_->is_audio_thread && + hostThreadCheck_->is_main_thread; + } - return is_input ? self.inputAudioPorts_.size() : self.outputAudioPorts_.size(); -} + ///////////////////// + // Thread Checking // + ///////////////////// -bool Plugin::clapAudioPortsInfo(clap_plugin * plugin, - uint32_t index, - bool is_input, - clap_audio_port_info *info) { - auto &self = from(plugin); - self.ensureMainThread("clap_plugin_audio_ports.info"); - auto count = clapAudioPortsCount(plugin, is_input); - if (index >= count) { - std::ostringstream msg; - msg << "Host called clap_plugin_audio_ports.info() with an index out of bounds: " << index - << " >= " << count; - self.hostMisbehaving(msg.str()); - return false; + void Plugin::checkMainThread() { + if (!hostThreadCheck_ || !hostThreadCheck_->is_main_thread || + hostThreadCheck_->is_main_thread(host_)) + return; + + std::terminate(); } - *info = is_input ? self.inputAudioPorts_[index] : self.outputAudioPorts_[index]; - return true; -} - -void Plugin::updateAudioPorts() { - scheduleAudioPortsUpdate_ = false; - inputAudioPorts_.clear(); - outputAudioPorts_.clear(); - defineAudioPorts(inputAudioPorts_, outputAudioPorts_); -} - -///////////// -// Logging // -///////////// -void Plugin::log(clap_log_severity severity, const char *msg) const { - if (canUseHostLog()) - hostLog_->log(host_, severity, msg); - else - std::clog << msg << std::endl; -} - -void Plugin::hostMisbehaving(const char *msg) { log(CLAP_LOG_HOST_MISBEHAVING, msg); } - -///////////////////////////////// -// Interface consistency check // -///////////////////////////////// - -bool Plugin::canUseHostLog() const noexcept { return hostLog_ && hostLog_->log; } - -bool Plugin::canUseThreadCheck() const noexcept { - return hostThreadCheck_ && hostThreadCheck_->is_audio_thread && hostThreadCheck_->is_main_thread; -} - -///////////////////// -// Thread Checking // -///////////////////// - -void Plugin::checkMainThread() { - if (!hostThreadCheck_ || !hostThreadCheck_->is_main_thread || - hostThreadCheck_->is_main_thread(host_)) - return; - - std::terminate(); -} - -void Plugin::ensureMainThread(const char *method) { - if (!hostThreadCheck_ || !hostThreadCheck_->is_main_thread || - hostThreadCheck_->is_main_thread(host_)) - return; - - std::ostringstream msg; - msg << "Host called the method " << method - << "() on wrong thread! It must be called on main thread!"; - hostMisbehaving(msg.str()); - std::terminate(); -} - -void Plugin::ensureAudioThread(const char *method) { - if (!hostThreadCheck_ || !hostThreadCheck_->is_audio_thread || - hostThreadCheck_->is_audio_thread(host_)) - return; - - std::ostringstream msg; - msg << "Host called the method " << method - << "() on wrong thread! It must be called on audio thread!"; - hostMisbehaving(msg.str()); - std::terminate(); -} - -/////////////// -// Utilities // -/////////////// -Plugin &Plugin::from(clap_plugin *plugin) { - if (!plugin) { - std::cerr << "called with a null clap_plugin pointer!" << std::endl; + void Plugin::ensureMainThread(const char *method) { + if (!hostThreadCheck_ || !hostThreadCheck_->is_main_thread || + hostThreadCheck_->is_main_thread(host_)) + return; + + std::ostringstream msg; + msg << "Host called the method " << method + << "() on wrong thread! It must be called on main thread!"; + hostMisbehaving(msg.str()); std::terminate(); } - if (!plugin->plugin_data) { - std::cerr << "called with a null clap_plugin->plugin_data pointer! The host must never " - "change this pointer!" - << std::endl; + void Plugin::ensureAudioThread(const char *method) { + if (!hostThreadCheck_ || !hostThreadCheck_->is_audio_thread || + hostThreadCheck_->is_audio_thread(host_)) + return; + + std::ostringstream msg; + msg << "Host called the method " << method + << "() on wrong thread! It must be called on audio thread!"; + hostMisbehaving(msg.str()); std::terminate(); } - return *static_cast<Plugin *>(plugin->plugin_data); -} - -template <typename T> -void Plugin::initInterface(const T *&ptr, const char *id) { - assert(!ptr); - assert(id); - - if (host_->extension) - ptr = static_cast<const T *>(host_->extension(host_, id)); -} - -void Plugin::initInterfaces() { - initInterface(hostLog_, CLAP_EXT_LOG); - initInterface(hostThreadCheck_, CLAP_EXT_THREAD_CHECK); - initInterface(hostThreadPool_, CLAP_EXT_THREAD_POOL); - initInterface(hostAudioPorts_, CLAP_EXT_AUDIO_PORTS); - initInterface(hostEventLoop_, CLAP_EXT_EVENT_LOOP); - initInterface(hostEventFilter_, CLAP_EXT_EVENT_FILTER); - initInterface(hostFileReference_, CLAP_EXT_FILE_REFERENCE); - initInterface(hostLatency_, CLAP_EXT_LATENCY); - initInterface(hostGui_, CLAP_EXT_GUI); - initInterface(hostParams_, CLAP_EXT_PARAMS); - initInterface(hostTrackInfo_, CLAP_EXT_TRACK_INFO); - initInterface(hostState_, CLAP_EXT_STATE); - initInterface(hostNoteName_, CLAP_EXT_NOTE_NAME); -} - -int Plugin::sampleRate() const noexcept { - assert(isActive_ && - "there is no point in querying the sample rate if the plugin isn't activated"); - assert(isActive_ ? sampleRate_ > 0 : sampleRate_ == 0); - return sampleRate_; -} -\ No newline at end of file + /////////////// + // Utilities // + /////////////// + Plugin &Plugin::from(clap_plugin *plugin) { + if (!plugin) { + std::cerr << "called with a null clap_plugin pointer!" << std::endl; + std::terminate(); + } + + if (!plugin->plugin_data) { + std::cerr << "called with a null clap_plugin->plugin_data pointer! The host must never " + "change this pointer!" + << std::endl; + std::terminate(); + } + + return *static_cast<Plugin *>(plugin->plugin_data); + } + + template <typename T> + void Plugin::initInterface(const T *&ptr, const char *id) { + assert(!ptr); + assert(id); + + if (host_->extension) + ptr = static_cast<const T *>(host_->extension(host_, id)); + } + + void Plugin::initInterfaces() { + initInterface(hostLog_, CLAP_EXT_LOG); + initInterface(hostThreadCheck_, CLAP_EXT_THREAD_CHECK); + initInterface(hostThreadPool_, CLAP_EXT_THREAD_POOL); + initInterface(hostAudioPorts_, CLAP_EXT_AUDIO_PORTS); + initInterface(hostEventLoop_, CLAP_EXT_EVENT_LOOP); + initInterface(hostEventFilter_, CLAP_EXT_EVENT_FILTER); + initInterface(hostFileReference_, CLAP_EXT_FILE_REFERENCE); + initInterface(hostLatency_, CLAP_EXT_LATENCY); + initInterface(hostGui_, CLAP_EXT_GUI); + initInterface(hostParams_, CLAP_EXT_PARAMS); + initInterface(hostTrackInfo_, CLAP_EXT_TRACK_INFO); + initInterface(hostState_, CLAP_EXT_STATE); + initInterface(hostNoteName_, CLAP_EXT_NOTE_NAME); + } + + uint32_t Plugin::compareAudioPortsInfo(const clap_audio_port_info &a, + const clap_audio_port_info &b) noexcept { + if (a.supports_64_bits != b.supports_64_bits || a.supports_in_place != b.supports_in_place || + a.is_cv != b.is_cv || a.is_main != b.is_main || a.channel_count != b.channel_count || + a.channel_map != b.channel_map || a.id != b.id) + return CLAP_AUDIO_PORTS_RESCAN_ALL; + + if (strncmp(a.name, b.name, sizeof(a.name))) + return CLAP_AUDIO_PORTS_RESCAN_NAMES; + + return 0; + } + + int Plugin::sampleRate() const noexcept { + assert(isActive_ && + "there is no point in querying the sample rate if the plugin isn't activated"); + assert(isActive_ ? sampleRate_ > 0 : sampleRate_ == 0); + return sampleRate_; + } +} // namespace clap +\ No newline at end of file diff --git a/examples/plugins/plugin.hh b/examples/plugins/plugin.hh @@ -7,168 +7,182 @@ #include <clap/all.h> -class Plugin { -public: - clap_plugin *clapPlugin() noexcept { return &plugin_; } - -protected: - Plugin(const clap_plugin_descriptor *desc, clap_host *host); - virtual ~Plugin() = default; - - // not copyable, not moveable - Plugin(const Plugin &) = delete; - Plugin(Plugin &&) = delete; - Plugin &operator=(const Plugin &) = delete; - Plugin &operator=(Plugin &&) = delete; - - ///////////////////////// - // Methods to override // - ///////////////////////// - - virtual bool init() { return true; } - virtual bool activate(int sample_rate) { return true; } - virtual void deactivate() {} - virtual bool startProcessing() { return true; } - virtual void stopProcessing() {} - virtual clap_process_status process(const clap_process *process) { return CLAP_PROCESS_SLEEP; } - virtual const void * extension(const char *id) { return nullptr; } - - virtual void defineAudioPorts(std::vector<clap_audio_port_info> &inputPorts, - std::vector<clap_audio_port_info> &outputPorts) {} - void invalidateAudioPortsDefinition(); - virtual bool shouldInvalidateAudioPortsDefinitionOnTrackChannelChange() const { return false; } - - virtual void trackInfoChanged() {} - - ///////////// - // Logging // - ///////////// - void log(clap_log_severity severity, const char *msg) const; - void hostMisbehaving(const char *msg); - void hostMisbehaving(const std::string &msg) { hostMisbehaving(msg.c_str()); } - - ///////////////////////////////// - // Interface consistency check // - ///////////////////////////////// - bool canUseHostLog() const noexcept; - bool canUseThreadCheck() const noexcept; - bool canUseTrackInfo() const noexcept; - bool canChangeAudioPorts() const noexcept; - - ///////////////////// - // Thread Checking // - ///////////////////// - void checkMainThread(); - void ensureMainThread(const char *method); - void ensureAudioThread(const char *method); - - /////////////// - // Utilities // - /////////////// - static Plugin &from(clap_plugin *plugin); - - template <typename T> - void initInterface(const T *&ptr, const char *id); - void initInterfaces(); - - ////////////////////// - // Processing State // - ////////////////////// - bool isActive() const noexcept { return isActive_; } - bool isProcessing() const noexcept { return isProcessing_; } - int sampleRate() const noexcept; - - ////////////////////// - // Cached Host Info // - ////////////////////// - bool hasTrackInfo() const noexcept { return hasTrackInfo_; } - const clap_track_info &trackInfo() const noexcept { - assert(hasTrackInfo_); - return trackInfo_; - } - uint32_t trackChannelCount() const noexcept { - return hasTrackInfo_ ? trackInfo_.channel_count : 2; - } - clap_chmap trackChannelMap() const noexcept { - return hasTrackInfo_ ? trackInfo_.channel_map : CLAP_CHMAP_STEREO; - } - -protected: - clap_plugin_event_filter pluginEventFilter_; - clap_plugin_latency pluginLatency_; - clap_plugin_params pluginParams_; - clap_plugin_render pluginRender_; - clap_plugin_note_name pluginNoteName_; - clap_plugin_thread_pool pluginThreadPool_; - - /* state related */ - clap_plugin_state pluginState_; - clap_plugin_preset_load pluginPresetLoad_; - clap_plugin_file_reference pluginFileReference_; - - /* GUI related */ - clap_plugin_gui pluginGui_; - clap_plugin_gui_win32 pluginGuiWin32_; - clap_plugin_gui_cocoa pluginGuiCocoa_; - clap_plugin_gui_x11 pluginGuiX11_; - clap_plugin_event_loop pluginEventLoop_; - - clap_host *const host_ = nullptr; - const clap_host_log * hostLog_ = nullptr; - const clap_host_thread_check * hostThreadCheck_ = nullptr; - const clap_host_thread_pool * hostThreadPool_ = nullptr; - const clap_host_audio_ports * hostAudioPorts_ = nullptr; - const clap_host_event_filter * hostEventFilter_ = nullptr; - const clap_host_file_reference *hostFileReference_ = nullptr; - const clap_host_latency * hostLatency_ = nullptr; - const clap_host_gui * hostGui_ = nullptr; - const clap_host_event_loop * hostEventLoop_ = nullptr; - const clap_host_params * hostParams_ = nullptr; - const clap_host_track_info * hostTrackInfo_ = nullptr; - const clap_host_state * hostState_ = nullptr; - const clap_host_note_name * hostNoteName_ = nullptr; - -private: - ///////////////////// - // CLAP Interfaces // - ///////////////////// - - clap_plugin plugin_; - // clap_plugin - static bool clapInit(clap_plugin *plugin); - static void clapDestroy(clap_plugin *plugin); - static bool clapActivate(clap_plugin *plugin, int sample_rate); - static void clapDeactivate(clap_plugin *plugin); - static bool clapStartProcessing(clap_plugin *plugin); - static void clapStopProcessing(clap_plugin *plugin); - static clap_process_status clapProcess(struct clap_plugin *plugin, const clap_process *process); - static const void * clapExtension(struct clap_plugin *plugin, const char *id); - - // clap_plugin_track_info - static void clapTrackInfoChanged(clap_plugin *plugin); - void initTrackInfo(); - - // clap_plugin_audio_ports - static uint32_t clapAudioPortsCount(clap_plugin *plugin, bool is_input); - static bool clapAudioPortsInfo(clap_plugin * plugin, - uint32_t index, - bool is_input, - clap_audio_port_info *info); - void updateAudioPorts(); - - static const constexpr clap_plugin_track_info pluginTrackInfo_ = {clapTrackInfoChanged}; - static const constexpr clap_plugin_audio_ports pluginAudioPorts_ = {clapAudioPortsCount, - clapAudioPortsInfo}; - - // state - bool isActive_ = false; - bool isProcessing_ = false; - int sampleRate_ = 0; - - bool hasTrackInfo_ = false; - clap_track_info trackInfo_; - - bool scheduleAudioPortsUpdate_ = false; - std::vector<clap_audio_port_info> inputAudioPorts_; - std::vector<clap_audio_port_info> outputAudioPorts_; -}; -\ No newline at end of file +namespace clap { + class Plugin { + public: + clap_plugin *clapPlugin() noexcept { return &plugin_; } + + protected: + Plugin(const clap_plugin_descriptor *desc, clap_host *host); + virtual ~Plugin() = default; + + // not copyable, not moveable + Plugin(const Plugin &) = delete; + Plugin(Plugin &&) = delete; + Plugin &operator=(const Plugin &) = delete; + Plugin &operator=(Plugin &&) = delete; + + ///////////////////////// + // Methods to override // + ///////////////////////// + + virtual bool init() { return true; } + virtual bool activate(int sample_rate) { return true; } + virtual void deactivate() {} + virtual bool startProcessing() { return true; } + virtual void stopProcessing() {} + virtual clap_process_status process(const clap_process *process) { + return CLAP_PROCESS_SLEEP; + } + virtual const void *extension(const char *id) { return nullptr; } + + virtual void defineAudioPorts(std::vector<clap_audio_port_info> &inputPorts, + std::vector<clap_audio_port_info> &outputPorts) {} + virtual bool shouldInvalidateAudioPortsDefinitionOnTrackChannelChange() const { + return false; + } + + virtual void trackInfoChanged() {} + + ////////////////// + // Invalidation // + ////////////////// + void invalidateAudioPortsDefinition(); + + ///////////// + // Logging // + ///////////// + void log(clap_log_severity severity, const char *msg) const; + void hostMisbehaving(const char *msg); + void hostMisbehaving(const std::string &msg) { hostMisbehaving(msg.c_str()); } + + ///////////////////////////////// + // Interface consistency check // + ///////////////////////////////// + bool canUseHostLog() const noexcept; + bool canUseThreadCheck() const noexcept; + bool canUseTrackInfo() const noexcept; + bool canChangeAudioPorts() const noexcept; + + ///////////////////// + // Thread Checking // + ///////////////////// + void checkMainThread(); + void ensureMainThread(const char *method); + void ensureAudioThread(const char *method); + + /////////////// + // Utilities // + /////////////// + static Plugin &from(clap_plugin *plugin); + + template <typename T> + void initInterface(const T *&ptr, const char *id); + void initInterfaces(); + + static uint32_t compareAudioPortsInfo(const clap_audio_port_info &a, + const clap_audio_port_info &b) noexcept; + + ////////////////////// + // Processing State // + ////////////////////// + bool isActive() const noexcept { return isActive_; } + bool isProcessing() const noexcept { return isProcessing_; } + int sampleRate() const noexcept; + + ////////////////////// + // Cached Host Info // + ////////////////////// + bool hasTrackInfo() const noexcept { return hasTrackInfo_; } + const clap_track_info &trackInfo() const noexcept { + assert(hasTrackInfo_); + return trackInfo_; + } + uint32_t trackChannelCount() const noexcept { + return hasTrackInfo_ ? trackInfo_.channel_count : 2; + } + clap_chmap trackChannelMap() const noexcept { + return hasTrackInfo_ ? trackInfo_.channel_map : CLAP_CHMAP_STEREO; + } + + protected: + clap_plugin_event_filter pluginEventFilter_; + clap_plugin_latency pluginLatency_; + clap_plugin_params pluginParams_; + clap_plugin_render pluginRender_; + clap_plugin_note_name pluginNoteName_; + clap_plugin_thread_pool pluginThreadPool_; + + /* state related */ + clap_plugin_state pluginState_; + clap_plugin_preset_load pluginPresetLoad_; + clap_plugin_file_reference pluginFileReference_; + + /* GUI related */ + clap_plugin_gui pluginGui_; + clap_plugin_gui_win32 pluginGuiWin32_; + clap_plugin_gui_cocoa pluginGuiCocoa_; + clap_plugin_gui_x11 pluginGuiX11_; + clap_plugin_event_loop pluginEventLoop_; + + clap_host *const host_ = nullptr; + const clap_host_log * hostLog_ = nullptr; + const clap_host_thread_check * hostThreadCheck_ = nullptr; + const clap_host_thread_pool * hostThreadPool_ = nullptr; + const clap_host_audio_ports * hostAudioPorts_ = nullptr; + const clap_host_event_filter * hostEventFilter_ = nullptr; + const clap_host_file_reference *hostFileReference_ = nullptr; + const clap_host_latency * hostLatency_ = nullptr; + const clap_host_gui * hostGui_ = nullptr; + const clap_host_event_loop * hostEventLoop_ = nullptr; + const clap_host_params * hostParams_ = nullptr; + const clap_host_track_info * hostTrackInfo_ = nullptr; + const clap_host_state * hostState_ = nullptr; + const clap_host_note_name * hostNoteName_ = nullptr; + + private: + ///////////////////// + // CLAP Interfaces // + ///////////////////// + + clap_plugin plugin_; + // clap_plugin + static bool clapInit(clap_plugin *plugin); + static void clapDestroy(clap_plugin *plugin); + static bool clapActivate(clap_plugin *plugin, int sample_rate); + static void clapDeactivate(clap_plugin *plugin); + static bool clapStartProcessing(clap_plugin *plugin); + static void clapStopProcessing(clap_plugin *plugin); + static clap_process_status clapProcess(struct clap_plugin *plugin, + const clap_process *process); + static const void * clapExtension(struct clap_plugin *plugin, const char *id); + + // clap_plugin_track_info + static void clapTrackInfoChanged(clap_plugin *plugin); + void initTrackInfo(); + + // clap_plugin_audio_ports + static uint32_t clapAudioPortsCount(clap_plugin *plugin, bool is_input); + static bool clapAudioPortsInfo(clap_plugin * plugin, + uint32_t index, + bool is_input, + clap_audio_port_info *info); + void updateAudioPorts(); + + static const constexpr clap_plugin_track_info pluginTrackInfo_ = {clapTrackInfoChanged}; + static const constexpr clap_plugin_audio_ports pluginAudioPorts_ = {clapAudioPortsCount, + clapAudioPortsInfo}; + + // state + bool isActive_ = false; + bool isProcessing_ = false; + int sampleRate_ = 0; + + bool hasTrackInfo_ = false; + clap_track_info trackInfo_; + + bool scheduleAudioPortsUpdate_ = false; + std::vector<clap_audio_port_info> inputAudioPorts_; + std::vector<clap_audio_port_info> outputAudioPorts_; + }; +} // namespace clap +\ No newline at end of file