MOD Gui support.

This commit is contained in:
Robin E. R. Davies
2025-07-10 11:37:02 -04:00
parent e7cdd38056
commit 890368352f
45 changed files with 1971 additions and 806 deletions
+41 -16
View File
@@ -100,7 +100,7 @@ Lv2Effect::Lv2Effect(
lilv_node_free(uriNode);
{
AutoLilvNode bundleUri = lilv_plugin_get_bundle_uri(pPlugin);
char *bundleUriString = lilv_file_uri_parse(bundleUri.AsUri().c_str(), nullptr);
char* bundleUriString = lilv_file_uri_parse(bundleUri.AsUri().c_str(), nullptr);
std::string storagePath = pHost_->GetPluginStoragePath();
@@ -108,7 +108,7 @@ Lv2Effect::Lv2Effect(
pHost_->GetMapFeature().GetMap(),
logFeature.GetLog(),
bundleUriString,
storagePath);
storagePath);
mapPathFeature.Prepare(&(pHost_->GetMapFeature()));
mapPathFeature.SetPluginStoragePath(pHost_->GetPluginStoragePath());
@@ -117,11 +117,12 @@ Lv2Effect::Lv2Effect(
const auto &fileProperties = info_->piPedalUI()->fileProperties();
for (const auto &fileProperty : fileProperties)
{
if (!fileProperty->resourceDirectory().empty())
{
mapPathFeature.AddResourceFileMapping({makeAbsolutePath(fileProperty->resourceDirectory(), bundleUriString),
makeAbsolutePath(fileProperty->directory(), pHost_->GetPluginStoragePath())});
}
fs::path targetPath = fileProperty->directory() / std::filesystem::path(bundleUriString).parent_path().filename();
mapPathFeature.AddResourceFileMapping({
bundleUriString,
storagePath / targetPath,
fileProperty->fileTypes()
});
}
}
@@ -243,14 +244,41 @@ Lv2Effect::Lv2Effect(
// 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());
RestoreState(pedalboardItem); // reload it with OUR map/unmap handling.
pedalboardItem.lilvPresetUri("");
}
else
{
RestoreState(pedalboardItem);
if (!RestoreState(pedalboardItem))
{
if (info->hasDefaultState())
{
// restore the default state.
try
{
// REsTORE from LV2_STATE__state default state.
AutoLilvNode pluginNode = lilv_new_uri(pWorld, info->uri().c_str());
LilvState *pState = lilv_state_new_from_world(pWorld, pHost->GetMapFeature().GetMap(), pluginNode);
if (pState)
{
if (this->stateInterface)
{
this->stateInterface->RestoreState(pState);
}
lilv_state_free(pState);
}
pedalboardItem.lv2State(this->stateInterface->Save());
RestoreState(pedalboardItem); // do it with OUR map/unmap file handling.
}
catch (const std::exception &e)
{
Lv2Log::warning(SS("Failed to restore default state for " << info->name() << ": " << e.what()));
}
}
}
}
}
void Lv2Effect::RestoreState(PedalboardItem &pedalboardItem)
bool Lv2Effect::RestoreState(PedalboardItem &pedalboardItem)
{
// Restore state if present.
if (this->stateInterface)
@@ -260,20 +288,18 @@ void Lv2Effect::RestoreState(PedalboardItem &pedalboardItem)
if (pedalboardItem.lv2State().isValid_)
{
this->stateInterface->Restore(pedalboardItem.lv2State());
return true;
}
else
{
// set the state to default state.
auto savedState = this->stateInterface->Save();
pedalboardItem.lv2State(savedState);
}
return false;
}
catch (const std::exception &e)
{
std::string name = pedalboardItem.pluginName();
Lv2Log::warning(SS(name << ": " << e.what()));
return false;
}
}
return true;
}
void Lv2Effect::ConnectControlPorts()
@@ -1116,4 +1142,3 @@ void Lv2Effect::SetPathPatchProperty(const std::string &propertyUri, const std::
{
mainThreadPathProperties[propertyUri] = jsonAtom;
}
+1 -1
View File
@@ -61,7 +61,7 @@ namespace pipedal
FileBrowserFilesFeature fileBrowserFilesFeature;
std::unique_ptr<StateInterface> stateInterface;
void RestoreState(PedalboardItem&pedalboardItem);
bool RestoreState(PedalboardItem&pedalboardItem);
LogFeature logFeature;
std::map<std::string,AtomBuffer> patchPropertyPrototypes;
+3
View File
@@ -65,5 +65,8 @@ namespace pipedal {
const LV2_URID_Map *GetMap() const { return &map;}
LV2_URID_Map *GetMap() { return &map;}
const LV2_URID_Unmap *GetUnmap() const { return &unmap;}
LV2_URID_Unmap *GetUnmap() { return &unmap;}
};
}
+44
View File
@@ -22,9 +22,12 @@
#include <algorithm>
#include "json.hpp"
#include <sstream>
#include "PluginHost.hpp"
#include "util.hpp"
using namespace pipedal;
namespace fs = std::filesystem;
MapPathFeature::MapPathFeature()
{
@@ -69,6 +72,27 @@ void MapPathFeature::FnFreePath(LV2_State_Free_Path_Handle handle, char* path)
return ((MapPathFeature *)handle)->AbsolutePath(abstract_path);
}
static std::string CreateMappathLink(const fs::path&sourceFile, const fs::path &resourceDirectory, const fs::path &targetDirectory)
{
try {
fs::path targetFile = targetDirectory / sourceFile.filename();
if (fs::exists(targetFile)) {
return targetFile;
}
fs::create_directories(targetDirectory);
fs::create_symlink(sourceFile,targetFile);
return targetFile;
}catch (const std::exception &e) {
Lv2Log::error(SS("Failed to convert resource file to media file. "
<< sourceFile
<< " -> "
<< targetDirectory
<< " (" << e.what() << ")"
));
return sourceFile;
}
}
char *MapPathFeature::AbsolutePath(const char *abstract_path)
{
if (strlen(abstract_path) == 0)
@@ -76,7 +100,27 @@ char *MapPathFeature::AbsolutePath(const char *abstract_path)
return strdup("");
}
std::filesystem::path t (abstract_path);
std::string extension = t.extension();
if (t.is_absolute()) {
// Handle bundle files, mapping them to the storage path for this plugin.
for (const auto&mapping: this->resourceFileMappings)
{
if (IsSubdirectory(t,mapping.resourcePath))
{
bool allowed = false;
for (const auto&fileType: mapping.fileTypes) {
if (fileType.IsValidExtension(extension)) {
allowed = true;
break;
}
}
if (allowed) {
std::string result = CreateMappathLink(t,mapping.resourcePath, mapping.storagePath);
return strdup(result.c_str());
}
}
}
return strdup(abstract_path);
}
std::filesystem::path result = storagePath / t;
+11 -2
View File
@@ -21,17 +21,24 @@
#include "lv2/state/state.h"
#include <filesystem>
#include "MapFeature.hpp"
#include <memory>
#include <vector>
#include <filesystem>
#include "PiPedalUI.hpp"
namespace pipedal
{
class Lv2PluginInfo;
struct ResourceFileMapping {
std::string resourcePath; // absolute path of the resource directory.
std::string storagePath; // absolute path of where resource directory files get placed.
std::filesystem::path resourcePath; // absolute path of the resource directory.
std::filesystem::path storagePath; // absolute path of where resource directory files get placed.
std::vector<UiFileType> fileTypes;
};
class MapPathFeature
{
public:
MapPathFeature();
void Prepare(MapFeature* map);
void SetPluginStoragePath(const std::string&path) { storagePath = path;}
@@ -46,6 +53,8 @@ namespace pipedal
const LV2_Feature*GetMakePathFeature() { return &makePathFeature;}
const LV2_Feature*GetFreePathFeature() { return &freePathFeature;}
private:
std::shared_ptr<Lv2PluginInfo> pluginInfo;
char *AbsolutePath(const char *abstract_path);
static char *FnAbsolutePath(LV2_State_Map_Path_Handle handle,
const char *abstract_path);
+1
View File
@@ -57,6 +57,7 @@ namespace pipedal
const std::vector<std::string> fileTypesX; // mixture of .ext and mime types. Use fileExtensions instead.
const std::set<std::string> fileExtensions;
};
static const std::vector<ModDirectory> &ModDirectories();
+1 -1
View File
@@ -439,7 +439,7 @@ json_variant pipedal::MakeModGuiTemplateData(
// Feed them no files; they will be added later.
json_variant filesObj = json_variant::make_array();
json_variant fileObj = json_variant::make_object();
fileObj["fileType"] = "{{filetype}}"; // magic tags that we will use to generate files at runtime.
fileObj["filetype"] = "{{filetype}}"; // magic tags that we will use to generate files at runtime.
fileObj["fullname"] = "{{fullname}}";
fileObj["basename"] = "{{basename}}";
filesObj.as_array()->push_back(fileObj);
+13
View File
@@ -115,6 +115,18 @@ const ControlValue* PedalboardItem::GetControlValue(const std::string&symbol) co
return nullptr;
}
bool Pedalboard::SetItemUseModUi(int64_t pedalItemId, bool enabled)
{
PedalboardItem*item = GetItem(pedalItemId);
if (!item) return false;
if (item->useModUi() != enabled)
{
item->useModUi(enabled);
return true;
}
return false;
}
bool Pedalboard::SetItemEnabled(int64_t pedalItemId, bool enabled)
{
PedalboardItem*item = GetItem(pedalItemId);
@@ -513,6 +525,7 @@ JSON_MAP_BEGIN(PedalboardItem)
JSON_MAP_REFERENCE(PedalboardItem,lilvPresetUri)
JSON_MAP_REFERENCE(PedalboardItem,pathProperties)
JSON_MAP_REFERENCE(PedalboardItem,title)
JSON_MAP_REFERENCE(PedalboardItem,useModUi)
JSON_MAP_END()
+3
View File
@@ -99,6 +99,7 @@ public:
std::string lilvPresetUri_;
std::map<std::string,std::string> pathProperties_;
std::string title_;
bool useModUi_ = false;
// non persistent state.
PropertyMap patchProperties;
@@ -128,6 +129,7 @@ public:
GETTER_SETTER(stateUpdateCount)
GETTER_SETTER_REF(lv2State)
GETTER_SETTER_REF(title)
GETTER_SETTER(useModUi)
Lv2PluginState&lv2State() { return lv2State_; } // non-const version.
GETTER_SETTER_REF(lilvPresetUri)
@@ -203,6 +205,7 @@ public:
bool SetControlValue(int64_t pedalItemId, const std::string &symbol, float value);
bool SetItemTitle(int64_t pedalItemId, const std::string &title);
bool SetItemEnabled(int64_t pedalItemId, bool enabled);
bool SetItemUseModUi(int64_t pedalItemId, bool enabled);
void SetCurrentSnapshotModified(bool modified);
bool IsStructureIdentical(const Pedalboard &other) const; // caan we just send a snapshot-style uddate instead of reloading plugins? All settings are ignored.
+18
View File
@@ -617,6 +617,24 @@ void PiPedalModel::UpdateCurrentPedalboard(int64_t clientId, Pedalboard &pedalbo
}
}
void PiPedalModel::SetPedalboardItemUseModUi(int64_t clientId, int64_t instanceId, bool enabled)
{
std::lock_guard<std::recursive_mutex> guard{mutex};
{
this->pedalboard.SetItemUseModUi(instanceId, enabled);
// Notify clients.
std::vector<IPiPedalModelSubscriber::ptr> t{subscribers.begin(), subscribers.end()};
for (auto &subscriber : t)
{
subscriber->OnItemUseModUiChanged(clientId, instanceId, enabled);
}
this->SetPresetChanged(clientId, true);
}
}
void PiPedalModel::SetPedalboardItemEnable(int64_t clientId, int64_t pedalItemId, bool enabled)
{
std::lock_guard<std::recursive_mutex> guard{mutex};
+2
View File
@@ -59,6 +59,7 @@ namespace pipedal
virtual int64_t GetClientId() = 0;
virtual void OnItemEnabledChanged(int64_t clientId, int64_t pedalItemId, bool enabled) = 0;
virtual void OnItemUseModUiChanged(int64_t clientId, int64_t pedalItemId, bool enabled) = 0;
virtual void OnControlChanged(int64_t clientId, int64_t pedalItemId, const std::string &symbol, float value) = 0;
virtual void OnInputVolumeChanged(float value) = 0;
virtual void OnOutputVolumeChanged(float value) = 0;
@@ -348,6 +349,7 @@ namespace pipedal
void RemoveNotificationSubsription(std::shared_ptr<IPiPedalModelSubscriber> pSubscriber);
void SetPedalboardItemEnable(int64_t clientId, int64_t instanceId, bool enabled);
void SetPedalboardItemUseModUi(int64_t clientId, int64_t instanceId, bool enabled);
void SetControl(int64_t clientId, int64_t pedalItemId, const std::string &symbol, float value);
void PreviewControl(int64_t clientId, int64_t pedalItemId, const std::string &symbol, float value);
+30
View File
@@ -428,6 +428,22 @@ JSON_MAP_REFERENCE(PedalboardItemEnabledBody, instanceId)
JSON_MAP_REFERENCE(PedalboardItemEnabledBody, enabled)
JSON_MAP_END()
class PedalboardItemUseModGuiBody
{
public:
int64_t clientId_ = -1;
int64_t instanceId_ = -1;
bool useModUi_ = true;
DECLARE_JSON_MAP(PedalboardItemUseModGuiBody);
};
JSON_MAP_BEGIN(PedalboardItemUseModGuiBody)
JSON_MAP_REFERENCE(PedalboardItemUseModGuiBody, clientId)
JSON_MAP_REFERENCE(PedalboardItemUseModGuiBody, instanceId)
JSON_MAP_REFERENCE(PedalboardItemUseModGuiBody, useModUi)
JSON_MAP_END()
class UpdateCurrentPedalboardBody
{
public:
@@ -1303,6 +1319,11 @@ public:
pReader->read(&body);
model.SetPedalboardItemEnable(body.clientId_, body.instanceId_, body.enabled_);
}
else if (message == "setPedalboardItemUseModUi") {
PedalboardItemUseModGuiBody body;
pReader->read(&body);
model.SetPedalboardItemUseModUi(body.clientId_, body.instanceId_, body.useModUi_);
}
else if (message == "updateCurrentPedalboard")
{
{
@@ -2221,6 +2242,15 @@ private:
body.enabled_ = enabled;
Send("onItemEnabledChanged", body);
}
virtual void OnItemUseModUiChanged(int64_t clientId, int64_t pedalItemId, bool enabled)
{
PedalboardItemEnabledBody body;
body.clientId_ = clientId;
body.instanceId_ = pedalItemId;
body.enabled_ = enabled;
Send("onUseItemModUiChanged", body);
}
};
std::atomic<uint64_t> PiPedalSocketHandler::nextClientId = 0;
+8
View File
@@ -140,6 +140,7 @@ UiFileType::UiFileType(PluginHost *pHost, const LilvNode *node)
UiFileProperty::UiFileProperty(PluginHost *pHost, const LilvNode *node, const std::filesystem::path &resourcePath)
{
auto pWorld = pHost->getWorld();
@@ -455,6 +456,13 @@ bool UiFileType::IsValidExtension(const std::string&extension) const {
return false;
}
UiFileType::UiFileType(const std::string&label, const std::string &mimeType,const std::string &fileType)
: label_(label)
, mimeType_(mimeType)
, fileExtension_(fileType)
{
}
UiFileType::UiFileType(const std::string&label, const std::string &fileType)
: label_(label)
, fileExtension_(fileType)
+7
View File
@@ -75,6 +75,7 @@ namespace pipedal
{
class PluginHost;
class ModFileTypes;
class UiFileType
{
@@ -87,12 +88,16 @@ namespace pipedal
UiFileType() {}
UiFileType(PluginHost *pHost, const LilvNode *node);
UiFileType(const std::string &label, const std::string &fileType);
UiFileType(const std::string &label, const std::string&mimeType, const std::string &fileType);
static std::vector<UiFileType> GetArray(PluginHost *pHost, const LilvNode *node, const LilvNode *uri);
const std::string &label() const { return label_; }
void label(const std::string &value) { label_ = value; }
const std::string &fileExtension() const { return fileExtension_; }
void fileExtension(const std::string &value) { fileExtension_ = value; }
const std::string &mimeType() const { return mimeType_; }
void mimeType(const std::string &value) { mimeType_ = value; }
bool IsValidExtension(const std::string &extension) const;
public:
@@ -160,6 +165,8 @@ namespace pipedal
const std::vector<UiFileType> &fileTypes() const { return fileTypes_; }
std::vector<UiFileType> &fileTypes() { return fileTypes_; }
void fileTypes(const std::vector<UiFileType> &value) { fileTypes_ = value; }
void fileTypes(std::vector<UiFileType> &&value) { fileTypes_ = std::move(value); }
const std::string &patchProperty() const { return patchProperty_; }
+83 -11
View File
@@ -647,6 +647,37 @@ bool Lv2PluginInfo::HasFactoryPresets(PluginHost *lv2Host, const LilvPlugin *plu
return result;
}
static std::vector<UiFileType> ToPiPedalFileTypes(
const std::set<std::string> &modFileTypes)
{
std::vector<UiFileType> result;
for (const auto &fileType : modFileTypes)
{
UiFileType uiFileType;
std::string type = fileType;
if (type.find("/") != std::string::npos) // mime type?
{
UiFileType t = UiFileType(type,type,"");
result.push_back(t);
}
else
{
// add a leading dot.
if (!type.starts_with(".")) {
type = "." + type;
}
auto t = UiFileType(SS(type << " file"), type);
result.push_back(t);
}
}
std::sort(result.begin(), result.end(),
[](const UiFileType &left, const UiFileType &right)
{
return left.label() < right.label();
});
return result;
}
Lv2PluginInfo::FindWritablePathPropertiesResult
Lv2PluginInfo::FindWritablePathProperties(PluginHost *lv2Host, const LilvPlugin *pPlugin)
{
@@ -710,6 +741,13 @@ Lv2PluginInfo::FindWritablePathProperties(PluginHost *lv2Host, const LilvPlugin
// "nam,nammodel"
fileTypes = mod__fileTypes.AsString();
}
std::string modFileExtensions;
AutoLilvNode mod__extensionsNode = lilv_world_get(pWorld, propertyUri, lv2Host->lilvUris->mod__supportedExtensions, nullptr);
{
// "wav,flac,mp3"
modFileExtensions = mod__extensionsNode.AsString();
}
std::string fileExtensions = modFileExtensions;
ModFileTypes modFileTypes(fileTypes);
// Legacy case: audio_uploads/<plugin_directory> exists.
@@ -734,6 +772,12 @@ Lv2PluginInfo::FindWritablePathProperties(PluginHost *lv2Host, const LilvPlugin
std::string modName = modFileTypes.rootDirectories()[0];
auto modDirectory = modFileTypes.GetModDirectory(modName);
fileProperty->directory(modDirectory->pipedalPath);
std::set<std::string> fileExtensions = modDirectory->fileExtensions;
if (!modFileExtensions.empty()) {
auto extensions = split(modFileExtensions, ',');
fileExtensions = std::set<std::string>(extensions.begin(), extensions.end());
}
fileProperty->fileTypes(ToPiPedalFileTypes(fileExtensions));
}
}
@@ -750,8 +794,11 @@ Lv2PluginInfo::FindWritablePathProperties(PluginHost *lv2Host, const LilvPlugin
if (fileProperties.size() != 0)
{
std::sort(fileProperties.begin(), fileProperties.end(), [](const UiFileProperty::ptr &left, const UiFileProperty::ptr &right)
{
std::sort(
fileProperties.begin(),
fileProperties.end(),
[](const UiFileProperty::ptr &left, const UiFileProperty::ptr &right)
{
// properies with indexes first.
int32_t indexL = left->index();
if (indexL < 0)
@@ -888,7 +935,7 @@ Lv2PluginInfo::Lv2PluginInfo(PluginHost *lv2Host, LilvWorld *pWorld, const LilvP
// a lv2:Parameter?
if (lilv_world_ask(pWorld, propertyUri, lv2Host->lilvUris->isA, lv2Host->lilvUris->lv2core__Parameter))
{
Lv2PatchPropertyInfo propertyInfo{lv2Host, propertyUri};
Lv2PatchPropertyInfo propertyInfo{lv2Host, propertyUri};
propertyInfo.writable(true);
patchProperties_.push_back(std::move(propertyInfo));
}
@@ -915,18 +962,33 @@ Lv2PluginInfo::Lv2PluginInfo(PluginHost *lv2Host, LilvWorld *pWorld, const LilvP
break;
}
}
if (!found)
if (!found)
{
if (lilv_world_ask(pWorld, propertyUri, lv2Host->lilvUris->isA, lv2Host->lilvUris->lv2core__Parameter))
{
Lv2PatchPropertyInfo propertyInfo{lv2Host, propertyUri};
Lv2PatchPropertyInfo propertyInfo{lv2Host, propertyUri};
propertyInfo.readable(true);
patchProperties_.push_back(std::move(propertyInfo));
}
}
}
}
// default state.
{
AutoLilvNodes stateNodes = lilv_plugin_get_value(pPlugin, lv2Host->lilvUris->state__state);
if (stateNodes)
{
auto stateNode = lilv_nodes_get_first(stateNodes);
LilvState *defaultState = lilv_state_new_from_world(pWorld, lv2Host->GetMapFeature().GetMap(), stateNode);
if (defaultState)
{
Lv2Log::debug("Plugin %s (%s) has default state.", this->name_.c_str(), this->uri_.c_str());
this->hasDefaultState_ = true;
lilv_state_free(defaultState);
}
}
}
}
// Fetch pipedal plugin UI
@@ -976,6 +1038,10 @@ Lv2PluginInfo::Lv2PluginInfo(PluginHost *lv2Host, LilvWorld *pWorld, const LilvP
++nOutputs;
}
}
if (nOutputs == 0 || nInputs == 0)
{
isValid = false;
}
this->modGui_ = ModGui::Create(lv2Host, pPlugin);
this->is_valid_ = isValid;
}
@@ -1776,7 +1842,7 @@ Lv2PatchPropertyInfo::Lv2PatchPropertyInfo(PluginHost *pluginHost, const LilvNod
{
LilvWorld *pWorld = pluginHost->getWorld();
this->uri_ = nodeAsString(propertyUri);
AutoLilvNode range = lilv_world_get(pWorld, propertyUri, pluginHost->lilvUris->rdfs__range, nullptr);
if (range)
{
@@ -1791,7 +1857,9 @@ Lv2PatchPropertyInfo::Lv2PatchPropertyInfo(PluginHost *pluginHost, const LilvNod
if (shortName)
{
this->shortName_ = shortName.AsString();
} else {
}
else
{
this->shortName_ = this->label_;
}
@@ -1799,14 +1867,16 @@ Lv2PatchPropertyInfo::Lv2PatchPropertyInfo(PluginHost *pluginHost, const LilvNod
this->index_ = indexNode.AsInt(-1);
AutoLilvNode modFileTypesNode = lilv_world_get(pWorld, propertyUri, pluginHost->lilvUris->mod__fileTypes, nullptr);
if (modFileTypesNode) {
if (modFileTypesNode)
{
std::string strFileTypes = modFileTypesNode.AsString();
this->fileTypes_ = split(strFileTypes,',');
this->fileTypes_ = split(strFileTypes, ',');
}
AutoLilvNode modSupportedExtensionsNode = lilv_world_get(pWorld, propertyUri, pluginHost->lilvUris->mod__supportedExtensions, nullptr);
if (modSupportedExtensionsNode) {
if (modSupportedExtensionsNode)
{
std::string strSupportedExtensions = modSupportedExtensionsNode.AsString();
this->supportedExtensions_ = split(strSupportedExtensions,',');
this->supportedExtensions_ = split(strSupportedExtensions, ',');
}
}
@@ -1908,6 +1978,8 @@ json_map::storage_type<Lv2PluginInfo> Lv2PluginInfo::jmap{{
json_map::reference("has_factory_presets", &Lv2PluginInfo::has_factory_presets_),
json_map::reference("modGui", &Lv2PluginInfo::modGui_),
json_map::reference("patchProperties", &Lv2PluginInfo::patchProperties_),
json_map::reference("hasDefaultState", &Lv2PluginInfo::hasDefaultState_),
}};
json_map::storage_type<Lv2PluginClass> Lv2PluginClass::jmap{{
+4 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2022 Robin Davies
// Copyright (c) 2025 Robin E. R. Davies
//
// 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
@@ -417,6 +417,8 @@ namespace pipedal
std::vector<std::shared_ptr<Lv2PortInfo>> ports_;
std::vector<std::shared_ptr<Lv2PortGroup>> port_groups_;
std::vector<Lv2PatchPropertyInfo> patchProperties_;
bool hasDefaultState_;
bool is_valid_ = false;
PiPedalUI::ptr piPedalUI_;
@@ -450,6 +452,7 @@ namespace pipedal
LV2_PROPERTY_GETSET(hasUnsupportedPatchProperties)
LV2_PROPERTY_GETSET(modGui)
LV2_PROPERTY_GETSET(patchProperties)
LV2_PROPERTY_GETSET(hasDefaultState)
const Lv2PortInfo &getPort(const std::string &symbol)
{
+4 -1
View File
@@ -23,6 +23,7 @@
#include "json.hpp"
#include <sstream>
#include <string.h>
#include "Lv2Log.hpp"
using namespace pipedal;
@@ -83,6 +84,7 @@ static void CheckState(LV2_State_Status status)
case LV2_State_Status::LV2_STATE_SUCCESS:
return;
default:
case LV2_State_Status::LV2_STATE_ERR_UNKNOWN:
lv2Error = "Unknown error.";
break;
case LV2_State_Status::LV2_STATE_ERR_BAD_TYPE:
@@ -126,7 +128,8 @@ Lv2PluginState StateInterface::Save()
}
catch (const std::exception &e)
{
throw std::logic_error(SS("State save failed. " << e.what()));
Lv2Log::debug(SS("State save failed. " << e.what()));
return Lv2PluginState(); // an invalid state.
}
return std::move(callState.state);
+2
View File
@@ -103,6 +103,8 @@ static void setCacheControl(HttpResponse &res, const fs::path &path)
res.set("Cache-Control", "public, max-age=31536000"); // 1 month
auto lastModified = std::filesystem::last_write_time(path);
res.set(HttpField::LastModified, HtmlHelper::timeToHttpDate(lastModified));
// Set ETag for cache validation
res.set("ETag", pipedal::HtmlHelper::generateEtag(path));
}
else
{