Import Prests from Bank

This commit is contained in:
Robin E. R. Davies
2025-09-20 07:27:51 -04:00
parent 9470699527
commit b25e90df7b
15 changed files with 1215 additions and 400 deletions
+299 -271
View File
@@ -23,316 +23,344 @@
#include "Pedalboard.hpp"
#include "PiPedalException.hpp"
namespace pipedal {
namespace pipedal
{
#define GETTER_SETTER_REF(name) \
decltype(name##_)& name() { return name##_;} \
const decltype(name##_)& name() const { return name##_;} \
#define GETTER_SETTER_REF(name) \
decltype(name##_) &name() { return name##_; } \
const decltype(name##_) &name() const { return name##_; } \
void name(const decltype(name##_) &value) { name##_ = value; }
#define GETTER_SETTER_VEC(name) \
decltype(name##_)& name() { return name##_;} \
const decltype(name##_)& name() const { return name##_;}
#define GETTER_SETTER_VEC(name) \
decltype(name##_) &name() { return name##_; } \
const decltype(name##_) &name() const { return name##_; }
#define GETTER_SETTER(name) \
decltype(name##_) name() const { return name##_;} \
#define GETTER_SETTER(name) \
decltype(name##_) name() const { return name##_; } \
void name(decltype(name##_) value) { name##_ = value; }
class PresetIndexEntry
{
int64_t instanceId_ = 0;
std::string name_;
class PresetIndexEntry {
int64_t instanceId_ = 0;
std::string name_;
public:
GETTER_SETTER(instanceId);
GETTER_SETTER(name);
DECLARE_JSON_MAP(PresetIndexEntry);
};
class PresetIndex {
int64_t selectedInstanceId_ = -1;
bool presetChanged_ = false;
std::vector<PresetIndexEntry> presets_;
public:
bool GetPresetName(int64_t instanceId, std::string*pResult) {
for (size_t i = 0; i < presets_.size(); ++i)
public:
PresetIndexEntry() {}
PresetIndexEntry(uint64_t instanceId, const std::string &name)
: instanceId_(instanceId), name_(name)
{
if (presets_[i].instanceId() == instanceId)
}
GETTER_SETTER(instanceId);
GETTER_SETTER(name);
DECLARE_JSON_MAP(PresetIndexEntry);
};
class PresetIndex
{
int64_t selectedInstanceId_ = -1;
bool presetChanged_ = false;
std::vector<PresetIndexEntry> presets_;
public:
bool GetPresetName(int64_t instanceId, std::string *pResult)
{
for (size_t i = 0; i < presets_.size(); ++i)
{
*pResult = presets_[i].name();
return true;
}
}
return false;
}
GETTER_SETTER(selectedInstanceId);
GETTER_SETTER_VEC(presets);
GETTER_SETTER(presetChanged);
DECLARE_JSON_MAP(PresetIndex);
};
class BankFileEntry {
int64_t instanceId_;
Pedalboard preset_;
public:
GETTER_SETTER(instanceId);
GETTER_SETTER_REF(preset);
DECLARE_JSON_MAP(BankFileEntry);
};
class BankFile {
std::string name_;
int64_t nextInstanceId_ = 0;
int64_t selectedPreset_ = -1;
std::vector<std::unique_ptr<BankFileEntry>> presets_;
public:
GETTER_SETTER(name);
GETTER_SETTER(nextInstanceId);
GETTER_SETTER(selectedPreset);
GETTER_SETTER_VEC(presets);
void clear()
{
nextInstanceId_ = 0;
presets_.clear();
selectedPreset_ = -1;
}
void move(size_t from, size_t to)
{
if (from >= this->presets_.size()) {
throw std::invalid_argument("Argument out of range.");
}
if (to >= this->presets_.size()) {
throw std::invalid_argument("Argument out of range.");
}
std::unique_ptr<BankFileEntry> t = std::move(this->presets_[from]);
presets_.erase(presets_.begin()+from);
presets_.insert(presets_.begin()+to,std::move(t));
}
void updateNextIndex() {
int64_t t = 0;
for (size_t i = 0; i < this->presets_.size(); ++i)
{
int64_t instanceId = this->presets_[i]->instanceId();
if (instanceId > t) t = instanceId;
}
this->nextInstanceId_ = t;
}
int64_t addPreset(const Pedalboard&preset, int64_t afterItem = -1)
{
if (hasName(preset.name()))
{
throw PiPedalStateException("A preset by that name already exists.");
}
std::unique_ptr<BankFileEntry> entry = std::make_unique<BankFileEntry>();
entry->instanceId(++nextInstanceId_);
entry->preset(preset);
int64_t instanceId = entry->instanceId();
if (afterItem == -1)
{
this->presets_.push_back(std::move(entry));
} else {
for (auto it = this->presets_.begin(); it != this->presets_.end(); ++it)
{
if ((*it)->instanceId() == afterItem)
if (presets_[i].instanceId() == instanceId)
{
++it;
this->presets_.insert(it,std::move(entry));
break;
*pResult = presets_[i].name();
return true;
}
}
return false;
}
return instanceId;
}
bool hasItem(int64_t instanceId)
{
for (size_t i = 0; i < presets_.size(); ++i)
{
if (presets_[i]->instanceId() == instanceId)
{
return true;
}
}
return false;
}
bool hasName(const std::string &name) {
for (size_t i = 0; i < presets_.size(); ++i)
{
if (presets_[i]->preset().name() == name)
{
return true;
}
}
return false;
}
bool renamePreset(int64_t instanceId, const std::string&name)
{
if (hasName(name))
{
throw PiPedalStateException("A preset by that name already exists.");
}
for (size_t i = 0; i < presets_.size(); ++i)
{
if (presets_[i]->instanceId() ==instanceId)
{
presets_[i]->preset().name(name);
return true;
}
}
return false;
GETTER_SETTER(selectedInstanceId);
GETTER_SETTER_VEC(presets);
GETTER_SETTER(presetChanged);
}
DECLARE_JSON_MAP(PresetIndex);
};
BankFileEntry &getItem(int64_t instanceId)
class BankFileEntry
{
for (size_t i = 0; i < presets_.size(); ++i)
int64_t instanceId_;
Pedalboard preset_;
public:
GETTER_SETTER(instanceId);
GETTER_SETTER_REF(preset);
DECLARE_JSON_MAP(BankFileEntry);
};
class BankFile
{
std::string name_;
int64_t nextInstanceId_ = 0;
int64_t selectedPreset_ = -1;
std::vector<std::unique_ptr<BankFileEntry>> presets_;
public:
GETTER_SETTER(name);
GETTER_SETTER(nextInstanceId);
GETTER_SETTER(selectedPreset);
GETTER_SETTER_VEC(presets);
void clear()
{
if (presets_[i]->instanceId() == instanceId)
{
return *(presets_[i].get());
}
nextInstanceId_ = 0;
presets_.clear();
selectedPreset_ = -1;
}
throw PiPedalArgumentException("Instance not found.");
}
int64_t deletePreset(int64_t instanceId) {
for (size_t i = 0; i < presets_.size(); ++i)
void move(size_t from, size_t to)
{
if (presets_[i]->instanceId() == instanceId)
if (from >= this->presets_.size())
{
presets_.erase(presets_.begin()+i);
int64_t newSelection;
if (i < presets_.size())
throw std::invalid_argument("Argument out of range.");
}
if (to >= this->presets_.size())
{
throw std::invalid_argument("Argument out of range.");
}
std::unique_ptr<BankFileEntry> t = std::move(this->presets_[from]);
presets_.erase(presets_.begin() + from);
presets_.insert(presets_.begin() + to, std::move(t));
}
void updateNextIndex()
{
int64_t t = 0;
for (size_t i = 0; i < this->presets_.size(); ++i)
{
int64_t instanceId = this->presets_[i]->instanceId();
if (instanceId > t)
t = instanceId;
}
this->nextInstanceId_ = t;
}
int64_t addPreset(const Pedalboard &preset, int64_t afterItem = -1)
{
if (hasName(preset.name()))
{
throw PiPedalStateException("A preset by that name already exists.");
}
std::unique_ptr<BankFileEntry> entry = std::make_unique<BankFileEntry>();
entry->instanceId(++nextInstanceId_);
entry->preset(preset);
int64_t instanceId = entry->instanceId();
if (afterItem == -1)
{
this->presets_.push_back(std::move(entry));
}
else
{
for (auto it = this->presets_.begin(); it != this->presets_.end(); ++it)
{
newSelection = presets_[i]->instanceId();
} else if (presets_.size() > 1) {
newSelection = presets_[0]->instanceId();
} else {
// zero length? We can never have a zero-length bank.
// Add a default preset and make it the selected preset.
Pedalboard pedalboard = Pedalboard::MakeDefault();
this->addPreset(pedalboard);
newSelection = presets_[0]->instanceId();
if ((*it)->instanceId() == afterItem)
{
++it;
this->presets_.insert(it, std::move(entry));
break;
}
}
if (instanceId == this->selectedPreset_)
}
return instanceId;
}
bool hasItem(int64_t instanceId)
{
for (size_t i = 0; i < presets_.size(); ++i)
{
if (presets_[i]->instanceId() == instanceId)
{
this->selectedPreset_ = newSelection;
return true;
}
return newSelection;
}
return false;
}
throw PiPedalStateException("Preset not found.");
}
DECLARE_JSON_MAP(BankFile);
};
class BankIndexEntry {
int64_t instanceId_ = 0;
std::string name_;
public:
GETTER_SETTER(instanceId);
GETTER_SETTER_REF(name);
DECLARE_JSON_MAP(BankIndexEntry);
};
class BankIndex {
int64_t selectedBank_ = 0;
int64_t nextInstanceId_ = 0;
std::vector<BankIndexEntry> entries_;
public:
GETTER_SETTER(selectedBank);
GETTER_SETTER_VEC(entries);
DECLARE_JSON_MAP(BankIndex);
bool hasName(const std::string&name) const {
for (size_t i = 0; i < this->entries_.size(); ++i)
bool hasName(const std::string &name)
{
if (this->entries_[i].name() == name) return true;
}
return false;
}
void move(size_t from, size_t to)
{
if (from >= this->entries_.size()) {
throw std::invalid_argument("Argument out of range.");
}
if (to >= this->entries_.size()) {
throw std::invalid_argument("Argument out of range.");
}
BankIndexEntry t = std::move(this->entries_[from]);
entries_.erase(entries_.begin()+from);
entries_.insert(entries_.begin()+to,std::move(t));
}
void clear() {
entries_.clear();
selectedBank_ = 0;
}
int64_t addBank(int64_t afterId,const std::string& name)
{
BankIndexEntry bank;
bank.name(name);
bank.instanceId(++nextInstanceId_);
for (size_t i = 0; i < this->entries_.size(); ++i)
{
if (entries_[i].instanceId() == afterId)
for (size_t i = 0; i < presets_.size(); ++i)
{
entries_.insert(entries_.begin()+(i+1), bank);
return bank.instanceId();
if (presets_[i]->preset().name() == name)
{
return true;
}
}
return false;
}
// else at the end.
entries_.push_back(bank);
return bank.instanceId();
}
BankIndexEntry* getEntryByName(const std::string &name)
{
for (size_t i = 0; i < entries_.size(); ++i)
bool renamePreset(int64_t instanceId, const std::string &name)
{
if (entries_[i].name() == name)
if (hasName(name))
{
return &entries_[i];
throw PiPedalStateException("A preset by that name already exists.");
}
}
return nullptr;
}
BankIndexEntry&getBankIndexEntry(int64_t instanceId)
{
for (size_t i = 0; i < entries_.size(); ++i)
{
if (entries_[i].instanceId() == instanceId)
for (size_t i = 0; i < presets_.size(); ++i)
{
return entries_[i];
if (presets_[i]->instanceId() == instanceId)
{
presets_[i]->preset().name(name);
return true;
}
}
return false;
}
throw PiPedalArgumentException("Bank not found.");
}
const BankIndexEntry&getBankIndexEntry(int64_t instanceId) const
{
for (size_t i = 0; i < entries_.size(); ++i)
{
if (entries_[i].instanceId() == instanceId)
{
return entries_[i];
}
}
throw PiPedalArgumentException("Bank not found.");
}
};
BankFileEntry &getItem(int64_t instanceId)
{
for (size_t i = 0; i < presets_.size(); ++i)
{
if (presets_[i]->instanceId() == instanceId)
{
return *(presets_[i].get());
}
}
throw PiPedalArgumentException("Instance not found.");
}
int64_t deletePreset(int64_t instanceId)
{
for (size_t i = 0; i < presets_.size(); ++i)
{
if (presets_[i]->instanceId() == instanceId)
{
presets_.erase(presets_.begin() + i);
int64_t newSelection;
if (i < presets_.size())
{
newSelection = presets_[i]->instanceId();
}
else if (presets_.size() > 1)
{
newSelection = presets_[0]->instanceId();
}
else
{
// zero length? We can never have a zero-length bank.
// Add a default preset and make it the selected preset.
Pedalboard pedalboard = Pedalboard::MakeDefault();
this->addPreset(pedalboard);
newSelection = presets_[0]->instanceId();
}
if (instanceId == this->selectedPreset_)
{
this->selectedPreset_ = newSelection;
}
return newSelection;
}
}
throw PiPedalStateException("Preset not found.");
}
DECLARE_JSON_MAP(BankFile);
};
class BankIndexEntry
{
int64_t instanceId_ = 0;
std::string name_;
public:
GETTER_SETTER(instanceId);
GETTER_SETTER_REF(name);
DECLARE_JSON_MAP(BankIndexEntry);
};
class BankIndex
{
int64_t selectedBank_ = 0;
int64_t nextInstanceId_ = 0;
std::vector<BankIndexEntry> entries_;
public:
GETTER_SETTER(selectedBank);
GETTER_SETTER_VEC(entries);
DECLARE_JSON_MAP(BankIndex);
bool hasName(const std::string &name) const
{
for (size_t i = 0; i < this->entries_.size(); ++i)
{
if (this->entries_[i].name() == name)
return true;
}
return false;
}
void move(size_t from, size_t to)
{
if (from >= this->entries_.size())
{
throw std::invalid_argument("Argument out of range.");
}
if (to >= this->entries_.size())
{
throw std::invalid_argument("Argument out of range.");
}
BankIndexEntry t = std::move(this->entries_[from]);
entries_.erase(entries_.begin() + from);
entries_.insert(entries_.begin() + to, std::move(t));
}
void clear()
{
entries_.clear();
selectedBank_ = 0;
}
int64_t addBank(int64_t afterId, const std::string &name)
{
BankIndexEntry bank;
bank.name(name);
bank.instanceId(++nextInstanceId_);
for (size_t i = 0; i < this->entries_.size(); ++i)
{
if (entries_[i].instanceId() == afterId)
{
entries_.insert(entries_.begin() + (i + 1), bank);
return bank.instanceId();
}
}
// else at the end.
entries_.push_back(bank);
return bank.instanceId();
}
BankIndexEntry *getEntryByName(const std::string &name)
{
for (size_t i = 0; i < entries_.size(); ++i)
{
if (entries_[i].name() == name)
{
return &entries_[i];
}
}
return nullptr;
}
BankIndexEntry &getBankIndexEntry(int64_t instanceId)
{
for (size_t i = 0; i < entries_.size(); ++i)
{
if (entries_[i].instanceId() == instanceId)
{
return entries_[i];
}
}
throw PiPedalArgumentException("Bank not found.");
}
const BankIndexEntry &getBankIndexEntry(int64_t instanceId) const
{
for (size_t i = 0; i < entries_.size(); ++i)
{
if (entries_[i].instanceId() == instanceId)
{
return entries_[i];
}
}
throw PiPedalArgumentException("Bank not found.");
}
};
#undef GETTER_SETTER
#undef GETTER_SETTER_VEC
#undef GETTER_SETTER_REF
}
+60 -28
View File
@@ -33,6 +33,7 @@
#include <iomanip>
#include <chrono>
#include <thread>
#include <sched.h>
using namespace pipedal;
@@ -59,7 +60,7 @@ void PrintHelp()
pp << Indent(0) << "Syntax\n\n";
pp << Indent(2) << "pipedal_latency_test [<options>] <input-device> [<output-device>]\n\n";
pp << "where <input-device> is the name of an ALSA capture device and <output-device> is the name of a playback device. "
"If <output-device> is omitted, the input device will be used for both capture and playback. Typically the device names start with 'hw:'.\n\n";
"If <output-device> is omitted, the input device will be used for both capture and playback. Typically the device names start with 'hw:'.\n\n";
pp << Indent(0) << "Options\n\n";
pp << Indent(15);
@@ -85,13 +86,13 @@ void PrintHelp()
<< "PiPedal Latency Tester measures internal buffer delays as well as operating system and "
<< "signal delays in hardware peripherals. Latency figures will therefore be somewhat higher than "
<< "most reported latency figures which typically only include internal buffer delays.\n\n";
pp
<< "The tests run over a variety of buffer sizes. A nominal compute load is provided in order to put some "
"stress on the audio system.\n\n"
<< "You may need to stop the pipedald audio service in order to access the ALSA device:\n\n"
<< Indent(6) << "sudo systemctl stop pipedald\n\n";
pp
<< "The tests run over a variety of buffer sizes. A nominal compute load is provided in order to put some "
"stress on the audio system.\n\n"
<< "You may need to stop the pipedald audio service in order to access the ALSA device:\n\n"
<< Indent(6) << "sudo systemctl stop pipedald\n\n";
pp << Indent(0) << "Examples\n\n";
pp << Indent(2) << "pipedal_latency_test --list\n\n";
@@ -123,7 +124,6 @@ void ListDevices()
}
}
using ChannelsT = std::vector<int>;
class AlsaTester : private AudioDriverHost
@@ -169,12 +169,13 @@ public:
delete[] inputBuffers;
delete[] outputBuffers;
}
std::vector<std::string> SelectChannels(const std::vector<std::string>&available, const std::vector<int>& selection)
std::vector<std::string> SelectChannels(const std::vector<std::string> &available, const std::vector<int> &selection)
{
if (selection.size() == 0) return available;
if (selection.size() == 0)
return available;
std::vector<std::string> result;
for (int sel: selection)
for (int sel : selection)
{
if (sel < 0 || sel >= available.size())
{
@@ -195,14 +196,13 @@ public:
JackConfiguration jackConfiguration;
jackConfiguration.AlsaInitialize(serverSettings);
auto & availableInputs = jackConfiguration.inputAudioPorts();
auto & availableOutputs = jackConfiguration.outputAudioPorts();
auto &availableInputs = jackConfiguration.inputAudioPorts();
auto &availableOutputs = jackConfiguration.outputAudioPorts();
std::vector<std::string> inputAudioPorts, outputAudioPorts;
inputAudioPorts = SelectChannels(availableInputs,this->inputChannels);
outputAudioPorts = SelectChannels(availableOutputs,this->outputChannels);
inputAudioPorts = SelectChannels(availableInputs, this->inputChannels);
outputAudioPorts = SelectChannels(availableOutputs, this->outputChannels);
JackChannelSelection channelSelection(
inputAudioPorts, outputAudioPorts,
@@ -360,7 +360,6 @@ public:
}
virtual void OnAudioTerminated()
{
}
virtual void OnProcess(size_t nFrames)
{
@@ -413,7 +412,7 @@ TestResult RunLatencyTest(
const ChannelsT &outputChannels,
uint32_t sampleRate, int bufferSize, int buffers)
{
AlsaTester tester(inputDeviceId, outputDeviceId, inputChannels, outputChannels, sampleRate, bufferSize, buffers);
AlsaTester tester(inputDeviceId, outputDeviceId, inputChannels, outputChannels, sampleRate, bufferSize, buffers);
return tester.Test();
}
@@ -432,6 +431,28 @@ static std::string overheadDisplay(float value)
return s.str();
}
static bool testRealtimePriorityPrivileges()
{
struct sched_param currentParam;
int currentPolicy = sched_getscheduler(0);
if (currentPolicy == -1) {
return false;
}
if (sched_getparam(0, &currentParam) != 0) {
return false;
}
struct sched_param param;
param.sched_priority = 85;
if (sched_setscheduler(0, SCHED_RR, &param) != 0)
{
return false;
}
// Restore normal priority
sched_setscheduler(0, currentPolicy, &currentParam);
return true;
}
void RunLatencyTest(
const std::string &inputDeviceId,
const std::string &outputDeviceId,
@@ -439,8 +460,20 @@ void RunLatencyTest(
const ChannelsT &outputChannels,
uint32_t sampleRate)
{
PrettyPrinter pp;
pp << "Input: " << inputDeviceId << " Output: " << outputDeviceId << " Rate: " << sampleRate << "\n\n";
if (!testRealtimePriorityPrivileges())
{
pp << "Unable to enable realtime scheduling. Add your user id to the pipedal_d group to fix this problem:" << "\n\n";
pp.AddIndent(4);
pp << "sudo usermod -a -G pipedal_d <your user id>" << "\n\n";
pp.AddIndent(-4);
pp << "You will need to log out or reboot your system in order for the change to take effect.\n\n";
throw std::runtime_error("Unable to set relatime thread priority.");
}
pp << "Input: " << inputDeviceId << " Output: " << outputDeviceId << " Rate: " << sampleRate << "\n\n";
const int SIZE_COLUMN_WIDTH = 8;
const int BUFFERS_COLUMN_WIDTH = 20;
@@ -467,7 +500,7 @@ void RunLatencyTest(
for (auto bufferCount : bufferCounts)
{
auto result = RunLatencyTest(inputDeviceId, outputDeviceId, inputChannels,outputChannels, sampleRate, bufferSize, bufferCount);
auto result = RunLatencyTest(inputDeviceId, outputDeviceId, inputChannels, outputChannels, sampleRate, bufferSize, bufferCount);
pp.Column(column);
column += BUFFERS_COLUMN_WIDTH;
@@ -495,8 +528,7 @@ void RunLatencyTest(
}
}
ChannelsT ParseChannels(const std::string&channels)
ChannelsT ParseChannels(const std::string &channels)
{
ChannelsT result;
std::stringstream s(channels);
@@ -504,9 +536,11 @@ ChannelsT ParseChannels(const std::string&channels)
while (true)
{
int c = s.peek();
if (c == -1) break;
if (c == -1)
break;
if (c == ',') {
if (c == ',')
{
s.get();
c = s.peek();
}
@@ -518,14 +552,12 @@ ChannelsT ParseChannels(const std::string&channels)
while (s.peek() >= '0' && s.peek() <= '9')
{
c = s.get();
v = v*10 + c-'0';
v = v * 10 + c - '0';
}
result.push_back(v);
}
return result;
}
int main(int argc, const char **argv)
@@ -567,7 +599,7 @@ std:
inputChannels = ParseChannels(strInputChannels);
outputChannels = ParseChannels(strOutputChannels);
std::string inDev = parser.Arguments()[0];
std::string inDev = parser.Arguments()[0];
std::string outDev = parser.Arguments().size() == 2 ? parser.Arguments()[1] : inDev;
RunLatencyTest(inDev, outDev, inputChannels, outputChannels, sampleRate);
}
+22 -2
View File
@@ -887,7 +887,7 @@ int64_t PiPedalModel::SavePluginPresetAs(int64_t instanceId, const std::string &
return presetId;
}
int64_t PiPedalModel::SaveCurrentPresetAs(int64_t clientId, const std::string &name, int64_t saveAfterInstanceId)
int64_t PiPedalModel::SaveCurrentPresetAs(int64_t clientId, int64_t bankInstanceId,const std::string &name, int64_t saveAfterInstanceId)
{
std::lock_guard<std::recursive_mutex> guard{mutex};
@@ -897,7 +897,7 @@ int64_t PiPedalModel::SaveCurrentPresetAs(int64_t clientId, const std::string &n
UpdateVst3Settings(pedalboard);
pedalboard.name(name);
int64_t result = storage.SaveCurrentPresetAs(pedalboard, name, saveAfterInstanceId);
int64_t result = storage.SaveCurrentPresetAs(pedalboard, bankInstanceId, name, saveAfterInstanceId);
FirePresetsChanged(clientId);
return result;
}
@@ -3257,3 +3257,23 @@ bool PiPedalModel::HasTone3000Auth() const
{
return storage.GetTone3000Auth() != "";
}
std::vector<PresetIndexEntry> PiPedalModel::RequestBankPresets(int64_t bankInstanceId)
{
std::lock_guard<std::recursive_mutex> lock(mutex);
return storage.RequestBankPresets(bankInstanceId);
}
int64_t PiPedalModel::ImportPresetsFromBank(int64_t bankInstanceId, const std::vector<int64_t> &presets)
{
std::lock_guard<std::recursive_mutex> lock(mutex);
uint64_t lastAdded = storage.ImportPresetsFromBank(bankInstanceId, presets);
if (lastAdded != -1) {
}
FirePresetsChanged(-1);
return lastAdded;
}
+4 -1
View File
@@ -378,9 +378,10 @@ namespace pipedal
int64_t UploadPreset(const BankFile &bankFile, int64_t uploadAfter = -1);
void UploadPluginPresets(const PluginPresets &pluginPresets);
void SaveCurrentPreset(int64_t clientId);
int64_t SaveCurrentPresetAs(int64_t clientId, const std::string &name, int64_t saveAfterInstanceId = -1);
int64_t SaveCurrentPresetAs(int64_t clientId, int64_t bankInstanceId, const std::string &name, int64_t saveAfterInstanceId = -1);
int64_t SavePluginPresetAs(int64_t instanceId, const std::string &name);
void LoadPreset(int64_t clientId, int64_t instanceId);
bool UpdatePresets(int64_t clientId, const PresetIndex &presets);
void UpdatePluginPresets(const PluginUiPresets &pluginPresets);
@@ -493,6 +494,8 @@ namespace pipedal
void SetSelectedPedalboardPlugin(uint64_t clientId, uint64_t pedalboardId);
std::vector<PresetIndexEntry> RequestBankPresets(int64_t bankInstanceId);
int64_t ImportPresetsFromBank(int64_t bankInstanceId, const std::vector<int64_t> &presets);
};
} // namespace pipedal.
+37 -1
View File
@@ -46,6 +46,27 @@
using namespace std;
using namespace pipedal;
class ImportPresetsFromBankBody {
public:
int64_t bankInstanceId_;
std::vector<int64_t> presets_;
DECLARE_JSON_MAP(ImportPresetsFromBankBody);
};
JSON_MAP_BEGIN(ImportPresetsFromBankBody)
JSON_MAP_REFERENCE(ImportPresetsFromBankBody, bankInstanceId)
JSON_MAP_REFERENCE(ImportPresetsFromBankBody, presets)
JSON_MAP_END()
class RequestBankPresetsBody {
public:
int64_t bankInstanceId_;
DECLARE_JSON_MAP(RequestBankPresetsBody);
};
JSON_MAP_BEGIN(RequestBankPresetsBody)
JSON_MAP_REFERENCE(RequestBankPresetsBody, bankInstanceId)
JSON_MAP_END()
class PathPatchPropertyChangedBody
{
public:
@@ -336,6 +357,7 @@ class SaveCurrentPresetAsBody
{
public:
int64_t clientId_ = -1;
int64_t bankInstanceId_ = -1;
std::string name_;
int64_t saveAfterInstanceId_ = -1;
@@ -343,6 +365,7 @@ public:
};
JSON_MAP_BEGIN(SaveCurrentPresetAsBody)
JSON_MAP_REFERENCE(SaveCurrentPresetAsBody, clientId)
JSON_MAP_REFERENCE(SaveCurrentPresetAsBody, bankInstanceId)
JSON_MAP_REFERENCE(SaveCurrentPresetAsBody, name)
JSON_MAP_REFERENCE(SaveCurrentPresetAsBody, saveAfterInstanceId)
JSON_MAP_END()
@@ -1314,7 +1337,7 @@ public:
{
SaveCurrentPresetAsBody body;
pReader->read(&body);
int64_t result = this->model.SaveCurrentPresetAs(this->clientId, body.name_, body.saveAfterInstanceId_);
int64_t result = this->model.SaveCurrentPresetAs(this->clientId, body.bankInstanceId_,body.name_, body.saveAfterInstanceId_);
Reply(replyTo, "saveCurrentPresetsAs", result);
}
else if (message == "setSelectedPedalboardPlugin")
@@ -1794,6 +1817,19 @@ public:
{
std::vector<AlsaSequencerPortSelection> result = model.GetAlsaSequencerPorts();
this->Reply(replyTo,"getAlsaSequencerPorts", result);
} else if (message == "requestBankPresets") {
RequestBankPresetsBody args;
pReader->read(&args);
auto result = this->model.RequestBankPresets(args.bankInstanceId_);
this->Reply(replyTo,"requestBankPresets",result);
}
else if (message == "importPresetsFromBank") {
ImportPresetsFromBankBody args;
pReader->read(&args);
auto result = this->model.ImportPresetsFromBank(args.bankInstanceId_, args.presets_);
this->Reply(replyTo,"importPresetsFromBank",result);
}
else
{
+137 -13
View File
@@ -225,7 +225,6 @@ static void CopyDirectory(const std::filesystem::path &source, const std::filesy
}
}
void Storage::MaybeCopyDefaultPresets()
{
auto presetsDirectory = this->GetPresetsDirectory();
@@ -239,13 +238,17 @@ void Storage::MaybeCopyDefaultPresets()
}
static void removeFileNoThrow(const std::filesystem::path &path)
{
try {
{
try
{
fs::remove(path);
} catch (const std::exception&)
}
catch (const std::exception &)
{
}
}
void Storage::UpgradeFactoryPresets()
{
auto presetsDirectory = this->GetPresetsDirectory();
@@ -520,7 +523,6 @@ void Storage::SavePluginPresetIndex()
}
}
void Storage::SaveBankIndex()
{
pipedal::ofstream_synced os;
@@ -653,17 +655,133 @@ void Storage::SaveCurrentPreset(const Pedalboard &pedalboard)
item.preset(pedalboard);
SaveCurrentBank();
}
int64_t Storage::SaveCurrentPresetAs(const Pedalboard &pedalboard, const std::string &name, int64_t saveAfterInstanceId)
int64_t Storage::SaveCurrentPresetAs(const Pedalboard &pedalboard, int64_t bankInstanceId, const std::string &name, int64_t saveAfterInstanceId)
{
Pedalboard newPedalboard = pedalboard;
newPedalboard.name(name);
int64_t newInstanceId = currentBank.addPreset(newPedalboard, saveAfterInstanceId);
currentBank.selectedPreset(newInstanceId);
SaveCurrentBank();
return newInstanceId;
if (bankInstanceId == this->bankIndex.selectedBank())
{
int64_t newInstanceId = currentBank.addPreset(newPedalboard, saveAfterInstanceId);
currentBank.selectedPreset(newInstanceId);
SaveCurrentBank();
return newInstanceId;
}
else
{
auto indexEntry = this->bankIndex.getBankIndexEntry(bankInstanceId);
try
{
BankFile bankFile;
LoadBankFile(indexEntry.name(), &(bankFile));
int64_t newInstanceId = bankFile.addPreset(newPedalboard, -1);
SaveBankFile(indexEntry.name(), bankFile);
return -1;
}
catch (const std::exception &e)
{
throw std::logic_error(SS("Bank file corrupted. " << e.what() << "(" << GetBankFileName(indexEntry.name()) << ")"));
}
}
}
static std::string stripNumericSuffix(const std::string &name) {
// remove (digit*) from the end of the string.
// Find the last '(' character
size_t pos = name.find_last_of('(');
if (pos == std::string::npos) {
return name;
}
// Check if everything between '(' and ')' is digits
size_t len = name.length()-1;
if (name[len] != ')')
{
return name;
}
bool allDigits = true;
for (size_t i = pos + 1; i < len - 1; ++i) {
if (name[i] < '0' || name[i] > '9') {
allDigits = false;
break;
}
}
if (!allDigits) {
return name;
}
// Remove trailing spaces before the '('
while (pos > 0 && name[pos - 1] == ' ') {
--pos;
}
return name.substr(0, pos);
}
static std::string makeUniqueName(const std::string &name, const std::set<std::string>&existingNames)
{
if (!existingNames.contains(name))
{
return name;
}
std::string baseName = stripNumericSuffix(name);
size_t i = 2;
while (true) {
std::string newName = SS(baseName << " (" << i << ")");
if (!existingNames.contains(newName)) {
return newName;
}
++i;
}
}
int64_t Storage::ImportPresetsFromBank(int64_t bankInstanceId, const std::vector<int64_t> &presets)
{
if (bankIndex.selectedBank() == bankInstanceId) {
throw std::runtime_error("Can't import to self.");
}
std::set<int64_t> presetsSet { presets.begin(), presets.end()};
auto indexEntry = this->bankIndex.getBankIndexEntry(bankInstanceId);
std::set<std::string> existingNames;
for (auto&preset: this->currentBank.presets()) {
existingNames.insert(preset->preset().name());
}
BankFile bankFile;
LoadBankFile(indexEntry.name(),&bankFile);
int64_t lastPresetId = -1;
for (auto &presetEntry: bankFile.presets()) {
if (presetsSet.contains(presetEntry->instanceId())) {
std::string uniqueName = makeUniqueName(presetEntry->preset().name(),existingNames);
existingNames.insert(uniqueName);
Pedalboard t = presetEntry->preset();
t.name(uniqueName);
lastPresetId = this->currentBank.addPreset(t);
}
}
SaveCurrentBank();
return lastPresetId;
}
std::vector<PresetIndexEntry> Storage::RequestBankPresets(int64_t bankInstanceId)
{
auto indexEntry = this->bankIndex.getBankIndexEntry(bankInstanceId);
std::vector<PresetIndexEntry> result;
BankFile bankFile;
LoadBankFile(indexEntry.name(),&bankFile);
for (auto &preset: bankFile.presets()) {
result.push_back(
PresetIndexEntry(preset->instanceId(),preset->preset().name())
);
}
return result;
}
void Storage::SetPresetIndex(const PresetIndex &presets)
{
// painful because we must move unique_ptrs.
@@ -826,25 +944,30 @@ int64_t Storage::CreateNewPreset()
}
}
newPedalboard.name(name);
return this->currentBank.addPreset(newPedalboard, -1);
auto t = this->currentBank.addPreset(newPedalboard, -1);
SaveCurrentBank();
return t;
}
int64_t Storage::CopyPreset(int64_t fromId, int64_t toId)
{
auto &fromItem = this->currentBank.getItem(fromId);
int64_t result;
if (toId == -1)
{
Pedalboard newPedalboard = fromItem.preset();
std::string name = GetPresetCopyName(fromItem.preset().name());
newPedalboard.name(name);
return this->currentBank.addPreset(newPedalboard, fromId);
result = this->currentBank.addPreset(newPedalboard, fromId);
}
else
{
auto &toItem = this->currentBank.getItem(toId);
toItem.preset(fromItem.preset());
return toId;
result = toId;
}
SaveCurrentBank();
return result;
}
void Storage::SetJackChannelSelection(const JackChannelSelection &channelSelection)
@@ -2861,6 +2984,7 @@ std::string Storage::FromAbstractPathJson(const std::string &pathJson)
return v.to_string();
}
JSON_MAP_BEGIN(UserSettings)
JSON_MAP_REFERENCE(UserSettings, governor)
JSON_MAP_REFERENCE(UserSettings, showStatusMonitor)
+5 -1
View File
@@ -149,7 +149,7 @@ public:
int64_t GetBankByMidiBankNumber(uint8_t bankNumber);
const Pedalboard& GetCurrentPreset();
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, int64_t bankInstanceId,const std::string&namne,int64_t saveAfterInstanceId = -1);
int64_t GetCurrentPresetId() const;
void GetPresetIndex(PresetIndex*pResult);
@@ -268,6 +268,10 @@ public:
void LoadTone3000Auth();
void SetTone3000Auth(const std::string&apiKey);
std::string GetTone3000Auth() const;
std::vector<PresetIndexEntry> RequestBankPresets(int64_t bankInstanceId);
int64_t ImportPresetsFromBank(int64_t bankInstanceId, const std::vector<int64_t> &presets);
};