commit 1f428a898fca85585e4d5ee183d6c143f8a86099
parent 54bcf55d56219cc1714a07f276ac4e3ccd8ae2e8
Author: Alexandre BIQUE <bique.alexandre@gmail.com>
Date: Mon, 24 May 2021 14:02:44 +0200
Nicer way to deal with channel count
Diffstat:
6 files changed, 261 insertions(+), 103 deletions(-)
diff --git a/examples/plugins/gain/gain.cc b/examples/plugins/gain/gain.cc
@@ -1,3 +1,5 @@
+#include <cstring>
+
#include "gain.hh"
const clap_plugin_descriptor *Gain::descriptor() {
@@ -21,52 +23,38 @@ const clap_plugin_descriptor *Gain::descriptor() {
Gain::Gain(clap_host *host) : Plugin(descriptor(), host) {}
-bool Gain::init() {
- updateChannelCount(false);
+bool Gain::activate(int sample_rate) {
+ channelCount_ = trackChannelCount();
return true;
}
-void Gain::updateChannelCount(bool shouldNotifyHost) {
- if (!canUseTrackInfo())
- return;
-
- clap_track_info info;
- if (!hostTrackInfo_->get(host_, &info))
- return;
-
- if (channelCount_ == info.channel_count)
- return;
-
- if (isActive_)
- // we can't change our ports now, delay it
- schedulePortUpdate_ = true;
- else
- channelCount_ = info.channel_count;
-
- if (canChangeAudioPorts())
- hostAudioPorts_->changed(host_);
-}
-
-bool Gain::activate(int sample_rate) { return true; }
-
-void Gain::deactivate() {
- if (schedulePortUpdate_) {
- schedulePortUpdate_ = false;
- updateChannelCount(true);
- }
-}
-
-void Gain::trackInfoChanged() { updateChannelCount(true); }
+void Gain::deactivate() { channelCount_ = 0; }
clap_process_status Gain::process(const clap_process *process) {
- float *in = process->audio_inputs[0].data32i;
- float *out = process->audio_outputs[0].data32i;
+ float **in = process->audio_inputs[0].data32;
+ float **out = process->audio_outputs[0].data32;
float k = 1;
- const auto N = process->frames_count * channelCount_;
- for (int i = 0; i < N; ++i) {
- out[i] = k * in[i];
+ 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);
+}
diff --git a/examples/plugins/gain/gain.hh b/examples/plugins/gain/gain.hh
@@ -4,22 +4,21 @@
#include "../plugin.hh"
-class Gain : public Plugin {
+class Gain final : public Plugin {
public:
Gain(clap_host *host);
static const clap_plugin_descriptor *descriptor();
protected:
- bool init() override;
bool activate(int sample_rate) override;
void deactivate() override;
clap_process_status process(const clap_process *process) override;
- void trackInfoChanged() override;
- void updateChannelCount(bool shouldNotifyHost);
+ 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_ = 2;
- bool schedulePortUpdate_ = false;
+ int channelCount_ = 0;
};
\ No newline at end of file
diff --git a/examples/plugins/plugin.cc b/examples/plugins/plugin.cc
@@ -1,22 +1,22 @@
#include <cassert>
+#include <cstring>
#include <iostream>
#include <sstream>
#include <stdexcept>
-#include <cstring>
#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::clapPluginInit;
- plugin_.destroy = Plugin::clapPluginDestroy;
- plugin_.extension = Plugin::clapPluginExtension;
- plugin_.process = Plugin::clapPluginProcess;
- plugin_.activate = Plugin::clapPluginActivate;
- plugin_.deactivate = Plugin::clapPluginDeactivate;
- plugin_.start_processing = Plugin::clapPluginStartProcessing;
- plugin_.stop_processing = Plugin::clapPluginStopProcessing;
+ 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;
}
/////////////////////
@@ -24,20 +24,30 @@ Plugin::Plugin(const clap_plugin_descriptor *desc, clap_host *host) : host_(host
/////////////////////
// clap_plugin interface
-bool Plugin::clapPluginInit(clap_plugin *plugin) {
+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::clapPluginDestroy(clap_plugin *plugin) {
+void Plugin::clapDestroy(clap_plugin *plugin) {
auto &self = from(plugin);
self.ensureMainThread("clap_plugin.destroy");
delete &from(plugin);
}
-bool Plugin::clapPluginActivate(clap_plugin *plugin, int sample_rate) {
+bool Plugin::clapActivate(clap_plugin *plugin, int sample_rate) {
auto &self = from(plugin);
self.ensureMainThread("clap_plugin.activate");
@@ -51,7 +61,7 @@ bool Plugin::clapPluginActivate(clap_plugin *plugin, int sample_rate) {
<< ". The host must deactivate the plugin first." << std::endl
<< "Simulating deactivation.";
self.hostMisbehaving(msg.str());
- clapPluginDeactivate(plugin);
+ clapDeactivate(plugin);
}
}
@@ -76,7 +86,7 @@ bool Plugin::clapPluginActivate(clap_plugin *plugin, int sample_rate) {
return true;
}
-void Plugin::clapPluginDeactivate(clap_plugin *plugin) {
+void Plugin::clapDeactivate(clap_plugin *plugin) {
auto &self = from(plugin);
self.ensureMainThread("clap_plugin.deactivate");
@@ -85,10 +95,13 @@ void Plugin::clapPluginDeactivate(clap_plugin *plugin) {
return;
}
+ if (self.scheduleAudioPortsUpdate_)
+ self.updateAudioPorts();
+
self.deactivate();
}
-bool Plugin::clapPluginStartProcessing(clap_plugin *plugin) {
+bool Plugin::clapStartProcessing(clap_plugin *plugin) {
auto &self = from(plugin);
self.ensureAudioThread("clap_plugin.start_processing");
@@ -106,7 +119,7 @@ bool Plugin::clapPluginStartProcessing(clap_plugin *plugin) {
return self.isProcessing_;
}
-void Plugin::clapPluginStopProcessing(clap_plugin *plugin) {
+void Plugin::clapStopProcessing(clap_plugin *plugin) {
auto &self = from(plugin);
self.ensureAudioThread("clap_plugin.stop_processing");
@@ -124,8 +137,7 @@ void Plugin::clapPluginStopProcessing(clap_plugin *plugin) {
self.isProcessing_ = false;
}
-clap_process_status Plugin::clapPluginProcess(struct clap_plugin *plugin,
- const clap_process *process) {
+clap_process_status Plugin::clapProcess(struct clap_plugin *plugin, const clap_process *process) {
auto &self = from(plugin);
self.ensureAudioThread("clap_plugin.process");
@@ -135,23 +147,114 @@ clap_process_status Plugin::clapPluginProcess(struct clap_plugin *plugin,
}
if (!self.isProcessing_) {
- self.hostMisbehaving("Host called clap_plugin.process() without calling clap_plugin.start_processing()");
+ self.hostMisbehaving(
+ "Host called clap_plugin.process() without calling clap_plugin.start_processing()");
return CLAP_PROCESS_ERROR;
}
return self.process(process);
}
-const void *Plugin::clapPluginExtension(struct clap_plugin *plugin, const char *id) {
+const void *Plugin::clapExtension(struct clap_plugin *plugin, const char *id) {
auto &self = from(plugin);
self.ensureMainThread("clap_plugin.extension");
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_;
return from(plugin).extension(id);
}
+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();
+}
+
+void Plugin::invalidateAudioPortsDefinition() {
+ checkMainThread();
+
+ if (isActive()) {
+ scheduleAudioPortsUpdate_ = true;
+ hostAudioPorts_->rescan(host_, CLAP_AUDIO_PORTS_RESCAN_ALL);
+ return;
+ }
+
+ updateAudioPorts();
+ hostAudioPorts_->rescan(host_, CLAP_AUDIO_PORTS_RESCAN_ALL);
+}
+
+void Plugin::initTrackInfo() {
+ checkMainThread();
+
+ assert(!hasTrackInfo_);
+ if (!canUseTrackInfo())
+ return;
+
+ hasTrackInfo_ = hostTrackInfo_->get(host_, &trackInfo_);
+}
+
+uint32_t Plugin::clapAudioPortsCount(clap_plugin *plugin, bool is_input) {
+ auto &self = from(plugin);
+ self.ensureMainThread("clap_plugin_audio_ports.count");
+
+ return is_input ? self.inputAudioPorts_.size() : self.outputAudioPorts_.size();
+}
+
+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;
+}
+
+void Plugin::updateAudioPorts() {
+ scheduleAudioPortsUpdate_ = false;
+ inputAudioPorts_.clear();
+ outputAudioPorts_.clear();
+ defineAudioPorts(inputAudioPorts_, outputAudioPorts_);
+}
+
/////////////
// Logging //
/////////////
@@ -178,6 +281,14 @@ bool Plugin::canUseThreadCheck() const noexcept {
// 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_))
@@ -244,4 +355,11 @@ void Plugin::initInterfaces() {
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
diff --git a/examples/plugins/plugin.hh b/examples/plugins/plugin.hh
@@ -1,7 +1,9 @@
#pragma once
-#include <string_view>
+#include <cassert>
#include <string>
+#include <string_view>
+#include <vector>
#include <clap/all.h>
@@ -13,11 +15,16 @@ 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() {}
@@ -26,29 +33,19 @@ protected:
virtual clap_process_status process(const clap_process *process) { return CLAP_PROCESS_SLEEP; }
virtual const void * extension(const char *id) { return nullptr; }
- virtual void trackInfoChanged() {}
+ 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; }
- /////////////////////
- // CLAP Interfaces //
- /////////////////////
-
- // clap_plugin interface
- static bool clapPluginInit(clap_plugin *plugin);
- static void clapPluginDestroy(clap_plugin *plugin);
- static bool clapPluginActivate(clap_plugin *plugin, int sample_rate);
- static void clapPluginDeactivate(clap_plugin *plugin);
- static bool clapPluginStartProcessing(clap_plugin *plugin);
- static void clapPluginStopProcessing(clap_plugin *plugin);
- static clap_process_status clapPluginProcess(struct clap_plugin *plugin,
- const clap_process *process);
- static const void * clapPluginExtension(struct clap_plugin *plugin, const char *id);
+ 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()); }
+ void hostMisbehaving(const std::string &msg) { hostMisbehaving(msg.c_str()); }
/////////////////////////////////
// Interface consistency check //
@@ -61,6 +58,7 @@ protected:
/////////////////////
// Thread Checking //
/////////////////////
+ void checkMainThread();
void ensureMainThread(const char *method);
void ensureAudioThread(const char *method);
@@ -73,14 +71,33 @@ protected:
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 plugin_;
- clap_plugin_audio_ports pluginAudioPorts_;
clap_plugin_event_filter pluginEventFilter_;
clap_plugin_latency pluginLatency_;
clap_plugin_params pluginParams_;
clap_plugin_render pluginRender_;
- clap_plugin_track_info pluginTrackInfo_;
clap_plugin_note_name pluginNoteName_;
clap_plugin_thread_pool pluginThreadPool_;
@@ -111,8 +128,47 @@ protected:
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
diff --git a/include/clap/clap.h b/include/clap/clap.h
@@ -60,11 +60,9 @@ enum {
typedef int32_t clap_process_status;
typedef struct clap_audio_buffer {
- // Only one of dataXXX pointer will be set.
- float ** data32; // non-interleaved
- float * data32i; // interleaved
- double **data64; // non-interleaved
- double * data64i; // interleaved
+ // Either data32 or data64 pointer will be set.
+ float ** data32;
+ double **data64;
int32_t channel_count;
uint32_t latency; // latency from/to the audio interface
uint64_t constant_mask; // mask & (1 << N) to test if channel N is constant
diff --git a/include/clap/ext/audio-ports.h b/include/clap/ext/audio-ports.h
@@ -20,33 +20,32 @@ typedef struct clap_audio_port_info {
// and output, only for main input to main output
int32_t channel_count;
clap_chmap channel_map;
- bool interleave; // selects interleaved buffer
} clap_audio_port_info;
// The audio ports scan has to be done while the plugin is deactivated.
typedef struct clap_plugin_audio_ports {
// number of ports, for either input or output
// [main-thread]
- int32_t (*get_count)(clap_plugin *plugin, bool is_input);
+ uint32_t (*count)(clap_plugin *plugin, bool is_input);
// get info about about an audio port.
// [main-thread]
- void (*get_info)(clap_plugin *plugin, int32_t index, bool is_input, clap_audio_port_info *info);
-
- void (*set_active)(
- clap_plugin *plugin, int32_t index, bool is_input, bool use_64, bool is_active);
+ bool (*get_info)(clap_plugin *plugin, uint32_t index, bool is_input, clap_audio_port_info *info);
} clap_plugin_audio_ports;
-typedef struct clap_host_audio_ports {
- // Tell the host that the plugin ports has changed.
- // The host shall deactivate the plugin and then scan the ports again.
- // [main-thread]
- void (*changed)(clap_host *host);
+enum {
+ // The ports name did change, the host can scan them right away.
+ CLAP_AUDIO_PORTS_RESCAN_NAMES = 1 << 0,
- // Tell the host that the plugin ports name have changed.
- // It is not necessary to deactivates the plugin.
+ // The ports have changed, the host shall deactivate the plugin
+ // and perform a full scan of the ports.
+ CLAP_AUDIO_PORTS_RESCAN_ALL = 1 << 1,
+};
+
+typedef struct clap_host_audio_ports {
+ // Rescan the full list of audio ports according to the flags.
// [main-thread]
- void (*name_changed)(clap_host *host);
+ void (*rescan)(clap_host *host, uint32_t flags);
} clap_host_audio_ports;
#ifdef __cplusplus