From ac194fbbb5784b7b0edfc68fe659d0988c254a9c Mon Sep 17 00:00:00 2001 From: "Robin E. R. Davies" Date: Thu, 4 Sep 2025 15:20:56 -0400 Subject: [PATCH] Fixes to handling of path properties in presets. --- PiPedalCommon/src/include/json_variant.hpp | 2 + PiPedalCommon/src/json_variant.cpp | 9 +++ modules/CMakeLists.txt | 7 ++ src/AtomConverter.cpp | 33 ++++++---- src/AtomConverter.hpp | 8 ++- src/AudioHost.hpp | 1 + src/CapturePresetsMain.cpp | 17 +++-- src/ModFileTypes.cpp | 2 +- src/Pedalboard.hpp | 3 + src/PiPedalModel.cpp | 73 +++++++++++++++------ src/PiPedalModel.hpp | 3 +- src/PluginHost.cpp | 43 ++++++++----- src/PluginPreset.cpp | 1 + src/PluginPreset.hpp | 5 ++ src/Storage.cpp | 74 +++++++++++++++++++--- src/Storage.hpp | 17 +++-- src/WebServerConfig.cpp | 2 +- todo.txt | 22 ++----- vite/src/pipedal/ModFileTypes.tsx | 2 +- vite/src/pipedal/PiPedalModel.tsx | 3 +- 20 files changed, 231 insertions(+), 96 deletions(-) diff --git a/PiPedalCommon/src/include/json_variant.hpp b/PiPedalCommon/src/include/json_variant.hpp index 9c4b69b..f4dba47 100644 --- a/PiPedalCommon/src/include/json_variant.hpp +++ b/PiPedalCommon/src/include/json_variant.hpp @@ -87,6 +87,8 @@ namespace pipedal json_variant(const void *) = delete; // do NOT allow implicit conversion of pointers to bool + static json_variant parse(const std::string&text); + json_variant &operator=(json_variant &&value); json_variant &operator=(const json_variant &value); json_variant &operator=(bool value); diff --git a/PiPedalCommon/src/json_variant.cpp b/PiPedalCommon/src/json_variant.cpp index 6469a78..eb8b8f1 100644 --- a/PiPedalCommon/src/json_variant.cpp +++ b/PiPedalCommon/src/json_variant.cpp @@ -570,6 +570,15 @@ json_variant::json_variant(json_reader&reader) } +json_variant json_variant::parse(const std::string&jsonText) +{ + std::istringstream s(jsonText); + json_reader reader(s); + json_variant result; + reader.read(&result); + return result; +} + /*static*/ json_null json_null::instance; /*static*/ int64_t json_array::allocation_count_ = 0; // strictly for testing purposes. not thread safe. diff --git a/modules/CMakeLists.txt b/modules/CMakeLists.txt index 820457b..f9aee83 100644 --- a/modules/CMakeLists.txt +++ b/modules/CMakeLists.txt @@ -10,6 +10,13 @@ set(SDBUSCPP_BUILD_TESTS off) add_subdirectory("sdbus-cpp") +option(ENABLE_COMMONCRYPTO "Enable use of CommonCrypto" OFF) +option(ENABLE_GNUTLS "Enable use of GnuTLS" OFF) +option(ENABLE_MBEDTLS "Enable use of mbed TLS" OFF) +option(ENABLE_OPENSSL "Enable use of OpenSSL" OFF) +option(ENABLE_WINDOWS_CRYPTO "Enable use of Windows cryptography libraries" OFF) + + option(BUILD_TOOLS "Build tools in the src directory (zipcmp, zipmerge, ziptool)" OFF) option(BUILD_REGRESS "Build regression tests" OFF) option(BUILD_OSSFUZZ "Build fuzzers for ossfuzz" OFF) diff --git a/src/AtomConverter.cpp b/src/AtomConverter.cpp index 8054e47..3e23fcb 100644 --- a/src/AtomConverter.cpp +++ b/src/AtomConverter.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include "json.hpp" #include "ss.hpp" #include "lv2/atom/util.h" @@ -90,20 +91,21 @@ std::string AtomConverter::ToString(const LV2_Atom*atom) } json_variant AtomConverter::MapPath(const json_variant&json, const std::string &pluginStoragePath) { - std::string oType = json[OTYPE_TAG].as_string(); - if (oType == SHORT_ATOM__Path) + if (json.is_null()) { - std::string path = json["value"].as_string().c_str(); - if (path.length() == 0 || path[0] == '/') - { - return json; + return EmptyPath(); + } + if (json.is_object() && json[OTYPE_TAG].as_string() == SHORT_ATOM__Path) + { + std::filesystem::path path = json["value"].as_string(); + if (path.empty()) { + return EmptyPath(); } - std::string newPath = SS(pluginStoragePath << "/" << path); - json_variant result = json_variant::make_object(); - result[OTYPE_TAG] = json_variant(SHORT_ATOM__Path); - result["value"] = newPath; - - return result; + if (path.is_relative()) + { + path = pluginStoragePath / path; + } + return TypedProperty(SHORT_ATOM__Path,path.string()); } else { return json; @@ -111,8 +113,7 @@ json_variant AtomConverter::MapPath(const json_variant&json, const std::string & } json_variant AtomConverter::AbstractPath(const json_variant&json, const std::string &pluginStoragePath) { - std::string oType = json[OTYPE_TAG].as_string(); - if (oType == SHORT_ATOM__Path) + if (json.is_object() && json[OTYPE_TAG].as_string() == SHORT_ATOM__Path) { std::string path = json["value"].as_string().c_str(); if (path.length() == 0) @@ -592,6 +593,10 @@ static bool gEmptyPathInitialized = false; static std::mutex gInitMutex; std::string gEmptyPathstring; + + +json_variant AtomConverter::gEmptyPath = TypedProperty(SHORT_ATOM__Path,std::string("")); + std::string AtomConverter::EmptyPathstring() { std::lock_guard lock{gInitMutex}; diff --git a/src/AtomConverter.hpp b/src/AtomConverter.hpp index 4336ae6..5000531 100644 --- a/src/AtomConverter.hpp +++ b/src/AtomConverter.hpp @@ -153,17 +153,21 @@ namespace pipedal std::string ToString(const LV2_Atom*atom); - json_variant MapPath(const json_variant&json, const std::string &pluginStoragePath); - json_variant AbstractPath(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 std::string EmptyPathstring(); + static const json_variant& EmptyPath() { return gEmptyPath; } + private: static const std::string OTYPE_TAG; static const std::string VTYPE_TAG; static const std::string ID_TAG; + static json_variant gEmptyPath; + std::map stringToTypeUrid; std::map typeUridToString; diff --git a/src/AudioHost.hpp b/src/AudioHost.hpp index 16a9fb3..17ad7da 100644 --- a/src/AudioHost.hpp +++ b/src/AudioHost.hpp @@ -56,6 +56,7 @@ namespace pipedal int64_t clientId; int64_t instanceId; LV2_URID uridUri; + std::string uri; enum class RequestType { diff --git a/src/CapturePresetsMain.cpp b/src/CapturePresetsMain.cpp index 79d0bbe..01d28a2 100644 --- a/src/CapturePresetsMain.cpp +++ b/src/CapturePresetsMain.cpp @@ -30,7 +30,7 @@ using namespace pipedal; using namespace std; using namespace std::filesystem; -Storage storage; +std::unique_ptr storage; static std::string GetPluginid(const std::string &pluginUrl) { @@ -136,7 +136,7 @@ void WritePrefixes(ofstream &f) static void WriteSeeAlso(ofstream &f, const std::string &uri) { std::string id = GetPluginid(uri); - PluginPresets presets = storage.GetPluginPresets(uri); + PluginPresets presets = storage->GetPluginPresets(uri); f << "###" << endl; @@ -197,7 +197,7 @@ void WritePresetFile(const path &outputDirectory, const std::string &uri) WritePrefixes(f); - PluginPresets presets = storage.GetPluginPresets(uri); + PluginPresets presets = storage->GetPluginPresets(uri); for (size_t i = 0; i < presets.presets_.size(); ++i) { @@ -273,6 +273,9 @@ void WritePresetFile(const path &outputDirectory, const std::string &uri) int main(int argc, const char *argv[]) { + + storage = std::make_unique(); + // 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. @@ -284,11 +287,11 @@ int main(int argc, const char *argv[]) try { - storage.SetConfigRoot("/etc/pipedal/config"); - storage.SetDataRoot("/var/pipedal"); - storage.Initialize(); + storage->SetConfigRoot("/etc/pipedal/config"); + storage->SetDataRoot("/var/pipedal"); + storage->Initialize(); - const PluginPresetIndex &index = storage.GetPluginPresetIndex(); + const PluginPresetIndex &index = storage->GetPluginPresetIndex(); std::filesystem::create_directories(outputPath); diff --git a/src/ModFileTypes.cpp b/src/ModFileTypes.cpp index 5d94d55..f230f09 100644 --- a/src/ModFileTypes.cpp +++ b/src/ModFileTypes.cpp @@ -49,7 +49,7 @@ static std::vector *CreateModDirectories() {"sfz", "shared/sfz", "Sfz Files", {".sfz"}}, // SFZ Instruments, must have sfz file extension // extensions observed in the field. {"audio", "shared/audio", "Audio", {"audio/*"}}, // all audio files (Ratatouille) - {"nammodel", "NeuralAmpModels", "Neural Amp Models", {".nam"}}, // Ratatouille, Mike's NAM. + {"nammodel", "NeuralAmpModels", "Neural Amp Models", {".nam" ,".aidax"}}, // Ratatouille, Mike's NAM. {"aidadspmodel", "shared/aidaaix", "AIDA IAX Models", {".json", ".aidaiax"}}, // Ratatouille {"mlmodel", "ToobMlModels", "ML Models", {".json"}}, // diff --git a/src/Pedalboard.hpp b/src/Pedalboard.hpp index cf9771e..18bc24f 100644 --- a/src/Pedalboard.hpp +++ b/src/Pedalboard.hpp @@ -125,6 +125,7 @@ public: GETTER_SETTER_VEC(bottomChain) GETTER_SETTER_VEC(midiBindings) GETTER_SETTER_REF(midiChannelBinding) + GETTER_SETTER_REF(pathProperties) GETTER_SETTER(stateUpdateCount) GETTER_SETTER_REF(lv2State) GETTER_SETTER_REF(title) @@ -134,6 +135,8 @@ public: GETTER_SETTER_REF(lilvPresetUri) PropertyMap&PatchProperties() { return patchProperties; } + const PropertyMap&PatchProperties() const { return patchProperties; } + bool isSplit() const { diff --git a/src/PiPedalModel.cpp b/src/PiPedalModel.cpp index 4d9c5fa..cc1fcbd 100644 --- a/src/PiPedalModel.cpp +++ b/src/PiPedalModel.cpp @@ -1721,23 +1721,20 @@ void PiPedalModel::SendSetPatchProperty( // save the property to the preset (currently used to reconstruct snapshots only) PedalboardItem *pedalboardItem = this->pedalboard.GetItem(instanceId); - if (pedalboardItem && value.is_string()) + if (pedalboardItem) { std::shared_ptr pluginInfo = GetPluginInfo(pedalboardItem->uri_); auto pipedalUi = pluginInfo->piPedalUI(); auto fileProperty = pipedalUi->GetFileProperty(propertyUri); - if (fileProperty && value.is_string()) + if (fileProperty) { json_variant abstractPath = pluginHost.AbstractPath(value); - std::ostringstream ss; - json_writer writer(ss); - writer.write(abstractPath); - std::string atomString = ss.str(); + std::string atomString = abstractPath.to_string(); pedalboardItem->pathProperties_[propertyUri] = atomString; } this->SetPresetChanged(clientId, true); - } +} LV2_Atom *atomValue = atomConverter.ToAtom(value); std::function onRequestComplete{ @@ -1827,7 +1824,24 @@ void PiPedalModel::SendGetPatchProperty( { if (pParameter->onError) { - pParameter->onError("No response."); + // For plugins that don't respond (e.g. a buncha MOD plugins), use the value we last set on the plugin! + bool foundValue = false; + std::lock_guard lock(mutex); + auto pedalboardItem = this->pedalboard.GetItem(pParameter->instanceId); + if (pedalboardItem) { + if (pedalboardItem->pathProperties_.contains(pParameter->uri)) + { + pParameter->jsonResponse = pedalboardItem->pathProperties_[pParameter->uri]; + if (pParameter->onSuccess) { + foundValue = true; + pParameter->onSuccess(pParameter->jsonResponse); + } + } + } + if (!foundValue) + { + pParameter->onError("No response."); + } } } else @@ -1854,6 +1868,7 @@ void PiPedalModel::SendGetPatchProperty( RealtimePatchPropertyRequest *request = new RealtimePatchPropertyRequest( onRequestComplete, clientId, instanceId, urid, onSuccess, onError, sampleTimeout); + request->uri = uri; outstandingParameterRequests.push_back(request); this->audioHost->sendRealtimeParameterRequest(request); @@ -2058,14 +2073,24 @@ void PiPedalModel::UpdateDefaults(PedalboardItem *pedalboardItem, std::unordered if (pPlugin->piPedalUI()) { PiPedalUI::ptr piPedalUI = pPlugin->piPedalUI(); + std::set validFileProperties; for (auto &fileProperty : piPedalUI->fileProperties()) { + validFileProperties.insert(fileProperty->patchProperty()); if (!pedalboardItem->pathProperties_.contains(fileProperty->patchProperty())) { // make sure each pedalboard item has a complete list of path properties, even if it doesn't yet have values. pedalboardItem->pathProperties_[fileProperty->patchProperty()] = "null"; } } + for (auto i = pedalboardItem->pathProperties_.begin(); i != pedalboardItem->pathProperties_.end(); /**/) + { + if (!validFileProperties.contains(i->first)) { + i = pedalboardItem->pathProperties_.erase(i); + } else { + ++i; + } + } } } else @@ -2162,8 +2187,9 @@ void PiPedalModel::LoadPluginPreset(int64_t pluginInstanceId, uint64_t presetIns this->pedalboard.SetControlValue(pluginInstanceId, control.key(), control.value()); } - if ((!presetValues.state.isValid_) && presetValues.lilvPresetUri.empty()) + if ((!presetValues.state.isValid_) && presetValues.lilvPresetUri.empty() && presetValues.pathProperties.empty()) { + // fast path for control changes only. audioHost->SetPluginPreset(pluginInstanceId, presetValues.controls); std::vector t{subscribers.begin(), subscribers.end()}; @@ -2177,6 +2203,7 @@ void PiPedalModel::LoadPluginPreset(int64_t pluginInstanceId, uint64_t presetIns pedalboardItem->lv2State(presetValues.state); pedalboardItem->lilvPresetUri(presetValues.lilvPresetUri); pedalboardItem->stateUpdateCount(oldStateUpdateCount + 1); + pedalboardItem->pathProperties(presetValues.pathProperties); FirePedalboardChanged(-1); // does a complete reload of both client and audio server. } this->SetPresetChanged(-1, true); @@ -2297,7 +2324,8 @@ void PiPedalModel::OnNotifyPathPatchPropertyReceived( auto i = pedalboardItem->pathProperties_.find(pathPatchPropertyUri); if (i != pedalboardItem->pathProperties_.end()) { - pedalboardItem->pathProperties_[pathPatchPropertyUri] = atomString; + std::string abstractAtomString = storage.ToAbstractPathJson(atomString); + pedalboardItem->pathProperties_[pathPatchPropertyUri] = abstractAtomString; std::vector t{subscribers.begin(), subscribers.end()}; for (auto &subscriber : t) @@ -2305,7 +2333,7 @@ void PiPedalModel::OnNotifyPathPatchPropertyReceived( subscriber->OnNotifyPathPatchPropertyChanged( instanceId, pathPatchPropertyUri, - atomString); + abstractAtomString); } } } @@ -2382,16 +2410,23 @@ void PiPedalModel::MonitorPatchProperty(int64_t clientId, int64_t clientHandle, PedalboardItem *item = this->pedalboard.GetItem(instanceId); if (item) { - auto &map = item->PatchProperties(); + auto &map = item->pathProperties(); if (map.contains(propertyUri)) { - const auto &value = map[propertyUri]; - std::string json = this->audioHost->AtomToJson(value.get()); - for (auto &subscriber : this->subscribers) - { - if (subscriber->GetClientId() == clientId) - { - subscriber->OnNotifyPatchProperty(clientHandle, instanceId, propertyUri, json); + const auto &value = map.at(propertyUri); + if (value != "null") { + try { + std::string json = storage.FromAbstractPathJson(value); + + for (auto &subscriber : this->subscribers) + { + if (subscriber->GetClientId() == clientId) + { + subscriber->OnNotifyPatchProperty(clientHandle, instanceId, propertyUri, json); + } + } + } catch (const std::exception& ignored) { + } } } diff --git a/src/PiPedalModel.hpp b/src/PiPedalModel.hpp index 9145be4..3e4c86c 100644 --- a/src/PiPedalModel.hpp +++ b/src/PiPedalModel.hpp @@ -279,7 +279,8 @@ namespace pipedal Decrease }; - bool GetHasWifi(); + Storage&GetStorage() { return storage; } + bool GetHasWifi(); void NextBank(Direction direction = Direction::Increase); void PreviousBank() { NextBank(Direction::Decrease); } diff --git a/src/PluginHost.cpp b/src/PluginHost.cpp index 78d9a12..d744454 100644 --- a/src/PluginHost.cpp +++ b/src/PluginHost.cpp @@ -49,7 +49,7 @@ #include "StdErrorCapture.hpp" #include "util.hpp" #include "ModFileTypes.hpp" -#include +#include #include "Locale.hpp" @@ -304,7 +304,6 @@ PluginHost::PluginHost() lv2Features.push_back(nullptr); this->urids = new Urids(mapFeature); - } void PluginHost::OnConfigurationChanged(const JackConfiguration &configuration, const JackChannelSelection &settings) @@ -660,13 +659,14 @@ static std::vector ToPiPedalFileTypes( std::string type = fileType; if (type.find("/") != std::string::npos) // mime type? { - UiFileType t = UiFileType(type,type,""); + UiFileType t = UiFileType(type, type, ""); result.push_back(t); } - else - { + else + { // add a leading dot. - if (!type.starts_with(".")) { + if (!type.starts_with(".")) + { type = "." + type; } auto t = UiFileType(SS(type << " file"), type); @@ -776,7 +776,8 @@ Lv2PluginInfo::FindWritablePathProperties(PluginHost *lv2Host, const LilvPlugin auto modDirectory = modFileTypes.GetModDirectory(modName); fileProperty->directory(modDirectory->pipedalPath); std::set fileExtensions = modDirectory->fileExtensions; - if (!modFileExtensions.empty()) { + if (!modFileExtensions.empty()) + { auto extensions = split(modFileExtensions, ','); fileExtensions = std::set(extensions.begin(), extensions.end()); } @@ -879,7 +880,6 @@ Lv2PluginInfo::Lv2PluginInfo(PluginHost *lv2Host, LilvWorld *pWorld, const LilvP this->plugin_class_ = "http://lv2plug.in/ns/lv2core#Plugin"; } - AutoLilvNodes required_features = lilv_plugin_get_required_features(pPlugin); this->required_features_ = nodeAsStringArray(required_features); @@ -1236,18 +1236,19 @@ Lv2PortInfo::Lv2PortInfo(PluginHost *host, const LilvPlugin *plugin, const LilvP if (unitsValueUri) { this->units_ = UriToUnits(nodeAsString(unitsValueUri)); - if (this->units_ == Units::unknown) { + if (this->units_ == Units::unknown) + { // try for a custom format string. AutoLilvNode render = lilv_world_get( pWorld, unitsValueUri, host->lilvUris->units__render, nullptr); - if (render) { - const char*strRender = lilv_node_as_string(render); + if (render) + { + const char *strRender = lilv_node_as_string(render); custom_units_ = strRender; } - } } else @@ -1630,7 +1631,13 @@ PluginPresets PluginHost::GetFactoryPluginPresets(const std::string &pluginUri) { if (!cbData.failed) { - result.presets_.push_back(PluginPreset(result.nextInstanceId_++, strLabel, controlValues, Lv2PluginState())); + result.presets_.push_back( + PluginPreset( + result.nextInstanceId_++, + strLabel, + controlValues, + std::map(), + Lv2PluginState())); } } else @@ -1904,11 +1911,13 @@ Lv2PatchPropertyInfo::Lv2PatchPropertyInfo(PluginHost *pluginHost, const LilvNod } // ffs. -static inline bool contains(const std::vector &vec, const std::string &value) { - return std::find(vec.begin(),vec.end(),value) != vec.end(); +static inline bool contains(const std::vector &vec, const std::string &value) +{ + return std::find(vec.begin(), vec.end(), value) != vec.end(); } -bool Lv2PluginInfo::WantsWorkerThread() const { - return contains(this->required_features_,LV2_WORKER__schedule) || contains(this->supported_features_,LV2_WORKER__schedule); +bool Lv2PluginInfo::WantsWorkerThread() const +{ + return contains(this->required_features_, LV2_WORKER__schedule) || contains(this->supported_features_, LV2_WORKER__schedule); } // void PiPedalHostLogError(const std::string &error) // { diff --git a/src/PluginPreset.cpp b/src/PluginPreset.cpp index 2d34e47..e13727b 100644 --- a/src/PluginPreset.cpp +++ b/src/PluginPreset.cpp @@ -98,6 +98,7 @@ JSON_MAP_BEGIN(PluginPreset) JSON_MAP_REFERENCE(PluginPreset,instanceId) JSON_MAP_REFERENCE(PluginPreset,label) JSON_MAP_REFERENCE(PluginPreset,controlValues) + JSON_MAP_REFERENCE(PluginPreset,pathProperties) JSON_MAP_REFERENCE(PluginPreset,state) JSON_MAP_REFERENCE(PluginPreset,lilvPresetUri) JSON_MAP_END() diff --git a/src/PluginPreset.hpp b/src/PluginPreset.hpp index d53906e..5daea14 100644 --- a/src/PluginPreset.hpp +++ b/src/PluginPreset.hpp @@ -76,6 +76,7 @@ public: PluginPreset(uint64_t instanceId, const std::string&label,const PedalboardItem&pedalboardItem) : instanceId_(instanceId) , label_(label) + , pathProperties_(pedalboardItem.pathProperties()) , state_(pedalboardItem.lv2State()) { for (auto & controlValue : pedalboardItem.controlValues()) @@ -89,10 +90,12 @@ public: const std::string&label, const std::map & controlValues, + const std::map&pathProperties, const Lv2PluginState &state) : instanceId_(instanceId), label_(label), controlValues_((controlValues)), + pathProperties_(pathProperties), state_(state) { } @@ -117,7 +120,9 @@ public: std::string label_; std::string lilvPresetUri_; std::map controlValues_; + std::map pathProperties_; Lv2PluginState state_; + DECLARE_JSON_MAP(PluginPreset); }; diff --git a/src/Storage.cpp b/src/Storage.cpp index 29872c5..7db950a 100644 --- a/src/Storage.cpp +++ b/src/Storage.cpp @@ -40,6 +40,7 @@ #include "util.hpp" #include "AudioFiles.hpp" #include "Utf8Utils.hpp" +#include "AtomConverter.hpp" using namespace pipedal; namespace fs = std::filesystem; @@ -189,6 +190,7 @@ void Storage::SetConfigRoot(const std::filesystem::path &path) void Storage::SetDataRoot(const std::filesystem::path &path) { this->dataRoot = ResolveHomePath(path); + this->dataRoot__audio_uploads = dataRoot / "audio_uploads"; } const std::filesystem::path &Storage::GetConfigRoot() @@ -302,9 +304,9 @@ std::filesystem::path Storage::GetPluginPresetsDirectory() const { return this->dataRoot / "plugin_presets"; } -std::filesystem::path Storage::GetPluginUploadDirectory() const +const std::filesystem::path &Storage::GetPluginUploadDirectory() const { - return this->dataRoot / "audio_uploads"; + return dataRoot__audio_uploads; } std::filesystem::path Storage::GetCurrentPresetPath() const @@ -1409,6 +1411,10 @@ PluginPresetValues Storage::GetPluginPresetValues(const std::string &pluginUri, { result.controls.push_back(ControlValue(valuePair.first.c_str(), valuePair.second)); } + for (const auto&pair: preset.pathProperties_) + { + result.pathProperties[pair.first] = pair.second; + } result.state = preset.state_; result.lilvPresetUri = preset.lilvPresetUri_; return result; @@ -1417,6 +1423,31 @@ PluginPresetValues Storage::GetPluginPresetValues(const std::string &pluginUri, throw PiPedalException("Plugin preset not found."); } +void Storage::ToAbstractPaths(PluginPreset &pluginPreset) +{ + if (pluginPreset.pathProperties_.size() != 0) + { + std::map newPathProperties; + for (const auto &pair : pluginPreset.pathProperties_) + { + newPathProperties[pair.first] = this->ToAbstractPathJson(pair.second); + } + pluginPreset.pathProperties_ = newPathProperties; + } +} +void Storage::FromAbstractPaths(PluginPreset &pluginPreset) +{ + if (pluginPreset.pathProperties_.size() != 0) + { + std::map newPathProperties; + for (const auto &pair : pluginPreset.pathProperties_) + { + newPathProperties[pair.first] = this->FromAbstractPathJson(pair.second); + } + pluginPreset.pathProperties_ = newPathProperties; + } +} + uint64_t Storage::SavePluginPreset( const std::string &name, const PedalboardItem &pedalboardItem) @@ -1425,6 +1456,7 @@ uint64_t Storage::SavePluginPreset( auto presets = GetPluginPresets(pluginUri); uint64_t result = -1; bool existing = false; + for (size_t i = 0; i < presets.presets_.size(); ++i) { auto &preset = presets.presets_[i]; @@ -1432,6 +1464,8 @@ uint64_t Storage::SavePluginPreset( { existing = true; result = preset.instanceId_; + PluginPreset pluginPreset{preset.instanceId_, preset.label_, pedalboardItem}; + presets.presets_[i] = PluginPreset(preset.instanceId_, preset.label_, pedalboardItem); break; } @@ -1439,10 +1473,12 @@ uint64_t Storage::SavePluginPreset( if (!existing) { result = presets.nextInstanceId_++; - presets.presets_.push_back( - PluginPreset(result, - name, - pedalboardItem)); + auto preset = PluginPreset( + result, + name, + pedalboardItem); + ToAbstractPaths(preset); + presets.presets_.push_back(preset); } this->SavePluginPresets(pluginUri, presets); return result; @@ -1451,9 +1487,8 @@ uint64_t Storage::SavePluginPreset( uint64_t Storage::SavePluginPreset( const std::string &pluginUri, const std::string &name, - float inputVolume, - float outputVolume, const std::map &values, + const std::map &pathProperties, const Lv2PluginState &lv2State) { auto presets = GetPluginPresets(pluginUri); @@ -1466,6 +1501,7 @@ uint64_t Storage::SavePluginPreset( { existing = true; preset.controlValues_ = values; + preset.pathProperties_ = pathProperties; preset.state_ = lv2State; result = preset.instanceId_; @@ -1480,6 +1516,7 @@ uint64_t Storage::SavePluginPreset( result, name, values, + pathProperties, lv2State)); } this->SavePluginPresets(pluginUri, presets); @@ -1533,7 +1570,7 @@ uint64_t Storage::CopyPluginPreset(const std::string &pluginUri, uint64_t preset size_t pos = presets.Find(presetId); if (pos == -1) { - throw PiPedalException("Perset not found."); + throw PiPedalException("Preset not found."); } PluginPreset t = presets.presets_[pos]; t.instanceId_ = presets.nextInstanceId_++; @@ -1922,7 +1959,7 @@ static void AddTracksToResult( FileRequestResult Storage::GetModFileList2(const std::string &relativePath, const UiFileProperty &fileProperty) { FileRequestResult result; - fs::path uploadsDirectory = GetPluginUploadDirectory(); + const fs::path &uploadsDirectory = GetPluginUploadDirectory(); if (relativePath.empty()) { @@ -2651,6 +2688,23 @@ std::string Storage::GetTone3000Auth() const return tone3000Auth; } +std::string Storage::ToAbstractPathJson(const std::string &pathJson) +{ + json_variant v = json_variant::parse(pathJson); + + v = AtomConverter::AbstractPath(v,GetPluginUploadDirectory().string()); + + return v.to_string(); +} +std::string Storage::FromAbstractPathJson(const std::string &pathJson) +{ + json_variant v = json_variant::parse(pathJson); + + v = AtomConverter::MapPath(v,GetPluginUploadDirectory().string()); + + return v.to_string(); +} + JSON_MAP_BEGIN(UserSettings) JSON_MAP_REFERENCE(UserSettings, governor) JSON_MAP_REFERENCE(UserSettings, showStatusMonitor) diff --git a/src/Storage.hpp b/src/Storage.hpp index 6d45d63..db6cbc4 100644 --- a/src/Storage.hpp +++ b/src/Storage.hpp @@ -60,15 +60,19 @@ public: struct PluginPresetValues { std::vector controls; + std::map pathProperties; Lv2PluginState state; std::string lilvPresetUri; }; + // controls user-defined storage. Implmentation hidden to allow to later migration to a database (perhaps) -class Storage { +class Storage { private: std::filesystem::path dataRoot; + std::filesystem::path dataRoot__audio_uploads; + std::filesystem::path configRoot; BankIndex bankIndex; BankFile currentBank; @@ -125,7 +129,11 @@ public: const std::filesystem::path&GetConfigRoot(); const std::filesystem::path&GetDataRoot(); - std::filesystem::path GetPluginUploadDirectory() const; + const std::filesystem::path &GetPluginUploadDirectory() const; + + std::string ToAbstractPathJson(const std::string &jsonAtomPath); + std::string FromAbstractPathJson(const std::string &jsonAtomPath); + //std::vector GetPedalboards(); @@ -208,6 +216,8 @@ private: std::filesystem::path GetPluginPresetPath(const std::string &pluginUri) const; bool IsValidSampleFileName(const std::filesystem::path&fileName); std::filesystem::path MakeUserFilePath(const std::string &directory, const std::string&filename); + void ToAbstractPaths(PluginPreset&pluginPreset); + void FromAbstractPaths(PluginPreset&pluginPreset); public: bool HasPluginPresets(const std::string&pluginUri) const; @@ -227,9 +237,8 @@ public: uint64_t SavePluginPreset( const std::string &pluginUri, const std::string &name, - float inputVolume, - float outputVolume, const std::map &values, + const std::map&pathProperties, const Lv2PluginState& lv2State); void UpdatePluginPresets(const PluginUiPresets &pluginPresets); diff --git a/src/WebServerConfig.cpp b/src/WebServerConfig.cpp index abda288..1fff419 100644 --- a/src/WebServerConfig.cpp +++ b/src/WebServerConfig.cpp @@ -494,7 +494,7 @@ public: res.set(HttpField::content_type, "application/json"); res.set(HttpField::cache_control, "no-cache"); - fs::path path = request_uri.query("path"); + fs::path path = model->GetStorage().FromAbstractPathJson(request_uri.query("path")); if (!fs::exists(path) || !this->model->IsInUploadsDirectory(path) || HasDotDot(path)) { diff --git a/todo.txt b/todo.txt index b3ca870..0cdcf93 100644 --- a/todo.txt +++ b/todo.txt @@ -1,26 +1,12 @@ -ffmpeg -ss -i input.mp4 -filter_complex "loop=loop=:size=:start=" output.mp4 --ss : Seeks to the desired start point (e.g., 00:00:10 or 10). -loop=: Number of times to loop the segment (e.g., 3 for 4 total plays). -size=: The number of frames in the segment to loop. -start=: The starting frame of the segment to loop within the input. -Note: This method requires re-encoding, as filters are applied. -3. Looping a single image: -To loop a single image for a specific duration, use the -loop option with the image demuxer: -Code - - -Use ffmpeg -Check ToOB Cab SIM F/R range. - -Convert CRVB presets! - double select on double-click/ok in file dialog. sudo apt install libbz2-dev -Move status to pedalboard view. -Default to dark theme. + +README, license files in file browser. + +ls Carla Project icons. check reload after change of LV2 plugins. diff --git a/vite/src/pipedal/ModFileTypes.tsx b/vite/src/pipedal/ModFileTypes.tsx index a03960f..e4f4f63 100644 --- a/vite/src/pipedal/ModFileTypes.tsx +++ b/vite/src/pipedal/ModFileTypes.tsx @@ -42,7 +42,7 @@ export const modDirectories: ModDirectory[] = [ {modType: "sfz", pipedalPath: "shared/sfz",displayName: "Sfz Files", defaultFileExtensions: [".sfz"]}, // SFZ Instruments, must have sfz file extension // extensions observed in the field. {modType: "audio", pipedalPath: "shared/audio", displayName: "Audio", defaultFileExtensions: ["audio/*"]}, // all audio files (Ratatoille) - {modType: "nammodel", pipedalPath: "NeuralAmpModels", displayName: "Neural Amp Models", defaultFileExtensions: [".nam"]}, // Ratatoille, Mike's NAM. + {modType: "nammodel", pipedalPath: "NeuralAmpModels", displayName: "Neural Amp Models", defaultFileExtensions: [".nam","aidax"]}, // Ratatoille, Mike's NAM. {modType: "aidadspmodel", pipedalPath: "shared/aidaaix", displayName: "AIDA IAX Models", defaultFileExtensions: [".json", ".aidaiax"]}, // Ratatoille {modType: "mlmodel", pipedalPath: "ToobMlModels", displayName: "ML Models", defaultFileExtensions: [".json"]}, // diff --git a/vite/src/pipedal/PiPedalModel.tsx b/vite/src/pipedal/PiPedalModel.tsx index 8984ddb..a42cef1 100644 --- a/vite/src/pipedal/PiPedalModel.tsx +++ b/vite/src/pipedal/PiPedalModel.tsx @@ -2623,7 +2623,8 @@ export class PiPedalModel //implements PiPedalModel let pedalboard = this.pedalboard.get(); try { let pedalboardItem = pedalboard.tryGetItem(instanceId); - if (pedalboardItem) { + + if (pedalboardItem && pedalboardItem.pathProperties[propertyUri] !== undefined) { pedalboardItem.pathProperties[propertyUri] = JSON.stringify(jsonObject); } } catch (ignored) {