commit 04d86c71962b8f21fd96b0d1f119d62e58715165
parent 23692d024e57c617bd0692beb1835d48f98eb914
Author: falkTX <falktx@falktx.com>
Date: Mon, 5 Sep 2022 18:12:13 +0100
Start implementing CLAP
Diffstat:
21 files changed, 1474 insertions(+), 3 deletions(-)
diff --git a/Makefile.plugins.mk b/Makefile.plugins.mk
@@ -309,7 +309,7 @@ SYMBOLS_LV2UI = -sEXPORTED_FUNCTIONS="['lv2ui_descriptor']"
SYMBOLS_LV2 = -sEXPORTED_FUNCTIONS="['lv2_descriptor','lv2_generate_ttl','lv2ui_descriptor']"
SYMBOLS_VST2 = -sEXPORTED_FUNCTIONS="['VSTPluginMain']"
SYMBOLS_VST3 = -sEXPORTED_FUNCTIONS="['GetPluginFactory','ModuleEntry','ModuleExit']"
-SYMBOLS_CLAP = -sEXPORTED_FUNCTIONS="['']"
+SYMBOLS_CLAP = -sEXPORTED_FUNCTIONS="['clap_entry']"
SYMBOLS_SHARED = -sEXPORTED_FUNCTIONS="['createSharedPlugin']"
else ifeq ($(WINDOWS),true)
SYMBOLS_LADSPA = $(DPF_PATH)/utils/symbols/ladspa.def
@@ -605,6 +605,7 @@ endif
-include $(BUILD_DIR)/DistrhoPluginMain_LV2.cpp.d
-include $(BUILD_DIR)/DistrhoPluginMain_VST2.cpp.d
-include $(BUILD_DIR)/DistrhoPluginMain_VST3.cpp.d
+-include $(BUILD_DIR)/DistrhoPluginMain_CLAP.cpp.d
-include $(BUILD_DIR)/DistrhoPluginMain_SHARED.cpp.d
-include $(BUILD_DIR)/DistrhoPluginMain_STATIC.cpp.d
@@ -613,6 +614,7 @@ endif
-include $(BUILD_DIR)/DistrhoUIMain_LV2.cpp.d
-include $(BUILD_DIR)/DistrhoUIMain_VST2.cpp.d
-include $(BUILD_DIR)/DistrhoUIMain_VST3.cpp.d
+-include $(BUILD_DIR)/DistrhoUIMain_CLAP.cpp.d
-include $(BUILD_DIR)/DistrhoUIMain_SHARED.cpp.d
-include $(BUILD_DIR)/DistrhoUIMain_STATIC.cpp.d
diff --git a/distrho/DistrhoPluginMain.cpp b/distrho/DistrhoPluginMain.cpp
@@ -19,7 +19,7 @@
#if defined(DISTRHO_PLUGIN_TARGET_CARLA)
# include "src/DistrhoPluginCarla.cpp"
#elif defined(DISTRHO_PLUGIN_TARGET_CLAP)
-# include "src/DistrhoPluginStub.cpp"
+# include "src/DistrhoPluginCLAP.cpp"
#elif defined(DISTRHO_PLUGIN_TARGET_JACK)
# include "src/DistrhoPluginJACK.cpp"
#elif (defined(DISTRHO_PLUGIN_TARGET_LADSPA) || defined(DISTRHO_PLUGIN_TARGET_DSSI))
diff --git a/distrho/src/DistrhoPluginCLAP.cpp b/distrho/src/DistrhoPluginCLAP.cpp
@@ -0,0 +1,515 @@
+/*
+ * DISTRHO Plugin Framework (DPF)
+ * Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com>
+ *
+ * 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
+ * permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
+ * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
+ * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "DistrhoPluginInfo.h"
+#include "DistrhoPluginInternal.hpp"
+#include "extra/ScopedPointer.hpp"
+
+#include "clap/entry.h"
+#include "clap/plugin-factory.h"
+#include "clap/ext/audio-ports.h"
+#include "src/DistrhoDefines.h"
+#include "src/clap/version.h"
+
+START_NAMESPACE_DISTRHO
+
+// --------------------------------------------------------------------------------------------------------------------
+
+#if ! DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
+static constexpr const writeMidiFunc writeMidiCallback = nullptr;
+#endif
+#if ! DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST
+static constexpr const requestParameterValueChangeFunc requestParameterValueChangeCallback = nullptr;
+#endif
+#if ! DISTRHO_PLUGIN_WANT_STATE
+static const updateStateValueFunc updateStateValueCallback = nullptr;
+#endif
+
+// --------------------------------------------------------------------------------------------------------------------
+
+/**
+ * CLAP plugin class.
+ */
+class PluginCLAP
+{
+public:
+ PluginCLAP(const clap_host_t* const host)
+ : fPlugin(this,
+ writeMidiCallback,
+ requestParameterValueChangeCallback,
+ updateStateValueCallback),
+ fHost(host),
+ fOutputEvents(nullptr)
+ {
+ }
+
+ bool init()
+ {
+ if (!clap_version_is_compatible(fHost->clap_version))
+ return false;
+
+ // TODO check host features
+ return true;
+ }
+
+ void activate(const double sampleRate, const uint32_t maxFramesCount)
+ {
+ fPlugin.setSampleRate(sampleRate, true);
+ fPlugin.setBufferSize(maxFramesCount, true);
+ fPlugin.activate();
+ }
+
+ void deactivate()
+ {
+ fPlugin.deactivate();
+ }
+
+ bool process(const clap_process_t* const process)
+ {
+ #if DISTRHO_PLUGIN_WANT_TIMEPOS
+ if (const clap_event_transport_t* const transport = process->transport)
+ {
+ fTimePosition.playing = transport->flags & CLAP_TRANSPORT_IS_PLAYING;
+
+ fTimePosition.frame = process->steady_time >= 0 ? process->steady_time : 0;
+
+ if (transport->flags & CLAP_TRANSPORT_HAS_TEMPO)
+ fTimePosition.bbt.beatsPerMinute = transport->tempo;
+ else
+ fTimePosition.bbt.beatsPerMinute = 120.0;
+
+ // ticksPerBeat is not possible with CLAP
+ fTimePosition.bbt.ticksPerBeat = 1920.0;
+
+ // TODO verify if this works or makes any sense
+ if ((transport->flags & (CLAP_TRANSPORT_HAS_BEATS_TIMELINE|CLAP_TRANSPORT_HAS_TIME_SIGNATURE)) == (CLAP_TRANSPORT_HAS_BEATS_TIMELINE|CLAP_TRANSPORT_HAS_TIME_SIGNATURE))
+ {
+ const double ppqPos = std::abs(transport->song_pos_beats);
+ const int ppqPerBar = transport->tsig_num * 4 / transport->tsig_denom;
+ const double barBeats = (std::fmod(ppqPos, ppqPerBar) / ppqPerBar) * transport->tsig_num;
+ const double rest = std::fmod(barBeats, 1.0);
+
+ fTimePosition.bbt.valid = true;
+ fTimePosition.bbt.bar = static_cast<int32_t>(ppqPos) / ppqPerBar + 1;
+ fTimePosition.bbt.beat = static_cast<int32_t>(barBeats - rest + 0.5) + 1;
+ fTimePosition.bbt.tick = rest * fTimePosition.bbt.ticksPerBeat;
+ fTimePosition.bbt.beatsPerBar = transport->tsig_num;
+ fTimePosition.bbt.beatType = transport->tsig_denom;
+
+ if (transport->song_pos_beats < 0.0)
+ {
+ --fTimePosition.bbt.bar;
+ fTimePosition.bbt.beat = transport->tsig_num - fTimePosition.bbt.beat + 1;
+ fTimePosition.bbt.tick = fTimePosition.bbt.ticksPerBeat - fTimePosition.bbt.tick - 1;
+ }
+ }
+ else
+ {
+ fTimePosition.bbt.valid = false;
+ fTimePosition.bbt.bar = 1;
+ fTimePosition.bbt.beat = 1;
+ fTimePosition.bbt.tick = 0.0;
+ fTimePosition.bbt.beatsPerBar = 4.0f;
+ fTimePosition.bbt.beatType = 4.0f;
+ }
+
+ fTimePosition.bbt.barStartTick = fTimePosition.bbt.ticksPerBeat*
+ fTimePosition.bbt.beatsPerBar*
+ (fTimePosition.bbt.bar-1);
+ }
+ else
+ {
+ fTimePosition.playing = false;
+ fTimePosition.frame = 0;
+ fTimePosition.bbt.valid = false;
+ fTimePosition.bbt.beatsPerMinute = 120.0;
+ fTimePosition.bbt.bar = 1;
+ fTimePosition.bbt.beat = 1;
+ fTimePosition.bbt.tick = 0.0;
+ fTimePosition.bbt.beatsPerBar = 4.f;
+ fTimePosition.bbt.beatType = 4.f;
+ fTimePosition.bbt.barStartTick = 0;
+ }
+
+ fPlugin.setTimePosition(fTimePosition);
+ #endif
+
+ if (const clap_input_events_t* const inputEvents = process->in_events)
+ {
+ if (const uint32_t len = inputEvents->size(inputEvents))
+ {
+ for (uint32_t i=0; i<len; ++i)
+ {
+ const clap_event_header_t* const event = inputEvents->get(inputEvents, i);
+
+ // event->time
+ switch (event->type)
+ {
+ case CLAP_EVENT_NOTE_ON:
+ case CLAP_EVENT_NOTE_OFF:
+ case CLAP_EVENT_NOTE_CHOKE:
+ case CLAP_EVENT_NOTE_END:
+ case CLAP_EVENT_NOTE_EXPRESSION:
+ case CLAP_EVENT_PARAM_VALUE:
+ case CLAP_EVENT_PARAM_MOD:
+ case CLAP_EVENT_PARAM_GESTURE_BEGIN:
+ case CLAP_EVENT_PARAM_GESTURE_END:
+ case CLAP_EVENT_TRANSPORT:
+ case CLAP_EVENT_MIDI:
+ case CLAP_EVENT_MIDI_SYSEX:
+ case CLAP_EVENT_MIDI2:
+ break;
+ }
+ }
+ }
+ }
+
+ if (const uint32_t frames = process->frames_count)
+ {
+ DISTRHO_SAFE_ASSERT_UINT2_RETURN(process->audio_inputs_count == DISTRHO_PLUGIN_NUM_INPUTS,
+ process->audio_inputs_count, DISTRHO_PLUGIN_NUM_INPUTS, false);
+ DISTRHO_SAFE_ASSERT_UINT2_RETURN(process->audio_outputs_count == DISTRHO_PLUGIN_NUM_OUTPUTS,
+ process->audio_outputs_count, DISTRHO_PLUGIN_NUM_OUTPUTS, false);
+
+ // TODO multi-port bus stuff
+ const float** inputs = process->audio_inputs != nullptr
+ ? const_cast<const float**>(process->audio_inputs->data32)
+ : nullptr;
+ /**/ float** outputs = process->audio_outputs != nullptr
+ ? process->audio_inputs->data32
+ : nullptr;
+
+ fOutputEvents = process->out_events;
+
+ #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
+ fPlugin.run(inputs, outputs, frames, fMidiEvents, midiEventCount);
+ #else
+ fPlugin.run(inputs, outputs, frames);
+ #endif
+
+ fOutputEvents = nullptr;
+ }
+
+ return true;
+ }
+
+ // ----------------------------------------------------------------------------------------------------------------
+
+private:
+ // Plugin
+ PluginExporter fPlugin;
+
+ // CLAP stuff
+ const clap_host_t* const fHost;
+ const clap_output_events_t* fOutputEvents;
+ #if DISTRHO_PLUGIN_WANT_TIMEPOS
+ TimePosition fTimePosition;
+ #endif
+
+ // ----------------------------------------------------------------------------------------------------------------
+ // DPF callbacks
+
+ #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
+ bool writeMidi(const MidiEvent&)
+ {
+ return true;
+ }
+
+ static bool writeMidiCallback(void* const ptr, const MidiEvent& midiEvent)
+ {
+ return ((PluginStub*)ptr)->writeMidi(midiEvent);
+ }
+ #endif
+
+ #if DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST
+ bool requestParameterValueChange(uint32_t, float)
+ {
+ return true;
+ }
+
+ static bool requestParameterValueChangeCallback(void* const ptr, const uint32_t index, const float value)
+ {
+ return ((PluginStub*)ptr)->requestParameterValueChange(index, value);
+ }
+ #endif
+
+ #if DISTRHO_PLUGIN_WANT_STATE
+ bool updateState(const char*, const char*)
+ {
+ return true;
+ }
+
+ static bool updateStateValueCallback(void* const ptr, const char* const key, const char* const value)
+ {
+ return ((PluginStub*)ptr)->updateState(key, value);
+ }
+ #endif
+};
+
+// --------------------------------------------------------------------------------------------------------------------
+
+static ScopedPointer<PluginExporter> sPlugin;
+
+// --------------------------------------------------------------------------------------------------------------------
+// plugin audio ports
+
+static uint32_t clap_plugin_audio_ports_count(const clap_plugin_t*, const bool is_input)
+{
+ return is_input ? DISTRHO_PLUGIN_NUM_INPUTS : DISTRHO_PLUGIN_NUM_OUTPUTS;
+}
+
+static bool clap_plugin_audio_ports_get(const clap_plugin_t*,
+ 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);
+
+ AudioPortWithBusId& audioPort(sPlugin->getAudioPort(is_input, index));
+
+ info->id = index;
+ std::strncpy(info->name, audioPort.name, CLAP_NAME_SIZE-1);
+
+ // 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;
+ info->port_type = nullptr;
+
+ info->in_place_pair = index < (is_input ? DISTRHO_PLUGIN_NUM_OUTPUTS : DISTRHO_PLUGIN_NUM_INPUTS)
+ ? index
+ : CLAP_INVALID_ID;
+
+ return true;
+}
+
+static const clap_plugin_audio_ports_t clap_plugin_audio_ports = {
+ clap_plugin_audio_ports_count,
+ clap_plugin_audio_ports_get
+};
+
+// --------------------------------------------------------------------------------------------------------------------
+// plugin
+
+static bool clap_plugin_init(const clap_plugin_t* const plugin)
+{
+ PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
+ return instance->init();
+}
+
+static void clap_plugin_destroy(const clap_plugin_t* const plugin)
+{
+ delete static_cast<PluginCLAP*>(plugin->plugin_data);
+ std::free(const_cast<clap_plugin_t*>(plugin));
+}
+
+static bool clap_plugin_activate(const clap_plugin_t* const plugin,
+ const double sample_rate,
+ uint32_t,
+ const uint32_t max_frames_count)
+{
+ d_nextBufferSize = max_frames_count;
+ d_nextSampleRate = sample_rate;
+
+ PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
+ instance->activate(sample_rate, max_frames_count);
+ return true;
+}
+
+static void clap_plugin_deactivate(const clap_plugin_t* const plugin)
+{
+ PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
+ instance->deactivate();
+}
+
+static bool clap_plugin_start_processing(const clap_plugin_t*)
+{
+ // nothing to do
+ return true;
+}
+
+static void clap_plugin_stop_processing(const clap_plugin_t*)
+{
+ // nothing to do
+}
+
+static void clap_plugin_reset(const clap_plugin_t*)
+{
+ // nothing to do
+}
+
+static clap_process_status clap_plugin_process(const clap_plugin_t* const plugin, const clap_process_t* const process)
+{
+ PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
+ return instance->process(process) ? CLAP_PROCESS_CONTINUE : CLAP_PROCESS_ERROR;
+}
+
+static const void* clap_plugin_get_extension(const clap_plugin_t*, const char* const id)
+{
+ if (std::strcmp(id, CLAP_EXT_AUDIO_PORTS) == 0)
+ return &clap_plugin_audio_ports;
+ return nullptr;
+}
+
+static void clap_plugin_on_main_thread(const clap_plugin_t*)
+{
+ // nothing to do
+}
+
+// --------------------------------------------------------------------------------------------------------------------
+// plugin factory
+
+static uint32_t clap_get_plugin_count(const clap_plugin_factory_t*)
+{
+ return 1;
+}
+
+static const clap_plugin_descriptor_t* clap_get_plugin_descriptor(const clap_plugin_factory_t*, const uint32_t index)
+{
+ DISTRHO_SAFE_ASSERT_UINT_RETURN(index == 0, index, nullptr);
+
+ static const char* features[] = {
+ #ifdef DISTRHO_PLUGIN_CLAP_FEATURES
+ DISTRHO_PLUGIN_CLAP_FEATURES,
+ #elif DISTRHO_PLUGIN_IS_SYNTH
+ "instrument",
+ #endif
+ nullptr
+ };
+
+ static const clap_plugin_descriptor_t descriptor = {
+ CLAP_VERSION,
+ sPlugin->getLabel(),
+ sPlugin->getName(),
+ sPlugin->getMaker(),
+ // TODO url
+ "",
+ // TODO manual url
+ "",
+ // TODO support url
+ "",
+ // TODO version string
+ "",
+ sPlugin->getDescription(),
+ features
+ };
+
+ return &descriptor;
+}
+
+static const clap_plugin_t* clap_create_plugin(const clap_plugin_factory_t* const factory,
+ const clap_host_t* const host,
+ const char*)
+{
+ clap_plugin_t* const pluginptr = static_cast<clap_plugin_t*>(std::malloc(sizeof(clap_plugin_t)));
+ DISTRHO_SAFE_ASSERT_RETURN(pluginptr != nullptr, nullptr);
+
+ // default early values
+ if (d_nextBufferSize == 0)
+ d_nextBufferSize = 1024;
+ if (d_nextSampleRate <= 0.0)
+ d_nextSampleRate = 44100.0;
+
+ d_nextCanRequestParameterValueChanges = true;
+
+ const clap_plugin_t plugin = {
+ clap_get_plugin_descriptor(factory, 0),
+ new PluginCLAP(host),
+ clap_plugin_init,
+ clap_plugin_destroy,
+ clap_plugin_activate,
+ clap_plugin_deactivate,
+ clap_plugin_start_processing,
+ clap_plugin_stop_processing,
+ clap_plugin_reset,
+ clap_plugin_process,
+ clap_plugin_get_extension,
+ clap_plugin_on_main_thread
+ };
+
+ std::memcpy(pluginptr, &plugin, sizeof(clap_plugin_t));
+ return pluginptr;
+}
+
+static const clap_plugin_factory_t clap_plugin_factory = {
+ clap_get_plugin_count,
+ clap_get_plugin_descriptor,
+ clap_create_plugin
+};
+
+// --------------------------------------------------------------------------------------------------------------------
+// plugin entry
+
+static bool clap_plugin_entry_init(const char* const plugin_path)
+{
+ static String bundlePath;
+ bundlePath = plugin_path;
+ d_nextBundlePath = bundlePath.buffer();
+
+ // init dummy plugin
+ if (sPlugin == nullptr)
+ {
+ // set valid but dummy values
+ d_nextBufferSize = 512;
+ d_nextSampleRate = 44100.0;
+ d_nextPluginIsDummy = true;
+ d_nextCanRequestParameterValueChanges = true;
+
+ // Create dummy plugin to get data from
+ sPlugin = new PluginExporter(nullptr, nullptr, nullptr, nullptr);
+
+ // unset
+ d_nextBufferSize = 0;
+ d_nextSampleRate = 0.0;
+ d_nextPluginIsDummy = false;
+ d_nextCanRequestParameterValueChanges = false;
+ }
+
+ return true;
+}
+
+static void clap_plugin_entry_deinit(void)
+{
+ sPlugin = nullptr;
+}
+
+static const void* clap_plugin_entry_get_factory(const char* const factory_id)
+{
+ if (std::strcmp(factory_id, CLAP_PLUGIN_FACTORY_ID) == 0)
+ return &clap_plugin_factory;
+ return nullptr;
+}
+
+static const clap_plugin_entry_t clap_plugin_entry = {
+ CLAP_VERSION,
+ clap_plugin_entry_init,
+ clap_plugin_entry_deinit,
+ clap_plugin_entry_get_factory
+};
+
+// --------------------------------------------------------------------------------------------------------------------
+
+END_NAMESPACE_DISTRHO
+
+// --------------------------------------------------------------------------------------------------------------------
+
+DISTRHO_PLUGIN_EXPORT
+const clap_plugin_entry_t clap_entry = DISTRHO_NAMESPACE::clap_plugin_entry;
+
+// --------------------------------------------------------------------------------------------------------------------
diff --git a/distrho/src/clap/audio-buffer.h b/distrho/src/clap/audio-buffer.h
@@ -0,0 +1,37 @@
+#pragma once
+
+#include "private/std.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Sample code for reading a stereo buffer:
+//
+// bool isLeftConstant = (buffer->constant_mask & (1 << 0)) != 0;
+// bool isRightConstant = (buffer->constant_mask & (1 << 1)) != 0;
+//
+// for (int i = 0; i < N; ++i) {
+// float l = data32[0][isLeftConstant ? 0 : i];
+// float r = data32[1][isRightConstant ? 0 : i];
+// }
+//
+// Note: checking the constant mask is optional, and this implies that
+// the buffer must be filled with the constant value.
+// Rationale: if a buffer reader doesn't check the constant mask, then it may
+// process garbage samples and in result, garbage samples may be transmitted
+// to the audio interface with all the bad consequences it can have.
+//
+// The constant mask is a hint.
+typedef struct clap_audio_buffer {
+ // Either data32 or data64 pointer will be set.
+ float **data32;
+ double **data64;
+ uint32_t channel_count;
+ uint32_t latency; // latency from/to the audio interface
+ uint64_t constant_mask;
+} clap_audio_buffer_t;
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/distrho/src/clap/entry.h b/distrho/src/clap/entry.h
@@ -0,0 +1,68 @@
+#pragma once
+
+#include "version.h"
+#include "private/macros.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// This interface is the entry point of the dynamic library.
+//
+// CLAP plugins standard search path:
+//
+// Linux
+// - ~/.clap
+// - /usr/lib/clap
+//
+// Windows
+// - %CommonFilesFolder%/CLAP/
+// - %LOCALAPPDATA%/Programs/Common/CLAP/
+//
+// MacOS
+// - /Library/Audio/Plug-Ins/CLAP
+// - ~/Library/Audio/Plug-Ins/CLAP
+//
+// In addition to the OS-specific default locations above, a CLAP host must query the environment
+// for a CLAP_PATH variable, which is a list of directories formatted in the same manner as the host
+// OS binary search path (PATH on Unix, separated by `:` and Path on Windows, separated by ';', as
+// of this writing).
+//
+// Each directory should be recursively searched for files and/or bundles as appropriate in your OS
+// ending with the extension `.clap`.
+//
+// Every method must be thread-safe.
+typedef struct clap_plugin_entry {
+ clap_version_t clap_version; // initialized to CLAP_VERSION
+
+ // This function must be called first, and can only be called once.
+ //
+ // It should be as fast as possible, in order to perform a very quick scan of the plugin
+ // descriptors.
+ //
+ // It is forbidden to display graphical user interface in this call.
+ // It is forbidden to perform user interaction in this call.
+ //
+ // If the initialization depends upon expensive computation, maybe try to do them ahead of time
+ // and cache the result.
+ //
+ // If init() returns false, then the host must not call deinit() nor any other clap
+ // related symbols from the DSO.
+ bool (*init)(const char *plugin_path);
+
+ // No more calls into the DSO must be made after calling deinit().
+ void (*deinit)(void);
+
+ // Get the pointer to a factory. See plugin-factory.h for an example.
+ //
+ // Returns null if the factory is not provided.
+ // The returned pointer must *not* be freed by the caller.
+ const void *(*get_factory)(const char *factory_id);
+} clap_plugin_entry_t;
+
+/* Entry point */
+CLAP_EXPORT extern const clap_plugin_entry_t clap_entry;
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/distrho/src/clap/events.h b/distrho/src/clap/events.h
@@ -0,0 +1,283 @@
+#pragma once
+
+#include "private/std.h"
+#include "fixedpoint.h"
+#include "id.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// event header
+// must be the first attribute of the event
+typedef struct clap_event_header {
+ uint32_t size; // event size including this header, eg: sizeof (clap_event_note)
+ uint32_t time; // sample offset within the buffer for this event
+ uint16_t space_id; // event space, see clap_host_event_registry
+ uint16_t type; // event type
+ uint32_t flags; // see clap_event_flags
+} clap_event_header_t;
+
+// The clap core event space
+static const CLAP_CONSTEXPR uint16_t CLAP_CORE_EVENT_SPACE_ID = 0;
+
+enum clap_event_flags {
+ // Indicate a live user event, for example a user turning a physical knob
+ // or playing a physical key.
+ CLAP_EVENT_IS_LIVE = 1 << 0,
+
+ // Indicate that the event should not be recorded.
+ // For example this is useful when a parameter changes because of a MIDI CC,
+ // because if the host records both the MIDI CC automation and the parameter
+ // automation there will be a conflict.
+ CLAP_EVENT_DONT_RECORD = 1 << 1,
+};
+
+// Some of the following events overlap, a note on can be expressed with:
+// - CLAP_EVENT_NOTE_ON
+// - CLAP_EVENT_MIDI
+// - CLAP_EVENT_MIDI2
+//
+// The preferred way of sending a note event is to use CLAP_EVENT_NOTE_*.
+//
+// The same event must not be sent twice: it is forbidden to send a the same note on
+// encoded with both CLAP_EVENT_NOTE_ON and CLAP_EVENT_MIDI.
+//
+// The plugins are encouraged to be able to handle note events encoded as raw midi or midi2,
+// or implement clap_plugin_event_filter and reject raw midi and midi2 events.
+enum {
+ // NOTE_ON and NOTE_OFF represent a key pressed and key released event, respectively.
+ // A NOTE_ON with a velocity of 0 is valid and should not be interpreted as a NOTE_OFF.
+ //
+ // NOTE_CHOKE is meant to choke the voice(s), like in a drum machine when a closed hihat
+ // chokes an open hihat. This event can be sent by the host to the plugin. Here are two use cases:
+ // - a plugin is inside a drum pad in Bitwig Studio's drum machine, and this pad is choked by
+ // another one
+ // - the user double clicks the DAW's stop button in the transport which then stops the sound on
+ // every tracks
+ //
+ // NOTE_END is sent by the plugin to the host. The port, channel, key and note_id are those given
+ // by the host in the NOTE_ON event. In other words, this event is matched against the
+ // plugin's note input port.
+ // NOTE_END is useful to help the host to match the plugin's voice life time.
+ //
+ // When using polyphonic modulations, the host has to allocate and release voices for its
+ // polyphonic modulator. Yet only the plugin effectively knows when the host should terminate
+ // a voice. NOTE_END solves that issue in a non-intrusive and cooperative way.
+ //
+ // CLAP assumes that the host will allocate a unique voice on NOTE_ON event for a given port,
+ // channel and key. This voice will run until the plugin will instruct the host to terminate
+ // it by sending a NOTE_END event.
+ //
+ // Consider the following sequence:
+ // - process()
+ // Host->Plugin NoteOn(port:0, channel:0, key:16, time:t0)
+ // Host->Plugin NoteOn(port:0, channel:0, key:64, time:t0)
+ // Host->Plugin NoteOff(port:0, channel:0, key:16, t1)
+ // Host->Plugin NoteOff(port:0, channel:0, key:64, t1)
+ // # on t2, both notes did terminate
+ // Host->Plugin NoteOn(port:0, channel:0, key:64, t3)
+ // # Here the plugin finished processing all the frames and will tell the host
+ // # to terminate the voice on key 16 but not 64, because a note has been started at t3
+ // Plugin->Host NoteEnd(port:0, channel:0, key:16, time:ignored)
+ //
+ // These four events use clap_event_note.
+ CLAP_EVENT_NOTE_ON,
+ CLAP_EVENT_NOTE_OFF,
+ CLAP_EVENT_NOTE_CHOKE,
+ CLAP_EVENT_NOTE_END,
+
+ // Represents a note expression.
+ // Uses clap_event_note_expression.
+ CLAP_EVENT_NOTE_EXPRESSION,
+
+ // PARAM_VALUE sets the parameter's value; uses clap_event_param_value.
+ // PARAM_MOD sets the parameter's modulation amount; uses clap_event_param_mod.
+ //
+ // The value heard is: param_value + param_mod.
+ //
+ // In case of a concurrent global value/modulation versus a polyphonic one,
+ // the voice should only use the polyphonic one and the polyphonic modulation
+ // amount will already include the monophonic signal.
+ CLAP_EVENT_PARAM_VALUE,
+ CLAP_EVENT_PARAM_MOD,
+
+ // Indicates that the user started or finished adjusting a knob.
+ // This is not mandatory to wrap parameter changes with gesture events, but this improves
+ // the user experience a lot when recording automation or overriding automation playback.
+ // Uses clap_event_param_gesture.
+ CLAP_EVENT_PARAM_GESTURE_BEGIN,
+ CLAP_EVENT_PARAM_GESTURE_END,
+
+ CLAP_EVENT_TRANSPORT, // update the transport info; clap_event_transport
+ CLAP_EVENT_MIDI, // raw midi event; clap_event_midi
+ CLAP_EVENT_MIDI_SYSEX, // raw midi sysex event; clap_event_midi_sysex
+ CLAP_EVENT_MIDI2, // raw midi 2 event; clap_event_midi2
+};
+
+// Note on, off, end and choke events.
+// In the case of note choke or end events:
+// - the velocity is ignored.
+// - key and channel are used to match active notes, a value of -1 matches all.
+typedef struct clap_event_note {
+ clap_event_header_t header;
+
+ int32_t note_id; // -1 if unspecified, otherwise >=0
+ int16_t port_index;
+ int16_t channel; // 0..15
+ int16_t key; // 0..127
+ double velocity; // 0..1
+} clap_event_note_t;
+
+enum {
+ // with 0 < x <= 4, plain = 20 * log(x)
+ CLAP_NOTE_EXPRESSION_VOLUME,
+
+ // pan, 0 left, 0.5 center, 1 right
+ CLAP_NOTE_EXPRESSION_PAN,
+
+ // relative tuning in semitone, from -120 to +120
+ CLAP_NOTE_EXPRESSION_TUNING,
+
+ // 0..1
+ CLAP_NOTE_EXPRESSION_VIBRATO,
+ CLAP_NOTE_EXPRESSION_EXPRESSION,
+ CLAP_NOTE_EXPRESSION_BRIGHTNESS,
+ CLAP_NOTE_EXPRESSION_PRESSURE,
+};
+typedef int32_t clap_note_expression;
+
+typedef struct clap_event_note_expression {
+ clap_event_header_t header;
+
+ clap_note_expression expression_id;
+
+ // target a specific note_id, port, key and channel, -1 for global
+ int32_t note_id;
+ int16_t port_index;
+ int16_t channel;
+ int16_t key;
+
+ double value; // see expression for the range
+} clap_event_note_expression_t;
+
+typedef struct clap_event_param_value {
+ clap_event_header_t header;
+
+ // target parameter
+ clap_id param_id; // @ref clap_param_info.id
+ void *cookie; // @ref clap_param_info.cookie
+
+ // target a specific note_id, port, key and channel, -1 for global
+ int32_t note_id;
+ int16_t port_index;
+ int16_t channel;
+ int16_t key;
+
+ double value;
+} clap_event_param_value_t;
+
+typedef struct clap_event_param_mod {
+ clap_event_header_t header;
+
+ // target parameter
+ clap_id param_id; // @ref clap_param_info.id
+ void *cookie; // @ref clap_param_info.cookie
+
+ // target a specific note_id, port, key and channel, -1 for global
+ int32_t note_id;
+ int16_t port_index;
+ int16_t channel;
+ int16_t key;
+
+ double amount; // modulation amount
+} clap_event_param_mod_t;
+
+typedef struct clap_event_param_gesture {
+ clap_event_header_t header;
+
+ // target parameter
+ clap_id param_id; // @ref clap_param_info.id
+} clap_event_param_gesture_t;
+
+enum clap_transport_flags {
+ CLAP_TRANSPORT_HAS_TEMPO = 1 << 0,
+ CLAP_TRANSPORT_HAS_BEATS_TIMELINE = 1 << 1,
+ CLAP_TRANSPORT_HAS_SECONDS_TIMELINE = 1 << 2,
+ CLAP_TRANSPORT_HAS_TIME_SIGNATURE = 1 << 3,
+ CLAP_TRANSPORT_IS_PLAYING = 1 << 4,
+ CLAP_TRANSPORT_IS_RECORDING = 1 << 5,
+ CLAP_TRANSPORT_IS_LOOP_ACTIVE = 1 << 6,
+ CLAP_TRANSPORT_IS_WITHIN_PRE_ROLL = 1 << 7,
+};
+
+typedef struct clap_event_transport {
+ clap_event_header_t header;
+
+ uint32_t flags; // see clap_transport_flags
+
+ clap_beattime song_pos_beats; // position in beats
+ clap_sectime song_pos_seconds; // position in seconds
+
+ double tempo; // in bpm
+ double tempo_inc; // tempo increment for each samples and until the next
+ // time info event
+
+ clap_beattime loop_start_beats;
+ clap_beattime loop_end_beats;
+ clap_sectime loop_start_seconds;
+ clap_sectime loop_end_seconds;
+
+ clap_beattime bar_start; // start pos of the current bar
+ int32_t bar_number; // bar at song pos 0 has the number 0
+
+ uint16_t tsig_num; // time signature numerator
+ uint16_t tsig_denom; // time signature denominator
+} clap_event_transport_t;
+
+typedef struct clap_event_midi {
+ clap_event_header_t header;
+
+ uint16_t port_index;
+ uint8_t data[3];
+} clap_event_midi_t;
+
+typedef struct clap_event_midi_sysex {
+ clap_event_header_t header;
+
+ uint16_t port_index;
+ const uint8_t *buffer; // midi buffer
+ uint32_t size;
+} clap_event_midi_sysex_t;
+
+// While it is possible to use a series of midi2 event to send a sysex,
+// prefer clap_event_midi_sysex if possible for efficiency.
+typedef struct clap_event_midi2 {
+ clap_event_header_t header;
+
+ uint16_t port_index;
+ uint32_t data[4];
+} clap_event_midi2_t;
+
+// Input event list, events must be sorted by time.
+typedef struct clap_input_events {
+ void *ctx; // reserved pointer for the list
+
+ uint32_t (*size)(const struct clap_input_events *list);
+
+ // Don't free the returned event, it belongs to the list
+ const clap_event_header_t *(*get)(const struct clap_input_events *list, uint32_t index);
+} clap_input_events_t;
+
+// Output event list, events must be sorted by time.
+typedef struct clap_output_events {
+ void *ctx; // reserved pointer for the list
+
+ // Pushes a copy of the event
+ // returns false if the event could not be pushed to the queue (out of memory?)
+ bool (*try_push)(const struct clap_output_events *list, const clap_event_header_t *event);
+} clap_output_events_t;
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/distrho/src/clap/ext/audio-ports.h b/distrho/src/clap/ext/audio-ports.h
@@ -0,0 +1,116 @@
+#pragma once
+
+#include "../plugin.h"
+#include "../string-sizes.h"
+
+/// @page Audio Ports
+///
+/// This extension provides a way for the plugin to describe its current audio ports.
+///
+/// If the plugin does not implement this extension, it won't have audio ports.
+///
+/// 32 bits support is required for both host and plugins. 64 bits audio is optional.
+///
+/// The plugin is only allowed to change its ports configuration while it is deactivated.
+
+static CLAP_CONSTEXPR const char CLAP_EXT_AUDIO_PORTS[] = "clap.audio-ports";
+static CLAP_CONSTEXPR const char CLAP_PORT_MONO[] = "mono";
+static CLAP_CONSTEXPR const char CLAP_PORT_STEREO[] = "stereo";
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum {
+ // This port is the main audio input or output.
+ // There can be only one main input and main output.
+ // Main port must be at index 0.
+ CLAP_AUDIO_PORT_IS_MAIN = 1 << 0,
+
+ // This port can be used with 64 bits audio
+ CLAP_AUDIO_PORT_SUPPORTS_64BITS = 1 << 1,
+
+ // 64 bits audio is preferred with this port
+ CLAP_AUDIO_PORT_PREFERS_64BITS = 1 << 2,
+
+ // This port must be used with the same sample size as all the other ports which have this flag.
+ // In other words if all ports have this flag then the plugin may either be used entirely with
+ // 64 bits audio or 32 bits audio, but it can't be mixed.
+ CLAP_AUDIO_PORT_REQUIRES_COMMON_SAMPLE_SIZE = 1 << 3,
+};
+
+typedef struct clap_audio_port_info {
+ // id identifies a port and must be stable.
+ // id may overlap between input and output ports.
+ clap_id id;
+ char name[CLAP_NAME_SIZE]; // displayable name
+
+ uint32_t flags;
+ uint32_t channel_count;
+
+ // If null or empty then it is unspecified (arbitrary audio).
+ // This filed can be compared against:
+ // - CLAP_PORT_MONO
+ // - CLAP_PORT_STEREO
+ // - CLAP_PORT_SURROUND (defined in the surround extension)
+ // - CLAP_PORT_AMBISONIC (defined in the ambisonic extension)
+ // - CLAP_PORT_CV (defined in the cv extension)
+ //
+ // An extension can provide its own port type and way to inspect the channels.
+ const char *port_type;
+
+ // in-place processing: allow the host to use the same buffer for input and output
+ // if supported set the pair port id.
+ // if not supported set to CLAP_INVALID_ID
+ clap_id in_place_pair;
+} clap_audio_port_info_t;
+
+// 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]
+ uint32_t (*count)(const clap_plugin_t *plugin, bool is_input);
+
+ // get info about about an audio port.
+ // [main-thread]
+ bool (*get)(const clap_plugin_t *plugin,
+ uint32_t index,
+ bool is_input,
+ clap_audio_port_info_t *info);
+} clap_plugin_audio_ports_t;
+
+enum {
+ // The ports name did change, the host can scan them right away.
+ CLAP_AUDIO_PORTS_RESCAN_NAMES = 1 << 0,
+
+ // [!active] The flags did change
+ CLAP_AUDIO_PORTS_RESCAN_FLAGS = 1 << 1,
+
+ // [!active] The channel_count did change
+ CLAP_AUDIO_PORTS_RESCAN_CHANNEL_COUNT = 1 << 2,
+
+ // [!active] The port type did change
+ CLAP_AUDIO_PORTS_RESCAN_PORT_TYPE = 1 << 3,
+
+ // [!active] The in-place pair did change, this requires.
+ CLAP_AUDIO_PORTS_RESCAN_IN_PLACE_PAIR = 1 << 4,
+
+ // [!active] The list of ports have changed: entries have been removed/added.
+ CLAP_AUDIO_PORTS_RESCAN_LIST = 1 << 5,
+};
+
+typedef struct clap_host_audio_ports {
+ // Checks if the host allows a plugin to change a given aspect of the audio ports definition.
+ // [main-thread]
+ bool (*is_rescan_flag_supported)(const clap_host_t *host, uint32_t flag);
+
+ // Rescan the full list of audio ports according to the flags.
+ // It is illegal to ask the host to rescan with a flag that is not supported.
+ // Certain flags require the plugin to be de-activated.
+ // [main-thread]
+ void (*rescan)(const clap_host_t *host, uint32_t flags);
+} clap_host_audio_ports_t;
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/distrho/src/clap/fixedpoint.h b/distrho/src/clap/fixedpoint.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "private/std.h"
+#include "private/macros.h"
+
+/// We use fixed point representation of beat time and seconds time
+/// Usage:
+/// double x = ...; // in beats
+/// clap_beattime y = round(CLAP_BEATTIME_FACTOR * x);
+
+// This will never change
+static const CLAP_CONSTEXPR int64_t CLAP_BEATTIME_FACTOR = 1LL << 31;
+static const CLAP_CONSTEXPR int64_t CLAP_SECTIME_FACTOR = 1LL << 31;
+
+typedef int64_t clap_beattime;
+typedef int64_t clap_sectime;
diff --git a/distrho/src/clap/host.h b/distrho/src/clap/host.h
@@ -0,0 +1,41 @@
+#pragma once
+
+#include "version.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct clap_host {
+ clap_version_t clap_version; // initialized to CLAP_VERSION
+
+ void *host_data; // reserved pointer for the host
+
+ // name and version are mandatory.
+ const char *name; // eg: "Bitwig Studio"
+ const char *vendor; // eg: "Bitwig GmbH"
+ const char *url; // eg: "https://bitwig.com"
+ const char *version; // eg: "4.3"
+
+ // Query an extension.
+ // [thread-safe]
+ const void *(*get_extension)(const struct clap_host *host, const char *extension_id);
+
+ // Request the host to deactivate and then reactivate the plugin.
+ // The operation may be delayed by the host.
+ // [thread-safe]
+ void (*request_restart)(const struct clap_host *host);
+
+ // Request the host to activate and start processing the plugin.
+ // This is useful if you have external IO and need to wake up the plugin from "sleep".
+ // [thread-safe]
+ void (*request_process)(const struct clap_host *host);
+
+ // Request the host to schedule a call to plugin->on_main_thread(plugin) on the main thread.
+ // [thread-safe]
+ void (*request_callback)(const struct clap_host *host);
+} clap_host_t;
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/distrho/src/clap/id.h b/distrho/src/clap/id.h
@@ -0,0 +1,8 @@
+#pragma once
+
+#include "private/std.h"
+#include "private/macros.h"
+
+typedef uint32_t clap_id;
+
+static const CLAP_CONSTEXPR clap_id CLAP_INVALID_ID = UINT32_MAX;
diff --git a/distrho/src/clap/plugin-factory.h b/distrho/src/clap/plugin-factory.h
@@ -0,0 +1,39 @@
+#pragma once
+
+#include "plugin.h"
+
+static const CLAP_CONSTEXPR char CLAP_PLUGIN_FACTORY_ID[] = "clap.plugin-factory";
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Every method must be thread-safe.
+// It is very important to be able to scan the plugin as quickly as possible.
+//
+// If the content of the factory may change due to external events, like the user installed
+typedef struct clap_plugin_factory {
+ // Get the number of plugins available.
+ // [thread-safe]
+ uint32_t (*get_plugin_count)(const struct clap_plugin_factory *factory);
+
+ // Retrieves a plugin descriptor by its index.
+ // Returns null in case of error.
+ // The descriptor must not be freed.
+ // [thread-safe]
+ const clap_plugin_descriptor_t *(*get_plugin_descriptor)(
+ const struct clap_plugin_factory *factory, uint32_t index);
+
+ // Create a clap_plugin by its plugin_id.
+ // The returned pointer must be freed by calling plugin->destroy(plugin);
+ // The plugin is not allowed to use the host callbacks in the create method.
+ // Returns null in case of error.
+ // [thread-safe]
+ const clap_plugin_t *(*create_plugin)(const struct clap_plugin_factory *factory,
+ const clap_host_t *host,
+ const char *plugin_id);
+} clap_plugin_factory_t;
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/distrho/src/clap/plugin-features.h b/distrho/src/clap/plugin-features.h
@@ -0,0 +1,76 @@
+#pragma once
+
+#include "private/macros.h"
+
+// This file provides a set of standard plugin features meant to be used
+// within clap_plugin_descriptor.features.
+//
+// For practical reasons we'll avoid spaces and use `-` instead to facilitate
+// scripts that generate the feature array.
+//
+// Non-standard features should be formated as follow: "$namespace:$feature"
+
+/////////////////////
+// Plugin category //
+/////////////////////
+
+// Add this feature if your plugin can process note events and then produce audio
+#define CLAP_PLUGIN_FEATURE_INSTRUMENT "instrument"
+
+// Add this feature if your plugin is an audio effect
+#define CLAP_PLUGIN_FEATURE_AUDIO_EFFECT "audio-effect"
+
+// Add this feature if your plugin is a note effect or a note generator/sequencer
+#define CLAP_PLUGIN_FEATURE_NOTE_EFFECT "note-effect"
+
+// Add this feature if your plugin is an analyzer
+#define CLAP_PLUGIN_FEATURE_ANALYZER "analyzer"
+
+/////////////////////////
+// Plugin sub-category //
+/////////////////////////
+
+#define CLAP_PLUGIN_FEATURE_SYNTHESIZER "synthesizer"
+#define CLAP_PLUGIN_FEATURE_SAMPLER "sampler"
+#define CLAP_PLUGIN_FEATURE_DRUM "drum" // For single drum
+#define CLAP_PLUGIN_FEATURE_DRUM_MACHINE "drum-machine"
+
+#define CLAP_PLUGIN_FEATURE_FILTER "filter"
+#define CLAP_PLUGIN_FEATURE_PHASER "phaser"
+#define CLAP_PLUGIN_FEATURE_EQUALIZER "equalizer"
+#define CLAP_PLUGIN_FEATURE_DEESSER "de-esser"
+#define CLAP_PLUGIN_FEATURE_PHASE_VOCODER "phase-vocoder"
+#define CLAP_PLUGIN_FEATURE_GRANULAR "granular"
+#define CLAP_PLUGIN_FEATURE_FREQUENCY_SHIFTER "frequency-shifter"
+#define CLAP_PLUGIN_FEATURE_PITCH_SHIFTER "pitch-shifter"
+
+#define CLAP_PLUGIN_FEATURE_DISTORTION "distortion"
+#define CLAP_PLUGIN_FEATURE_TRANSIENT_SHAPER "transient-shaper"
+#define CLAP_PLUGIN_FEATURE_COMPRESSOR "compressor"
+#define CLAP_PLUGIN_FEATURE_LIMITER "limiter"
+
+#define CLAP_PLUGIN_FEATURE_FLANGER "flanger"
+#define CLAP_PLUGIN_FEATURE_CHORUS "chorus"
+#define CLAP_PLUGIN_FEATURE_DELAY "delay"
+#define CLAP_PLUGIN_FEATURE_REVERB "reverb"
+
+#define CLAP_PLUGIN_FEATURE_TREMOLO "tremolo"
+#define CLAP_PLUGIN_FEATURE_GLITCH "glitch"
+
+#define CLAP_PLUGIN_FEATURE_UTILITY "utility"
+#define CLAP_PLUGIN_FEATURE_PITCH_CORRECTION "pitch-correction"
+#define CLAP_PLUGIN_FEATURE_RESTORATION "restoration" // repair the sound
+
+#define CLAP_PLUGIN_FEATURE_MULTI_EFFECTS "multi-effects"
+
+#define CLAP_PLUGIN_FEATURE_MIXING "mixing"
+#define CLAP_PLUGIN_FEATURE_MASTERING "mastering"
+
+////////////////////////
+// Audio Capabilities //
+////////////////////////
+
+#define CLAP_PLUGIN_FEATURE_MONO "mono"
+#define CLAP_PLUGIN_FEATURE_STEREO "stereo"
+#define CLAP_PLUGIN_FEATURE_SURROUND "surround"
+#define CLAP_PLUGIN_FEATURE_AMBISONIC "ambisonic"
diff --git a/distrho/src/clap/plugin.h b/distrho/src/clap/plugin.h
@@ -0,0 +1,96 @@
+#pragma once
+
+#include "private/macros.h"
+#include "host.h"
+#include "process.h"
+#include "plugin-features.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct clap_plugin_descriptor {
+ clap_version_t clap_version; // initialized to CLAP_VERSION
+
+ // Mandatory fields must be set and must not be blank.
+ // Otherwise the fields can be null or blank, though it is safer to make them blank.
+ const char *id; // eg: "com.u-he.diva", mandatory
+ const char *name; // eg: "Diva", mandatory
+ const char *vendor; // eg: "u-he"
+ const char *url; // eg: "https://u-he.com/products/diva/"
+ const char *manual_url; // eg: "https://dl.u-he.com/manuals/plugins/diva/Diva-user-guide.pdf"
+ const char *support_url; // eg: "https://u-he.com/support/"
+ const char *version; // eg: "1.4.4"
+ const char *description; // eg: "The spirit of analogue"
+
+ // Arbitrary list of keywords.
+ // They can be matched by the host indexer and used to classify the plugin.
+ // The array of pointers must be null terminated.
+ // For some standard features see plugin-features.h
+ const char **features;
+} clap_plugin_descriptor_t;
+
+typedef struct clap_plugin {
+ const clap_plugin_descriptor_t *desc;
+
+ void *plugin_data; // reserved pointer for the plugin
+
+ // Must be called after creating the plugin.
+ // If init returns false, the host must destroy the plugin instance.
+ // [main-thread]
+ bool (*init)(const struct clap_plugin *plugin);
+
+ // Free the plugin and its resources.
+ // It is required to deactivate the plugin prior to this call.
+ // [main-thread & !active]
+ void (*destroy)(const struct clap_plugin *plugin);
+
+ // Activate and deactivate the plugin.
+ // In this call the plugin may allocate memory and prepare everything needed for the process
+ // call. The process's sample rate will be constant and process's frame count will included in
+ // the [min, max] range, which is bounded by [1, INT32_MAX].
+ // Once activated the latency and port configuration must remain constant, until deactivation.
+ //
+ // [main-thread & !active_state]
+ bool (*activate)(const struct clap_plugin *plugin,
+ double sample_rate,
+ uint32_t min_frames_count,
+ uint32_t max_frames_count);
+
+ // [main-thread & active_state]
+ void (*deactivate)(const struct clap_plugin *plugin);
+
+ // Call start processing before processing.
+ // [audio-thread & active_state & !processing_state]
+ bool (*start_processing)(const struct clap_plugin *plugin);
+
+ // Call stop processing before sending the plugin to sleep.
+ // [audio-thread & active_state & processing_state]
+ void (*stop_processing)(const struct clap_plugin *plugin);
+
+ // - Clears all buffers, performs a full reset of the processing state (filters, oscillators,
+ // enveloppes, lfo, ...) and kills all voices.
+ // - The parameter's value remain unchanged.
+ // - clap_process.steady_time may jump backward.
+ //
+ // [audio-thread & active_state]
+ void (*reset)(const struct clap_plugin *plugin);
+
+ // process audio, events, ...
+ // [audio-thread & active_state & processing_state]
+ clap_process_status (*process)(const struct clap_plugin *plugin, const clap_process_t *process);
+
+ // Query an extension.
+ // The returned pointer is owned by the plugin.
+ // [thread-safe]
+ const void *(*get_extension)(const struct clap_plugin *plugin, const char *id);
+
+ // Called by the host on the main thread in response to a previous call to:
+ // host->request_callback(host);
+ // [main-thread]
+ void (*on_main_thread)(const struct clap_plugin *plugin);
+} clap_plugin_t;
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/distrho/src/clap/private/macros.h b/distrho/src/clap/private/macros.h
@@ -0,0 +1,36 @@
+#pragma once
+
+// Define CLAP_EXPORT
+#if !defined(CLAP_EXPORT)
+# if defined _WIN32 || defined __CYGWIN__
+# ifdef __GNUC__
+# define CLAP_EXPORT __attribute__((dllexport))
+# else
+# define CLAP_EXPORT __declspec(dllexport)
+# endif
+# else
+# if __GNUC__ >= 4 || defined(__clang__)
+# define CLAP_EXPORT __attribute__((visibility("default")))
+# else
+# define CLAP_EXPORT
+# endif
+# endif
+#endif
+
+#if defined(__cplusplus) && __cplusplus >= 201103L
+# define CLAP_HAS_CXX11
+# define CLAP_CONSTEXPR constexpr
+#else
+# define CLAP_CONSTEXPR
+#endif
+
+#if defined(__cplusplus) && __cplusplus >= 201703L
+# define CLAP_HAS_CXX17
+# define CLAP_NODISCARD [[nodiscard]]
+#else
+# define CLAP_NODISCARD
+#endif
+
+#if defined(__cplusplus) && __cplusplus >= 202002L
+# define CLAP_HAS_CXX20
+#endif
diff --git a/distrho/src/clap/private/std.h b/distrho/src/clap/private/std.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "macros.h"
+
+#ifdef CLAP_HAS_CXX11
+# include <cstdint>
+#else
+# include <stdint.h>
+#endif
+
+#ifdef __cplusplus
+# include <cstddef>
+#else
+# include <stddef.h>
+# include <stdbool.h>
+#endif
diff --git a/distrho/src/clap/process.h b/distrho/src/clap/process.h
@@ -0,0 +1,65 @@
+#pragma once
+
+#include "events.h"
+#include "audio-buffer.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum {
+ // Processing failed. The output buffer must be discarded.
+ CLAP_PROCESS_ERROR = 0,
+
+ // Processing succeeded, keep processing.
+ CLAP_PROCESS_CONTINUE = 1,
+
+ // Processing succeeded, keep processing if the output is not quiet.
+ CLAP_PROCESS_CONTINUE_IF_NOT_QUIET = 2,
+
+ // Rely upon the plugin's tail to determine if the plugin should continue to process.
+ // see clap_plugin_tail
+ CLAP_PROCESS_TAIL = 3,
+
+ // Processing succeeded, but no more processing is required,
+ // until the next event or variation in audio input.
+ CLAP_PROCESS_SLEEP = 4,
+};
+typedef int32_t clap_process_status;
+
+typedef struct clap_process {
+ // A steady sample time counter.
+ // This field can be used to calculate the sleep duration between two process calls.
+ // This value may be specific to this plugin instance and have no relation to what
+ // other plugin instances may receive.
+ //
+ // Set to -1 if not available, otherwise the value must be greater or equal to 0,
+ // and must be increased by at least `frames_count` for the next call to process.
+ int64_t steady_time;
+
+ // Number of frames to process
+ uint32_t frames_count;
+
+ // time info at sample 0
+ // If null, then this is a free running host, no transport events will be provided
+ const clap_event_transport_t *transport;
+
+ // Audio buffers, they must have the same count as specified
+ // by clap_plugin_audio_ports->get_count().
+ // The index maps to clap_plugin_audio_ports->get_info().
+ const clap_audio_buffer_t *audio_inputs;
+ clap_audio_buffer_t *audio_outputs;
+ uint32_t audio_inputs_count;
+ uint32_t audio_outputs_count;
+
+ // Input and output events.
+ //
+ // Events must be sorted by time.
+ // The input event list can't be modified.
+ const clap_input_events_t *in_events;
+ const clap_output_events_t *out_events;
+} clap_process_t;
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/distrho/src/clap/string-sizes.h b/distrho/src/clap/string-sizes.h
@@ -0,0 +1,21 @@
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum {
+ // String capacity for names that can be displayed to the user.
+ CLAP_NAME_SIZE = 256,
+
+ // String capacity for describing a path, like a parameter in a module hierarchy or path within a
+ // set of nested track groups.
+ //
+ // This is not suited for describing a file path on the disk, as NTFS allows up to 32K long
+ // paths.
+ CLAP_PATH_SIZE = 1024,
+};
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/distrho/src/clap/version.h b/distrho/src/clap/version.h
@@ -0,0 +1,34 @@
+#pragma once
+
+#include "private/macros.h"
+#include "private/std.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct clap_version {
+ // This is the major ABI and API design
+ // Version 0.X.Y correspond to the development stage, API and ABI are not stable
+ // Version 1.X.Y correspont to the release stage, API and ABI are stable
+ uint32_t major;
+ uint32_t minor;
+ uint32_t revision;
+} clap_version_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#define CLAP_VERSION_MAJOR ((uint32_t)1)
+#define CLAP_VERSION_MINOR ((uint32_t)1)
+#define CLAP_VERSION_REVISION ((uint32_t)1)
+#define CLAP_VERSION_INIT {CLAP_VERSION_MAJOR, CLAP_VERSION_MINOR, CLAP_VERSION_REVISION}
+
+static const CLAP_CONSTEXPR clap_version_t CLAP_VERSION = CLAP_VERSION_INIT;
+
+CLAP_NODISCARD static inline CLAP_CONSTEXPR bool
+clap_version_is_compatible(const clap_version_t v) {
+ // versions 0.x.y were used during development stage and aren't compatible
+ return v.major >= 1;
+}
diff --git a/utils/symbols/clap.def b/utils/symbols/clap.def
@@ -1 +1,2 @@
EXPORTS
+clap_entry
diff --git a/utils/symbols/clap.exp b/utils/symbols/clap.exp
@@ -0,0 +1 @@
+_clap_entry
diff --git a/utils/symbols/clap.version b/utils/symbols/clap.version
@@ -1,4 +1,4 @@
{
- global: __name;
+ global: clap_entry;
local: *;
};