MOD Gui support.
This commit is contained in:
+41
-16
@@ -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
@@ -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;
|
||||
|
||||
|
||||
@@ -65,5 +65,8 @@ namespace pipedal {
|
||||
const LV2_URID_Map *GetMap() const { return ↦}
|
||||
LV2_URID_Map *GetMap() { return ↦}
|
||||
|
||||
const LV2_URID_Unmap *GetUnmap() const { return &unmap;}
|
||||
LV2_URID_Unmap *GetUnmap() { return &unmap;}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user