commit 847000e4f224a0024a65e9c998ae481d91fd847a
parent ca8f83de76bad741286620b5060fedd9ad598a0e
Author: falkTX <falktx@falktx.com>
Date: Wed, 12 Oct 2022 02:02:17 +0100
Implement CLAP latency, cleanup
Signed-off-by: falkTX <falktx@falktx.com>
Diffstat:
4 files changed, 192 insertions(+), 46 deletions(-)
diff --git a/distrho/src/DistrhoPluginCLAP.cpp b/distrho/src/DistrhoPluginCLAP.cpp
@@ -40,10 +40,12 @@
#include "clap/entry.h"
#include "clap/plugin-factory.h"
#include "clap/ext/audio-ports.h"
-#include "clap/ext/note-ports.h"
+#include "clap/ext/latency.h"
#include "clap/ext/gui.h"
+#include "clap/ext/note-ports.h"
#include "clap/ext/params.h"
#include "clap/ext/state.h"
+#include "clap/ext/thread-check.h"
#include "clap/ext/timer-support.h"
#if (defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WINDOWS)) && ! DISTRHO_PLUGIN_HAS_EXTERNAL_UI
@@ -621,7 +623,7 @@ private:
void editParameter(const uint32_t rindex, const bool started) const
{
const ClapEventQueue::Event ev = {
- started ? ClapEventQueue::kEventGestureBegin : ClapEventQueue::kEventGestureBegin,
+ started ? ClapEventQueue::kEventGestureBegin : ClapEventQueue::kEventGestureEnd,
rindex, 0.f
};
fEventQueue.addEventFromUI(ev);
@@ -736,10 +738,15 @@ public:
requestParameterValueChangeCallback,
updateStateValueCallback),
fHost(host),
- fOutputEvents(nullptr)
+ fOutputEvents(nullptr),
+ #if DISTRHO_PLUGIN_WANT_LATENCY
+ fLatencyChanged(false),
+ fLastKnownLatency(0),
+ #endif
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
- , fMidiEventCount(0)
+ fMidiEventCount(0),
#endif
+ fHostExtensions(host)
{
fCachedParameters.setup(fPlugin.getParameterCount());
#if DISTRHO_PLUGIN_HAS_UI && DISTRHO_PLUGIN_WANT_MIDI_INPUT
@@ -768,8 +775,7 @@ public:
if (!clap_version_is_compatible(fHost->clap_version))
return false;
- // TODO check host features
- return true;
+ return fHostExtensions.init();
}
void activate(const double sampleRate, const uint32_t maxFramesCount)
@@ -782,6 +788,10 @@ public:
void deactivate()
{
fPlugin.deactivate();
+ #if DISTRHO_PLUGIN_WANT_LATENCY
+ checkForLatencyChanges(false, true);
+ reportLatencyChangeIfNeeded();
+ #endif
}
bool process(const clap_process_t* const process)
@@ -1009,9 +1019,20 @@ public:
fOutputEvents = nullptr;
}
+ #if DISTRHO_PLUGIN_WANT_LATENCY
+ checkForLatencyChanges(true, false);
+ #endif
+
return true;
}
+ void onMainThread()
+ {
+ #if DISTRHO_PLUGIN_WANT_LATENCY
+ reportLatencyChangeIfNeeded();
+ #endif
+ }
+
// ----------------------------------------------------------------------------------------------------------------
// parameters
@@ -1176,6 +1197,10 @@ public:
}
}
}
+
+ #if DISTRHO_PLUGIN_WANT_LATENCY
+ checkForLatencyChanges(fPlugin.isActive(), false);
+ #endif
}
// ----------------------------------------------------------------------------------------------------------------
@@ -1218,6 +1243,57 @@ public:
}
// ----------------------------------------------------------------------------------------------------------------
+ // latency
+
+ #if DISTRHO_PLUGIN_WANT_LATENCY
+ uint32_t getLatency() const noexcept
+ {
+ return fPlugin.getLatency();
+ }
+
+ void checkForLatencyChanges(const bool isActive, const bool fromMainThread)
+ {
+ const uint32_t latency = fPlugin.getLatency();
+
+ if (fLastKnownLatency == latency)
+ return;
+
+ fLastKnownLatency = latency;
+
+ if (isActive)
+ {
+ fLatencyChanged = true;
+ fHost->request_restart(fHost);
+ }
+ else
+ {
+ // if this is main-thread we can report latency change directly
+ if (fromMainThread || (fHostExtensions.threadCheck != nullptr && fHostExtensions.threadCheck->is_main_thread(fHost)))
+ {
+ fLatencyChanged = false;
+ fHostExtensions.latency->changed(fHost);
+ }
+ // otherwise we need to request a main-thread callback
+ else
+ {
+ fLatencyChanged = true;
+ fHost->request_callback(fHost);
+ }
+ }
+ }
+
+ // called from main thread
+ void reportLatencyChangeIfNeeded()
+ {
+ if (fLatencyChanged)
+ {
+ fLatencyChanged = false;
+ fHostExtensions.latency->changed(fHost);
+ }
+ }
+ #endif
+
+ // ----------------------------------------------------------------------------------------------------------------
// state
bool stateSave(const clap_ostream_t* const stream)
@@ -1501,8 +1577,13 @@ public:
}
}
- if (const clap_host_params_t* const hostParams = getHostExtension<clap_host_params_t>(CLAP_EXT_PARAMS))
- hostParams->rescan(fHost, CLAP_PARAM_RESCAN_VALUES|CLAP_PARAM_RESCAN_TEXT);
+ if (fHostExtensions.params != nullptr)
+ fHostExtensions.params->rescan(fHost, CLAP_PARAM_RESCAN_VALUES|CLAP_PARAM_RESCAN_TEXT);
+
+ #if DISTRHO_PLUGIN_WANT_LATENCY
+ checkForLatencyChanges(fPlugin.isActive(), true);
+ reportLatencyChangeIfNeeded();
+ #endif
#if DISTRHO_PLUGIN_HAS_UI
if (ui != nullptr)
@@ -1591,6 +1672,10 @@ private:
const clap_host_t* const fHost;
const clap_output_events_t* fOutputEvents;
+ #if DISTRHO_PLUGIN_WANT_LATENCY
+ bool fLatencyChanged;
+ uint32_t fLastKnownLatency;
+ #endif
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
uint32_t fMidiEventCount;
MidiEvent fMidiEvents[kMaxMidiEvents];
@@ -1602,6 +1687,36 @@ private:
TimePosition fTimePosition;
#endif
+ struct HostExtensions {
+ const clap_host_t* const host;
+ const clap_host_params_t* params;
+ #if DISTRHO_PLUGIN_WANT_LATENCY
+ const clap_host_latency_t* latency;
+ const clap_host_thread_check_t* threadCheck;
+ #endif
+
+ HostExtensions(const clap_host_t* const host)
+ : host(host),
+ params(nullptr)
+ #if DISTRHO_PLUGIN_WANT_LATENCY
+ , latency(nullptr)
+ , threadCheck(nullptr)
+ #endif
+ {}
+
+ bool init()
+ {
+ params = static_cast<const clap_host_params_t*>(host->get_extension(host, CLAP_EXT_PARAMS));
+ #if DISTRHO_PLUGIN_WANT_LATENCY
+ DISTRHO_SAFE_ASSERT_RETURN(host->request_restart != nullptr, false);
+ DISTRHO_SAFE_ASSERT_RETURN(host->request_callback != nullptr, false);
+ latency = static_cast<const clap_host_latency_t*>(host->get_extension(host, CLAP_EXT_LATENCY));
+ threadCheck = static_cast<const clap_host_thread_check_t*>(host->get_extension(host, CLAP_EXT_THREAD_CHECK));
+ #endif
+ return true;
+ }
+ } fHostExtensions;
+
// ----------------------------------------------------------------------------------------------------------------
// DPF callbacks
@@ -1940,37 +2055,37 @@ static const clap_plugin_note_ports_t clap_plugin_note_ports = {
// --------------------------------------------------------------------------------------------------------------------
// plugin parameters
-static uint32_t clap_plugin_params_count(const clap_plugin_t* const plugin)
+static CLAP_ABI uint32_t clap_plugin_params_count(const clap_plugin_t* const plugin)
{
PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
return instance->getParameterCount();
}
-static bool clap_plugin_params_get_info(const clap_plugin_t* const plugin, const uint32_t index, clap_param_info_t* const info)
+static CLAP_ABI bool clap_plugin_params_get_info(const clap_plugin_t* const plugin, const uint32_t index, clap_param_info_t* const info)
{
PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
return instance->getParameterInfo(index, info);
}
-static bool clap_plugin_params_get_value(const clap_plugin_t* const plugin, const clap_id param_id, double* const value)
+static CLAP_ABI bool clap_plugin_params_get_value(const clap_plugin_t* const plugin, const clap_id param_id, double* const value)
{
PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
return instance->getParameterValue(param_id, value);
}
-static bool clap_plugin_params_value_to_text(const clap_plugin_t* plugin, const clap_id param_id, const double value, char* const display, const uint32_t size)
+static CLAP_ABI bool clap_plugin_params_value_to_text(const clap_plugin_t* plugin, const clap_id param_id, const double value, char* const display, const uint32_t size)
{
PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
return instance->getParameterStringForValue(param_id, value, display, size);
}
-static bool clap_plugin_params_text_to_value(const clap_plugin_t* plugin, const clap_id param_id, const char* const display, double* const value)
+static CLAP_ABI bool clap_plugin_params_text_to_value(const clap_plugin_t* plugin, const clap_id param_id, const char* const display, double* const value)
{
PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
return instance->getParameterValueForString(param_id, display, value);
}
-static void clap_plugin_params_flush(const clap_plugin_t* plugin, const clap_input_events_t* in, const clap_output_events_t* out)
+static CLAP_ABI void clap_plugin_params_flush(const clap_plugin_t* plugin, const clap_input_events_t* in, const clap_output_events_t* out)
{
PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
return instance->flushParameters(in, out);
@@ -1985,16 +2100,32 @@ static const clap_plugin_params_t clap_plugin_params = {
clap_plugin_params_flush
};
+#if DISTRHO_PLUGIN_WANT_LATENCY
+// --------------------------------------------------------------------------------------------------------------------
+// plugin latency
+
+static CLAP_ABI uint32_t clap_plugin_latency_get(const clap_plugin_t* const plugin)
+{
+ PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
+ return instance->getLatency();
+}
+
+static const clap_plugin_latency_t clap_plugin_latency = {
+ clap_plugin_latency_get
+};
+#endif
+
+#if DISTRHO_PLUGIN_WANT_STATE
// --------------------------------------------------------------------------------------------------------------------
// plugin state
-static bool clap_plugin_state_save(const clap_plugin_t* const plugin, const clap_ostream_t* const stream)
+static CLAP_ABI bool clap_plugin_state_save(const clap_plugin_t* const plugin, const clap_ostream_t* const stream)
{
PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
return instance->stateSave(stream);
}
-static bool clap_plugin_state_load(const clap_plugin_t* const plugin, const clap_istream_t* const stream)
+static CLAP_ABI bool clap_plugin_state_load(const clap_plugin_t* const plugin, const clap_istream_t* const stream)
{
PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
return instance->stateLoad(stream);
@@ -2004,26 +2135,27 @@ static const clap_plugin_state_t clap_plugin_state = {
clap_plugin_state_save,
clap_plugin_state_load
};
+#endif
// --------------------------------------------------------------------------------------------------------------------
// plugin
-static bool clap_plugin_init(const clap_plugin_t* const plugin)
+static CLAP_ABI 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)
+static CLAP_ABI 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)
+static CLAP_ABI 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;
@@ -2033,35 +2165,35 @@ static bool clap_plugin_activate(const clap_plugin_t* const plugin,
return true;
}
-static void clap_plugin_deactivate(const clap_plugin_t* const plugin)
+static CLAP_ABI 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*)
+static CLAP_ABI bool clap_plugin_start_processing(const clap_plugin_t*)
{
// nothing to do
return true;
}
-static void clap_plugin_stop_processing(const clap_plugin_t*)
+static CLAP_ABI void clap_plugin_stop_processing(const clap_plugin_t*)
{
// nothing to do
}
-static void clap_plugin_reset(const clap_plugin_t*)
+static CLAP_ABI 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)
+static CLAP_ABI 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)
+static CLAP_ABI 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;
@@ -2069,6 +2201,10 @@ static const void* clap_plugin_get_extension(const clap_plugin_t*, const char* c
return &clap_plugin_note_ports;
if (std::strcmp(id, CLAP_EXT_PARAMS) == 0)
return &clap_plugin_params;
+ #if DISTRHO_PLUGIN_WANT_LATENCY
+ if (std::strcmp(id, CLAP_EXT_LATENCY) == 0)
+ return &clap_plugin_latency;
+ #endif
#if DISTRHO_PLUGIN_WANT_STATE
if (std::strcmp(id, CLAP_EXT_STATE) == 0)
return &clap_plugin_state;
@@ -2084,9 +2220,10 @@ static const void* clap_plugin_get_extension(const clap_plugin_t*, const char* c
return nullptr;
}
-static void clap_plugin_on_main_thread(const clap_plugin_t*)
+static void clap_plugin_on_main_thread(const clap_plugin_t* const plugin)
{
- // nothing to do
+ PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
+ instance->onMainThread();
}
// --------------------------------------------------------------------------------------------------------------------
diff --git a/examples/Latency/DistrhoPluginInfo.h b/examples/Latency/DistrhoPluginInfo.h
@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
- * Copyright (C) 2012-2015 Filipe Coelho <falktx@falktx.com>
+ * 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
@@ -17,9 +17,10 @@
#ifndef DISTRHO_PLUGIN_INFO_H_INCLUDED
#define DISTRHO_PLUGIN_INFO_H_INCLUDED
-#define DISTRHO_PLUGIN_BRAND "DISTRHO"
-#define DISTRHO_PLUGIN_NAME "Latency"
-#define DISTRHO_PLUGIN_URI "http://distrho.sf.net/examples/Latency"
+#define DISTRHO_PLUGIN_BRAND "DISTRHO"
+#define DISTRHO_PLUGIN_NAME "Latency"
+#define DISTRHO_PLUGIN_URI "http://distrho.sf.net/examples/Latency"
+#define DISTRHO_PLUGIN_CLAP_ID "studio.kx.distrho.examples.latency"
#define DISTRHO_PLUGIN_HAS_UI 0
#define DISTRHO_PLUGIN_IS_RT_SAFE 1
diff --git a/examples/Latency/LatencyExamplePlugin.cpp b/examples/Latency/LatencyExamplePlugin.cpp
@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
- * Copyright (C) 2012-2015 Filipe Coelho <falktx@falktx.com>
+ * 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
@@ -31,7 +31,8 @@ public:
fLatency(1.0f),
fLatencyInFrames(0),
fBuffer(nullptr),
- fBufferPos(0)
+ fBufferPos(0),
+ fBufferSize(0)
{
// allocates buffer
sampleRateChanged(getSampleRate());
@@ -114,7 +115,7 @@ protected:
*/
void initAudioPort(bool input, uint32_t index, AudioPort& port) override
{
- // treat meter audio ports as stereo
+ // mark the (single) latency audio port as mono
port.groupId = kPortGroupMono;
// everything else is as default
@@ -167,7 +168,6 @@ protected:
fLatency = value;
fLatencyInFrames = value*getSampleRate();
-
setLatency(fLatencyInFrames);
}
@@ -175,6 +175,15 @@ protected:
* Audio/MIDI Processing */
/**
+ Activate this plugin.
+ */
+ void activate()
+ {
+ fBufferPos = 0;
+ std::memset(fBuffer, 0, sizeof(float)*fBufferSize);
+ }
+
+ /**
Run/process function for plugins without MIDI input.
@note Some parameters might be null if there are no audio inputs or outputs.
*/
@@ -222,16 +231,14 @@ protected:
*/
void sampleRateChanged(double newSampleRate) override
{
- if (fBuffer != nullptr)
- delete[] fBuffer;
-
- const uint32_t maxFrames = newSampleRate*6; // 6 seconds
+ fBufferSize = newSampleRate*6; // 6 seconds
- fBuffer = new float[maxFrames];
- std::memset(fBuffer, 0, sizeof(float)*maxFrames);
+ delete[] fBuffer;
+ fBuffer = new float[fBufferSize];
+ // buffer reset is done during activate()
fLatencyInFrames = fLatency*newSampleRate;
- fBufferPos = 0;
+ setLatency(fLatencyInFrames);
}
// -------------------------------------------------------------------------------------------------------
@@ -243,7 +250,7 @@ private:
// Buffer for previous audio, size depends on sample rate
float* fBuffer;
- uint32_t fBufferPos;
+ uint32_t fBufferPos, fBufferSize;
/**
Set our plugin class as non-copyable and add a leak detector just in case.
diff --git a/examples/Latency/Makefile b/examples/Latency/Makefile
@@ -27,6 +27,7 @@ TARGETS += ladspa
TARGETS += lv2_sep
TARGETS += vst2
TARGETS += vst3
+TARGETS += clap
all: $(TARGETS)