Side-chains

This commit is contained in:
Robin E. R. Davies
2025-09-30 19:59:47 -04:00
parent ca1d4bad69
commit 25494d6f3c
19 changed files with 723 additions and 180 deletions
+3
View File
@@ -54,7 +54,9 @@ namespace pipedal {
virtual int GetNumberOfInputAudioBuffers() const = 0; // may be different if plugin has zero inputs.
virtual int GetNumberOfOutputAudioBuffers() const = 0; // may be different if plugin has zero inputs.
virtual int GetNumberOfSidechainAudioBuffers() const { return 0; }
virtual float *GetAudioInputBuffer(int index) const = 0;
virtual float *GetAudioSidechainBuffer(int index) const { throw std::runtime_error("Not implemented"); }
virtual float *GetAudioOutputBuffer(int index) const = 0;
virtual void ResetAtomBuffers() = 0;
@@ -65,6 +67,7 @@ namespace pipedal {
virtual void SetAudioInputBuffer(int index, float *buffer) = 0;
virtual void SetAudioOutputBuffer(int index, float*buffer) = 0;
virtual void SetAudioSidechainBuffer(int index, float *buffer) { throw std::runtime_error("Not implemented");}
virtual void Activate() = 0;
+78 -5
View File
@@ -384,7 +384,12 @@ void Lv2Effect::PreparePortIndices()
{
if (port->is_input())
{
this->inputAudioPortIndices.push_back(portIndex);
if (port->is_sidechain())
{
this->inputSidechainPortIndices.push_back(portIndex);
} else {
this->inputAudioPortIndices.push_back(portIndex);
}
}
else
{
@@ -432,6 +437,7 @@ void Lv2Effect::PreparePortIndices()
this->maxInputControlPort = maxInputControlPort;
inputAudioBuffers.resize(inputAudioPortIndices.size());
inputSidechainBuffers.resize(inputSidechainPortIndices.size());
outputAudioBuffers.resize(outputAudioPortIndices.size());
inputAtomBuffers.resize(inputAtomPortIndices.size());
outputAtomBuffers.resize(outputAtomPortIndices.size());
@@ -439,9 +445,7 @@ void Lv2Effect::PreparePortIndices()
if (RequiresBufferStaging())
{
EnableBufferStaging(
GetStagedBufferSize(),
this->GetNumberOfInputAudioBuffers(),
this->GetNumberOfOutputAudioBuffers());
GetStagedBufferSize());
}
}
@@ -473,6 +477,7 @@ void Lv2Effect::PrepareNoInputEffect(int numberOfInputs, size_t maxBufferSize)
}
}
void Lv2Effect::SetAudioInputBuffer(int index, float *buffer)
{
this->inputAudioBuffers.at(index) = buffer;
@@ -512,6 +517,32 @@ void Lv2Effect::SetAudioInputBuffer(int index, float *buffer)
// }
}
}
void Lv2Effect::SetAudioSidechainBuffer(int index, float *buffer)
{
this->inputSidechainBuffers.at(index) = buffer;
if (borrowedEffect)
{
// Already running on the realtime thread,
// so don't update the audio ports until the effect gets placed on the realtime thread.
return;
}
if (stagingBufferSize != 0)
{
int pluginIndex = this->inputSidechainPortIndices.at(index);
if (index >= sidechainStagingBufferPointers.size())
{
throw std::runtime_error("Invalid input staging buffer index.");
}
lilv_instance_connect_port(this->pInstance, pluginIndex, sidechainStagingBufferPointers.at(index));
}
else
{
int pluginIndex = this->inputSidechainPortIndices.at(index);
lilv_instance_connect_port(this->pInstance, pluginIndex, buffer);
}
}
void Lv2Effect::SetAudioInputBuffer(float *left)
{
@@ -664,6 +695,14 @@ void Lv2Effect::UpdateAudioPorts()
lilv_instance_connect_port(pInstance, portIndex, outputStagingBufferPointers.at(i));
}
}
for (size_t i = 0; i < this->inputSidechainPortIndices.size(); ++i)
{
int portIndex = this->inputSidechainPortIndices.at(i);
if (sidechainStagingBufferPointers.at(i) != nullptr)
{
lilv_instance_connect_port(pInstance, portIndex, sidechainStagingBufferPointers.at(i));
}
}
for (size_t i = 0; i < this->inputAtomPortIndices.size(); ++i)
{
if (i == 0)
@@ -710,6 +749,15 @@ void Lv2Effect::UpdateAudioPorts()
lilv_instance_connect_port(pInstance, portIndex, GetAudioOutputBuffer(i));
}
}
for (size_t i = 0; i < this->inputSidechainPortIndices.size(); ++i)
{
int portIndex = this->inputSidechainPortIndices.at(i);
if (GetAudioSidechainBuffer(i) != nullptr)
{
lilv_instance_connect_port(pInstance, portIndex, GetAudioSidechainBuffer(i));
}
}
for (size_t i = 0; i < this->inputAtomPortIndices.size(); ++i)
{
auto atomInputBuffer = this->GetAtomInputBuffer(i);
@@ -877,6 +925,16 @@ size_t Lv2Effect::stageToInput(size_t inputSampleOffset, size_t samples)
pOutput[i] = pInput[i];
}
}
for (size_t nSidechain = 0; nSidechain < this->inputSidechainBuffers.size(); ++nSidechain)
{
float *restrict pInput = this->inputSidechainBuffers[nSidechain] + inputSampleOffset;
float *restrict pOutput = this->sidechainStagingBufferPointers.at(nSidechain) + this->stagingInputIx;
for (size_t i = 0; i < thisTime; ++i)
{
pOutput[i] = pInput[i];
}
}
this->stagingInputIx += thisTime;
inputSampleOffset += thisTime;
@@ -1487,8 +1545,11 @@ void Lv2Effect::SetPathPatchProperty(const std::string &propertyUri, const std::
mainThreadPathProperties[propertyUri] = jsonAtom;
}
void Lv2Effect::EnableBufferStaging(size_t bufferSize, size_t nInputs, size_t nOutputs)
void Lv2Effect::EnableBufferStaging(size_t bufferSize )
{
size_t nInputs = this->GetNumberOfInputAudioBuffers();
size_t nSidechainInputs = this->GetNumberOfSidechainAudioBuffers();
size_t nOutputs = this->GetNumberOfOutputAudioBuffers();
stagingBufferSize = bufferSize;
stagingOutputIx = bufferSize;
@@ -1520,6 +1581,12 @@ void Lv2Effect::EnableBufferStaging(size_t bufferSize, size_t nInputs, size_t nO
inputStagingBuffers[i][bufferSize] = 99.9f; // guard entry
inputStagingBufferPointers.at(i) = inputStagingBuffers.at(i).data();
}
for (size_t i = 0; i < nSidechainInputs; ++i)
{
sidechainStagingBuffers.at(i).resize(bufferSize + 1);
sidechainStagingBuffers[i][bufferSize] = 99.9f; // guard entry
sidechainStagingBufferPointers.at(i) = sidechainStagingBuffers.at(i).data();
}
for (size_t i = 0; i < nOutputs; ++i)
{
outputStagingBuffers.at(i).resize(bufferSize + 1);
@@ -1571,6 +1638,12 @@ float *Lv2Effect::GetAudioInputBuffer(int index) const
throw std::range_error("Lv2Effect::GetAudioInputBuffer");
return this->inputAudioBuffers.at(index);
}
float *Lv2Effect::GetAudioSidechainBuffer(int index) const
{
if (index < 0 || index >= this->inputSidechainBuffers.size())
throw std::range_error("Lv2Effect::GetAudioSidechainBuffer");
return this->inputSidechainBuffers.at(index);
}
float *Lv2Effect::GetAudioOutputBuffer(int index) const
{
+20 -11
View File
@@ -81,6 +81,8 @@ namespace pipedal
std::vector<int> inputAudioPortIndices;
std::vector<int> outputAudioPortIndices;
std::vector<int> inputSidechainPortIndices;
std::vector<int> inputAtomPortIndices;
std::vector<int> outputAtomPortIndices;
@@ -90,6 +92,7 @@ namespace pipedal
std::vector<int> midiInputIndices;
std::vector<float *> inputAudioBuffers;
std::vector<float *> inputSidechainBuffers;
std::vector<float *> outputAudioBuffers;
std::vector<char *> inputAtomBuffers;
@@ -212,7 +215,7 @@ namespace pipedal
bool borrowedEffect = false;
bool activated = false;
void EnableBufferStaging(size_t bufferSize, size_t nInputs, size_t nOutputs);
void EnableBufferStaging(size_t bufferSize);
void CheckStagingBufferSentries();
public:
@@ -262,8 +265,10 @@ namespace pipedal
size_t stagingInputIx = 0;
size_t stagingOutputIx = 0;
std::vector<std::vector<float>> inputStagingBuffers;
std::vector<std::vector<float>> sidechainStagingBuffers;
std::vector<std::vector<float>> outputStagingBuffers;
std::vector<float*> inputStagingBufferPointers;
std::vector<float*> sidechainStagingBufferPointers;
std::vector<float*> outputStagingBufferPointers;
std::vector<uint8_t> stagedInputAtomBuffer;
@@ -297,22 +302,26 @@ namespace pipedal
virtual void ResetAtomBuffers();
virtual uint64_t GetInstanceId() const { return instanceId; }
virtual int GetNumberOfInputAudioPorts() const { return inputAudioPortIndices.size(); }
virtual int GetNumberOfOutputAudioPorts() const { return outputAudioPortIndices.size(); }
virtual int GetNumberOfInputAudioPorts() const override { return inputAudioPortIndices.size(); }
virtual int GetNumberOfOutputAudioPorts() const override { return outputAudioPortIndices.size(); }
virtual int GetNumberOfInputAtomPorts() const { return inputAtomPortIndices.size(); }
virtual int GetNumberOfInputAtomPorts() const { return inputAtomPortIndices.size(); }
virtual int GetNumberOfOutputAtomPorts() const { return outputAtomPortIndices.size(); }
virtual int GetNumberOfMidiInputPorts() const { return inputMidiPortIndices.size(); }
virtual int GetNumberOfMidiOutputPorts() const { return outputMidiPortIndices.size(); }
virtual int GetNumberOfOutputAtomPorts() const { return outputAtomPortIndices.size(); }
virtual int GetNumberOfMidiInputPorts() const { return inputMidiPortIndices.size(); }
virtual int GetNumberOfMidiOutputPorts() const { return outputMidiPortIndices.size(); }
virtual int GetNumberOfInputAudioBuffers() const { return this->inputAudioBuffers.size(); }
virtual int GetNumberOfOutputAudioBuffers() const {return this->outputAudioBuffers.size(); }
virtual int GetNumberOfInputAudioBuffers() const override { return this->inputAudioBuffers.size(); }
virtual int GetNumberOfSidechainAudioBuffers() const override { return this->inputSidechainBuffers.size(); }
virtual int GetNumberOfOutputAudioBuffers() const override {return this->outputAudioBuffers.size(); }
virtual void SetAudioInputBuffer(int index, float *buffer);
virtual float *GetAudioInputBuffer(int index) const;
virtual void SetAudioInputBuffer(int index, float *buffer) override;
virtual float *GetAudioInputBuffer(int index) const override;
virtual void SetAudioSidechainBuffer(int index, float *buffer) override;
virtual float *GetAudioSidechainBuffer(int index) const override;
virtual void SetAudioInputBuffer(float *buffer);
+54 -1
View File
@@ -176,6 +176,60 @@ std::vector<float *> Lv2Pedalboard::PrepareItems(
auto bufferR = inputBuffers[1];
}
}
// Connect sidechain buffers.
if (pLv2Effect->GetNumberOfSidechainAudioBuffers() != 0)
{
if (item.sideChainInputId() == -2) // -2 means "use pedalboard inputs".
{
for (size_t i = 0; i < pLv2Effect->GetNumberOfSidechainAudioBuffers(); ++i)
{
if (i < this->pedalboardInputBuffers.size())
{
pLv2Effect->SetAudioSidechainBuffer(i, this->pedalboardInputBuffers[i]);
}
else
{
// just use the first output buffer for all sidechain inputs.
pLv2Effect->SetAudioSidechainBuffer(i, this->pedalboardInputBuffers[0]);
}
}
} else if (item.sideChainInputId() != -1) {
IEffect *pSideChainInput = GetEffect(item.sideChainInputId());
if (pSideChainInput)
{
for (size_t i = 0; i < pLv2Effect->GetNumberOfSidechainAudioBuffers(); ++i)
{
if (i < pSideChainInput->GetNumberOfOutputAudioBuffers())
{
pLv2Effect->SetAudioSidechainBuffer(i, pSideChainInput->GetAudioOutputBuffer(i));
}
else
{
// just use the first output buffer for all sidechain inputs.
pLv2Effect->SetAudioSidechainBuffer(i, pSideChainInput->GetAudioOutputBuffer(0));
}
}
} else {
throw std::runtime_error("Internal error: Sidechain input IEffect not found.");
}
}
else
{
// No sidechain input. Feed zero buffer to all sidechain audio ports..
for (size_t i = 0; i < pLv2Effect->GetNumberOfSidechainAudioBuffers(); ++i)
{
// just use one buffer for all plugins
if (!this->pedalboardSidechainBuffer)
{
this->pedalboardSidechainBuffer = CreateNewAudioBuffer();
}
pLv2Effect->SetAudioSidechainBuffer(i, this->pedalboardSidechainBuffer);
}
}
}
// check to see whether we need buffer staging.
bool requiresBufferStaging = false;
@@ -194,7 +248,6 @@ std::vector<float *> Lv2Pedalboard::PrepareItems(
}
}
if (!requiresBufferStaging)
{
this->processActions.push_back(
+1
View File
@@ -57,6 +57,7 @@ namespace pipedal
BufferPool bufferPool;
std::vector<float *> pedalboardInputBuffers;
std::vector<float *> pedalboardOutputBuffers;
float *pedalboardSidechainBuffer = nullptr;
std::vector<std::shared_ptr<IEffect>> effects;
std::vector<IEffect *> realtimeEffects;
+1
View File
@@ -534,6 +534,7 @@ JSON_MAP_BEGIN(PedalboardItem)
JSON_MAP_REFERENCE(PedalboardItem,title)
JSON_MAP_REFERENCE(PedalboardItem,useModUi)
JSON_MAP_REFERENCE(PedalboardItem,iconColor)
JSON_MAP_REFERENCE(PedalboardItem,sideChainInputId)
JSON_MAP_END()
+2
View File
@@ -107,6 +107,7 @@ public:
std::string title_;
bool useModUi_ = false;
std::string iconColor_;
int64_t sideChainInputId_ = -1;
// non persistent state.
PropertyMap patchProperties;
@@ -138,6 +139,7 @@ public:
GETTER_SETTER_REF(title)
GETTER_SETTER_REF(iconColor)
GETTER_SETTER(useModUi)
GETTER_SETTER(sideChainInputId)
Lv2PluginState&lv2State() { return lv2State_; } // non-const version.
GETTER_SETTER_REF(lilvPresetUri)
+1 -1
View File
@@ -2428,7 +2428,7 @@ void PiPedalModel::OnNotifyPathPatchPropertyReceived(
auto i = pedalboardItem->pathProperties_.find(pathPatchPropertyUri);
if (i != pedalboardItem->pathProperties_.end())
{
std::string abstractAtomString = storage.ToAbstractPathJson(atomString);
std::string abstractAtomString = storage.ToAbstractPathFromJson(atomString);
pedalboardItem->pathProperties_[pathPatchPropertyUri] = abstractAtomString;
std::vector<IPiPedalModelSubscriber::ptr> t{subscribers.begin(), subscribers.end()};
+111 -7
View File
@@ -121,6 +121,7 @@ void PluginHost::LilvUris::Initialize(LilvWorld *pWorld)
enumeration_property_uri = lilv_new_uri(pWorld, LV2_CORE__enumeration);
core__toggled = lilv_new_uri(pWorld, LV2_CORE__toggled);
core__connectionOptional = lilv_new_uri(pWorld, LV2_CORE__connectionOptional);
core__isSideChain = lilv_new_uri(pWorld, LV2_CORE_PREFIX "isSideChain"); // missing in lv2.h
portprops__not_on_gui_property_uri = lilv_new_uri(pWorld, LV2_PORT_PROPS__notOnGUI);
portprops__trigger = lilv_new_uri(pWorld, LV2_PORT_PROPS__trigger);
midi__event = lilv_new_uri(pWorld, LV2_MIDI__MidiEvent);
@@ -134,6 +135,7 @@ void PluginHost::LilvUris::Initialize(LilvWorld *pWorld)
atom__bufferType = lilv_new_uri(pWorld, LV2_ATOM__bufferType);
atom__Path = lilv_new_uri(pWorld, LV2_ATOM__Path);
atom__String = lilv_new_uri(pWorld, LV2_ATOM__String);
presets__preset = lilv_new_uri(pWorld, LV2_PRESETS__Preset);
state__state = lilv_new_uri(pWorld, LV2_STATE__state);
rdfs__label = lilv_new_uri(pWorld, LILV_NS_RDFS "label");
@@ -214,6 +216,7 @@ void PluginHost::LilvUris::Initialize(LilvWorld *pWorld)
buf_size__maxBlockLength = lilv_new_uri(pWorld, LV2_BUF_SIZE__maxBlockLength);
buf_size__fixedBlockLength = lilv_new_uri(pWorld, LV2_BUF_SIZE__fixedBlockLength);
buf_size__coarseBlockLength = lilv_new_uri(pWorld, LV2_BUF_SIZE__coarseBlockLength);
port_groups__sideChainOf = lilv_new_uri(pWorld, LV2_PORT_GROUPS__sideChainOf);
}
@@ -794,7 +797,12 @@ Lv2PluginInfo::FindWritablePathProperties(PluginHost *lv2Host, const LilvPlugin
}
else
{
unsupportedPatchProperty = true;
if (lilv_world_ask(pWorld, propertyUri, lv2Host->lilvUris->rdfs__range, lv2Host->lilvUris->atom__String))
{
} else {
unsupportedPatchProperty = true;
}
}
}
}
@@ -945,6 +953,60 @@ Lv2PluginInfo::Lv2PluginInfo(PluginHost *lv2Host, LilvWorld *pWorld, const LilvP
std::sort(ports_.begin(), ports_.end(), ports_sort_compare);
// Check the portgroup for audio inputs to see if they are sidechains.
std::string last_sidechain_title;
for (auto&port: this->ports_)
{
if (port->is_audio_port() && port->is_input()) {
if (!port->port_group().empty())
{
for (auto &pg: this->port_groups_)
{
if (pg->uri() == port->port_group())
{
if (!pg->sideChainOf().empty())
{
port->is_sidechain(true);
audio_sidechain_title_ = pg->name();
if (last_sidechain_title.empty()) {
last_sidechain_title = pg->name();
}
else if (last_sidechain_title != pg->name()) {
// multiple sidechain titles!
isValid = false;
Lv2Log::debug("Plugin %s (%s) has multiple sidechain titles (%s and %s).",
this->name_.c_str(),
this->uri_.c_str(),
last_sidechain_title.c_str(),
pg->name().c_str());
}
}
break;
}
}
} else {
if (port->is_sidechain()) {
// port is marked as sidechain, but has no port group.
if (last_sidechain_title.empty())
{
last_sidechain_title = port->name();
this->audio_sidechain_title_ = port->name();
}
else if (last_sidechain_title != port->name()) {
isValid = false;
Lv2Log::debug("Plugin %s (%s) has multiple sidechain ports, but no portgroup. (%s)",
this->name_.c_str(),
this->uri_.c_str(),
port->name().c_str());
}
}
}
}
}
// Fetch patch properties.
{
AutoLilvNodes patchWritables = lilv_world_find_nodes(pWorld, plugUri, lv2Host->lilvUris->patch__writable, nullptr);
@@ -1034,17 +1096,28 @@ Lv2PluginInfo::Lv2PluginInfo(PluginHost *lv2Host, LilvWorld *pWorld, const LilvP
}
int nInputs = 0;
int nSideChains = 0;
for (size_t i = 0; i < ports_.size(); ++i)
{
auto port = ports_[i];
if (port->is_audio_port() && port->is_input())
{
if (nInputs >= 2 && !port->connection_optional())
{
isValid = false;
break;
if (!port->is_sidechain()) {
if (nInputs >= 2)
{
isValid = false;
Lv2Log::debug("Plugin %s (%s) has more than 2 audio inputs.", this->name_.c_str(), this->uri_.c_str());
break;
}
++nInputs;
} else {
if (nSideChains >= 2) {
Lv2Log::debug("Plugin %s (%s) has more than 2 audio sidechain inputs.", this->name_.c_str(), this->uri_.c_str());
isValid = false;
break;
}
++nSideChains;
}
++nInputs;
}
}
int nOutputs = 0;
@@ -1199,6 +1272,7 @@ Lv2PortInfo::Lv2PortInfo(PluginHost *host, const LilvPlugin *plugin, const LilvP
this->not_on_gui_ = lilv_port_has_property(plugin, pPort, host->lilvUris->portprops__not_on_gui_property_uri);
this->connection_optional_ = lilv_port_has_property(plugin, pPort, host->lilvUris->core__connectionOptional);
this->trigger_property_ = lilv_port_has_property(plugin, pPort, host->lilvUris->portprops__trigger);
this->is_sidechain_ = lilv_port_has_property(plugin,pPort,host->lilvUris->core__isSideChain);
AutoLilvNode port_ledColor = lilv_port_get(plugin, pPort, host->lilvUris->pipedalUI__ledColor);
if (port_ledColor)
@@ -1402,7 +1476,12 @@ Lv2PluginUiInfo::Lv2PluginUiInfo(PluginHost *pHost, const Lv2PluginInfo *plugin)
}
else if (port->is_audio_port())
{
++audio_inputs_;
if (port->is_sidechain()) {
// sidechain outputs don't count.
++audio_side_chain_inputs_;
} else {
++audio_inputs_;
}
}
}
else if (port->is_output())
@@ -1918,12 +1997,29 @@ Lv2PortGroup::Lv2PortGroup(PluginHost *lv2Host, const std::string &groupUri)
{
LilvWorld *pWorld = lv2Host->pWorld;
// Enumerate all Lilv nodes for groupUri that have an 'a' predicate
this->uri_ = groupUri;
AutoLilvNode uri = lilv_new_uri(pWorld, groupUri.c_str());
AutoLilvNode aPredicate = lilv_new_uri(pWorld, "http://www.w3.org/1999/02/22-rdf-syntax-ns#type");
AutoLilvNodes types = lilv_world_find_nodes(pWorld, uri, aPredicate, nullptr);
LILV_FOREACH(nodes, iNode, types)
{
AutoLilvNode typeNode = lilv_nodes_get(types, iNode);
std::string typeUri = nodeAsString(typeNode);
this->isA_.push_back(std::move(typeUri));
}
AutoLilvNode symbolNode = lilv_world_get(pWorld, uri, lv2Host->lilvUris->lv2core__symbol, nullptr);
symbol_ = nodeAsString(symbolNode);
AutoLilvNode nameNode = lilv_world_get(pWorld, uri, lv2Host->lilvUris->lv2core__name, nullptr);
name_ = nodeAsString(nameNode);
AutoLilvNode sideChainOfNode = lilv_world_get(pWorld, uri, lv2Host->lilvUris->port_groups__sideChainOf, nullptr);
sideChainOf_ = nodeAsString(sideChainOfNode);
}
bool Lv2PluginInfo::isSplit() const
@@ -2187,6 +2283,7 @@ json_map::storage_type<Lv2PortInfo> Lv2PortInfo::jmap{
json_map::reference("is_input", &Lv2PortInfo::is_input_),
json_map::reference("is_output", &Lv2PortInfo::is_output_),
json_map::reference("is_sidechain", &Lv2PortInfo::is_sidechain_),
json_map::reference("is_control_port", &Lv2PortInfo::is_control_port_),
json_map::reference("is_audio_port", &Lv2PortInfo::is_audio_port_),
@@ -2226,7 +2323,11 @@ json_map::storage_type<Lv2PortInfo> Lv2PortInfo::jmap{
json_map::storage_type<Lv2PortGroup> Lv2PortGroup::jmap{{
MAP_REF(Lv2PortGroup, isA),
MAP_REF(Lv2PortGroup, uri),
MAP_REF(Lv2PortGroup, name),
MAP_REF(Lv2PortGroup, symbol),
MAP_REF(Lv2PortGroup, sideChainOf),
}};
@@ -2281,6 +2382,7 @@ json_map::storage_type<Lv2PluginUiPort> Lv2PluginUiPort::jmap{{
json_map::reference("name", &Lv2PluginUiPort::name_),
MAP_REF(Lv2PluginUiPort, index),
MAP_REF(Lv2PluginUiPort, is_input),
MAP_REF(Lv2PluginUiPort, is_sidechain),
MAP_REF(Lv2PluginUiPort, min_value),
MAP_REF(Lv2PluginUiPort, max_value),
MAP_REF(Lv2PluginUiPort, default_value),
@@ -2324,7 +2426,9 @@ json_map::storage_type<Lv2PluginUiInfo>
json_map::reference("author_name", &Lv2PluginUiInfo::author_name_),
json_map::reference("author_homepage", &Lv2PluginUiInfo::author_homepage_),
json_map::reference("audio_inputs", &Lv2PluginUiInfo::audio_inputs_),
json_map::reference("audio_side_chain_inputs", &Lv2PluginUiInfo::audio_side_chain_inputs_),
json_map::reference("audio_outputs", &Lv2PluginUiInfo::audio_outputs_),
json_map::reference("audio_side_chain_title", &Lv2PluginUiInfo::audio_side_chain_title_),
json_map::reference("description", &Lv2PluginUiInfo::description_),
json_map::reference("has_midi_input", &Lv2PluginUiInfo::has_midi_input_),
json_map::reference("has_midi_output", &Lv2PluginUiInfo::has_midi_output_),
+61 -49
View File
@@ -33,7 +33,7 @@
#include <set>
#include "ModGui.hpp"
//#include "lv2.h"
// #include "lv2.h"
#include "Units.hpp"
#include "PluginPreset.hpp"
@@ -206,6 +206,7 @@ namespace pipedal
bool is_control_port_ = false;
bool is_audio_port_ = false;
bool is_sidechain_ = false;
bool is_atom_port_ = false;
bool is_cv_port_ = false;
bool connection_optional_ = false;
@@ -287,6 +288,7 @@ namespace pipedal
LV2_PROPERTY_GETSET_SCALAR(is_output);
LV2_PROPERTY_GETSET_SCALAR(is_control_port);
LV2_PROPERTY_GETSET_SCALAR(is_audio_port);
LV2_PROPERTY_GETSET_SCALAR(is_sidechain);
LV2_PROPERTY_GETSET_SCALAR(is_atom_port);
LV2_PROPERTY_GETSET_SCALAR(connection_optional);
LV2_PROPERTY_GETSET_SCALAR(is_cv_port);
@@ -321,7 +323,6 @@ namespace pipedal
return Lv2BufferType::Unknown;
}
public:
Lv2PortInfo() {}
~Lv2PortInfo() = default;
@@ -330,7 +331,8 @@ namespace pipedal
static json_map::storage_type<Lv2PortInfo> jmap;
};
class Lv2PatchPropertyInfo {
class Lv2PatchPropertyInfo
{
private:
std::string uri_;
@@ -343,9 +345,10 @@ namespace pipedal
std::string shortName_;
std::vector<std::string> fileTypes_;
std::vector<std::string> supportedExtensions_;
public:
Lv2PatchPropertyInfo() {}
Lv2PatchPropertyInfo(PluginHost *pluginHost, const LilvNode *propertyUri);
Lv2PatchPropertyInfo(PluginHost *pluginHost, const LilvNode *propertyUri);
LV2_PROPERTY_GETSET(uri);
LV2_PROPERTY_GETSET(writable);
@@ -359,21 +362,24 @@ namespace pipedal
LV2_PROPERTY_GETSET(supportedExtensions);
DECLARE_JSON_MAP(Lv2PatchPropertyInfo);
};
class Lv2PortGroup
{
private:
std::vector<std::string> isA_;
std::string uri_;
std::string symbol_;
std::string name_;
std::string sideChainOf_;
public:
LV2_PROPERTY_GETSET(isA);
LV2_PROPERTY_GETSET(uri);
LV2_PROPERTY_GETSET(symbol);
LV2_PROPERTY_GETSET(name);
LV2_PROPERTY_GETSET(sideChainOf);
Lv2PortGroup() {}
Lv2PortGroup(PluginHost *lv2Host, const std::string &groupUri);
@@ -389,10 +395,11 @@ namespace pipedal
using ptr = std::shared_ptr<Lv2PluginInfo>;
Lv2PluginInfo(PluginHost *lv2Host, LilvWorld *pWorld, const LilvPlugin *);
Lv2PluginInfo() {}
bool isSplit() const ;
bool isSplit() const;
private:
struct FindWritablePathPropertiesResult {
struct FindWritablePathPropertiesResult
{
std::shared_ptr<PiPedalUI> pipedalUi;
bool hasUnsupportedPatchProperties = false;
};
@@ -406,7 +413,7 @@ namespace pipedal
std::string name_;
std::string plugin_class_;
std::string brand_;
std::string label_;
std::string label_;
std::vector<std::string> supported_features_;
std::vector<std::string> required_features_;
std::vector<std::string> optional_features_;
@@ -420,9 +427,9 @@ namespace pipedal
std::vector<std::shared_ptr<Lv2PortInfo>> ports_;
std::vector<std::shared_ptr<Lv2PortGroup>> port_groups_;
std::vector<Lv2PatchPropertyInfo> patchProperties_;
std::string audio_sidechain_title_;
bool hasDefaultState_;
bool is_valid_ = false;
PiPedalUI::ptr piPedalUI_;
ModGui::ptr modGui_;
@@ -463,6 +470,7 @@ namespace pipedal
LV2_PROPERTY_GETSET(minBlockLength)
LV2_PROPERTY_GETSET(maxBlockLength)
LV2_PROPERTY_GETSET(powerOf2BlockLength)
LV2_PROPERTY_GETSET(audio_sidechain_title)
bool WantsWorkerThread() const;
@@ -529,9 +537,11 @@ namespace pipedal
class Lv2PluginUiPortGroup
{
private:
std::vector<std::string> isA_;
std::string uri_;
std::string symbol_;
std::string name_;
std::string sideChainOf_;
std::string parent_group_;
int32_t program_list_id_ = -1; // used by VST3.
@@ -546,9 +556,7 @@ namespace pipedal
public:
Lv2PluginUiPortGroup() {}
Lv2PluginUiPortGroup(Lv2PortGroup *pPortGroup)
: symbol_(pPortGroup->symbol())
, name_(pPortGroup->name())
, uri_(pPortGroup->uri())
: isA_(pPortGroup->isA()), symbol_(pPortGroup->symbol()), name_(pPortGroup->name()), uri_(pPortGroup->uri()), sideChainOf_(pPortGroup->sideChainOf())
{
}
Lv2PluginUiPortGroup(
@@ -570,14 +578,18 @@ namespace pipedal
}
Lv2PluginUiPort(const Lv2PluginInfo *pPlugin, const Lv2PortInfo *pPort)
: symbol_(pPort->symbol()), index_(pPort->index()),
is_input_(pPort->is_input()), name_(pPort->name()), min_value_(pPort->min_value()), max_value_(pPort->max_value()),
is_input_(pPort->is_input()),
is_sidechain_(pPort->is_sidechain()),
name_(pPort->name()),
min_value_(pPort->min_value()),
max_value_(pPort->max_value()),
default_value_(pPort->default_value()), range_steps_(pPort->range_steps()), display_priority_(pPort->display_priority()),
is_logarithmic_(pPort->is_logarithmic()),
integer_property_(pPort->integer_property()),
is_logarithmic_(pPort->is_logarithmic()),
integer_property_(pPort->integer_property()),
mod_momentaryOffByDefault_(pPort->mod_momentaryOffByDefault()),
mod_momentaryOnByDefault_(pPort->mod_momentaryOnByDefault()),
pipedal_graphicEq_(pPort->pipedal_graphicEq()),
enumeration_property_(pPort->enumeration_property()),
toggled_property_(pPort->toggled_property()), not_on_gui_(pPort->not_on_gui()), scale_points_(pPort->scale_points()),
trigger_property_(pPort->trigger_property()),
@@ -606,7 +618,7 @@ namespace pipedal
}
}
}
is_bypass_ =
is_bypass_ =
pPort->is_bypass() ||
name_ == "bypass" || name_ == "Bypass" || symbol_ == "bypass" || symbol_ == "Bypass";
}
@@ -616,6 +628,7 @@ namespace pipedal
int index_;
std::string name_;
bool is_input_ = true;
bool is_sidechain_ = false;
float min_value_ = 0, max_value_ = 1, default_value_ = 0;
bool is_logarithmic_ = false;
int display_priority_ = -1;
@@ -682,7 +695,8 @@ namespace pipedal
std::string uri_;
std::string name_;
uint32_t minorVersion_ = 0;
uint32_t microVersion_ = 0;;
uint32_t microVersion_ = 0;
;
std::string brand_;
std::string label_;
std::string author_name_;
@@ -690,12 +704,17 @@ namespace pipedal
PluginType plugin_type_;
std::string plugin_display_type_;
int audio_inputs_ = 0;
int audio_side_chain_inputs_ = 0;
int audio_outputs_ = 0;
std::string audio_side_chain_title_;
int has_midi_input_ = false;
int has_midi_output_ = false;
std::string description_;
bool is_vst3_ = false;
int64_t side_chain_input_id = -1;
std::vector<Lv2PluginUiPort> controls_;
std::vector<Lv2PluginUiPortGroup> port_groups_;
std::vector<UiFileProperty::ptr> fileProperties_;
@@ -704,7 +723,6 @@ namespace pipedal
ModGui::ptr modGui_;
std::vector<Lv2PatchPropertyInfo> patchProperties_;
public:
LV2_PROPERTY_GETSET(uri)
LV2_PROPERTY_GETSET(name)
@@ -718,6 +736,7 @@ namespace pipedal
LV2_PROPERTY_GETSET(plugin_display_type)
LV2_PROPERTY_GETSET_SCALAR(audio_inputs)
LV2_PROPERTY_GETSET_SCALAR(audio_outputs)
LV2_PROPERTY_GETSET(audio_side_chain_title)
LV2_PROPERTY_GETSET_SCALAR(has_midi_input)
LV2_PROPERTY_GETSET_SCALAR(has_midi_output)
LV2_PROPERTY_GETSET_SCALAR(description)
@@ -729,6 +748,7 @@ namespace pipedal
LV2_PROPERTY_GETSET(uiPortNotifications)
LV2_PROPERTY_GETSET(modGui)
LV2_PROPERTY_GETSET(patchProperties)
LV2_PROPERTY_GETSET_SCALAR(audio_side_chain_inputs)
static json_map::storage_type<Lv2PluginUiInfo> jmap;
};
@@ -772,6 +792,7 @@ namespace pipedal
AutoLilvNode enumeration_property_uri;
AutoLilvNode core__toggled;
AutoLilvNode core__connectionOptional;
AutoLilvNode core__isSideChain;
AutoLilvNode portprops__not_on_gui_property_uri;
AutoLilvNode portprops__trigger;
AutoLilvNode midi__event;
@@ -782,9 +803,9 @@ namespace pipedal
AutoLilvNode invada_units__unit; // typo in invada plugins.
AutoLilvNode invada_portprops__logarithmic; // typo in invada plugins.
AutoLilvNode atom__bufferType;
AutoLilvNode atom__Path;
AutoLilvNode atom__String;
AutoLilvNode presets__preset;
AutoLilvNode state__state;
AutoLilvNode rdfs__label;
@@ -816,7 +837,6 @@ namespace pipedal
AutoLilvNode pipedalUI__width;
AutoLilvNode pipedalUI__graphicEq;
AutoLilvNode pipedalUI__outputPorts;
AutoLilvNode pipedalUI__text;
@@ -846,7 +866,6 @@ namespace pipedal
AutoLilvNode patch__readable;
AutoLilvNode pipedal_patch__readable;
AutoLilvNode mod__brand;
AutoLilvNode mod__label;
AutoLilvNode mod__preferMomentaryOffByDefault;
@@ -861,11 +880,9 @@ namespace pipedal
AutoLilvNode buf_size__maxBlockLength;
AutoLilvNode buf_size__fixedBlockLength;
AutoLilvNode buf_size__coarseBlockLength;
AutoLilvNode port_groups__sideChainOf;
};
LilvUris* lilvUris = nullptr;
LilvUris *lilvUris = nullptr;
private:
bool vst3Enabled = true;
@@ -896,7 +913,7 @@ namespace pipedal
void free_world();
std::vector<std::shared_ptr<Lv2PluginInfo>> plugins_;
std::map<std::string,std::shared_ptr<Lv2PluginInfo>> pluginsByUri;
std::map<std::string, std::shared_ptr<Lv2PluginInfo>> pluginsByUri;
std::vector<Lv2PluginUiInfo> ui_plugins_;
std::map<std::string, std::shared_ptr<Lv2PluginClass>> classesMap;
@@ -947,22 +964,19 @@ namespace pipedal
public:
virtual MapFeature &GetMapFeature() override { return this->mapFeature; }
void CheckForResourceInitialization(const std::string& pluginUri,const std::filesystem::path& pluginUploadDirectory);
void CheckForResourceInitialization(const std::string &pluginUri, const std::filesystem::path &pluginUploadDirectory);
std::string MapResourcePath(const std::string&uri, const std::string&relativePath);
std::string MapResourcePath(const std::string &uri, const std::string &relativePath);
void ReloadPlugins();
// equivalent to LV2 MapPath AbstractPath features.
std::string MapPath(const std::string &abstractPath);
std::string AbstractPath(const std::string&path);
json_variant MapPath(const json_variant&json);
json_variant AbstractPath(const json_variant&json);
std::string AbstractPath(const std::string &path);
json_variant MapPath(const json_variant &json);
json_variant AbstractPath(const json_variant &json);
private:
std::set<std::string> pluginsThatHaveBeenCheckedForResources;
void CheckForResourceInitization(const std::string pluginUri);
@@ -973,12 +987,11 @@ namespace pipedal
}
static void PortValueCallback(const char *symbol, void *user_data, const void *value, uint32_t size, uint32_t type);
static void StateRestoreCallback(
const char* port_symbol,
void* user_data,
const void* value,
uint32_t size,
uint32_t type);
const char *port_symbol,
void *user_data,
const void *value,
uint32_t size,
uint32_t type);
virtual IEffect *CreateEffect(PedalboardItem &pedalboardItem);
void LoadPluginClassesFromLilv();
@@ -995,9 +1008,9 @@ namespace pipedal
IHost *asIHost() { return this; }
virtual Lv2Pedalboard *CreateLv2Pedalboard(Pedalboard &pedalboard,Lv2PedalboardErrorList &errorList);
virtual Lv2Pedalboard *CreateLv2Pedalboard(Pedalboard &pedalboard, Lv2PedalboardErrorList &errorList);
virtual Lv2Pedalboard *UpdateLv2PedalboardStructure(Pedalboard &pedalboard,Lv2Pedalboard *existingPedalboard,Lv2PedalboardErrorList &errorList);
virtual Lv2Pedalboard *UpdateLv2PedalboardStructure(Pedalboard &pedalboard, Lv2Pedalboard *existingPedalboard, Lv2PedalboardErrorList &errorList);
void setSampleRate(double sampleRate)
{
@@ -1010,9 +1023,8 @@ namespace pipedal
class Urids;
Urids *urids = nullptr;
ModGuiUris* mod_gui_uris = nullptr;
Urids *urids = nullptr;
ModGuiUris *mod_gui_uris = nullptr;
void OnConfigurationChanged(const JackConfiguration &configuration, const JackChannelSelection &settings);
@@ -1021,7 +1033,7 @@ namespace pipedal
std::shared_ptr<Lv2PluginClass> GetLv2PluginClass() const;
const std::vector<std::shared_ptr<Lv2PluginInfo>>& GetPlugins() const { return plugins_; }
const std::vector<std::shared_ptr<Lv2PluginInfo>> &GetPlugins() const { return plugins_; }
const std::vector<Lv2PluginUiInfo> &GetUiPlugins() const { return ui_plugins_; }
virtual std::shared_ptr<Lv2PluginInfo> GetPluginInfo(const std::string &uri) const;
+14 -2
View File
@@ -1717,7 +1717,7 @@ void Storage::ToAbstractPaths(PluginPreset &pluginPreset)
std::map<std::string, std::string> newPathProperties;
for (const auto &pair : pluginPreset.pathProperties_)
{
newPathProperties[pair.first] = this->ToAbstractPathJson(pair.second);
newPathProperties[pair.first] = this->ToAbstractPathFromJson(pair.second);
}
pluginPreset.pathProperties_ = newPathProperties;
}
@@ -2999,7 +2999,19 @@ std::string Storage::GetTone3000Auth() const
return tone3000Auth;
}
std::string Storage::ToAbstractPathJson(const std::string &pathJson)
std::filesystem::path Storage::FromAbstractPathString(const std::string &stringPath)
{
if (stringPath.empty()) {
return "";
}
fs::path path { stringPath};
if (path.is_absolute()) {
return path;
}
return GetPluginUploadDirectory() / path;
}
std::string Storage::ToAbstractPathFromJson(const std::string &pathJson)
{
json_variant v = json_variant::parse(pathJson);
+2 -1
View File
@@ -129,8 +129,9 @@ public:
const std::filesystem::path &GetPluginUploadDirectory() const;
std::string ToAbstractPathJson(const std::string &jsonAtomPath);
std::string ToAbstractPathFromJson(const std::string &jsonAtomPath);
std::string FromAbstractPathJson(const std::string &jsonAtomPath);
std::filesystem::path FromAbstractPathString(const std::string &stringPath);
//std::vector<std::string> GetPedalboards();
+1 -1
View File
@@ -520,7 +520,7 @@ public:
res.set(HttpField::content_type, "application/json");
res.set(HttpField::cache_control, "no-cache");
fs::path path = model->GetStorage().FromAbstractPathJson(request_uri.query("path"));
fs::path path = model->GetStorage().FromAbstractPathString(request_uri.query("path"));
if (!fs::exists(path) || !this->model->IsInUploadsDirectory(path) || HasDotDot(path))
{