Toob Flanger, Stereo Reverb, bug fixes

- support state in factory presets.
- Use uncompressed json for settings files.
- Set current plugin preset name to last loaded plugin preset.
- Append plugins after  start node,  before end node fixed.
- Correctly release web socket when web app goes idle.
- Fit and finish issues.
This commit is contained in:
Robin Davies
2023-06-07 04:57:13 -04:00
parent 9dcbf3d00b
commit a3dca9fa5c
133 changed files with 2715 additions and 998 deletions
+1 -1
View File
@@ -25,7 +25,7 @@
#pragma once
#include <vector>
#include "lv2/atom.lv2/atom.h"
#include "lv2/atom/atom.h"
namespace pipedal
{
+1 -1
View File
@@ -28,7 +28,7 @@
#include <sstream>
#include "json.hpp"
#include "ss.hpp"
#include "lv2/atom.lv2/util.h"
#include "lv2/atom/util.h"
using namespace pipedal;
+2 -2
View File
@@ -27,8 +27,8 @@
#include <cstddef>
#include <vector>
#include "MapFeature.hpp"
#include "lv2/atom.lv2/atom.h"
#include "lv2/atom.lv2/forge.h"
#include "lv2/atom/atom.h"
#include "lv2/atom/forge.h"
#include "json_variant.hpp"
namespace pipedal
+2 -2
View File
@@ -27,8 +27,8 @@
#include "json.hpp"
#include "json_variant.hpp"
#include "AtomConverter.hpp"
#include "lv2/atom.lv2/util.h"
#include "lv2/patch.lv2/patch.h"
#include "lv2/atom/util.h"
#include "lv2/patch/patch.h"
#include "MapFeature.hpp"
#include <cstring>
+1 -1
View File
@@ -188,7 +188,7 @@ private:
static constexpr size_t DEFERRED_MIDI_BUFFER_SIZE = 1024;
uint8_t deferredMidiMessages[DEFERRED_MIDI_BUFFER_SIZE];
size_t deferredMidiMessageCount;
size_t deferredMidiMessageCount = 0;
bool midiProgramChangePending = false;
int selectedBank = -1;
int64_t midiProgramChangeId = 0;
+8
View File
@@ -63,6 +63,14 @@ namespace pipedal
{
Free();
this->node = node;
this->isConst = false;
return *this;
}
AutoLilvNode &operator=(const LilvNode *node)
{
Free();
this->node = const_cast<LilvNode*>(node);
this->isConst = true;
return *this;
}
+21 -2
View File
@@ -80,8 +80,8 @@ endif()
# Use LV2 headers from the /usr/lib directory.
set (LV2DEV_INCLUDE_DIRS /usr/lib)
#set (LV2DEV_INCLUDE_DIRS /usr/lib)
set (LV2DEV_INCLUDE_DIRS )
pkg_check_modules(JACK "jack")
if(!JACK_FOUND)
@@ -284,6 +284,10 @@ add_executable(pipedaltest testMain.cpp
jsonTest.cpp
json_variant.cpp
json_variant.hpp
utilTest.cpp
util.cpp
util.hpp
AlsaDriverTest.cpp
AvahiServiceTest.cpp
@@ -565,6 +569,7 @@ add_test(NAME DevTest COMMAND pipedaltest "[Dev]")
#################################
add_executable(pipedalconfig
PrettyPrinter.hpp
ServiceConfiguration.cpp ServiceConfiguration.hpp
@@ -615,6 +620,20 @@ add_executable(pipedal_latency_test
target_link_libraries(pipedal_latency_test PRIVATE pthread asound
)
add_executable(capturepresets
CapturePresetsMain.cpp
Storage.cpp
Storage.hpp
CommandLineParser.hpp
)
target_include_directories(capturepresets PRIVATE ${PIPEDAL_INCLUDES})
target_link_libraries(capturepresets ${PIPEDAL_LIBS})
add_executable(pipedaladmind AdminMain.cpp CommandLineParser.hpp
UnixSocket.cpp UnixSocket.hpp
ServiceConfiguration.cpp ServiceConfiguration.hpp
+305
View File
@@ -0,0 +1,305 @@
#include "Storage.hpp"
#include "CommandLineParser.hpp"
#include <filesystem>
#include <fstream>
#include "Uri.hpp"
#include "ss.hpp"
#include <iomanip>
#include "util.hpp"
using namespace pipedal;
using namespace std;
using namespace std::filesystem;
Storage storage;
static std::string GetPluginid(const std::string &pluginUrl)
{
uri uri_(pluginUrl);
return uri_.segments()[uri_.segment_count() - 1];
}
static char hexC[] = "0123456788ABCDEF";
static void EncodeTurtleStringBody(std::stringstream&ss,const std::string &value)
{
std::u32string u32String = ToUtf32(value);
for (auto c : u32String)
{
if (c >= ' ' && c <= 0x7F && c != '\"' && c != '\'' && c != '<' && c != '>' && c != '\\' && c != '%')
{
ss << (char)c;
}
else if (c < 0x10000)
{
ss << "\\u"
<< hexC[(c >> 12) & 0x0F]
<< hexC[(c >> 8) & 0x0F]
<< hexC[(c >> 4) & 0x0F]
<< hexC[(c >> 0) & 0x0F];
}
else
{
ss << "\\U"
<< hexC[(c >> 28) & 0x0F]
<< hexC[(c >> 24) & 0x0F]
<< hexC[(c >> 20) & 0x0F]
<< hexC[(c >> 16) & 0x0F]
<< hexC[(c >> 12) & 0x0F]
<< hexC[(c >> 8) & 0x0F]
<< hexC[(c >> 4) & 0x0F]
<< hexC[(c >> 0) & 0x0F];
}
}
}
static std::string EncodeTurtleString(const std::string &value)
{
std::stringstream ss;
ss << '"';
EncodeTurtleStringBody(ss,value);
ss << '"';
return ss.str();
}
static std::string ToString(const std::vector<uint8_t>&value)
{
const char*start = (const char*)(&value[0]);
const char*end = start+value.size();
while (end != start && end[-1] == 0)
{
--end;
}
return std::string(start,end);
}
static std::string EncodeTurtleString(const std::vector<uint8_t> &value)
{
return EncodeTurtleString(ToString(value));
}
static std::string EncodeTurtlePath(const std::string &value)
{
std::stringstream ss;
ss << '<';
EncodeTurtleStringBody(ss,value);
ss << '>';
return ss.str();
}
static std::string EncodeTurtlePath(const std::vector<uint8_t>&value)
{
return EncodeTurtlePath(ToString(value));
}
std::string GetFileName(const std::string &uri)
{
std::string id = GetPluginid(uri);
return id + "-presets.ttl";
}
void WritePrefixes(ofstream &f)
{
f
<< "@prefix lv2: <http://lv2plug.in/ns/lv2core#> ." << endl
<< "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> ." << endl
<< "@prefix pset: <http://lv2plug.in/ns/ext/presets#> ." << endl
<< "@prefix tpset: <http://two-play.com/plugins/preset#> ." << endl
<< "@prefix state: <http://lv2plug.in/ns/ext/state#> ." << endl
<< endl;
}
static void WriteSeeAlso(ofstream &f, const std::string &uri)
{
std::string id = GetPluginid(uri);
PluginPresets presets = storage.GetPluginPresets(uri);
f << "###" << endl;
for (size_t i = 0; i < presets.presets_.size(); ++i)
{
std::string presetId = SS("tpset:" << id << "-preset-" << i);
f << presetId << endl
<< " a pset:Preset ;" << endl
<< " lv2:appliesTo <" << uri << "> ;" << endl
<< " rdfs:seeAlso "
<< "<" << GetFileName(uri) << "> ." << endl;
f << endl;
}
}
std::string EncodeTurtleFloat(float value)
{
int64_t iValue = (int64_t)value;
if (iValue == value)
{
return SS(iValue << ".0");
}
std::stringstream s;
s << setprecision(7) << value;
return s.str();
}
std::string EncodeTurtleDouble(double value)
{
int64_t iValue = (int64_t)value;
std::stringstream s;
s << '\"';
if (iValue == value)
{
if (iValue == value)
{
s << iValue << ".0";
}
} else {
s << setprecision(15) << value;
}
s << "\"^^xsd:double";
return s.str();
}
void WritePresetFile(const path &outputDirectory, const std::string &uri)
{
std::string id = GetPluginid(uri);
path fileName = outputDirectory / GetFileName(uri);
ofstream f(fileName);
if (!f.is_open())
{
throw logic_error(SS("Can't write to " << fileName));
}
cout << "Writing preset file " << fileName << endl;
WritePrefixes(f);
PluginPresets presets = storage.GetPluginPresets(uri);
for (size_t i = 0; i < presets.presets_.size(); ++i)
{
std::string presetId = SS("tpset:" << id << "-preset-" << i);
const auto &preset = presets.presets_[i];
f << presetId << endl
<< " a pset:Preset ; " << endl
<< " lv2:appliesTo <" << uri << "> ;" << endl
<< " rdfs:label " << EncodeTurtleString(preset.label_) << " ;" << endl;
if (preset.controlValues_.size() != 0)
{
f << " lv2:port ";
bool firstTime = true;
for (const auto &value : preset.controlValues_)
{
if (!firstTime)
{
f << " , ";
}
firstTime = false;
f << "[" << endl
<< " lv2:symbol " << EncodeTurtleString(value.first) << " ;" << endl
<< " pset:value " << EncodeTurtleFloat(value.second) << " ;" << endl;
f << " ]";
}
if (preset.state_.isValid_ && preset.state_.values_.size() != 0)
{
f << " ;" << endl;
f << " state:state [" << endl;
for (const auto &stateValue : preset.state_.values_)
{
f << " <"
<< stateValue.first << "> ";
const auto& t = stateValue.second;
if (t.atomType_ == LV2_ATOM__String)
{
f << EncodeTurtleString(t.value_);
} else if (t.atomType_ == LV2_ATOM__Path)
{
// encode it as a string, because Turtle paths have additional sematics.
// The type will be corrected at restore time.
f << EncodeTurtleString(t.value_);
} else if (t.atomType_ == LV2_ATOM__Float)
{
float *v = (float*)(&(t.value_[0]));
f << EncodeTurtleFloat(*v);
} else if (t.atomType_ == LV2_ATOM__Double)
{
double *v = (double*)(&(t.value_[0]));
f << EncodeTurtleDouble(*v);
} else if (t.atomType_ == LV2_ATOM__Int)
{
int32_t *v = (int32_t*)(&t.value_[0]);
f << *v;
} else if (t.atomType_ == LV2_ATOM__Long)
{
int64_t *v = (int64_t*)(&t.value_[0]);
f << *v;
}
f << " ;" << endl;
}
f << " ] ." << endl;
} else {
f << " ." << endl;
}
}
f << endl;
}
}
int main(int argc, const char *argv[])
{
// A utility for the ToobAmp project which converts Pipedal presets to LV2 presets so
// that they can be imported as pre-declared presets respective TTL files.
path outputPath = "/tmp/ToobPresets";
// Capture all ToobAmp plugin presets in LV2 format.
// CommandLineParser commandLineParser;
// commandLineParser.Parse(argc,argv);
try
{
storage.SetConfigRoot("/etc/pipedal/config");
storage.SetDataRoot("/var/pipedal");
storage.Initialize();
const PluginPresetIndex &index = storage.GetPluginPresetIndex();
std::filesystem::create_directories(outputPath);
{
ofstream f(outputPath / "add_to_manifest.ttl");
WritePrefixes(f);
for (const auto &entry : index.entries_)
{
if (entry.pluginUri_.starts_with("http://two-play.com/plugins/"))
{
cout << "Writing manifest.ttl declerations: " << entry.pluginUri_ << endl;
WriteSeeAlso(f, entry.pluginUri_);
}
}
}
for (const auto &entry : index.entries_)
{
if (entry.pluginUri_.starts_with("http://two-play.com/plugins/"))
{
WritePresetFile(outputPath, entry.pluginUri_);
}
}
}
catch (const std::exception &e)
{
cout << "ERROR: " << e.what() << endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
+2 -2
View File
@@ -19,8 +19,8 @@
#pragma once
#include <lv2/urid.lv2/urid.h>
#include <lv2/atom.lv2/atom.h>
#include <lv2/urid/urid.h>
#include <lv2/atom/atom.h>
namespace pipedal {
+7 -7
View File
@@ -19,14 +19,14 @@
#pragma once
#include "lv2.h"
//#include "lv2.h"
#include "lv2/atom.lv2/atom.h"
#include "lv2/atom.lv2/util.h"
#include "lv2/log.lv2/logger.h"
#include "lv2/midi.lv2/midi.h"
#include "lv2/urid.lv2/urid.h"
#include "lv2/atom.lv2/atom.h"
#include "lv2/atom/atom.h"
#include "lv2/atom/util.h"
#include "lv2/log/logger.h"
#include "lv2/midi/midi.h"
#include "lv2/urid/urid.h"
#include "lv2/atom/atom.h"
#include "MapFeature.hpp"
#include <map>
#include <string>
+41 -18
View File
@@ -22,22 +22,22 @@
#include "PiPedalException.hpp"
#include <lv2/lv2plug.in/ns/ext/worker/worker.h>
#include <lilv/lilv.h>
#include "lv2/atom.lv2/atom.h"
#include "lv2/atom.lv2/util.h"
#include "lv2.h"
#include "lv2/log.lv2/log.h"
#include "lv2/log.lv2/logger.h"
#include "lv2/midi.lv2/midi.h"
#include "lv2/urid.lv2/urid.h"
#include "lv2/log.lv2/logger.h"
#include "lv2/uri-map.lv2/uri-map.h"
#include "lv2/atom.lv2/forge.h"
#include "lv2/state.lv2/state.h"
#include "lv2/worker.lv2/worker.h"
#include "lv2/patch.lv2/patch.h"
#include "lv2/parameters.lv2/parameters.h"
#include "lv2/units.lv2/units.h"
#include "lv2/atom.lv2/util.h"
#include "lv2/atom/atom.h"
#include "lv2/atom/util.h"
//#include "lv2.h"
#include "lv2/log/log.h"
#include "lv2/log/logger.h"
#include "lv2/midi/midi.h"
#include "lv2/urid/urid.h"
#include "lv2/log/logger.h"
#include "lv2/uri-map/uri-map.h"
#include "lv2/atom/forge.h"
#include "lv2/state/state.h"
#include "lv2/worker/worker.h"
#include "lv2/patch/patch.h"
#include "lv2/parameters/parameters.h"
#include "lv2/units/units.h"
#include "lv2/atom/util.h"
#include "AudioHost.hpp"
#include <exception>
#include "RingBufferReader.hpp"
@@ -49,7 +49,7 @@ const float BYPASS_TIME_S = 0.1f;
Lv2Effect::Lv2Effect(
IHost *pHost_,
const std::shared_ptr<Lv2PluginInfo> &info_,
const PedalboardItem &pedalboardItem)
PedalboardItem &pedalboardItem)
: pHost(pHost_), pInstance(nullptr), info(info_), urids(pHost)
{
auto pWorld = pHost_->getWorld();
@@ -144,7 +144,26 @@ Lv2Effect::Lv2Effect(
PreparePortIndices();
ConnectControlPorts();
RestoreState(pedalboardItem);
if (!pedalboardItem.lilvPresetUri().empty())
{
AutoLilvNode presetNode = lilv_new_uri(pWorld,pedalboardItem.lilvPresetUri().c_str());
lilv_world_load_resource(pWorld,presetNode);
LilvState*pState = lilv_state_new_from_world(pWorld,pHost->GetMapFeature().GetMap(),presetNode);
if (pState)
{
if (this->stateInterface)
{
this->stateInterface->RestoreState(pState);
}
lilv_state_free(pState);
}
// now that we've loaded the preset, clear the uri, and save new state
// Why? because lilv doesn't provide facilities for reading state.
pedalboardItem.lv2State(this->stateInterface->Save());
pedalboardItem.lilvPresetUri("");
} else {
RestoreState(pedalboardItem);
}
}
void Lv2Effect::RestoreState(const PedalboardItem&pedalboardItem)
{
@@ -412,6 +431,9 @@ void Lv2Effect::Run(uint32_t samples,RealtimeRingBufferWriter *realtimeRingBuffe
{
CopyBuffer(this->inputAudioBuffers[0], this->outputAudioBuffers[0], samples);
CopyBuffer(this->inputAudioBuffers[0], this->outputAudioBuffers[1], samples);
} else {
CopyBuffer(this->inputAudioBuffers[0], this->outputAudioBuffers[0], samples);
CopyBuffer(this->inputAudioBuffers[1], this->outputAudioBuffers[1], samples);
}
}
} // else leave the output alone.
@@ -667,6 +689,7 @@ bool Lv2Effect::GetLv2State(Lv2PluginState*state)
state->Erase();
return false;
}
*state = this->stateInterface->Save();
state->isValid_ = true;
return true;
+5 -5
View File
@@ -27,11 +27,11 @@
#include "IEffect.hpp"
#include "Worker.hpp"
#include "lv2/patch.lv2/patch.h"
#include "lv2/log.lv2/log.h"
#include "lv2/log.lv2/logger.h"
#include "lv2/patch/patch.h"
#include "lv2/log/log.h"
#include "lv2/log/logger.h"
#include "lv2/lv2plug.in/ns/extensions/units/units.h"
#include "lv2/atom.lv2/forge.h"
#include "lv2/atom/forge.h"
#include "AtomBuffer.hpp"
#include "StateInterface.hpp"
#include "LogFeature.hpp"
@@ -202,7 +202,7 @@ namespace pipedal
Lv2Effect(
IHost *pHost,
const std::shared_ptr<Lv2PluginInfo> &info,
const PedalboardItem &pedalboardItem);
PedalboardItem &pedalboardItem);
~Lv2Effect();
bool HasErrorMessage() const { return this->hasErrorMessage; }
+3 -3
View File
@@ -42,9 +42,9 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include <stdint.h>
#include <cassert>
#include <lv2.h>
#include <lv2/atom.lv2/atom.h>
#include <lv2/midi.lv2/midi.h>
#include <lv2/urid.lv2/urid.h>
#include <lv2/atom/atom.h>
#include <lv2/midi/midi.h>
#include <lv2/urid/urid.h>
#include <cstring>
namespace pipedal
+74 -60
View File
@@ -21,7 +21,6 @@
#include "Lv2Pedalboard.hpp"
#include "Lv2Effect.hpp"
#include "SplitEffect.hpp"
#include "RingBufferReader.hpp"
#include "VuUpdate.hpp"
@@ -46,8 +45,6 @@ std::vector<float *> Lv2Pedalboard::AllocateAudioBuffers(int nChannels)
return result;
}
int Lv2Pedalboard::GetControlIndex(uint64_t instanceId, const std::string &symbol)
{
for (int i = 0; i < realtimeEffects.size(); ++i)
@@ -63,8 +60,7 @@ int Lv2Pedalboard::GetControlIndex(uint64_t instanceId, const std::string &symbo
std::vector<float *> Lv2Pedalboard::PrepareItems(
std::vector<PedalboardItem> &items,
std::vector<float *> inputBuffers,
Lv2PedalboardErrorList&errorList
)
Lv2PedalboardErrorList &errorList)
{
for (int i = 0; i < items.size(); ++i)
{
@@ -88,14 +84,17 @@ std::vector<float *> Lv2Pedalboard::PrepareItems(
this->processActions.push_back(preMixAction);
std::vector<float *> topResult = PrepareItems(item.topChain(), topInputs,errorList);
std::vector<float *> bottomResult = PrepareItems(item.bottomChain(), bottomInputs,errorList);
std::vector<float *> topResult = PrepareItems(item.topChain(), topInputs, errorList);
std::vector<float *> bottomResult = PrepareItems(item.bottomChain(), bottomInputs, errorList);
this->processActions.push_back(
[pSplit](uint32_t frames)
{ pSplit->PostMix(frames); });
auto controlValue = item.GetControlValue("splitType");
// if split is L/R, always output stereo.
pSplit->SetChainBuffers(topInputs, bottomInputs, topResult, bottomResult);
bool forceStereo = (controlValue != nullptr && controlValue->value() == 2);
pSplit->SetChainBuffers(topInputs, bottomInputs, topResult, bottomResult,forceStereo);
for (int i = 0; i < item.controlValues().size(); ++i)
{
@@ -110,22 +109,25 @@ std::vector<float *> Lv2Pedalboard::PrepareItems(
else
{
IEffect* pLv2Effect = nullptr;
try {
pLv2Effect = this->pHost->CreateEffect(item);
} catch (const std::exception &e)
IEffect *pLv2Effect = nullptr;
try
{
pLv2Effect = this->pHost->CreateEffect(item);
}
catch (const std::exception &e)
{
Lv2Log::warning(SS(e.what()));
}
if (pLv2Effect->HasErrorMessage())
{
std::string error = pLv2Effect->TakeErrorMessage();
Lv2Log::error(error);
errorList.push_back({item.instanceId(), error});
}
if (pLv2Effect)
{
if (pLv2Effect->HasErrorMessage())
{
std::string error = pLv2Effect->TakeErrorMessage();
Lv2Log::error(error);
errorList.push_back({item.instanceId(), error});
}
pEffect = pLv2Effect;
uint64_t instanceId = pEffect->GetInstanceId();
@@ -161,9 +163,9 @@ std::vector<float *> Lv2Pedalboard::PrepareItems(
}
this->processActions.push_back(
[pLv2Effect,this](uint32_t frames)
[pLv2Effect, this](uint32_t frames)
{
pLv2Effect->Run(frames,this->ringBufferWriter);
pLv2Effect->Run(frames, this->ringBufferWriter);
});
}
}
@@ -224,17 +226,16 @@ void Lv2Pedalboard::Prepare(IHost *pHost, Pedalboard &pedalboard, Lv2PedalboardE
outputVolume.SetSampleRate((float)(this->pHost->GetSampleRate()));
inputVolume.SetMinDb(-60);
outputVolume.SetMinDb(-60);
inputVolume.SetTarget(pedalboard.input_volume_db());
outputVolume.SetTarget(pedalboard.output_volume_db());
for (int i = 0; i < pHost->GetNumberOfInputAudioChannels(); ++i)
{
this->pedalboardInputBuffers.push_back(bufferPool.AllocateBuffer<float>(pHost->GetMaxAudioBufferSize()));
}
auto outputs = PrepareItems(pedalboard.items(), this->pedalboardInputBuffers,errorList);
auto outputs = PrepareItems(pedalboard.items(), this->pedalboardInputBuffers, errorList);
int nOutputs = pHost->GetNumberOfOutputAudioChannels();
if (nOutputs == 1)
{
@@ -265,7 +266,9 @@ void Lv2Pedalboard::PrepareMidiMap(const PedalboardItem &pedalboardItem)
if (pluginInfo == nullptr && pedalboardItem.uri() == SPLIT_PEDALBOARD_ITEM_URI)
{
pPluginInfo = GetSplitterPluginInfo();
} else {
}
else
{
pPluginInfo = pluginInfo.get();
}
@@ -277,24 +280,26 @@ void Lv2Pedalboard::PrepareMidiMap(const PedalboardItem &pedalboardItem)
{
auto &binding = pedalboardItem.midiBindings()[bindingIndex];
{
const Lv2PortInfo*pPortInfo;
const Lv2PortInfo *pPortInfo;
int controlIndex;
if (binding.symbol() == "__bypass")
{
pPortInfo = GetBypassPortInfo();
controlIndex = -1;
} else {
try {
}
else
{
try
{
pPortInfo = &pluginInfo->getPort(binding.symbol());
controlIndex = this->GetControlIndex(pedalboardItem.instanceId(), binding.symbol());
} catch (const std::exception&ignored)
}
catch (const std::exception &ignored)
{
continue;
}
}
MidiMapping mapping;
mapping.pluginInfo = pluginInfo; // for lifetime management. <shrugs> We're holding internal pointers to this. May save us in a disorderly shutdown.
mapping.pPortInfo = pPortInfo;
@@ -384,14 +389,14 @@ static void Copy(float *input, float *output, uint32_t samples)
output[i] = input[i];
}
}
bool Lv2Pedalboard::Run(float **inputBuffers, float **outputBuffers, uint32_t samples, RealtimeRingBufferWriter*ringBufferWriter)
bool Lv2Pedalboard::Run(float **inputBuffers, float **outputBuffers, uint32_t samples, RealtimeRingBufferWriter *ringBufferWriter)
{
this->ringBufferWriter = ringBufferWriter;
for (size_t i = 0; i < this->pedalboardInputBuffers.size(); ++i)
{
if (inputBuffers[i] == nullptr)
{
return false;
return false;
}
}
for (size_t i = 0; i < samples; ++i)
@@ -399,7 +404,7 @@ bool Lv2Pedalboard::Run(float **inputBuffers, float **outputBuffers, uint32_t sa
float volume = this->inputVolume.Tick();
for (int c = 0; c < this->pedalboardInputBuffers.size(); ++c)
{
this->pedalboardInputBuffers[c][i] = inputBuffers[c][i]*volume;
this->pedalboardInputBuffers[c][i] = inputBuffers[c][i] * volume;
}
}
for (int i = 0; i < this->processActions.size(); ++i)
@@ -408,10 +413,10 @@ bool Lv2Pedalboard::Run(float **inputBuffers, float **outputBuffers, uint32_t sa
}
for (size_t i = 0; i < this->effects.size(); ++i)
{
IEffect* effect = effects[i].get();
IEffect *effect = effects[i].get();
if (effect->HasErrorMessage())
{
ringBufferWriter->WriteLv2ErrorMessage(effect->GetInstanceId(),effect->TakeErrorMessage());
ringBufferWriter->WriteLv2ErrorMessage(effect->GetInstanceId(), effect->TakeErrorMessage());
}
}
for (size_t i = 0; i < samples; ++i)
@@ -419,7 +424,7 @@ bool Lv2Pedalboard::Run(float **inputBuffers, float **outputBuffers, uint32_t sa
float volume = outputVolume.Tick();
for (size_t c = 0; c < this->pedalboardOutputBuffers.size(); ++c)
{
outputBuffers[c][i] = this->pedalboardOutputBuffers[c][i]*volume;
outputBuffers[c][i] = this->pedalboardOutputBuffers[c][i] * volume;
}
}
return true;
@@ -442,7 +447,7 @@ void Lv2Pedalboard::SetBypass(int effectIndex, bool enabled)
effect->SetBypass(enabled);
}
void Lv2Pedalboard::ComputeVus(RealtimeVuBuffers *vuConfiguration, uint32_t samples, float**inputBuffers, float**outputBuffers)
void Lv2Pedalboard::ComputeVus(RealtimeVuBuffers *vuConfiguration, uint32_t samples, float **inputBuffers, float **outputBuffers)
{
for (size_t i = 0; i < vuConfiguration->enabledIndexes.size(); ++i)
{
@@ -453,25 +458,30 @@ void Lv2Pedalboard::ComputeVus(RealtimeVuBuffers *vuConfiguration, uint32_t samp
if (this->pedalboardInputBuffers.size() > 1)
{
GetInputBuffers();
pUpdate->AccumulateInputs(inputBuffers[0],inputBuffers[1],samples);
pUpdate->AccumulateOutputs(&(this->pedalboardInputBuffers[0][0]),&(this->pedalboardInputBuffers[1][0]),samples); // after outputVolume applied.
} else {
pUpdate->AccumulateInputs(inputBuffers[0],samples);
pUpdate->AccumulateOutputs(&(this->pedalboardInputBuffers[0][0]),samples); // after input volume applied.
pUpdate->AccumulateInputs(inputBuffers[0], inputBuffers[1], samples);
pUpdate->AccumulateOutputs(&(this->pedalboardInputBuffers[0][0]), &(this->pedalboardInputBuffers[1][0]), samples); // after outputVolume applied.
}
} else if (index == Pedalboard::OUTPUT_VOLUME_ID)
else
{
pUpdate->AccumulateInputs(inputBuffers[0], samples);
pUpdate->AccumulateOutputs(&(this->pedalboardInputBuffers[0][0]), samples); // after input volume applied.
}
}
else if (index == Pedalboard::OUTPUT_VOLUME_ID)
{
if (this->pedalboardOutputBuffers.size() > 1)
{
pUpdate->AccumulateInputs(&(this->pedalboardOutputBuffers[0][0]),&(this->pedalboardOutputBuffers[1][0]),samples);
pUpdate->AccumulateOutputs(outputBuffers[0],outputBuffers[1],samples);
} else {
pUpdate->AccumulateInputs(&(this->pedalboardOutputBuffers[0][0]),samples);
pUpdate->AccumulateOutputs(outputBuffers[0],samples);
pUpdate->AccumulateInputs(&(this->pedalboardOutputBuffers[0][0]), &(this->pedalboardOutputBuffers[1][0]), samples);
pUpdate->AccumulateOutputs(outputBuffers[0], outputBuffers[1], samples);
}
else
{
pUpdate->AccumulateInputs(&(this->pedalboardOutputBuffers[0][0]), samples);
pUpdate->AccumulateOutputs(outputBuffers[0], samples);
}
}
else {
else
{
auto effect = this->realtimeEffects[index];
if (effect->GetNumberOfInputAudioPorts() == 1)
@@ -516,22 +526,25 @@ void Lv2Pedalboard::ProcessParameterRequests(RealtimePatchPropertyRequest *pPara
if (pEffect == nullptr)
{
pParameterRequests->errorMessage = "No such effect.";
} else if (pEffect->IsVst3())
}
else if (pEffect->IsVst3())
{
pParameterRequests->errorMessage = "Not supported for VST3 plugins";
} else {
Lv2Effect *pLv2Effect = dynamic_cast<Lv2Effect*>(pEffect);
}
else
{
Lv2Effect *pLv2Effect = dynamic_cast<Lv2Effect *>(pEffect);
if (pParameterRequests->requestType == RealtimePatchPropertyRequest::RequestType::PatchGet)
{
pLv2Effect->RequestPatchProperty(pParameterRequests->uridUri);
} else if (pParameterRequests->requestType == RealtimePatchPropertyRequest::RequestType::PatchSet) {
}
else if (pParameterRequests->requestType == RealtimePatchPropertyRequest::RequestType::PatchSet)
{
pLv2Effect->SetPatchProperty(
pParameterRequests->uridUri,
pParameterRequests->GetSize(),
(LV2_Atom*)pParameterRequests->GetBuffer()
);
(LV2_Atom *)pParameterRequests->GetBuffer());
}
}
pParameterRequests = pParameterRequests->pNext;
@@ -548,13 +561,14 @@ void Lv2Pedalboard::GatherPatchProperties(RealtimePatchPropertyRequest *pParamet
if (effect == nullptr)
{
pParameterRequests->errorMessage = "No such effect.";
} else if (effect->IsVst3())
}
else if (effect->IsVst3())
{
pParameterRequests->errorMessage = "Not supported for VST3";
}
else
{
Lv2Effect *pLv2Effect = dynamic_cast<Lv2Effect*>(effect);
Lv2Effect *pLv2Effect = dynamic_cast<Lv2Effect *>(effect);
pLv2Effect->GatherPatchProperties(pParameterRequests);
}
}
+1 -1
View File
@@ -23,7 +23,7 @@
#include "Lv2Effect.hpp"
#include "BufferPool.hpp"
#include <functional>
#include <lv2/urid.lv2/urid.h>
#include <lv2/urid/urid.h>
#include <functional>
#include "DbDezipper.hpp"
+7 -6
View File
@@ -19,13 +19,14 @@
#pragma once
#include "lv2.h"
//#include "lv2.h"
#include "lv2/log.lv2/log.h"
#include "lv2/log.lv2/logger.h"
#include "lv2/midi.lv2/midi.h"
#include "lv2/urid.lv2/urid.h"
#include "lv2/atom.lv2/atom.h"
#include "lv2/core/lv2.h"
#include "lv2/log/logger.h"
#include "lv2/log/log.h"
#include "lv2/midi/midi.h"
#include "lv2/urid/urid.h"
#include "lv2/atom/atom.h"
#include <map>
#include <string>
#include <mutex>
+1 -1
View File
@@ -18,7 +18,7 @@
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#pragma once
#include "lv2/state.lv2/state.h"
#include "lv2/state/state.h"
#include <filesystem>
#include "MapFeature.hpp"
+13 -13
View File
@@ -19,19 +19,19 @@
#include "pch.h"
#include "OptionsFeature.hpp"
#include "lv2/atom.lv2/atom.h"
#include "lv2/atom.lv2/util.h"
#include "lv2/log.lv2/log.h"
#include "lv2/log.lv2/logger.h"
#include "lv2/midi.lv2/midi.h"
#include "lv2/urid.lv2/urid.h"
#include "lv2/log.lv2/logger.h"
#include "lv2/uri-map.lv2/uri-map.h"
#include "lv2/atom.lv2/forge.h"
#include "lv2/worker.lv2/worker.h"
#include "lv2/patch.lv2/patch.h"
#include "lv2/parameters.lv2/parameters.h"
#include "lv2/units.lv2/units.h"
#include "lv2/atom/atom.h"
#include "lv2/atom/util.h"
#include "lv2/log/log.h"
#include "lv2/log/logger.h"
#include "lv2/midi/midi.h"
#include "lv2/urid/urid.h"
#include "lv2/log/logger.h"
#include "lv2/uri-map/uri-map.h"
#include "lv2/atom/forge.h"
#include "lv2/worker/worker.h"
#include "lv2/patch/patch.h"
#include "lv2/parameters/parameters.h"
#include "lv2/units/units.h"
#include <lv2/lv2plug.in/ns/ext/worker/worker.h>
#include "lv2/lv2plug.in/ns/ext/buf-size/buf-size.h"
+6 -5
View File
@@ -24,7 +24,7 @@
using namespace pipedal;
static const PedalboardItem* GetItem_(const std::vector<PedalboardItem>&items,long pedalboardItemId)
static const PedalboardItem* GetItem_(const std::vector<PedalboardItem>&items,int64_t pedalboardItemId)
{
for (size_t i = 0; i < items.size(); ++i)
{
@@ -64,11 +64,11 @@ std::vector<PedalboardItem*> Pedalboard::GetAllPlugins()
}
const PedalboardItem*Pedalboard::GetItem(long pedalItemId) const
const PedalboardItem*Pedalboard::GetItem(int64_t pedalItemId) const
{
return GetItem_(this->items(),pedalItemId);
}
PedalboardItem*Pedalboard::GetItem(long pedalItemId)
PedalboardItem*Pedalboard::GetItem(int64_t pedalItemId)
{
return const_cast<PedalboardItem*>(GetItem_(this->items(),pedalItemId));
}
@@ -86,7 +86,7 @@ ControlValue* PedalboardItem::GetControlValue(const std::string&symbol)
return nullptr;
}
bool Pedalboard::SetItemEnabled(long pedalItemId, bool enabled)
bool Pedalboard::SetItemEnabled(int64_t pedalItemId, bool enabled)
{
PedalboardItem*item = GetItem(pedalItemId);
if (!item) return false;
@@ -100,7 +100,7 @@ bool Pedalboard::SetItemEnabled(long pedalItemId, bool enabled)
}
bool Pedalboard::SetControlValue(long pedalItemId, const std::string &symbol, float value)
bool Pedalboard::SetControlValue(int64_t pedalItemId, const std::string &symbol, float value)
{
PedalboardItem*item = GetItem(pedalItemId);
if (!item) return false;
@@ -201,6 +201,7 @@ JSON_MAP_BEGIN(PedalboardItem)
JSON_MAP_REFERENCE(PedalboardItem,midiBindings)
JSON_MAP_REFERENCE(PedalboardItem,stateUpdateCount)
JSON_MAP_REFERENCE(PedalboardItem,lv2State)
JSON_MAP_REFERENCE(PedalboardItem,lilvPresetUri)
JSON_MAP_END()
+8 -5
View File
@@ -87,6 +87,7 @@ class PedalboardItem: public JsonMemberWritable {
std::string vstState_;
uint32_t stateUpdateCount_ = 0;
Lv2PluginState lv2State_;
std::string lilvPresetUri_;
public:
ControlValue*GetControlValue(const std::string&symbol);
@@ -104,6 +105,8 @@ public:
GETTER_SETTER_VEC(midiBindings)
GETTER_SETTER(stateUpdateCount)
GETTER_SETTER_REF(lv2State)
Lv2PluginState&lv2State() { return lv2State_; } // non-const version.
GETTER_SETTER_REF(lilvPresetUri)
bool isSplit() const
@@ -142,14 +145,14 @@ class Pedalboard {
public:
static constexpr int64_t INPUT_VOLUME_ID = -2; // synthetic PedalboardItem for input volume.
static constexpr int64_t OUTPUT_VOLUME_ID = -3; // synthetic PedalboardItem for output volume.
bool SetControlValue(long pedalItemId, const std::string &symbol, float value);
bool SetItemEnabled(long pedalItemId, bool enabled);
bool SetControlValue(int64_t pedalItemId, const std::string &symbol, float value);
bool SetItemEnabled(int64_t pedalItemId, bool enabled);
PedalboardItem*GetItem(long pedalItemId);
const PedalboardItem*GetItem(long pedalItemId) const;
PedalboardItem*GetItem(int64_t pedalItemId);
const PedalboardItem*GetItem(int64_t pedalItemId) const;
std::vector<PedalboardItem*>GetAllPlugins();
bool HasItem(long pedalItemid) const { return GetItem(pedalItemid) != nullptr; }
bool HasItem(int64_t pedalItemid) const { return GetItem(pedalItemid) != nullptr; }
GETTER_SETTER_REF(name)
GETTER_SETTER_VEC(items)
+36 -15
View File
@@ -452,9 +452,23 @@ void PiPedalModel::SetControl(int64_t clientId, int64_t pedalItemId, const std::
{
{
std::lock_guard<std::recursive_mutex> lock(mutex);
if (!this->pedalboard.SetControlValue(pedalItemId, symbol, value))
{
return;
}
PedalboardItem*item = pedalboard.GetItem(pedalItemId);
// change of split type requires rebuild of the effect
// since it can change the number of output channels.
if (item != nullptr && item->isSplit() && symbol == "splitType")
{
this->FirePedalboardChanged(clientId);
return;
}
PreviewControl(clientId, pedalItemId, symbol, value);
this->pedalboard.SetControlValue(pedalItemId, symbol, value);
IEffect *pEffect = this->lv2Pedalboard->GetEffect(pedalItemId);
{
@@ -512,6 +526,17 @@ void PiPedalModel::FireBanksChanged(int64_t clientId)
void PiPedalModel::FirePedalboardChanged(int64_t clientId,bool loadAudioThread)
{
if (loadAudioThread)
{
// notify the audio thread.
if (audioHost->IsOpen())
{
LoadCurrentPedalboard();
UpdateRealtimeVuSubscriptions();
UpdateRealtimeMonitorPortSubscriptions();
}
}
// noify subscribers.
IPiPedalModelSubscriber **t = new IPiPedalModelSubscriber *[this->subscribers.size()];
for (size_t i = 0; i < subscribers.size(); ++i)
@@ -525,17 +550,6 @@ void PiPedalModel::FirePedalboardChanged(int64_t clientId,bool loadAudioThread)
}
delete[] t;
if (loadAudioThread)
{
// notify the audio thread.
if (audioHost->IsOpen())
{
LoadCurrentPedalboard();
UpdateRealtimeVuSubscriptions();
UpdateRealtimeMonitorPortSubscriptions();
}
}
}
void PiPedalModel::SetPedalboard(int64_t clientId, Pedalboard &pedalboard)
{
@@ -1725,13 +1739,18 @@ void PiPedalModel::LoadPluginPreset(int64_t pluginInstanceId, uint64_t presetIns
PluginPresetValues presetValues = storage.GetPluginPresetValues(pedalboardItem->uri(), presetInstanceId);
// if the plugin has state, we have to rebuild the pedalboard, since setting state is not thread-safe.
// Same goes if lilvPresetUri is not empty.
// lilvPresetUri: use lilv to load the preset from the RDF model. Occurs when using a factory preset
// that has state:state, because lilv doesn't allow us to read this data, but does load it.
// This is a transient condition.
for (auto&control : presetValues.controls)
{
this->pedalboard.SetControlValue(pluginInstanceId, control.key(), control.value());
}
if (!presetValues.state.isValid_)
if ((!presetValues.state.isValid_) && presetValues.lilvPresetUri.empty())
{
audioHost->SetPluginPreset(pluginInstanceId, presetValues.controls);
@@ -1748,6 +1767,7 @@ void PiPedalModel::LoadPluginPreset(int64_t pluginInstanceId, uint64_t presetIns
delete[] t;
} else {
pedalboardItem->lv2State(presetValues.state);
pedalboardItem->lilvPresetUri(presetValues.lilvPresetUri);
pedalboardItem->stateUpdateCount(oldStateUpdateCount+1);
FirePedalboardChanged(-1); // does a complete reload of both client and audio server.
}
@@ -2003,8 +2023,9 @@ bool PiPedalModel::LoadCurrentPedalboard()
Lv2PedalboardErrorList errorMessages;
std::shared_ptr<Lv2Pedalboard> lv2Pedalboard{this->lv2Host.CreateLv2Pedalboard(this->pedalboard,errorMessages)};
this->lv2Pedalboard = lv2Pedalboard;
// apply the error messages to the lv2Pedalboard.
// return true if the error messages have changed.
// return true if the error messages have changed
audioHost->SetPedalboard(lv2Pedalboard);
return true;
}
+203 -189
View File
@@ -30,10 +30,10 @@
#include "Lv2Pedalboard.hpp"
#include "OptionsFeature.hpp"
#include "JackConfiguration.hpp"
#include "lv2/urid.lv2/urid.h"
#include "lv2/ui.lv2/ui.h"
#include "lv2.h"
#include "lv2/atom.lv2/atom.h"
#include "lv2/urid/urid.h"
#include "lv2/ui/ui.h"
//#include "lv2.h"
#include "lv2/atom/atom.h"
#include "lv2/time/time.h"
#include "lv2/state/state.h"
#include "lv2/lv2plug.in/ns/ext/buf-size/buf-size.h"
@@ -71,20 +71,23 @@ namespace pipedal
static const char *LV2_MIDI_PLUGIN = "http://lv2plug.in/ns/lv2core#MIDIPlugin";
class PluginHost::Urids
{
public:
Urids(MapFeature &mapFeature)
{
atom_Float = mapFeature.GetUrid(LV2_ATOM__Float);
atom__Float = mapFeature.GetUrid(LV2_ATOM__Float);
atom__Double = mapFeature.GetUrid(LV2_ATOM__Double);
atom_Int = mapFeature.GetUrid(LV2_ATOM__Int);
ui__portNotification = mapFeature.GetUrid(LV2_UI__portNotification);
ui__plugin = mapFeature.GetUrid(LV2_UI__plugin);
ui__protocol = mapFeature.GetUrid(LV2_UI__protocol);
ui__floatProtocol = mapFeature.GetUrid(LV2_UI__protocol);
ui__peakProtocol = mapFeature.GetUrid(LV2_UI__peakProtocol);
}
LV2_URID atom_Float;
LV2_URID atom__Float;
LV2_URID atom__Double;
LV2_URID atom_Int;
LV2_URID ui__portNotification;
LV2_URID ui__plugin;
LV2_URID ui__protocol;
@@ -103,33 +106,33 @@ void PluginHost::SetConfiguration(const PiPedalConfiguration &configuration)
void PluginHost::LilvUris::Initialize(LilvWorld *pWorld)
{
rdfs__Comment = lilv_new_uri(pWorld, PluginHost::RDFS__comment);
rdfs__range = lilv_new_uri(pWorld,PluginHost::RDFS__range);
rdfs__range = lilv_new_uri(pWorld, PluginHost::RDFS__range);
port_logarithmic = lilv_new_uri(pWorld, LV2_PORT_PROPS__logarithmic);
port__display_priority = lilv_new_uri(pWorld, LV2_PORT_PROPS__displayPriority);
port_range_steps = lilv_new_uri(pWorld, LV2_PORT_PROPS__rangeSteps);
integer_property_uri = lilv_new_uri(pWorld, LV2_CORE__integer);
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__connectionOptional = lilv_new_uri(pWorld, LV2_CORE__connectionOptional);
portprops__not_on_gui_property_uri = lilv_new_uri(pWorld, LV2_PORT_PROPS__notOnGUI);
midi__event = lilv_new_uri(pWorld, LV2_MIDI__MidiEvent);
core__designation = lilv_new_uri(pWorld, LV2_CORE__designation);
portgroups__group = lilv_new_uri(pWorld, LV2_PORT_GROUPS__group);
units__unit = lilv_new_uri(pWorld, LV2_UNITS__unit);
invada_units__unit = lilv_new_uri(pWorld, "http://lv2plug.in/ns/extension/units#unit"); // a typo in invada plugin ttl files.
invada_units__unit = lilv_new_uri(pWorld, "http://lv2plug.in/ns/extension/units#unit"); // a typo in invada plugin ttl files.
invada_portprops__logarithmic = lilv_new_uri(pWorld, "http://lv2plug.in/ns/dev/extportinfo#logarithmic"); // a typo in invada plugin ttl files.
atom__bufferType = lilv_new_uri(pWorld, LV2_ATOM__bufferType);
atom__Path = lilv_new_uri(pWorld, LV2_ATOM__Path);
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");
lv2core__symbol = lilv_new_uri(pWorld, LV2_CORE__symbol);
lv2core__name = lilv_new_uri(pWorld, LV2_CORE__name);
lv2core__index = lilv_new_uri(pWorld, LV2_CORE__index);
lv2core__Parameter = lilv_new_uri(pWorld,LV2_CORE_PREFIX "Parameter");
lv2core__Parameter = lilv_new_uri(pWorld, LV2_CORE_PREFIX "Parameter");
pipedalUI__ui = lilv_new_uri(pWorld, PIPEDAL_UI__ui);
pipedalUI__fileProperties = lilv_new_uri(pWorld, PIPEDAL_UI__fileProperties);
@@ -142,16 +145,14 @@ void PluginHost::LilvUris::Initialize(LilvWorld *pWorld)
pipedalUI__outputPorts = lilv_new_uri(pWorld, PIPEDAL_UI__outputPorts);
pipedalUI__text = lilv_new_uri(pWorld, PIPEDAL_UI__text);
ui__portNotification = lilv_new_uri(pWorld, LV2_UI__portNotification);
ui__plugin = lilv_new_uri(pWorld, LV2_UI__plugin);
ui__protocol = lilv_new_uri(pWorld, LV2_UI__protocol);
ui__floatProtocol = lilv_new_uri(pWorld, LV2_UI__protocol);
ui__peakProtocol = lilv_new_uri(pWorld, LV2_UI__peakProtocol);
ui__portIndex = lilv_new_uri(pWorld,LV2_UI__portIndex);
lv2__symbol = lilv_new_uri(pWorld,LV2_CORE__symbol);
ui__portIndex = lilv_new_uri(pWorld, LV2_UI__portIndex);
lv2__symbol = lilv_new_uri(pWorld, LV2_CORE__symbol);
lv2__port = lilv_new_uri(pWorld, LV2_CORE__port);
// ui:portNotification
// [
@@ -170,11 +171,10 @@ void PluginHost::LilvUris::Initialize(LilvWorld *pWorld)
appliesTo = lilv_new_uri(pWorld, LV2_CORE__appliesTo);
patch__writable = lilv_new_uri(pWorld,LV2_PATCH__writable);
patch__readable = lilv_new_uri(pWorld,LV2_PATCH__readable);
patch__writable = lilv_new_uri(pWorld, LV2_PATCH__writable);
patch__readable = lilv_new_uri(pWorld, LV2_PATCH__readable);
dc__format = lilv_new_uri(pWorld, "http://purl.org/dc/terms/format");
}
void PluginHost::LilvUris::Free()
@@ -355,14 +355,14 @@ void PluginHost::LoadPluginClassesFromLilv()
if (lv2Class && lv2Class->parent_uri().size() != 0)
{
std::shared_ptr<Lv2PluginClass> parentClass = GetPluginClass(lv2Class->parent_uri());
if (parentClass)
{
Lv2PluginClass *ccClass = const_cast<Lv2PluginClass *>(lv2Class.get());
ccClass->set_parent(parentClass);
Lv2PluginClass *ccParentClass = parentClass.get();
ccParentClass->add_child(lv2Class);
}
std::shared_ptr<Lv2PluginClass> parentClass = GetPluginClass(lv2Class->parent_uri());
if (parentClass)
{
Lv2PluginClass *ccClass = const_cast<Lv2PluginClass *>(lv2Class.get());
ccClass->set_parent(parentClass);
Lv2PluginClass *ccParentClass = parentClass.get();
ccParentClass->add_child(lv2Class);
}
}
}
}
@@ -405,30 +405,30 @@ void PluginHost::Load(const char *lv2Path)
if (pluginInfo->hasCvPorts())
{
Lv2Log::debug("Plugin %s (%s) skipped. (Has CV ports).", pluginInfo->name().c_str(), pluginInfo->uri().c_str());
Lv2Log::debug("Plugin %s (%s) skipped. (Has CV ports).", pluginInfo->name().c_str(), pluginInfo->uri().c_str());
}
#if !SUPPORT_MIDI
else if (pluginInfo->plugin_class() == LV2_MIDI_PLUGIN)
{
Lv2Log::debug("Plugin %s (%s) skipped. (MIDI Plugin).", pluginInfo->name().c_str(), pluginInfo->uri().c_str());
Lv2Log::debug("Plugin %s (%s) skipped. (MIDI Plugin).", pluginInfo->name().c_str(), pluginInfo->uri().c_str());
}
#endif
else if (!pluginInfo->is_valid())
{
auto &ports = pluginInfo->ports();
for (int i = 0; i < ports.size(); ++i)
auto &ports = pluginInfo->ports();
for (int i = 0; i < ports.size(); ++i)
{
auto &port = ports[i];
if (!port->is_valid())
{
auto &port = ports[i];
if (!port->is_valid())
{
Lv2Log::debug("Plugin port %s:%s is invalid.", pluginInfo->name().c_str(), port->name().c_str());
}
Lv2Log::debug("Plugin port %s:%s is invalid.", pluginInfo->name().c_str(), port->name().c_str());
}
Lv2Log::debug("Plugin %s (%s) skipped. Not valid.", pluginInfo->name().c_str(), pluginInfo->uri().c_str());
}
Lv2Log::debug("Plugin %s (%s) skipped. Not valid.", pluginInfo->name().c_str(), pluginInfo->uri().c_str());
}
else
{
this->plugins_.push_back(pluginInfo);
this->plugins_.push_back(pluginInfo);
}
}
auto messages = stdoutCapture.GetOutputLines();
@@ -457,28 +457,28 @@ void PluginHost::Load(const char *lv2Path)
if (plugin->is_valid())
{
#if SUPPORT_MIDI
if (info.audio_inputs() == 0 && !info.has_midi_input())
{
Lv2Log::debug("Plugin %s (%s) skipped. No inputs.", plugin->name().c_str(), plugin->uri().c_str());
}
else if (info.audio_outputs() == 0 && !info.has_midi_output())
{
Lv2Log::debug("Plugin %s (%s) skipped. No audio outputs.", plugin->name().c_str(), plugin->uri().c_str());
}
if (info.audio_inputs() == 0 && !info.has_midi_input())
{
Lv2Log::debug("Plugin %s (%s) skipped. No inputs.", plugin->name().c_str(), plugin->uri().c_str());
}
else if (info.audio_outputs() == 0 && !info.has_midi_output())
{
Lv2Log::debug("Plugin %s (%s) skipped. No audio outputs.", plugin->name().c_str(), plugin->uri().c_str());
}
#else
if (info.audio_inputs() == 0)
{
Lv2Log::debug("Plugin %s (%s) skipped. No audio inputs.", plugin->name().c_str(), plugin->uri().c_str());
}
else if (info.audio_outputs() == 0)
{
Lv2Log::debug("Plugin %s (%s) skipped. No audio outputs.", plugin->name().c_str(), plugin->uri().c_str());
}
if (info.audio_inputs() == 0)
{
Lv2Log::debug("Plugin %s (%s) skipped. No audio inputs.", plugin->name().c_str(), plugin->uri().c_str());
}
else if (info.audio_outputs() == 0)
{
Lv2Log::debug("Plugin %s (%s) skipped. No audio outputs.", plugin->name().c_str(), plugin->uri().c_str());
}
#endif
else
{
ui_plugins_.push_back(std::move(info));
}
else
{
ui_plugins_.push_back(std::move(info));
}
}
}
@@ -493,8 +493,8 @@ void PluginHost::Load(const char *lv2Path)
const auto &vst3PluginList = this->vst3Host->getPluginList();
for (const auto &vst3Plugin : vst3PluginList)
{
// copy not move!
ui_plugins_.push_back(vst3Plugin->pluginInfo_);
// copy not move!
ui_plugins_.push_back(vst3Plugin->pluginInfo_);
}
auto ui_compare = [&collation](
Lv2PluginUiInfo &left,
@@ -511,7 +511,6 @@ void PluginHost::Load(const char *lv2Path)
#endif
};
static std::vector<std::string> nodeAsStringArray(const LilvNodes *nodes)
{
std::vector<std::string> result;
@@ -523,12 +522,10 @@ static std::vector<std::string> nodeAsStringArray(const LilvNodes *nodes)
}
const char *PluginHost::RDFS__comment = "http://www.w3.org/2000/01/rdf-schema#"
"comment";
"comment";
const char *PluginHost::RDFS__range = "http://www.w3.org/2000/01/rdf-schema#"
"range";
"range";
LilvNode *PluginHost::get_comment(const std::string &uri)
{
@@ -554,20 +551,20 @@ bool Lv2PluginInfo::HasFactoryPresets(PluginHost *lv2Host, const LilvPlugin *plu
return result;
}
std::shared_ptr<PiPedalUI> Lv2PluginInfo::FindWritablePathProperties(PluginHost *lv2Host,const LilvPlugin*pPlugin)
std::shared_ptr<PiPedalUI> Lv2PluginInfo::FindWritablePathProperties(PluginHost *lv2Host, const LilvPlugin *pPlugin)
{
// example:
// example:
// <http://github.com/mikeoliphant/neural-amp-modeler-lv2#model>
// a lv2:Parameter;
// rdfs:label "Model";
// rdfs:range atom:Path.
// ...
// patch:writable <http://github.com/mikeoliphant/neural-amp-modeler-lv2#model>;
// ...
// patch:writable <http://github.com/mikeoliphant/neural-amp-modeler-lv2#model>;
LilvWorld* pWorld = lv2Host->getWorld();
LilvWorld *pWorld = lv2Host->getWorld();
AutoLilvNode pluginUri = lilv_plugin_get_uri(pPlugin);
AutoLilvNodes patchWritables = lilv_world_find_nodes(pWorld,pluginUri,lv2Host->lilvUris.patch__writable,nullptr);
AutoLilvNodes patchWritables = lilv_world_find_nodes(pWorld, pluginUri, lv2Host->lilvUris.patch__writable, nullptr);
std::vector<UiFileProperty::ptr> fileProperties;
@@ -576,13 +573,13 @@ std::shared_ptr<PiPedalUI> Lv2PluginInfo::FindWritablePathProperties(PluginHost
AutoLilvNode propertyUri = lilv_nodes_get(patchWritables, iNode);
if (propertyUri)
{
// isA lv2:Parameter?
if (lilv_world_ask(pWorld,propertyUri,lv2Host->lilvUris.isA,lv2Host->lilvUris.lv2core__Parameter))
// isA lv2:Parameter?
if (lilv_world_ask(pWorld, propertyUri, lv2Host->lilvUris.isA, lv2Host->lilvUris.lv2core__Parameter))
{
// rfs:range atom:Path?
if (lilv_world_ask(pWorld,propertyUri,lv2Host->lilvUris.rdfs__range,lv2Host->lilvUris.atom__Path))
if (lilv_world_ask(pWorld, propertyUri, lv2Host->lilvUris.rdfs__range, lv2Host->lilvUris.atom__Path))
{
AutoLilvNode label = lilv_world_get(pWorld,propertyUri,lv2Host->lilvUris.rdfs__label,nullptr);
AutoLilvNode label = lilv_world_get(pWorld, propertyUri, lv2Host->lilvUris.rdfs__label, nullptr);
std::string strLabel = label.AsString();
if (strLabel.length() != 0)
{
@@ -591,33 +588,25 @@ std::shared_ptr<PiPedalUI> Lv2PluginInfo::FindWritablePathProperties(PluginHost
std::string lv2DirectoryName = path.filename().string();
// we have a valid path property!
auto fileProperty =
std::make_shared<UiFileProperty>(
strLabel, propertyUri.AsUri(), lv2DirectoryName);
auto fileProperty =
std::make_shared<UiFileProperty>(
strLabel,propertyUri.AsUri(),lv2DirectoryName);
AutoLilvNodes dc_types = lilv_world_find_nodes(pWorld,propertyUri,lv2Host->lilvUris.dc__format,nullptr);
AutoLilvNodes dc_types = lilv_world_find_nodes(pWorld, propertyUri, lv2Host->lilvUris.dc__format, nullptr);
LILV_FOREACH(nodes, i, dc_types)
{
AutoLilvNode dc_type = lilv_nodes_get(dc_types,i);
AutoLilvNode dc_type = lilv_nodes_get(dc_types, i);
std::string fileType = dc_type.AsString();
std::string label = "";
fileProperty->fileTypes().push_back(UiFileType(label,fileType));
fileProperty->fileTypes().push_back(UiFileType(label, fileType));
}
fileProperties.push_back(
fileProperty
);
fileProperty);
}
}
}
}
}
if (fileProperties.size() != 0)
{
@@ -625,7 +614,6 @@ std::shared_ptr<PiPedalUI> Lv2PluginInfo::FindWritablePathProperties(PluginHost
}
return std::shared_ptr<PiPedalUI>();
}
Lv2PluginInfo::Lv2PluginInfo(PluginHost *lv2Host, LilvWorld *pWorld, const LilvPlugin *pPlugin)
@@ -685,15 +673,15 @@ Lv2PluginInfo::Lv2PluginInfo(PluginHost *lv2Host, LilvWorld *pWorld, const LilvP
std::shared_ptr<Lv2PortInfo> portInfo = std::make_shared<Lv2PortInfo>(lv2Host, pPlugin, pPort);
if (!portInfo->is_valid())
{
isValid = false;
isValid = false;
}
const auto &portGroup = portInfo->port_group();
if (portGroup.size() != 0)
{
if (std::find(portGroups.begin(), portGroups.end(), portGroup) == portGroups.end())
{
portGroups.push_back(portGroup);
}
if (std::find(portGroups.begin(), portGroups.end(), portGroup) == portGroups.end())
{
portGroups.push_back(portGroup);
}
}
ports_.push_back(std::move(portInfo));
}
@@ -707,8 +695,8 @@ Lv2PluginInfo::Lv2PluginInfo(PluginHost *lv2Host, LilvWorld *pWorld, const LilvP
{
if (!this->IsSupportedFeature(this->required_features_[i]))
{
Lv2Log::debug("%s (%s) requires feature %s.", this->name_.c_str(), this->uri_.c_str(), this->required_features_[i].c_str());
isValid = false;
Lv2Log::debug("%s (%s) requires feature %s.", this->name_.c_str(), this->uri_.c_str(), this->required_features_[i].c_str());
isValid = false;
}
}
@@ -724,9 +712,11 @@ Lv2PluginInfo::Lv2PluginInfo(PluginHost *lv2Host, LilvWorld *pWorld, const LilvP
if (pipedalUINode)
{
this->piPedalUI_ = std::make_shared<PiPedalUI>(lv2Host, pipedalUINode, std::filesystem::path(bundlePath));
} else {
// look for
this->piPedalUI_ = FindWritablePathProperties(lv2Host,pPlugin);
}
else
{
// look for
this->piPedalUI_ = FindWritablePathProperties(lv2Host, pPlugin);
}
int nInputs = 0;
@@ -788,7 +778,7 @@ bool Lv2PluginInfo::IsSupportedFeature(const std::string &feature) const
for (int i = 0; i < supportedFeatures.size(); ++i)
{
if (supportedFeatures[i] == feature)
return true;
return true;
}
return false;
}
@@ -864,7 +854,7 @@ Lv2PortInfo::Lv2PortInfo(PluginHost *host, const LilvPlugin *plugin, const LilvP
auto priority_node = lilv_nodes_get_first(priority_nodes);
if (priority_node)
{
this->display_priority_ = lilv_node_as_int(priority_node);
this->display_priority_ = lilv_node_as_int(priority_node);
}
}
@@ -875,7 +865,7 @@ Lv2PortInfo::Lv2PortInfo(PluginHost *host, const LilvPlugin *plugin, const LilvP
auto range_steps_node = lilv_nodes_get_first(range_steps_nodes);
if (range_steps_node)
{
this->range_steps_ = lilv_node_as_int(range_steps_node);
this->range_steps_ = lilv_node_as_int(range_steps_node);
}
}
this->integer_property_ = lilv_port_has_property(plugin, pPort, host->lilvUris.integer_property_uri);
@@ -884,7 +874,7 @@ Lv2PortInfo::Lv2PortInfo(PluginHost *host, const LilvPlugin *plugin, const LilvP
this->toggled_property_ = lilv_port_has_property(plugin, pPort, host->lilvUris.core__toggled);
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->connection_optional_ = lilv_port_has_property(plugin, pPort, host->lilvUris.core__connectionOptional);
LilvScalePoints *pScalePoints = lilv_port_get_scale_points(plugin, pPort);
LILV_FOREACH(scale_points, iSP, pScalePoints)
@@ -921,23 +911,26 @@ Lv2PortInfo::Lv2PortInfo(PluginHost *host, const LilvPlugin *plugin, const LilvP
if (unitsValueUri)
{
this->units_ = UriToUnits(nodeAsString(unitsValueUri));
} else {
}
else
{
// invada plugins use the wrong URI.
AutoLilvNode invadaUnitsValueUri = lilv_port_get(plugin, pPort, host->lilvUris.invada_units__unit);
if (invadaUnitsValueUri)
{
std::string uri = nodeAsString(invadaUnitsValueUri);
static const char*INCORRECT_URI = "http://lv2plug.in/ns/extension/units#";
static const char*CORRECT_URI = "http://lv2plug.in/ns/extensions/units#";
static const char *INCORRECT_URI = "http://lv2plug.in/ns/extension/units#";
static const char *CORRECT_URI = "http://lv2plug.in/ns/extensions/units#";
if (uri.starts_with(INCORRECT_URI))
{
uri = uri.replace(0,strlen(INCORRECT_URI),CORRECT_URI);
uri = uri.replace(0, strlen(INCORRECT_URI), CORRECT_URI);
}
this->units_ = UriToUnits(uri);
} else {
}
else
{
this->units(Units::none);
}
}
AutoLilvNode commentNode = lilv_port_get(plugin, pPort, host->lilvUris.rdfs__Comment);
@@ -963,9 +956,9 @@ Lv2PluginClasses PluginHost::GetPluginPortClass(const LilvPlugin *lilvPlugin, co
{
LILV_FOREACH(nodes, iNode, nodes)
{
const LilvNode *node = lilv_nodes_get(nodes, iNode);
std::string classUri = nodeAsString(node);
result.push_back(classUri);
const LilvNode *node = lilv_nodes_get(nodes, iNode);
std::string classUri = nodeAsString(node);
result.push_back(classUri);
}
}
return Lv2PluginClasses(result);
@@ -994,7 +987,7 @@ bool Lv2PluginClasses::is_a(PluginHost *lv2Plugins, const char *classUri) const
{
if (lv2Plugins->is_a(classUri_, i))
{
return true;
return true;
}
}
return false;
@@ -1038,39 +1031,39 @@ Lv2PluginUiInfo::Lv2PluginUiInfo(PluginHost *pHost, const Lv2PluginInfo *plugin)
}
for (auto port : plugin->ports())
{
if (port->is_input())
{
if (port->is_control_port())
if (port->is_control_port())
{
controls_.push_back(Lv2PluginUiPort(plugin, port.get()));
}
else if (port->is_atom_port())
{
if (port->supports_midi())
{
controls_.push_back(Lv2PluginUiPort(plugin, port.get()));
}
else if (port->is_atom_port())
{
if (port->supports_midi())
{
has_midi_input_ = true;
}
}
else if (port->is_audio_port())
{
++audio_inputs_;
has_midi_input_ = true;
}
}
else if (port->is_audio_port())
{
++audio_inputs_;
}
}
else if (port->is_output())
{
if (port->is_control_port())
{
controls_.push_back(Lv2PluginUiPort(plugin, port.get()));
}
else if (port->is_atom_port() && port->supports_midi())
{
this->has_midi_output_ = true;
}
else if (port->is_audio_port())
{
++audio_outputs_;
}
if (port->is_control_port())
{
controls_.push_back(Lv2PluginUiPort(plugin, port.get()));
}
else if (port->is_atom_port() && port->supports_midi())
{
this->has_midi_output_ = true;
}
else if (port->is_audio_port())
{
++audio_outputs_;
}
}
}
for (auto &portGroup : plugin->port_groups())
@@ -1097,7 +1090,7 @@ std::shared_ptr<Lv2PluginInfo> PluginHost::GetPluginInfo(const std::string &uri)
{
if ((*i)->uri() == uri)
{
return (*i);
return (*i);
}
}
return nullptr;
@@ -1108,7 +1101,7 @@ Lv2Pedalboard *PluginHost::CreateLv2Pedalboard(Pedalboard &pedalboard, Lv2Pedalb
Lv2Pedalboard *pPedalboard = new Lv2Pedalboard();
try
{
pPedalboard->Prepare(this, pedalboard,errorMessages);
pPedalboard->Prepare(this, pedalboard, errorMessages);
return pPedalboard;
}
catch (const std::exception &e)
@@ -1134,7 +1127,7 @@ void PluginHost::fn_LilvSetPortValueFunc(const char *port_symbol,
PluginHost *pHost = pData->pHost;
std::vector<ControlValue> *pResult = pData->pResult;
LV2_URID valueType = (LV2_URID)type;
if (valueType != pHost->urids->atom_Float)
if (valueType != pHost->urids->atom__Float)
{
throw PiPedalStateException("Preset format not supported.");
}
@@ -1169,27 +1162,27 @@ std::vector<ControlValue> PluginHost::LoadFactoryPluginPreset(
if (presetUri == thisPresetUri)
{
/*********************************/
/*********************************/
// AutoLilvNode uriNode = lilv_new_uri(pWorld, presetUri.c_str());
// AutoLilvNode uriNode = lilv_new_uri(pWorld, presetUri.c_str());
auto lilvState = lilv_state_new_from_world(pWorld, GetLv2UridMap(), preset);
auto lilvState = lilv_state_new_from_world(pWorld, GetLv2UridMap(), preset);
if (lilvState == nullptr)
{
throw PiPedalStateException("Preset not found.");
}
if (lilvState == nullptr)
{
throw PiPedalStateException("Preset not found.");
}
StateCallbackData cbData;
cbData.pHost = this;
cbData.pResult = &result;
auto n = lilv_state_get_num_properties(lilvState);
lilv_state_emit_port_values(lilvState, fn_LilvSetPortValueFunc, &cbData);
StateCallbackData cbData;
cbData.pHost = this;
cbData.pResult = &result;
auto n = lilv_state_get_num_properties(lilvState);
lilv_state_emit_port_values(lilvState, fn_LilvSetPortValueFunc, &cbData);
lilv_state_free(lilvState);
break;
lilv_state_free(lilvState);
break;
/*********************************/
/*********************************/
}
}
lilv_nodes_free(presets);
@@ -1209,11 +1202,17 @@ void PluginHost::PortValueCallback(const char *symbol, void *user_data, const vo
PresetCallbackState *pState = static_cast<PresetCallbackState *>(user_data);
PluginHost *pHost = pState->pHost;
if (type == pHost->urids->atom_Float)
if (type == pHost->urids->atom__Double)
{
(*pState->values)[symbol] = (float)(*static_cast<const double *>(value));
} else if (type == pHost->urids->atom__Float)
{
(*pState->values)[symbol] = *static_cast<const float *>(value);
}
else
else if (type == pHost->urids->atom_Int)
{
(*pState->values)[symbol] = *static_cast<const int32_t *>(value);
} else
{
pState->failed = true;
}
@@ -1244,16 +1243,30 @@ PluginPresets PluginHost::GetFactoryPluginPresets(const std::string &pluginUri)
LilvState *state = lilv_state_new_from_world(pWorld, this->mapFeature.GetMap(), preset);
if (state != nullptr)
{
std::string label = lilv_state_get_label(state);
const char *t = this->mapFeature.UridToString(14);
const char *tLabel = lilv_state_get_label(state);
if (tLabel != nullptr)
{
std::string strLabel = tLabel;
std::map<std::string, float> controlValues;
PresetCallbackState cbData{this, &controlValues, false};
lilv_state_emit_port_values(state, PortValueCallback, (void *)&cbData);
int numProperties = lilv_state_get_num_properties(state);
// can't handle std:state part of preset.
if (numProperties == 0)
{
if (!cbData.failed)
{
result.presets_.push_back(PluginPreset(result.nextInstanceId_++, strLabel, controlValues, Lv2PluginState()));
}
} else {
result.presets_.push_back(PluginPreset::MakeLilvPreset(result.nextInstanceId_++, strLabel, controlValues, lilv_node_as_uri(preset)));
}
lilv_state_free(state);
if (!cbData.failed)
{
result.presets_.push_back(PluginPreset(result.nextInstanceId_++, label, controlValues,Lv2PluginState()));
}
}
}
}
lilv_nodes_free(presets);
@@ -1267,13 +1280,13 @@ IEffect *PluginHost::CreateEffect(PedalboardItem &pedalboardItem)
#if ENABLE_VST3
try
{
Vst3Effect::Ptr vst3Plugin = this->vst3Host->CreatePlugin(pedalboardItem, this);
return vst3Plugin.release();
Vst3Effect::Ptr vst3Plugin = this->vst3Host->CreatePlugin(pedalboardItem, this);
return vst3Plugin.release();
}
catch (const std::exception &e)
{
Lv2Log::error(std::string("Failed to create VST3 plugin: ") + e.what());
throw;
Lv2Log::error(std::string("Failed to create VST3 plugin: ") + e.what());
throw;
}
#else
Lv2Log::error(std::string("VST3 support not enabled at compile time."));
@@ -1285,7 +1298,7 @@ IEffect *PluginHost::CreateEffect(PedalboardItem &pedalboardItem)
{
auto info = this->GetPluginInfo(pedalboardItem.uri());
if (!info)
return nullptr;
return nullptr;
return new Lv2Effect(this, info, pedalboardItem);
}
@@ -1443,20 +1456,21 @@ json_map::storage_type<Lv2PluginUiPort> Lv2PluginUiPort::jmap{{
json_map::storage_type<Lv2PluginUiInfo>
Lv2PluginUiInfo::jmap{
{json_map::reference("uri", &Lv2PluginUiInfo::uri_),
json_map::reference("name", &Lv2PluginUiInfo::name_),
json_map::enum_reference("plugin_type", &Lv2PluginUiInfo::plugin_type_, get_plugin_type_enum_converter()),
json_map::reference("plugin_display_type", &Lv2PluginUiInfo::plugin_display_type_),
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_outputs", &Lv2PluginUiInfo::audio_outputs_),
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_),
json_map::reference("controls", &Lv2PluginUiInfo::controls_),
json_map::reference("port_groups", &Lv2PluginUiInfo::port_groups_),
json_map::reference("is_vst3", &Lv2PluginUiInfo::is_vst3_),
json_map::reference("fileProperties", &Lv2PluginUiInfo::fileProperties_),
json_map::reference("uiPortNotifications", &Lv2PluginUiInfo::uiPortNotifications_),
}};
{
json_map::reference("uri", &Lv2PluginUiInfo::uri_),
json_map::reference("name", &Lv2PluginUiInfo::name_),
json_map::enum_reference("plugin_type", &Lv2PluginUiInfo::plugin_type_, get_plugin_type_enum_converter()),
json_map::reference("plugin_display_type", &Lv2PluginUiInfo::plugin_display_type_),
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_outputs", &Lv2PluginUiInfo::audio_outputs_),
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_),
json_map::reference("controls", &Lv2PluginUiInfo::controls_),
json_map::reference("port_groups", &Lv2PluginUiInfo::port_groups_),
json_map::reference("is_vst3", &Lv2PluginUiInfo::is_vst3_),
json_map::reference("fileProperties", &Lv2PluginUiInfo::fileProperties_),
json_map::reference("uiPortNotifications", &Lv2PluginUiInfo::uiPortNotifications_),
}};
+10 -1
View File
@@ -31,7 +31,7 @@
#include <string>
#include "IHost.hpp"
#include "lv2.h"
//#include "lv2.h"
#include "Units.hpp"
#include "PluginPreset.hpp"
@@ -659,6 +659,7 @@ namespace pipedal
AutoLilvNode atom__bufferType;
AutoLilvNode atom__Path;
AutoLilvNode presets__preset;
AutoLilvNode state__state;
AutoLilvNode rdfs__label;
AutoLilvNode lv2core__symbol;
AutoLilvNode lv2core__name;
@@ -693,6 +694,7 @@ namespace pipedal
AutoLilvNode ui__peakProtocol;
AutoLilvNode ui__portIndex;
AutoLilvNode lv2__symbol;
AutoLilvNode lv2__port;
AutoLilvNode patch__writable;
AutoLilvNode patch__readable;
@@ -788,6 +790,13 @@ namespace pipedal
return this->mapFeature.GetMap();
}
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);
virtual IEffect *CreateEffect(PedalboardItem &pedalboardItem);
void LoadPluginClassesFromLilv();
+1
View File
@@ -28,6 +28,7 @@ JSON_MAP_BEGIN(PluginPreset)
JSON_MAP_REFERENCE(PluginPreset,label)
JSON_MAP_REFERENCE(PluginPreset,controlValues)
JSON_MAP_REFERENCE(PluginPreset,state)
JSON_MAP_REFERENCE(PluginPreset,lilvPresetUri)
JSON_MAP_END()
JSON_MAP_BEGIN(PluginPresets)
+15
View File
@@ -97,8 +97,23 @@ public:
{
}
static PluginPreset MakeLilvPreset(
uint64_t instanceId,
const std::string&label,
const std::map<std::string,float> & controlValues,
const char*presetUri
) {
PluginPreset preset;
preset.instanceId_ = instanceId;
preset.label_ = label;
preset.lilvPresetUri_ = presetUri;
preset.controlValues_ = controlValues;
return preset;
}
uint64_t instanceId_;
std::string label_;
std::string lilvPresetUri_;
std::map<std::string,float> controlValues_;
Lv2PluginState state_;
DECLARE_JSON_MAP(PluginPreset);
+1 -1
View File
@@ -23,7 +23,7 @@
#include "Lv2Log.hpp"
#include "VuUpdate.hpp"
#include "AudioHost.hpp"
#include "lv2/atom.lv2/atom.h"
#include "lv2/atom/atom.h"
#include <chrono>
namespace pipedal
{
+261 -34
View File
@@ -25,8 +25,8 @@
using namespace pipedal;
static std::shared_ptr<Lv2PortInfo> MakeBypassPortInfo() {
static std::shared_ptr<Lv2PortInfo> MakeBypassPortInfo()
{
std::shared_ptr<Lv2PortInfo> bypassPortInfo = std::make_shared<Lv2PortInfo>();
bypassPortInfo->symbol("__bypass");
bypassPortInfo->name("Bypass");
@@ -49,10 +49,8 @@ const Lv2PortInfo *pipedal::GetBypassPortInfo()
}
// Just enough to do control value defaulting, and midi value mapping :-/
static Lv2PluginInfo makeSplitterPluginInfo() {
static Lv2PluginInfo makeSplitterPluginInfo()
{
Lv2PluginInfo result;
result.uri(SPLIT_PEDALBOARD_ITEM_URI);
result.name("Split");
@@ -60,7 +58,7 @@ static Lv2PluginInfo makeSplitterPluginInfo() {
result.comment("Internal only.");
result.is_valid(true);
std::shared_ptr<Lv2PortInfo> splitTypeControl= std::make_shared<Lv2PortInfo>();
std::shared_ptr<Lv2PortInfo> splitTypeControl = std::make_shared<Lv2PortInfo>();
splitTypeControl->symbol(SPLIT_SPLITTYPE_KEY);
splitTypeControl->name("Type");
splitTypeControl->is_input(true);
@@ -71,18 +69,14 @@ static Lv2PluginInfo makeSplitterPluginInfo() {
splitTypeControl->default_value(0);
splitTypeControl->enumeration_property(true);
splitTypeControl->scale_points().push_back(
Lv2ScalePoint(0,"A/B")
);
Lv2ScalePoint(0, "A/B"));
splitTypeControl->scale_points().push_back(
Lv2ScalePoint(1,"Mix")
);
Lv2ScalePoint(1, "Mix"));
splitTypeControl->scale_points().push_back(
Lv2ScalePoint(1,"L/R")
);
Lv2ScalePoint(1, "L/R"));
result.ports().push_back(splitTypeControl);
std::shared_ptr<Lv2PortInfo> abControl= std::make_shared<Lv2PortInfo>();
std::shared_ptr<Lv2PortInfo> abControl = std::make_shared<Lv2PortInfo>();
abControl->symbol(SPLIT_SELECT_KEY);
abControl->name("Select");
abControl->is_input(true);
@@ -93,14 +87,12 @@ static Lv2PluginInfo makeSplitterPluginInfo() {
abControl->default_value(0);
abControl->enumeration_property(true);
abControl->scale_points().push_back(
Lv2ScalePoint(0,"A")
);
Lv2ScalePoint(0, "A"));
abControl->scale_points().push_back(
Lv2ScalePoint(1,"B")
);
Lv2ScalePoint(1, "B"));
result.ports().push_back(abControl);
std::shared_ptr<Lv2PortInfo> mixControl= std::make_shared<Lv2PortInfo>();
std::shared_ptr<Lv2PortInfo> mixControl = std::make_shared<Lv2PortInfo>();
mixControl->symbol(SPLIT_MIX_KEY);
mixControl->name("Mix");
mixControl->is_input(true);
@@ -112,7 +104,7 @@ static Lv2PluginInfo makeSplitterPluginInfo() {
result.ports().push_back(mixControl);
std::shared_ptr<Lv2PortInfo> panLControl= std::make_shared<Lv2PortInfo>();
std::shared_ptr<Lv2PortInfo> panLControl = std::make_shared<Lv2PortInfo>();
panLControl->symbol(SPLIT_PANL_KEY);
panLControl->name("Pan L");
panLControl->is_input(true);
@@ -124,7 +116,7 @@ static Lv2PluginInfo makeSplitterPluginInfo() {
result.ports().push_back(panLControl);
std::shared_ptr<Lv2PortInfo> volLControl= std::make_shared<Lv2PortInfo>();
std::shared_ptr<Lv2PortInfo> volLControl = std::make_shared<Lv2PortInfo>();
volLControl->symbol(SPLIT_VOLL_KEY);
volLControl->name("Vol L");
volLControl->is_input(true);
@@ -134,12 +126,11 @@ static Lv2PluginInfo makeSplitterPluginInfo() {
volLControl->max_value(12);
volLControl->default_value(-3);
volLControl->scale_points().push_back(
Lv2ScalePoint(-60,"-INF")
);
Lv2ScalePoint(-60, "-INF"));
result.ports().push_back(volLControl);
std::shared_ptr<Lv2PortInfo> panRControl= std::make_shared<Lv2PortInfo>();
std::shared_ptr<Lv2PortInfo> panRControl = std::make_shared<Lv2PortInfo>();
panRControl->symbol(SPLIT_PANR_KEY);
panRControl->name("Pan R");
panRControl->is_input(true);
@@ -151,7 +142,7 @@ static Lv2PluginInfo makeSplitterPluginInfo() {
result.ports().push_back(panRControl);
std::shared_ptr<Lv2PortInfo> volRControl= std::make_shared<Lv2PortInfo>();
std::shared_ptr<Lv2PortInfo> volRControl = std::make_shared<Lv2PortInfo>();
volRControl->symbol(SPLIT_VOLR_KEY);
volRControl->name("Vol R");
volRControl->is_input(true);
@@ -161,24 +152,260 @@ static Lv2PluginInfo makeSplitterPluginInfo() {
volRControl->max_value(12);
volRControl->default_value(-3);
volRControl->scale_points().push_back(
Lv2ScalePoint(-60,"-INF")
);
Lv2ScalePoint(-60, "-INF"));
result.ports().push_back(volRControl);
return result;
}
Lv2PluginInfo g_splitterPluginInfo = makeSplitterPluginInfo();
const Lv2PluginInfo * pipedal::GetSplitterPluginInfo() { return &g_splitterPluginInfo; }
const Lv2PluginInfo *pipedal::GetSplitterPluginInfo() { return &g_splitterPluginInfo; }
int SplitEffect::GetControlIndex(const std::string &symbol) const
{
if (symbol == "splitType")
return SPLIT_TYPE_CTL;
if (symbol == "select")
return SELECT_CTL;
if (symbol == "mix")
return MIX_CTL;
if (symbol == "panL")
return PANL_CTL;
if (symbol == "volL")
return VOLL_CTL;
if (symbol == "panR")
return PANR_CTL;
if (symbol == "volR")
return VOLR_CTL;
return -1;
}
void SplitEffect::Activate()
{
activated = true;
updateMixFunction();
snapToMixTarget();
mixBottomInputs.clear();
mixTopInputs.clear();
if (outputBuffers.size() == 1)
{
mixTopInputs.push_back(this->topOutputs[0]);
mixBottomInputs.push_back(this->bottomOutputs[0]);
}
else
{
if (this->topOutputs.size() == 1)
{
mixTopInputs.push_back(topOutputs[0]);
mixTopInputs.push_back(topOutputs[0]);
}
else
{
mixTopInputs.push_back(topOutputs[0]);
mixTopInputs.push_back(topOutputs[1]);
}
if (this->bottomOutputs.size() == 1)
{
mixBottomInputs.push_back(bottomOutputs[0]);
mixBottomInputs.push_back(bottomOutputs[0]);
}
else
{
mixBottomInputs.push_back(bottomOutputs[0]);
mixBottomInputs.push_back(bottomOutputs[1]);
}
}
}
void SplitEffect::Deactivate()
{
activated = false;
}
void SplitEffect::SetChainBuffers(
const std::vector<float *> &topInputs,
const std::vector<float *> &bottomInputs,
const std::vector<float *> &topOutputs,
const std::vector<float *> &bottomOutputs,
bool forceStereo)
{
this->topInputs = topInputs;
this->bottomInputs = bottomInputs;
this->topOutputs = topOutputs;
this->bottomOutputs = bottomOutputs;
if (forceStereo)
{
numberOfOutputPorts = 2;
} else {
if (topOutputs.size() > 1 || bottomOutputs.size() > 1)
{
numberOfOutputPorts = 2;
} else {
numberOfOutputPorts = 1;
}
}
if (this->topOutputs.size() == 1 && numberOfOutputPorts != 1)
{
this->topOutputs.push_back(this->topOutputs[0]);
}
if (this->bottomOutputs.size() == 1 && numberOfOutputPorts != 1)
{
this->bottomOutputs.push_back(this->bottomOutputs[0]);
}
outputBuffers.resize(numberOfOutputPorts);
}
void SplitEffect::snapToMixTarget()
{
this->blendLTop = targetBlendLTop;
this->blendRTop = targetBlendRTop;
this->blendRBottom = targetBlendRBottom;
this->blendLBottom = targetBlendLBottom;
this->blendFadeSamples = 0;
this->blendDxLTop = 0;
this->blendDxRTop = 0;
this->blendDxLBottom = 0;
this->blendDxRBottom = 0;
}
void SplitEffect::mixToTarget()
{
uint32_t transitionSamples = (uint32_t)(this->sampleRate * MIX_TRANSITION_TIME_S);
if (transitionSamples < 1)
transitionSamples = 1;
double dxScale = 1.0 / transitionSamples;
this->blendFadeSamples = transitionSamples;
this->blendDxLTop = dxScale * (this->targetBlendLTop - this->blendLTop);
this->blendDxRTop = dxScale * (this->targetBlendRTop - this->blendRTop);
this->blendDxLBottom = dxScale * (this->targetBlendLBottom - this->blendLBottom);
this->blendDxRBottom = dxScale * (this->targetBlendRBottom - this->blendRBottom);
}
void SplitEffect::mixTo(float value)
{
float blend = (value + 1) * 0.5f;
this->targetBlendRTop = this->targetBlendLTop = 1 - blend;
this->targetBlendLBottom = this->targetBlendRBottom = blend;
mixToTarget();
}
void SplitEffect::mixTo(float panL, float volL, float panR, float volR)
{
float aTop = (volL <= SPLIT_DB_MIN) ? 0 : db2a(volL);
float aBottom = (volR <= SPLIT_DB_MIN) ? 0 : db2a(volR);
if (this->outputBuffers.size() == 1)
{
// ignore pan. The R values actually have no effect.
this->targetBlendLTop = this->targetBlendRTop = aTop;
this->targetBlendLBottom = this->targetBlendRBottom = aBottom;
}
else
{
float blendTop = (panL + 1) * 0.5;
float blendBottom = (panR + 1) * 0.5;
this->targetBlendLTop = (1 - blendTop) * aTop;
this->targetBlendRTop = (blendTop)*aTop;
this->targetBlendLBottom = (1 - blendBottom) * aBottom;
this->targetBlendRBottom = (blendBottom)*aBottom;
}
mixToTarget();
}
float SplitEffect::GetControlValue(int portIndex) const
{
switch (portIndex)
{
case -1: /* (bypass) */
return 1;
case SPLIT_TYPE_CTL:
{
return (int)(this->splitType);
}
case SELECT_CTL:
{
return selectA ? 0 : 1;
}
case MIX_CTL:
return mix;
case PANL_CTL:
return this->panL;
case VOLL_CTL:
return this->volL;
case PANR_CTL:
return this->panR;
case VOLR_CTL:
return this->volR;
default:
throw PiPedalArgumentException("Invalid argument");
}
}
void SplitEffect::SetControl(int index, float value)
{
switch (index)
{
case -1: /* (bypass) */
return; // no can bypass.
case SPLIT_TYPE_CTL:
{
SplitType t = valueToSplitType(value);
if (splitType != t)
{
splitType = t;
updateMixFunction();
}
break;
}
case SELECT_CTL:
{
bool t = value == 0;
if (selectA != t)
{
selectA = t;
if (splitType == SplitType::Ab)
{
mixTo(selectA ? -1 : 1);
}
}
break;
}
case MIX_CTL:
mix = value;
if (splitType == SplitType::Mix)
{
mixTo(value);
}
break;
case PANL_CTL:
panL = value;
if (splitType == SplitType::Lr)
{
mixTo(panL, volL, panR, volR);
}
break;
case VOLL_CTL:
volL = value;
if (splitType == SplitType::Lr)
{
mixTo(panL, volL, panR, volR);
}
break;
case PANR_CTL:
panR = value;
if (splitType == SplitType::Lr)
{
mixTo(panL, volL, panR, volR);
}
break;
case VOLR_CTL:
volR = value;
if (splitType == SplitType::Lr)
{
mixTo(panL, volL, panR, volR);
}
break;
}
}
+23 -209
View File
@@ -314,132 +314,39 @@ namespace pipedal
}
static constexpr int SPLIT_TYPE_CTL = 0;
static constexpr int SELECT_CTL = 1;
static constexpr int MIX_CTL = 2;
static constexpr int PANL_CTL = 3;
static constexpr int VOLL_CTL = 4;
static constexpr int PANR_CTL = 5;
static constexpr int VOLR_CTL = 6;
virtual uint64_t GetInstanceId() const
{
return instanceId;
}
void Activate()
{
activated = true;
updateMixFunction();
snapToMixTarget();
void Activate();
void Deactivate();
mixBottomInputs.clear();
mixTopInputs.clear();
if (outputBuffers.size() == 1)
{
mixTopInputs.push_back(this->topOutputs[0]);
mixBottomInputs.push_back(this->bottomOutputs[0]);
}
else
{
if (this->topOutputs.size() == 1)
{
mixTopInputs.push_back(topOutputs[0]);
mixTopInputs.push_back(topOutputs[0]);
}
else
{
mixTopInputs.push_back(topOutputs[0]);
mixTopInputs.push_back(topOutputs[1]);
}
if (this->bottomOutputs.size() == 1)
{
mixBottomInputs.push_back(bottomOutputs[0]);
mixBottomInputs.push_back(bottomOutputs[0]);
}
else
{
mixBottomInputs.push_back(bottomOutputs[0]);
mixBottomInputs.push_back(bottomOutputs[1]);
}
}
}
void Deactivate()
{
activated = false;
}
void SetChainBuffers(
void SetChainBuffers(
const std::vector<float *> &topInputs,
const std::vector<float *> &bottomInputs,
const std::vector<float *> &topOutputs,
const std::vector<float *> &bottomOutputs)
{
this->topInputs = topInputs;
this->bottomInputs = bottomInputs;
this->topOutputs = topOutputs;
this->bottomOutputs = bottomOutputs;
const std::vector<float *> &bottomOutputs,
bool forceStereoOutput);
numberOfOutputPorts = topOutputs.size() > 1 || bottomOutputs.size() > 1 ? 2 : 1;
outputBuffers.resize(numberOfOutputPorts);
}
virtual void ResetAtomBuffers() {}
virtual int GetControlIndex(const std::string &symbol) const
{
if (symbol == "splitType")
return 0;
if (symbol == "select")
return 1;
if (symbol == "mix")
return 2;
return -1;
}
void snapToMixTarget()
{
this->blendLTop = targetBlendLTop;
this->blendRTop = targetBlendRTop;
this->blendRBottom = targetBlendRBottom;
this->blendLBottom = targetBlendLBottom;
this->blendFadeSamples = 0;
this->blendDxLTop = 0;
this->blendDxRTop = 0;
this->blendDxLBottom = 0;
this->blendDxRBottom = 0;
}
void mixToTarget()
{
uint32_t transitionSamples = (uint32_t)(this->sampleRate * MIX_TRANSITION_TIME_S);
if (transitionSamples < 1)
transitionSamples = 1;
double dxScale = 1.0 / transitionSamples;
this->blendFadeSamples = transitionSamples;
this->blendDxLTop = dxScale * (this->targetBlendLTop - this->blendLTop);
this->blendDxRTop = dxScale * (this->targetBlendRTop - this->blendRTop);
this->blendDxLBottom = dxScale * (this->targetBlendLBottom - this->blendLBottom);
this->blendDxRBottom = dxScale * (this->targetBlendRBottom - this->blendRBottom);
}
void mixTo(float value)
{
float blend = (value + 1) * 0.5f;
this->targetBlendRTop = this->targetBlendLTop = 1 - blend;
this->targetBlendLBottom = this->targetBlendLBottom = blend;
mixToTarget();
}
void mixTo(float panL, float volL, float panR, float volR)
{
float aTop = (volL <= SPLIT_DB_MIN) ? 0 : db2a(volL);
float aBottom = (volL <= SPLIT_DB_MIN) ? 0 : db2a(volR);
if (this->outputBuffers.size() == 1)
{
// ignore pan. The R values actually have no effect.
this->targetBlendLTop = this->targetBlendRTop = aTop;
this->targetBlendLBottom = this->targetBlendRBottom = aBottom;
}
else
{
float blendTop = (panL + 1) * 0.5;
float blendBottom = (panR + 1) * 0.5;
this->targetBlendLTop = (1 - blendTop) * aTop;
this->targetBlendRTop = (blendTop)*aTop;
this->targetBlendLBottom = (1 - blendBottom) * aBottom;
this->targetBlendRBottom = (blendBottom)*aBottom;
}
mixToTarget();
}
virtual int GetControlIndex(const std::string &symbol) const;
void snapToMixTarget();
void mixToTarget();
void mixTo(float value);
void mixTo(float panL, float volL, float panR, float volR);
virtual void SetBypass(bool enabled)
{
throw PiPedalArgumentException("Not implmented. Should not have been called.");
@@ -448,102 +355,9 @@ namespace pipedal
virtual float GetOutputControlValue(int index) const {
return GetControlValue(index);
}
virtual float GetControlValue(int portIndex) const;
virtual void SetControl(int index, float value);
virtual float GetControlValue(int portIndex) const
{
switch (portIndex)
{
case -1: /* (bypass) */
return 1;
case 0:
{
return (int)(this->splitType);
}
case 1:
{
return selectA ? 0 : 1;
}
case 2:
return mix;
case 3:
return this->panL;
case 4:
return this->volL;
case 5:
return this->panR;
case 6:
return this->volR;
default:
throw PiPedalArgumentException("Invalid argument");
}
}
virtual void SetControl(int index, float value)
{
switch (index)
{
case -1: /* (bypass) */
return; // no can bypass.
case 0:
{
SplitType t = valueToSplitType(value);
if (splitType != t)
{
splitType = t;
updateMixFunction();
}
break;
}
case 1:
{
bool t = value == 0;
if (selectA != t)
{
selectA = t;
if (splitType == SplitType::Ab)
{
mixTo(selectA ? -1 : 1);
}
}
break;
}
case 2:
mix = value;
if (splitType == SplitType::Mix)
{
mixTo(value);
}
break;
case 3:
panL = value;
if (splitType == SplitType::Lr)
{
mixTo(panL, volL, panR, volR);
}
break;
case 4:
volL = value;
if (splitType == SplitType::Lr)
{
mixTo(panL, volL, panR, volR);
}
break;
case 5:
panR = value;
if (splitType == SplitType::Lr)
{
mixTo(panL, volL, panR, volR);
}
break;
case 6:
volR = value;
if (splitType == SplitType::Lr)
{
mixTo(panL, volL, panR, volR);
}
break;
}
}
virtual int GetNumberOfOutputAudioPorts() const
{
+5
View File
@@ -121,6 +121,7 @@ Lv2PluginState StateInterface::Save()
features);
CheckState(status);
CheckState(callState.status);
callState.state.isValid_ = true;
}
catch (const std::exception &e)
{
@@ -130,6 +131,10 @@ Lv2PluginState StateInterface::Save()
return std::move(callState.state);
}
void StateInterface::RestoreState(LilvState*pLv2State)
{
lilv_state_restore(pLv2State,this->pInstance,nullptr,nullptr,LV2_STATE_IS_POD,&(features[0]));
}
void StateInterface::Restore(const Lv2PluginState &state)
{
RestoreCallState callState;
+3 -1
View File
@@ -20,11 +20,12 @@
#pragma once
#include "lilv/lilv.h"
#include "lv2/state.lv2/state.h"
#include "lv2/state/state.h"
#include <cstddef>
#include "json_variant.hpp"
#include "MapFeature.hpp"
#include "IHost.hpp"
#include "lilv/lilv.h"
namespace pipedal
{
@@ -65,6 +66,7 @@ namespace pipedal
public:
Lv2PluginState Save();
void Restore(const Lv2PluginState &state);
void RestoreState(LilvState *pLv2State);
private:
struct Urids {
+25 -18
View File
@@ -196,12 +196,13 @@ void Storage::MaybeCopyDefaultPresets()
presetsDirectory);
}
auto pluginDirectory = this->GetPluginPresetsDirectory();
if (!std::filesystem::exists(pluginDirectory / "index.json"))
{
CopyDirectory(this->configRoot / "default_presets" / "plugin_presets",
pluginDirectory);
}
// Obsolete: TooB effects now have correct preset declarations.
//auto pluginDirectory = this->GetPluginPresetsDirectory();
// if (!std::filesystem::exists(pluginDirectory / "index.json"))
// {
// CopyDirectory(this->configRoot / "default_presets" / "plugin_presets",
// pluginDirectory);
// }
}
void Storage::Initialize()
{
@@ -350,7 +351,7 @@ void Storage::SavePluginPresetIndex()
throw PiPedalException(SS("Can't write to " << path));
}
json_writer writer(os);
json_writer writer(os,false);
writer.write(this->pluginPresetIndex);
}
}
@@ -359,7 +360,7 @@ void Storage::SaveBankIndex()
{
std::ofstream os;
os.open(GetIndexFileName(), std::ios_base::trunc);
json_writer writer(os);
json_writer writer(os,false);
writer.write(this->bankIndex);
}
@@ -434,7 +435,7 @@ void Storage::SaveBankFile(const std::string &name, const BankFile &bankFile)
{
std::ofstream s;
s.open(fileName, std::ios_base::trunc);
json_writer writer(s);
json_writer writer(s,false);
writer.write(bankFile);
if (std::filesystem::exists(backupFile))
{
@@ -721,7 +722,7 @@ void Storage::SaveChannelSelection()
try
{
std::ofstream s(fileName);
json_writer writer(s);
json_writer writer(s,false);
writer.write(this->jackChannelSelection);
}
catch (const std::exception &e)
@@ -910,7 +911,7 @@ int64_t Storage::UploadBank(BankFile &bankFile, int64_t uploadAfter)
{
throw PiPedalException("Can't write to bank file.");
}
json_writer writer(f);
json_writer writer(f,false);
writer.write(bankFile);
lastBank = this->bankIndex.addBank(lastBank, bankFile.name());
@@ -932,7 +933,7 @@ void Storage::SaveUserSettings()
{
throw PiPedalException("Unable to write to " + ((std::string)path));
}
json_writer writer(f);
json_writer writer(f,false);
writer.write(userSettings);
}
}
@@ -968,7 +969,7 @@ void Storage::SetWifiConfigSettings(const WifiConfigSettings &wifiConfigSettings
{
throw PiPedalException("Unable to write to " + ((std::string)path));
}
json_writer writer(f);
json_writer writer(f,false);
writer.write(&copyToSave);
}
@@ -1077,7 +1078,7 @@ void Storage::SaveCurrentPreset(const CurrentPreset &currentPreset)
std::filesystem::path path = GetCurrentPresetPath();
std::ofstream f(path);
json_writer writer(f);
json_writer writer(f,false);
writer.write(currentPreset);
}
catch (std::exception &)
@@ -1157,7 +1158,7 @@ void Storage::SavePluginPresets(const std::string &pluginUri, const PluginPreset
{
throw PiPedalException(SS("Can't write to " << path));
}
json_writer writer(os);
json_writer writer(os,false);
writer.write(presets);
}
if (std::filesystem::exists(path))
@@ -1226,6 +1227,7 @@ PluginPresetValues Storage::GetPluginPresetValues(const std::string &pluginUri,
result.controls.push_back(ControlValue(valuePair.first.c_str(), valuePair.second));
}
result.state = preset.state_;
result.lilvPresetUri = preset.lilvPresetUri_;
return result;
}
}
@@ -1398,7 +1400,7 @@ void Storage::SetFavorites(const std::map<std::string, bool> &favorites)
f.open(fileName);
if (f.is_open())
{
json_writer writer(f);
json_writer writer(f,false);
writer.write(favorites);
}
}
@@ -1427,7 +1429,7 @@ void Storage::SetJackServerSettings(const pipedal::JackServerSettings &jackConfi
f.open(fileName);
if (f.is_open())
{
json_writer writer(f);
json_writer writer(f,false);
writer.write(jackConfiguration);
}
#if JACK_HOST
@@ -1442,7 +1444,7 @@ void Storage::SetSystemMidiBindings(const std::vector<MidiBinding> &bindings)
f.open(fileName);
if (f.is_open())
{
json_writer writer(f);
json_writer writer(f,false);
writer.write(bindings);
}
}
@@ -1630,6 +1632,11 @@ std::string Storage::UploadUserFile(const std::string &directory, const std::str
return path.string();
}
const PluginPresetIndex& Storage::GetPluginPresetIndex()
{
return pluginPresetIndex;
}
JSON_MAP_BEGIN(UserSettings)
JSON_MAP_REFERENCE(UserSettings, governor)
JSON_MAP_REFERENCE(UserSettings, showStatusMonitor)
+4
View File
@@ -57,6 +57,7 @@ struct PluginPresetValues {
std::vector<ControlValue> controls;
Lv2PluginState state;
std::string lilvPresetUri;
};
// controls user-defined storage. Implmentation hidden to allow to later migration to a database (perhaps)
@@ -173,6 +174,7 @@ private:
bool pluginPresetIndexChanged = false;
void LoadPluginPresetIndex();
void SavePluginPresetIndex();
std::filesystem::path GetPluginPresetPath(const std::string &pluginUri) const;
bool IsValidSampleFile(const std::filesystem::path&fileName);
std::filesystem::path MakeUserFilePath(const std::string &directory, const std::string&filename);
@@ -181,6 +183,8 @@ public:
bool HasPluginPresets(const std::string&pluginUri) const;
void SavePluginPresets(const std::string&pluginUri, const PluginPresets&presets);
PluginPresets GetPluginPresets(const std::string&pluginUri) const;
const PluginPresetIndex& GetPluginPresetIndex();
PluginUiPresets GetPluginUiPresets(const std::string&pluginUri) const;
PluginPresetValues GetPluginPresetValues(const std::string&pluginUri, uint64_t instanceId);
+4
View File
@@ -70,6 +70,10 @@ public:
{
set(text);
}
uri(const std::string&text)
{
set(text.c_str());
}
void set(const char*text);
void set(const char*start, const char*end);
private:
+1 -1
View File
@@ -261,7 +261,7 @@ namespace pipedal
this->socketHandler = nullptr;
webSocket = nullptr;
pServer = nullptr;
Lv2Log::info("WebSocketSession closed.");
Lv2Log::info(SS("WebSocketSession closed. " << fromAddress));
}
using ptr = std::shared_ptr<WebSocketSession>;
WebSocketSession(WebServerImpl *pServer, server::connection_ptr &webSocket)
+9 -10
View File
@@ -21,17 +21,16 @@
#pragma once
#include <lilv/lilv.h>
#include "lv2.h"
//#include "lv2.h"
#include "lv2/atom.lv2/atom.h"
#include "lv2/atom.lv2/util.h"
#include "lv2.h"
#include "lv2/log.lv2/log.h"
#include "lv2/log.lv2/logger.h"
#include "lv2/midi.lv2/midi.h"
#include "lv2/urid.lv2/urid.h"
#include "lv2/atom.lv2/atom.h"
#include "lv2/worker.lv2/worker.h"
#include "lv2/atom/atom.h"
#include "lv2/atom/util.h"
#include "lv2/log/log.h"
#include "lv2/log/logger.h"
#include "lv2/midi/midi.h"
#include "lv2/urid/urid.h"
#include "lv2/atom/atom.h"
#include "lv2/worker/worker.h"
#include "condition_variable"
#include <map>
+2 -1
View File
@@ -23,11 +23,12 @@
#include <cctype>
#include "PiPedalException.hpp"
#include "json_variant.hpp"
#include "util.hpp"
using namespace pipedal;
uint32_t json_writer::continuation_byte(std::string_view::iterator &p, std::string_view::const_iterator end)
{
if (p == end)
+6 -6
View File
@@ -167,7 +167,7 @@ public:
PluginPresets pluginPresets = model->GetPluginPresets(pluginUri);
std::stringstream s;
json_writer writer(s);
json_writer writer(s,false);
writer.write(pluginPresets);
*pContent = s.str();
}
@@ -184,7 +184,7 @@ public:
file.selectedPreset(newInstanceId);
std::stringstream s;
json_writer writer(s);
json_writer writer(s,false);
writer.write(file);
*pContent = s.str();
*pName = pedalboard.name();
@@ -597,12 +597,12 @@ int main(int argc, char *argv[])
if (help)
{
std::cout << "Usage: pipedal <doc_root> [<web_root>] [options...]\n\n"
std::cout << "Usage: pipedald <doc_root> [<web_root>] [options...]\n\n"
<< "Options:\n"
<< " -systemd: Log to systemd journals, wait for jack service.\n"
<< " -systemd: Log to systemd journals instead of to the console.\n"
<< " -port: Port to listen on e.g. 0.0.0.0:80\n"
<< "Example:\n"
<< " pipedal /etc/pipedal/config /etc/pipedal/react -port 0.0.0.0:80 \n"
<< " pipedald /etc/pipedal/config /etc/pipedal/react -port 0.0.0.0:80 \n"
"\n"
"Description:\n\n"
" Configuration is read from <doc_root>/config.json\n"
@@ -611,7 +611,7 @@ int main(int argc, char *argv[])
"\n"
" While debugging, bind the port to 0.0.0.0:8080, and connect to the default React\n"
" server that's provided when you run 'npm run start' in 'react/src'. By default, the\n"
" React app will connect to the socket server on 0.0.0.0:8080.\n\n";
" React debug server will connect to the socket server on 0.0.0.0:8080.\n\n";
return error ? EXIT_FAILURE : EXIT_SUCCESS;
}
+15 -14
View File
@@ -19,6 +19,7 @@
#pragma once
// Diagnostic type used to report calculated type T in an error message.
template <typename T> class TypeDisplay;
@@ -44,20 +45,20 @@ template <typename T> class TypeDisplay;
#endif
/*
#include <lv2/lv2core.lv2/lv2.h>
#include <lv2/lv2core/lv2.h>
#include "lv2/atom.lv2/atom.h"
#include "lv2/atom.lv2/util.h"
#include "lv2/log.lv2/log.h"
#include "lv2/log.lv2/logger.h"
#include "lv2/midi.lv2/midi.h"
#include "lv2/urid.lv2/urid.h"
#include "lv2/log.lv2/logger.h"
#include "lv2/uri-map.lv2/uri-map.h"
#include "lv2/atom.lv2/forge.h"
#include "lv2/worker.lv2/worker.h"
#include "lv2/patch.lv2/patch.h"
#include "lv2/parameters.lv2/parameters.h"
#include "lv2/units.lv2/units.h"
#include "lv2/atom/atom.h"
#include "lv2/atom/util.h"
#include "lv2/log/log.h"
#include "lv2/log/logger.h"
#include "lv2/midi/midi.h"
#include "lv2/urid/urid.h"
#include "lv2/log/logger.h"
#include "lv2/uri-map/uri-map.h"
#include "lv2/atom/forge.h"
#include "lv2/worker/worker.h"
#include "lv2/patch/patch.h"
#include "lv2/parameters/parameters.h"
#include "lv2/units/units.h"
*/
#include "ss.hpp"
+61 -1
View File
@@ -26,6 +26,8 @@
#include <thread>
#include <unistd.h> // for gettid()
#include <codecvt>
#include <sstream>
using namespace pipedal;
@@ -39,4 +41,62 @@ void pipedal::SetThreadName(const std::string &name)
}
pthread_t pid = pthread_self();
pthread_setname_np(pid,threadName.c_str());
}
}
static const uint8_t utf8extraBytes[256] = {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
};
constexpr char32_t ILLEGAL_CHAR32 = U'';
static const uint8_t utf8Offset[] = { 0,0b11000000, 0b11100000,0b11110000,0b11111000,0b11111100};
std::u32string pipedal::ToUtf32(const std::string &s)
{
std::basic_stringstream<char32_t> result;
auto p = s.begin();
auto end = s.end();
while (p != end)
{
uint8_t c = (uint8_t)(*p++);
if (c < 0x80)
{
result << (char32_t)c;
} else {
auto extraBytes = utf8extraBytes[c];
if (extraBytes == 0)
{
result << ILLEGAL_CHAR32;
} else if (p+extraBytes > end)
{
result << ILLEGAL_CHAR32;
break;
} else {
char32_t cResult = c -= utf8Offset[extraBytes];
while (extraBytes != 0)
{
c = *p++;
--extraBytes;
if (c < 0x80 || c >= 0xC0)
{
result << ILLEGAL_CHAR32;
p += extraBytes;
break;
}
cResult = (cResult << 6) + (c & 0x3F);
}
result << cResult;
}
}
}
return result.str();
}
+3
View File
@@ -29,4 +29,7 @@ namespace pipedal {
return str.size() >= suffix.size() && 0 == str.compare(str.size()-suffix.size(), suffix.size(), suffix);
}
void SetThreadName(const std::string &name);
std::u32string ToUtf32(const std::string &s);
}
+37
View File
@@ -0,0 +1,37 @@
/*
* Copyright (c) 2023 Robin E. R. Davies
* All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "util.hpp"
#include "catch.hpp"
using namespace pipedal;
TEST_CASE("utf8 to utf32 test", "[utf8_to_utf32][Build][Dev]")
{
std::u32string t = ToUtf32((const char*)u8"0AZ|£ǧ谸😊");
std::u32string expected = U"0AZ|£ǧ谸😊";
REQUIRE(t == expected);
}