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:
+1
-1
@@ -25,7 +25,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "lv2/atom.lv2/atom.h"
|
||||
#include "lv2/atom/atom.h"
|
||||
|
||||
namespace pipedal
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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; }
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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>
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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();
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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(©ToSave);
|
||||
}
|
||||
|
||||
@@ -1077,7 +1078,7 @@ void Storage::SaveCurrentPreset(const CurrentPreset ¤tPreset)
|
||||
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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user