1
0

Split processing up according to events

This commit is contained in:
Geraint 2025-06-22 12:53:14 +01:00
parent 89aebf5d9e
commit 4dd9d55c62

View File

@ -5,6 +5,7 @@
#include <functional>
#include <initializer_list>
#include <cstdio>
#include <optional>
#include "./param-info.h"
@ -104,6 +105,10 @@ struct Crc32 {
return *this;
}
Crc32 copy() {
return {*this};
}
uint32_t done() const {
return crc^0xFFFFFFFFu;
}
@ -132,12 +137,25 @@ struct Plugin : public clap_plugin {
this->process = plugin_process;
this->get_extension = plugin_get_extension;
this->on_main_thread = plugin_on_main_thread;
scanParams();
}
// Configuration state, used by multiple extensions
// plugin state
bool isConfigured = false;
// for library STFX, all inputs/outputs (main and aux) get concatenated together
std::vector<const float *> inputBuffers;
std::vector<float *> outputBuffers;
void processEvent(const clap_event_header *header) {
LOG_EXPR(header->size);
LOG_EXPR(header->time);
LOG_EXPR(header->space_id);
LOG_EXPR(header->type);
LOG_EXPR(header->flags);
}
// Main plugin methods
// CLAP plugin methods
static bool plugin_init(const clap_plugin *obj) {
auto &plugin = *(Plugin *)obj;
LOG_EXPR(plugin.plugins.modulePath);
@ -148,24 +166,21 @@ struct Plugin : public clap_plugin {
}
static bool plugin_activate(const clap_plugin *obj, double sampleRate, uint32_t minFrames, uint32_t maxFrames) {
auto &plugin = *(Plugin *)obj;
auto &config = plugin.effect.config;
if (!plugin.isConfigured || sampleRate != plugin.effect.config.sampleRate) {
auto prevConfig = plugin.effect.config;
auto prevConfig = config;
plugin.isConfigured = plugin.effect.configure();
if (!plugin.isConfigured) {
// Can't change config (e.g. sample-rate/ports/etc.) here
plugin.effect.config = prevConfig;
// Can't change config (e.g. sample-rate/ports/etc.) here, return to previous
config = prevConfig;
}
}
auto &inputBuffers = plugin.inputBuffers;
inputBuffers.resize(plugin.effect.config.inputChannels);
for (auto &a : plugin.effect.config.auxInputs) {
inputBuffers.resize(inputBuffers.size() + a);
}
auto &outputBuffers = plugin.outputBuffers;
outputBuffers.resize(plugin.effect.config.outputChannels);
for (auto &a : plugin.effect.config.auxOutputs) {
outputBuffers.resize(outputBuffers.size() + a);
}
size_t inputChannels = config.inputChannels;
for (auto &a : config.auxInputs) inputChannels += a;
plugin.inputBuffers.reserve(inputChannels);
size_t outputChannels = config.outputChannels;
for (auto &a : config.auxOutputs) a += outputChannels;
plugin.outputBuffers.reserve(outputChannels);
return plugin.isConfigured;
}
static void plugin_deactivate(const clap_plugin *obj) {}
@ -198,12 +213,9 @@ struct Plugin : public clap_plugin {
}
return nullptr;
}
std::vector<const float *> inputBuffers;
std::vector<float *> outputBuffers;
static clap_process_status plugin_process(const clap_plugin *obj, const clap_process *process) {
auto &plugin = *(Plugin *)obj;
auto &inputBuffers = plugin.inputBuffers;
inputBuffers.resize(0);
for (uint32_t i = 0; i < process->audio_inputs_count; ++i) {
auto &buffer = process->audio_inputs[i];
for (uint32_t c = 0; c < buffer.channel_count; ++c) {
@ -211,14 +223,39 @@ struct Plugin : public clap_plugin {
}
}
auto &outputBuffers = plugin.outputBuffers;
outputBuffers.resize(0);
for (uint32_t i = 0; i < process->audio_outputs_count; ++i) {
auto &buffer = process->audio_outputs[i];
for (uint32_t c = 0; c < buffer.channel_count; ++c) {
outputBuffers.push_back(buffer.data32[c]);
}
}
plugin.effect.process(inputBuffers.data(), outputBuffers.data(), process->frames_count);
size_t length = process->frames_count;
auto inputEventCount = process->in_events->size(process->in_events);
size_t offset = 0, nextEventIndex = 0;
while (offset < length) {
size_t nextEventOffset = length;
const clap_event_header *event = nullptr;
if (nextEventIndex < inputEventCount) {
event = process->in_events->get(process->in_events, nextEventIndex);
nextEventOffset = std::max<size_t>(offset, event->time);
}
// process up until the next event (or end)
if (nextEventOffset > offset) {
auto delta = nextEventOffset - offset;
plugin.effect.process(inputBuffers.data(), outputBuffers.data(), delta);
offset = nextEventOffset;
for (auto &p : inputBuffers) p += delta;
for (auto &p : outputBuffers) p += delta;
}
if (event) {
plugin.processEvent(event);
++nextEventIndex;
}
}
inputBuffers.resize(0);
outputBuffers.resize(0);
return CLAP_PROCESS_CONTINUE;
}
static void plugin_on_main_thread(const clap_plugin *obj) {}
@ -226,15 +263,75 @@ struct Plugin : public clap_plugin {
// parameters
struct Param : public clap_param_info {
typename Effect::ParamRange *rangeParam = nullptr;
std::unique_ptr<RangeParamInfo> rangeInfo;
std::optional<RangeParamInfo> rangeInfo;
typename Effect::ParamStepped *steppedParam = nullptr;
std::unique_ptr<SteppedParamInfo> steppedInfo;
std::optional<SteppedParamInfo> steppedInfo;
void setClapInfo() {
flags |= CLAP_PARAM_IS_AUTOMATABLE;
cookie = this;
if (rangeParam) {
std::strncpy(name, rangeInfo->name.c_str(), CLAP_NAME_SIZE);
// STFX range params are mapped to [0, 1] so we can give them a nonlinear shape
min_value = 0;
max_value = 1;
default_value = rangeInfo->toUnit(rangeInfo->defaultValue);
} else {
std::strncpy(name, steppedInfo->name.c_str(), CLAP_NAME_SIZE);
flags |= CLAP_PARAM_IS_STEPPED;
min_value = steppedInfo->low;
max_value = steppedInfo->high;
default_value = steppedInfo->defaultValue;
}
}
};
std::vector<Param> params;
struct ParamScanner {
std::vector<Param> &params;
Crc32 crc;
// Do nothing for most types
template<class V>
void operator()(const char *key, V &v) {
LOG_EXPR(key);
if constexpr (std::is_void_v<decltype(v.state(*this))>) {
LOG_EXPR("recursing");
ParamScanner recurse{*this};
recurse.crc.addString(key, true);
v.state(recurse);
}
}
// Extra methods we add for STFX storage
void info(const char *, const char *) {}
int version(int v) {
return v;
}
template<class PR>
RangeParamInfo & range(const char *key, PR &param) {
params.emplace_back();
auto &entry = params.back();
entry.id = crc.copy().addString(key, true).done();
entry.rangeParam = &param;
return entry.rangeInfo.emplace(param);
}
template<class PS>
SteppedParamInfo & stepped(const char *key, PS &param) {
params.emplace_back();
auto &entry = params.back();
entry.id = crc.copy().addString(key, true).done();
entry.steppedParam = &param;
return entry.steppedInfo.emplace(param);
}
};
void scanParams() {
params.resize(0);
ParamScanner scanner{params};
effect.state(scanner);
for (auto &entry : params) entry.setClapInfo();
}
// CLAP parameter methods
static uint32_t params_count(const clap_plugin *obj) {
auto &plugin = *(Plugin *)obj;
return plugin.params.size();
@ -250,7 +347,7 @@ struct Plugin : public clap_plugin {
for (auto &param : plugin.params) {
if (param.id == paramId) {
if (param.rangeParam) {
*value = (double)*param.rangeParam;
*value = param.rangeInfo->toUnit((double)*param.rangeParam);
} else {
*value = (int)*param.steppedParam;
}
@ -259,16 +356,47 @@ struct Plugin : public clap_plugin {
}
return false;
}
static bool params_value_to_text(const clap_plugin *obj, clap_id paramId, double value, char *outBuffer, uint32_t outCapacity) {
static bool params_value_to_text(const clap_plugin *obj, clap_id paramId, double value, char *text, uint32_t textCapacity) {
auto &plugin = *(Plugin *)obj;
for (auto &param : plugin.params) {
if (param.id == paramId) {
if (param.rangeParam) {
auto str = param.rangeInfo->toString(value);
std::strncpy(text, str.c_str(), textCapacity);
} else {
auto str = param.steppedInfo->toString(int(std::round(value)));
std::strncpy(text, str.c_str(), textCapacity);
}
return true;
}
}
return false;
}
static bool params_text_to_value(const clap_plugin *obj, clap_id paramId, const char *text, double *value) {
auto &plugin = *(Plugin *)obj;
for (auto &param : plugin.params) {
if (param.id == paramId) {
std::string str = text;
if (param.rangeParam) {
*value = param.rangeInfo->toUnit(param.rangeInfo->fromString(str));
} else {
*value = param.steppedInfo->fromString(str);
}
return true;
}
}
return false;
}
static void params_flush(const clap_plugin *obj, const clap_input_events *inEvents, const clap_output_events *outEvents) {
// not implemented yet
auto &plugin = *(Plugin *)obj;
auto count = inEvents->size(inEvents);
for (uint32_t i = 0; i < count; ++i) {
auto *header = inEvents->get(inEvents, i);
plugin.processEvent(header);
}
}
// CLAP audio port methods
static uint32_t audio_ports_count(const clap_plugin *obj, bool inputPorts) {
auto &plugin = *(Plugin *)obj;
if (!plugin.isConfigured) {