Patch Properties in Plugin Settings

This commit is contained in:
Robin E. R. Davies
2025-09-05 06:31:51 -04:00
parent ac194fbbb5
commit e6733ec036
10 changed files with 2145 additions and 1514 deletions
File diff suppressed because it is too large Load Diff
+5
View File
@@ -140,6 +140,11 @@ json_variant AtomConverter::AbstractPath(const json_variant&json, const std::str
} }
json_variant AtomConverter::MakePathVariant(const std::string&pathString)
{
return TypedProperty(SHORT_ATOM__Path,pathString);
}
LV2_Atom*AtomConverter::ToAtom(const json_variant&json) LV2_Atom*AtomConverter::ToAtom(const json_variant&json)
{ {
+1
View File
@@ -156,6 +156,7 @@ namespace pipedal
static json_variant MapPath(const json_variant&json, const std::string &pluginStoragePath); static json_variant MapPath(const json_variant&json, const std::string &pluginStoragePath);
static json_variant AbstractPath(const json_variant&json, const std::string &pluginStoragePath); static json_variant AbstractPath(const json_variant&json, const std::string &pluginStoragePath);
static json_variant MakePathVariant(const std::string&pathString);
static std::string EmptyPathstring(); static std::string EmptyPathstring();
static const json_variant& EmptyPath() { return gEmptyPath; } static const json_variant& EmptyPath() { return gEmptyPath; }
+36 -9
View File
@@ -248,6 +248,8 @@ void PiPedalModel::LoadLv2PluginInfo()
// Copy all presets out of Lilv data to json files // Copy all presets out of Lilv data to json files
// so that we can close lilv while we're actually // so that we can close lilv while we're actually
// running. // running.
uint64_t pluginPresetIndexVersion = storage.GetPluginPresetIndexVersion();
for (const auto &plugin : pluginHost.GetPlugins()) for (const auto &plugin : pluginHost.GetPlugins())
{ {
if (plugin->has_factory_presets()) if (plugin->has_factory_presets())
@@ -256,9 +258,26 @@ void PiPedalModel::LoadLv2PluginInfo()
{ {
PluginPresets pluginPresets = pluginHost.GetFactoryPluginPresets(plugin->uri()); PluginPresets pluginPresets = pluginHost.GetFactoryPluginPresets(plugin->uri());
storage.SavePluginPresets(plugin->uri(), pluginPresets); storage.SavePluginPresets(plugin->uri(), pluginPresets);
} else {
if (pluginPresetIndexVersion == 0)
{
if (plugin->uri() == "http://two-play.com/plugins/toob-convolution-reverb" || plugin->uri() == "http://two-play.com/plugins/toob-convolution-reverb-stereo")
{
// overwrite previous factory presets!
PluginPresets pluginPresets = pluginHost.GetFactoryPluginPresets(plugin->uri());
for (auto & pluginPreset: pluginPresets.presets_)
{
storage.SavePluginPreset(
plugin->uri(),
pluginPreset);
}
}
}
} }
} }
} }
storage.SetPluginPresetIndexVersion(1);
} }
void PiPedalModel::Load() void PiPedalModel::Load()
@@ -1734,7 +1753,7 @@ void PiPedalModel::SendSetPatchProperty(
pedalboardItem->pathProperties_[propertyUri] = atomString; pedalboardItem->pathProperties_[propertyUri] = atomString;
} }
this->SetPresetChanged(clientId, true); this->SetPresetChanged(clientId, true);
} }
LV2_Atom *atomValue = atomConverter.ToAtom(value); LV2_Atom *atomValue = atomConverter.ToAtom(value);
std::function<void(RealtimePatchPropertyRequest *)> onRequestComplete{ std::function<void(RealtimePatchPropertyRequest *)> onRequestComplete{
@@ -1828,11 +1847,13 @@ void PiPedalModel::SendGetPatchProperty(
bool foundValue = false; bool foundValue = false;
std::lock_guard<std::recursive_mutex> lock(mutex); std::lock_guard<std::recursive_mutex> lock(mutex);
auto pedalboardItem = this->pedalboard.GetItem(pParameter->instanceId); auto pedalboardItem = this->pedalboard.GetItem(pParameter->instanceId);
if (pedalboardItem) { if (pedalboardItem)
{
if (pedalboardItem->pathProperties_.contains(pParameter->uri)) if (pedalboardItem->pathProperties_.contains(pParameter->uri))
{ {
pParameter->jsonResponse = pedalboardItem->pathProperties_[pParameter->uri]; pParameter->jsonResponse = pedalboardItem->pathProperties_[pParameter->uri];
if (pParameter->onSuccess) { if (pParameter->onSuccess)
{
foundValue = true; foundValue = true;
pParameter->onSuccess(pParameter->jsonResponse); pParameter->onSuccess(pParameter->jsonResponse);
} }
@@ -2085,9 +2106,12 @@ void PiPedalModel::UpdateDefaults(PedalboardItem *pedalboardItem, std::unordered
} }
for (auto i = pedalboardItem->pathProperties_.begin(); i != pedalboardItem->pathProperties_.end(); /**/) for (auto i = pedalboardItem->pathProperties_.begin(); i != pedalboardItem->pathProperties_.end(); /**/)
{ {
if (!validFileProperties.contains(i->first)) { if (!validFileProperties.contains(i->first))
{
i = pedalboardItem->pathProperties_.erase(i); i = pedalboardItem->pathProperties_.erase(i);
} else { }
else
{
++i; ++i;
} }
} }
@@ -2414,8 +2438,10 @@ void PiPedalModel::MonitorPatchProperty(int64_t clientId, int64_t clientHandle,
if (map.contains(propertyUri)) if (map.contains(propertyUri))
{ {
const auto &value = map.at(propertyUri); const auto &value = map.at(propertyUri);
if (value != "null") { if (value != "null")
try { {
try
{
std::string json = storage.FromAbstractPathJson(value); std::string json = storage.FromAbstractPathJson(value);
for (auto &subscriber : this->subscribers) for (auto &subscriber : this->subscribers)
@@ -2425,8 +2451,9 @@ void PiPedalModel::MonitorPatchProperty(int64_t clientId, int64_t clientHandle,
subscriber->OnNotifyPatchProperty(clientHandle, instanceId, propertyUri, json); subscriber->OnNotifyPatchProperty(clientHandle, instanceId, propertyUri, json);
} }
} }
} catch (const std::exception& ignored) { }
catch (const std::exception &ignored)
{
} }
} }
} }
+224 -2
View File
@@ -19,6 +19,7 @@
#include "pch.h" #include "pch.h"
#include "AtomConverter.hpp" #include "AtomConverter.hpp"
#include "HtmlHelper.hpp"
#include "PluginHost.hpp" #include "PluginHost.hpp"
#include <lilv/lilv.h> #include <lilv/lilv.h>
#include <stdexcept> #include <stdexcept>
@@ -1590,6 +1591,210 @@ void PluginHost::PortValueCallback(const char *symbol, void *user_data, const vo
pState->failed = true; pState->failed = true;
} }
} }
static bool lineStartsWith(const std::string &line, const char*text) {
for (char c: line) {
if (c == ' ' || c == '\t') {
continue;
}
if (c != *text) {
return false;
}
++text;
if (*text == '\0')
{
return true;
}
}
return false;
}
static void skipWhitespace(std::istream &s) {
while (true)
{
auto c = s.peek();
if (c == ' ' || c == '\t') {
s.get();
} else {
return;
}
}
}
static bool parseTtlUrl(std::istream &s, std::string *result)
{
if (s.peek() != '<') {
return false;
}
s.get();
std::ostringstream o;
while (s.peek() != '>')
{
int c = s.get();
if (c == EOF)
{
return false;
}
o << (char)c;
}
s.get();
*result = o.str();
return true;
}
static uint32_t readTtlUnicodeEscape(std::istream&s, size_t nChars)
{
uint32_t result = 0;
for (size_t i = 0; i < nChars; ++i)
{
int c = s.peek();
if (c >= '0' && c <= '9')
{
result = result*16 + (uint32_t)(c-'0');
} else if (c >= 'a' && c <= 'f') {
result = result*16 + (uint32_t)(c-'a'+10);
} else if (c >= 'A' && c <= 'F') {
result = result*16 + (uint32_t)(c-'A'+10);
}
s.get();
}
return result;
}
static bool parseTtlString(std::istream&s, std::string *result)
{
if (s.peek() != '"') {
return false;
}
s.get(); // consume opening quote
std::ostringstream o;
while (s.peek() != '"') {
int c = s.get();
if (c == EOF) {
return false;
}
if (c == '\\') {
// Handle escape sequences
int next = s.get();
if (next == EOF) {
return false;
}
switch (next) {
case 'n':
o << '\n';
break;
case 't':
o << '\t';
break;
case 'r':
o << '\r';
break;
case '\\':
o << '\\';
break;
case '"':
o << '"';
break;
case 'u':
{
uint32_t cc = readTtlUnicodeEscape(s,4);
HtmlHelper::utf32_to_utf8_stream(o,cc);
break;
}
case 'U':
{
uint32_t cc = readTtlUnicodeEscape(s,8);
HtmlHelper::utf32_to_utf8_stream(o,cc);
break;
}
default:
o << (char)next;
break;
}
} else {
o << (char)c;
}
}
s.get(); // consume closing quote
*result = o.str();
return true;
}
static bool parseStateProperty(const std::string &line, std::string *property, std::string *value) {
std::istringstream s { line};
skipWhitespace(s);
if (!parseTtlUrl(s,property)) {
return false;
}
skipWhitespace(s);
if (!parseTtlString(s,value))
{
return false;
}
return true;
}
static std::map<std::string,std::string> ExtractPathPropertiesFromLilvState(
Lv2PluginInfo *pluginInfo,
const std::string lilvPresetText
)
{
// <urn:xxx>
// a pset:Preset ;
// ...
// state:state [
// <http://two-play.com/plugins/toob-impulse#impulseFile> "ReverbImpulseFiles/Arthur Sykes Rymer Auditorium.wav"
// ] .
std::map<std::string,std::string> result;
std::istringstream s{lilvPresetText};
std::string line;
bool foundState = false;
for (std::string line; std::getline(s, line);) {
if (lineStartsWith(line,"state:state"))
{
foundState = true;
break;
}
}
if (foundState)
{
for (std::string line; std::getline(s, line);) {
if (lineStartsWith(line,"]"))
{
break;
}
std::string property, value;
if (parseStateProperty(line,&property,&value)) {
if (pluginInfo->IsPathProperty(property))
{ result[property] = AtomConverter::MakePathVariant(value).to_string();
}
}
}
}
return result;
}
bool Lv2PluginInfo::IsPathProperty(const std::string &uri) const {
if (!piPedalUI_) {
}
for (const UiFileProperty::ptr& fileProperty: this->piPedalUI_->fileProperties())
{
if (fileProperty->patchProperty() == uri)
{
return true;
}
}
return false;
}
PluginPresets PluginHost::GetFactoryPluginPresets(const std::string &pluginUri) PluginPresets PluginHost::GetFactoryPluginPresets(const std::string &pluginUri)
{ {
const LilvPlugins *plugins = lilv_world_get_all_plugins(this->pWorld); const LilvPlugins *plugins = lilv_world_get_all_plugins(this->pWorld);
@@ -1616,7 +1821,6 @@ PluginPresets PluginHost::GetFactoryPluginPresets(const std::string &pluginUri)
LilvState *state = lilv_state_new_from_world(pWorld, this->mapFeature.GetMap(), preset); LilvState *state = lilv_state_new_from_world(pWorld, this->mapFeature.GetMap(), preset);
if (state != nullptr) if (state != nullptr)
{ {
const char *t = this->mapFeature.UridToString(14);
const char *tLabel = lilv_state_get_label(state); const char *tLabel = lilv_state_get_label(state);
if (tLabel != nullptr) if (tLabel != nullptr)
{ {
@@ -1629,6 +1833,8 @@ PluginPresets PluginHost::GetFactoryPluginPresets(const std::string &pluginUri)
// can't handle std:state part of preset. // can't handle std:state part of preset.
if (numProperties == 0) if (numProperties == 0)
{ {
if (!cbData.failed) if (!cbData.failed)
{ {
result.presets_.push_back( result.presets_.push_back(
@@ -1642,7 +1848,23 @@ PluginPresets PluginHost::GetFactoryPluginPresets(const std::string &pluginUri)
} }
else else
{ {
result.presets_.push_back(PluginPreset::MakeLilvPreset(result.nextInstanceId_++, strLabel, controlValues, lilv_node_as_uri(preset)));
std::string stateString = lilv_state_to_string(
pWorld,mapFeature.GetMap(),mapFeature.GetUnmap(),
state,
"urn:xxx",
nullptr);
(void)stateString;
std::shared_ptr<Lv2PluginInfo> pluginInfo = GetPluginInfo(pluginUri);
PluginPreset pluginPreset = PluginPreset::MakeLilvPreset(result.nextInstanceId_++, strLabel, controlValues, lilv_node_as_uri(preset));
pluginPreset.pathProperties_ = ExtractPathPropertiesFromLilvState(
pluginInfo.get(),
stateString
);
result.presets_.push_back(pluginPreset);
} }
lilv_state_free(state); lilv_state_free(state);
} }
+1
View File
@@ -512,6 +512,7 @@ namespace pipedal
} }
} }
} }
bool IsPathProperty(const std::string &uri) const;
public: public:
virtual ~Lv2PluginInfo(); virtual ~Lv2PluginInfo();
+1
View File
@@ -90,6 +90,7 @@ JSON_MAP_END()
JSON_MAP_BEGIN(PluginPresetIndex) JSON_MAP_BEGIN(PluginPresetIndex)
JSON_MAP_REFERENCE(PluginPresetIndex,entries) JSON_MAP_REFERENCE(PluginPresetIndex,entries)
JSON_MAP_REFERENCE(PluginPresetIndex,version)
JSON_MAP_REFERENCE(PluginPresetIndex,nextInstanceId) JSON_MAP_REFERENCE(PluginPresetIndex,nextInstanceId)
JSON_MAP_END() JSON_MAP_END()
+1
View File
@@ -65,6 +65,7 @@ class PluginPresetIndex {
public: public:
std::vector<PluginPresetIndexEntry> entries_; std::vector<PluginPresetIndexEntry> entries_;
uint64_t nextInstanceId_ = 1; uint64_t nextInstanceId_ = 1;
uint64_t version_ = 0;
DECLARE_JSON_MAP(PluginPresetIndex); DECLARE_JSON_MAP(PluginPresetIndex);
}; };
+25 -15
View File
@@ -404,6 +404,7 @@ void Storage::SavePluginPresetIndex()
} }
} }
void Storage::SaveBankIndex() void Storage::SaveBankIndex()
{ {
pipedal::ofstream_synced os; pipedal::ofstream_synced os;
@@ -1234,6 +1235,22 @@ bool Storage::RestoreCurrentPreset(CurrentPreset *pResult)
return false; return false;
} }
} }
uint64_t Storage::GetPluginPresetIndexVersion()
{
return pluginPresetIndex.version_;
}
void Storage::SetPluginPresetIndexVersion(uint64_t version)
{
if (this->pluginPresetIndex.version_ != version)
{
this->pluginPresetIndexChanged = true;
this->pluginPresetIndex.version_ = version;
SavePluginPresetIndex();
}
}
bool Storage::HasPluginPresets(const std::string &pluginUri) const bool Storage::HasPluginPresets(const std::string &pluginUri) const
{ {
for (const auto &entry : this->pluginPresetIndex.entries_) for (const auto &entry : this->pluginPresetIndex.entries_)
@@ -1486,10 +1503,8 @@ uint64_t Storage::SavePluginPreset(
uint64_t Storage::SavePluginPreset( uint64_t Storage::SavePluginPreset(
const std::string &pluginUri, const std::string &pluginUri,
const std::string &name, PluginPreset &pluginPreset
const std::map<std::string, float> &values, )
const std::map<std::string,std::string> &pathProperties,
const Lv2PluginState &lv2State)
{ {
auto presets = GetPluginPresets(pluginUri); auto presets = GetPluginPresets(pluginUri);
uint64_t result = -1; uint64_t result = -1;
@@ -1497,13 +1512,12 @@ uint64_t Storage::SavePluginPreset(
for (size_t i = 0; i < presets.presets_.size(); ++i) for (size_t i = 0; i < presets.presets_.size(); ++i)
{ {
auto &preset = presets.presets_[i]; auto &preset = presets.presets_[i];
if (preset.label_ == name) if (preset.label_ == pluginPreset.label_)
{ {
existing = true; existing = true;
preset.controlValues_ = values; auto t = preset.instanceId_;
preset.pathProperties_ = pathProperties; preset = pluginPreset;
preset.state_ = lv2State; preset.instanceId_ = t;
result = preset.instanceId_; result = preset.instanceId_;
break; break;
} }
@@ -1511,13 +1525,9 @@ uint64_t Storage::SavePluginPreset(
if (!existing) if (!existing)
{ {
result = presets.nextInstanceId_++; result = presets.nextInstanceId_++;
pluginPreset.instanceId_ = result;
presets.presets_.push_back( presets.presets_.push_back(
PluginPreset( pluginPreset);
result,
name,
values,
pathProperties,
lv2State));
} }
this->SavePluginPresets(pluginUri, presets); this->SavePluginPresets(pluginUri, presets);
return result; return result;
+6 -5
View File
@@ -153,6 +153,7 @@ public:
void SaveCurrentPreset(const Pedalboard&pedalboard); void SaveCurrentPreset(const Pedalboard&pedalboard);
int64_t SaveCurrentPresetAs(const Pedalboard&pedalboard, const std::string&namne,int64_t saveAfterInstanceId = -1); int64_t SaveCurrentPresetAs(const Pedalboard&pedalboard, const std::string&namne,int64_t saveAfterInstanceId = -1);
int64_t GetCurrentPresetId() const; int64_t GetCurrentPresetId() const;
void GetPresetIndex(PresetIndex*pResult); void GetPresetIndex(PresetIndex*pResult);
void SetPresetIndex(const PresetIndex &presetIndex); void SetPresetIndex(const PresetIndex &presetIndex);
Pedalboard GetPreset(int64_t instanceId) const; Pedalboard GetPreset(int64_t instanceId) const;
@@ -213,6 +214,7 @@ private:
void LoadPluginPresetIndex(); void LoadPluginPresetIndex();
void SavePluginPresetIndex(); void SavePluginPresetIndex();
std::filesystem::path GetPluginPresetPath(const std::string &pluginUri) const; std::filesystem::path GetPluginPresetPath(const std::string &pluginUri) const;
bool IsValidSampleFileName(const std::filesystem::path&fileName); bool IsValidSampleFileName(const std::filesystem::path&fileName);
std::filesystem::path MakeUserFilePath(const std::string &directory, const std::string&filename); std::filesystem::path MakeUserFilePath(const std::string &directory, const std::string&filename);
@@ -220,6 +222,9 @@ private:
void FromAbstractPaths(PluginPreset&pluginPreset); void FromAbstractPaths(PluginPreset&pluginPreset);
public: public:
uint64_t GetPluginPresetIndexVersion();
void SetPluginPresetIndexVersion(uint64_t version);
bool HasPluginPresets(const std::string&pluginUri) const; bool HasPluginPresets(const std::string&pluginUri) const;
void SavePluginPresets(const std::string&pluginUri, const PluginPresets&presets); void SavePluginPresets(const std::string&pluginUri, const PluginPresets&presets);
void MergePluginPresets(const std::string&pluginUri, const PluginPresets&presets); void MergePluginPresets(const std::string&pluginUri, const PluginPresets&presets);
@@ -233,13 +238,9 @@ public:
const std::string&name, const std::string&name,
const PedalboardItem&pedalboardEntry const PedalboardItem&pedalboardEntry
); );
uint64_t SavePluginPreset( uint64_t SavePluginPreset(
const std::string &pluginUri, const std::string &pluginUri,
const std::string &name, PluginPreset& preset);
const std::map<std::string, float> &values,
const std::map<std::string,std::string>&pathProperties,
const Lv2PluginState& lv2State);
void UpdatePluginPresets(const PluginUiPresets &pluginPresets); void UpdatePluginPresets(const PluginUiPresets &pluginPresets);
uint64_t CopyPluginPreset(const std::string&pluginUri,uint64_t presetId); uint64_t CopyPluginPreset(const std::string&pluginUri,uint64_t presetId);