Import Prests from Bank
This commit is contained in:
Vendored
+2
-1
@@ -22,7 +22,8 @@
|
|||||||
// Resolved by CMake Tools:
|
// Resolved by CMake Tools:
|
||||||
"program": "${command:cmake.launchTargetPath}",
|
"program": "${command:cmake.launchTargetPath}",
|
||||||
"args": [
|
"args": [
|
||||||
"--gplot-downloads"
|
"hw:M2",
|
||||||
|
"hw:M2"
|
||||||
],
|
],
|
||||||
"stopAtEntry": false,
|
"stopAtEntry": false,
|
||||||
"cwd": "${workspaceFolder}",
|
"cwd": "${workspaceFolder}",
|
||||||
|
|||||||
+299
-271
@@ -23,316 +23,344 @@
|
|||||||
#include "Pedalboard.hpp"
|
#include "Pedalboard.hpp"
|
||||||
#include "PiPedalException.hpp"
|
#include "PiPedalException.hpp"
|
||||||
|
|
||||||
namespace pipedal {
|
namespace pipedal
|
||||||
|
{
|
||||||
|
|
||||||
#define GETTER_SETTER_REF(name) \
|
#define GETTER_SETTER_REF(name) \
|
||||||
decltype(name##_)& name() { return name##_;} \
|
decltype(name##_) &name() { return name##_; } \
|
||||||
const decltype(name##_)& name() const { return name##_;} \
|
const decltype(name##_) &name() const { return name##_; } \
|
||||||
void name(const decltype(name##_) &value) { name##_ = value; }
|
void name(const decltype(name##_) &value) { name##_ = value; }
|
||||||
|
|
||||||
#define GETTER_SETTER_VEC(name) \
|
#define GETTER_SETTER_VEC(name) \
|
||||||
decltype(name##_)& name() { return name##_;} \
|
decltype(name##_) &name() { return name##_; } \
|
||||||
const decltype(name##_)& name() const { return name##_;}
|
const decltype(name##_) &name() const { return name##_; }
|
||||||
|
|
||||||
|
#define GETTER_SETTER(name) \
|
||||||
#define GETTER_SETTER(name) \
|
decltype(name##_) name() const { return name##_; } \
|
||||||
decltype(name##_) name() const { return name##_;} \
|
|
||||||
void name(decltype(name##_) value) { name##_ = value; }
|
void name(decltype(name##_) value) { name##_ = value; }
|
||||||
|
|
||||||
|
class PresetIndexEntry
|
||||||
|
{
|
||||||
|
int64_t instanceId_ = 0;
|
||||||
|
std::string name_;
|
||||||
|
|
||||||
class PresetIndexEntry {
|
public:
|
||||||
int64_t instanceId_ = 0;
|
PresetIndexEntry() {}
|
||||||
std::string name_;
|
PresetIndexEntry(uint64_t instanceId, const std::string &name)
|
||||||
public:
|
: instanceId_(instanceId), name_(name)
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
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();
|
if (presets_[i].instanceId() == instanceId)
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
++it;
|
*pResult = presets_[i].name();
|
||||||
this->presets_.insert(it,std::move(entry));
|
return true;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
return instanceId;
|
GETTER_SETTER(selectedInstanceId);
|
||||||
}
|
GETTER_SETTER_VEC(presets);
|
||||||
bool hasItem(int64_t instanceId)
|
GETTER_SETTER(presetChanged);
|
||||||
{
|
|
||||||
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;
|
|
||||||
|
|
||||||
}
|
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)
|
nextInstanceId_ = 0;
|
||||||
{
|
presets_.clear();
|
||||||
return *(presets_[i].get());
|
selectedPreset_ = -1;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
throw PiPedalArgumentException("Instance not found.");
|
|
||||||
}
|
void move(size_t from, size_t to)
|
||||||
int64_t deletePreset(int64_t instanceId) {
|
|
||||||
for (size_t i = 0; i < presets_.size(); ++i)
|
|
||||||
{
|
{
|
||||||
if (presets_[i]->instanceId() == instanceId)
|
if (from >= this->presets_.size())
|
||||||
{
|
{
|
||||||
presets_.erase(presets_.begin()+i);
|
throw std::invalid_argument("Argument out of range.");
|
||||||
int64_t newSelection;
|
}
|
||||||
if (i < presets_.size())
|
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();
|
if ((*it)->instanceId() == afterItem)
|
||||||
} else if (presets_.size() > 1) {
|
{
|
||||||
newSelection = presets_[0]->instanceId();
|
++it;
|
||||||
} else {
|
this->presets_.insert(it, std::move(entry));
|
||||||
// zero length? We can never have a zero-length bank.
|
break;
|
||||||
// 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_)
|
}
|
||||||
|
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.");
|
bool hasName(const std::string &name)
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
for (size_t i = 0; i < presets_.size(); ++i)
|
||||||
}
|
|
||||||
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);
|
if (presets_[i]->preset().name() == name)
|
||||||
return bank.instanceId();
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
// else at the end.
|
bool renamePreset(int64_t instanceId, const std::string &name)
|
||||||
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)
|
if (hasName(name))
|
||||||
{
|
{
|
||||||
return &entries_[i];
|
throw PiPedalStateException("A preset by that name already exists.");
|
||||||
}
|
}
|
||||||
}
|
for (size_t i = 0; i < presets_.size(); ++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];
|
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
|
||||||
#undef GETTER_SETTER_VEC
|
#undef GETTER_SETTER_VEC
|
||||||
#undef GETTER_SETTER_REF
|
#undef GETTER_SETTER_REF
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+58
-26
@@ -33,6 +33,7 @@
|
|||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <sched.h>
|
||||||
|
|
||||||
using namespace pipedal;
|
using namespace pipedal;
|
||||||
|
|
||||||
@@ -59,7 +60,7 @@ void PrintHelp()
|
|||||||
pp << Indent(0) << "Syntax\n\n";
|
pp << Indent(0) << "Syntax\n\n";
|
||||||
pp << Indent(2) << "pipedal_latency_test [<options>] <input-device> [<output-device>]\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. "
|
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(0) << "Options\n\n";
|
||||||
pp << Indent(15);
|
pp << Indent(15);
|
||||||
|
|
||||||
@@ -87,11 +88,11 @@ void PrintHelp()
|
|||||||
<< "most reported latency figures which typically only include internal buffer delays.\n\n";
|
<< "most reported latency figures which typically only include internal buffer delays.\n\n";
|
||||||
|
|
||||||
pp
|
pp
|
||||||
<< "The tests run over a variety of buffer sizes. A nominal compute load is provided in order to put some "
|
<< "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"
|
"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"
|
<< "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";
|
<< Indent(6) << "sudo systemctl stop pipedald\n\n";
|
||||||
|
|
||||||
pp << Indent(0) << "Examples\n\n";
|
pp << Indent(0) << "Examples\n\n";
|
||||||
pp << Indent(2) << "pipedal_latency_test --list\n\n";
|
pp << Indent(2) << "pipedal_latency_test --list\n\n";
|
||||||
@@ -123,7 +124,6 @@ void ListDevices()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
using ChannelsT = std::vector<int>;
|
using ChannelsT = std::vector<int>;
|
||||||
|
|
||||||
class AlsaTester : private AudioDriverHost
|
class AlsaTester : private AudioDriverHost
|
||||||
@@ -169,12 +169,13 @@ public:
|
|||||||
delete[] inputBuffers;
|
delete[] inputBuffers;
|
||||||
delete[] outputBuffers;
|
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;
|
std::vector<std::string> result;
|
||||||
for (int sel: selection)
|
for (int sel : selection)
|
||||||
{
|
{
|
||||||
if (sel < 0 || sel >= available.size())
|
if (sel < 0 || sel >= available.size())
|
||||||
{
|
{
|
||||||
@@ -195,14 +196,13 @@ public:
|
|||||||
JackConfiguration jackConfiguration;
|
JackConfiguration jackConfiguration;
|
||||||
jackConfiguration.AlsaInitialize(serverSettings);
|
jackConfiguration.AlsaInitialize(serverSettings);
|
||||||
|
|
||||||
auto & availableInputs = jackConfiguration.inputAudioPorts();
|
auto &availableInputs = jackConfiguration.inputAudioPorts();
|
||||||
auto & availableOutputs = jackConfiguration.outputAudioPorts();
|
auto &availableOutputs = jackConfiguration.outputAudioPorts();
|
||||||
|
|
||||||
|
|
||||||
std::vector<std::string> inputAudioPorts, outputAudioPorts;
|
std::vector<std::string> inputAudioPorts, outputAudioPorts;
|
||||||
|
|
||||||
inputAudioPorts = SelectChannels(availableInputs,this->inputChannels);
|
inputAudioPorts = SelectChannels(availableInputs, this->inputChannels);
|
||||||
outputAudioPorts = SelectChannels(availableOutputs,this->outputChannels);
|
outputAudioPorts = SelectChannels(availableOutputs, this->outputChannels);
|
||||||
|
|
||||||
JackChannelSelection channelSelection(
|
JackChannelSelection channelSelection(
|
||||||
inputAudioPorts, outputAudioPorts,
|
inputAudioPorts, outputAudioPorts,
|
||||||
@@ -360,7 +360,6 @@ public:
|
|||||||
}
|
}
|
||||||
virtual void OnAudioTerminated()
|
virtual void OnAudioTerminated()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
virtual void OnProcess(size_t nFrames)
|
virtual void OnProcess(size_t nFrames)
|
||||||
{
|
{
|
||||||
@@ -413,7 +412,7 @@ TestResult RunLatencyTest(
|
|||||||
const ChannelsT &outputChannels,
|
const ChannelsT &outputChannels,
|
||||||
uint32_t sampleRate, int bufferSize, int buffers)
|
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();
|
return tester.Test();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -432,6 +431,28 @@ static std::string overheadDisplay(float value)
|
|||||||
return s.str();
|
return s.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool testRealtimePriorityPrivileges()
|
||||||
|
{
|
||||||
|
|
||||||
|
struct sched_param currentParam;
|
||||||
|
int currentPolicy = sched_getscheduler(0);
|
||||||
|
if (currentPolicy == -1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (sched_getparam(0, ¤tParam) != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
struct sched_param param;
|
||||||
|
param.sched_priority = 85;
|
||||||
|
if (sched_setscheduler(0, SCHED_RR, ¶m) != 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Restore normal priority
|
||||||
|
sched_setscheduler(0, currentPolicy, ¤tParam);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void RunLatencyTest(
|
void RunLatencyTest(
|
||||||
const std::string &inputDeviceId,
|
const std::string &inputDeviceId,
|
||||||
const std::string &outputDeviceId,
|
const std::string &outputDeviceId,
|
||||||
@@ -439,8 +460,20 @@ void RunLatencyTest(
|
|||||||
const ChannelsT &outputChannels,
|
const ChannelsT &outputChannels,
|
||||||
uint32_t sampleRate)
|
uint32_t sampleRate)
|
||||||
{
|
{
|
||||||
|
|
||||||
PrettyPrinter pp;
|
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 SIZE_COLUMN_WIDTH = 8;
|
||||||
const int BUFFERS_COLUMN_WIDTH = 20;
|
const int BUFFERS_COLUMN_WIDTH = 20;
|
||||||
@@ -467,7 +500,7 @@ void RunLatencyTest(
|
|||||||
|
|
||||||
for (auto bufferCount : bufferCounts)
|
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);
|
pp.Column(column);
|
||||||
column += BUFFERS_COLUMN_WIDTH;
|
column += BUFFERS_COLUMN_WIDTH;
|
||||||
@@ -495,8 +528,7 @@ void RunLatencyTest(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ChannelsT ParseChannels(const std::string &channels)
|
||||||
ChannelsT ParseChannels(const std::string&channels)
|
|
||||||
{
|
{
|
||||||
ChannelsT result;
|
ChannelsT result;
|
||||||
std::stringstream s(channels);
|
std::stringstream s(channels);
|
||||||
@@ -504,9 +536,11 @@ ChannelsT ParseChannels(const std::string&channels)
|
|||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
int c = s.peek();
|
int c = s.peek();
|
||||||
if (c == -1) break;
|
if (c == -1)
|
||||||
|
break;
|
||||||
|
|
||||||
if (c == ',') {
|
if (c == ',')
|
||||||
|
{
|
||||||
s.get();
|
s.get();
|
||||||
c = s.peek();
|
c = s.peek();
|
||||||
}
|
}
|
||||||
@@ -518,14 +552,12 @@ ChannelsT ParseChannels(const std::string&channels)
|
|||||||
while (s.peek() >= '0' && s.peek() <= '9')
|
while (s.peek() >= '0' && s.peek() <= '9')
|
||||||
{
|
{
|
||||||
c = s.get();
|
c = s.get();
|
||||||
v = v*10 + c-'0';
|
v = v * 10 + c - '0';
|
||||||
}
|
}
|
||||||
result.push_back(v);
|
result.push_back(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, const char **argv)
|
int main(int argc, const char **argv)
|
||||||
@@ -567,7 +599,7 @@ std:
|
|||||||
inputChannels = ParseChannels(strInputChannels);
|
inputChannels = ParseChannels(strInputChannels);
|
||||||
outputChannels = ParseChannels(strOutputChannels);
|
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;
|
std::string outDev = parser.Arguments().size() == 2 ? parser.Arguments()[1] : inDev;
|
||||||
RunLatencyTest(inDev, outDev, inputChannels, outputChannels, sampleRate);
|
RunLatencyTest(inDev, outDev, inputChannels, outputChannels, sampleRate);
|
||||||
}
|
}
|
||||||
|
|||||||
+22
-2
@@ -887,7 +887,7 @@ int64_t PiPedalModel::SavePluginPresetAs(int64_t instanceId, const std::string &
|
|||||||
return presetId;
|
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};
|
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);
|
UpdateVst3Settings(pedalboard);
|
||||||
pedalboard.name(name);
|
pedalboard.name(name);
|
||||||
int64_t result = storage.SaveCurrentPresetAs(pedalboard, name, saveAfterInstanceId);
|
int64_t result = storage.SaveCurrentPresetAs(pedalboard, bankInstanceId, name, saveAfterInstanceId);
|
||||||
FirePresetsChanged(clientId);
|
FirePresetsChanged(clientId);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -3257,3 +3257,23 @@ bool PiPedalModel::HasTone3000Auth() const
|
|||||||
{
|
{
|
||||||
return storage.GetTone3000Auth() != "";
|
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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -378,9 +378,10 @@ namespace pipedal
|
|||||||
int64_t UploadPreset(const BankFile &bankFile, int64_t uploadAfter = -1);
|
int64_t UploadPreset(const BankFile &bankFile, int64_t uploadAfter = -1);
|
||||||
void UploadPluginPresets(const PluginPresets &pluginPresets);
|
void UploadPluginPresets(const PluginPresets &pluginPresets);
|
||||||
void SaveCurrentPreset(int64_t clientId);
|
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);
|
int64_t SavePluginPresetAs(int64_t instanceId, const std::string &name);
|
||||||
|
|
||||||
|
|
||||||
void LoadPreset(int64_t clientId, int64_t instanceId);
|
void LoadPreset(int64_t clientId, int64_t instanceId);
|
||||||
bool UpdatePresets(int64_t clientId, const PresetIndex &presets);
|
bool UpdatePresets(int64_t clientId, const PresetIndex &presets);
|
||||||
void UpdatePluginPresets(const PluginUiPresets &pluginPresets);
|
void UpdatePluginPresets(const PluginUiPresets &pluginPresets);
|
||||||
@@ -493,6 +494,8 @@ namespace pipedal
|
|||||||
|
|
||||||
void SetSelectedPedalboardPlugin(uint64_t clientId, uint64_t pedalboardId);
|
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.
|
} // namespace pipedal.
|
||||||
+37
-1
@@ -46,6 +46,27 @@
|
|||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace pipedal;
|
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
|
class PathPatchPropertyChangedBody
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -336,6 +357,7 @@ class SaveCurrentPresetAsBody
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
int64_t clientId_ = -1;
|
int64_t clientId_ = -1;
|
||||||
|
int64_t bankInstanceId_ = -1;
|
||||||
std::string name_;
|
std::string name_;
|
||||||
int64_t saveAfterInstanceId_ = -1;
|
int64_t saveAfterInstanceId_ = -1;
|
||||||
|
|
||||||
@@ -343,6 +365,7 @@ public:
|
|||||||
};
|
};
|
||||||
JSON_MAP_BEGIN(SaveCurrentPresetAsBody)
|
JSON_MAP_BEGIN(SaveCurrentPresetAsBody)
|
||||||
JSON_MAP_REFERENCE(SaveCurrentPresetAsBody, clientId)
|
JSON_MAP_REFERENCE(SaveCurrentPresetAsBody, clientId)
|
||||||
|
JSON_MAP_REFERENCE(SaveCurrentPresetAsBody, bankInstanceId)
|
||||||
JSON_MAP_REFERENCE(SaveCurrentPresetAsBody, name)
|
JSON_MAP_REFERENCE(SaveCurrentPresetAsBody, name)
|
||||||
JSON_MAP_REFERENCE(SaveCurrentPresetAsBody, saveAfterInstanceId)
|
JSON_MAP_REFERENCE(SaveCurrentPresetAsBody, saveAfterInstanceId)
|
||||||
JSON_MAP_END()
|
JSON_MAP_END()
|
||||||
@@ -1314,7 +1337,7 @@ public:
|
|||||||
{
|
{
|
||||||
SaveCurrentPresetAsBody body;
|
SaveCurrentPresetAsBody body;
|
||||||
pReader->read(&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);
|
Reply(replyTo, "saveCurrentPresetsAs", result);
|
||||||
}
|
}
|
||||||
else if (message == "setSelectedPedalboardPlugin")
|
else if (message == "setSelectedPedalboardPlugin")
|
||||||
@@ -1794,6 +1817,19 @@ public:
|
|||||||
{
|
{
|
||||||
std::vector<AlsaSequencerPortSelection> result = model.GetAlsaSequencerPorts();
|
std::vector<AlsaSequencerPortSelection> result = model.GetAlsaSequencerPorts();
|
||||||
this->Reply(replyTo,"getAlsaSequencerPorts", result);
|
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
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
+136
-12
@@ -225,7 +225,6 @@ static void CopyDirectory(const std::filesystem::path &source, const std::filesy
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Storage::MaybeCopyDefaultPresets()
|
void Storage::MaybeCopyDefaultPresets()
|
||||||
{
|
{
|
||||||
auto presetsDirectory = this->GetPresetsDirectory();
|
auto presetsDirectory = this->GetPresetsDirectory();
|
||||||
@@ -240,12 +239,16 @@ void Storage::MaybeCopyDefaultPresets()
|
|||||||
|
|
||||||
static void removeFileNoThrow(const std::filesystem::path &path)
|
static void removeFileNoThrow(const std::filesystem::path &path)
|
||||||
{
|
{
|
||||||
try {
|
try
|
||||||
|
{
|
||||||
fs::remove(path);
|
fs::remove(path);
|
||||||
} catch (const std::exception&)
|
}
|
||||||
|
catch (const std::exception &)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Storage::UpgradeFactoryPresets()
|
void Storage::UpgradeFactoryPresets()
|
||||||
{
|
{
|
||||||
auto presetsDirectory = this->GetPresetsDirectory();
|
auto presetsDirectory = this->GetPresetsDirectory();
|
||||||
@@ -520,7 +523,6 @@ void Storage::SavePluginPresetIndex()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Storage::SaveBankIndex()
|
void Storage::SaveBankIndex()
|
||||||
{
|
{
|
||||||
pipedal::ofstream_synced os;
|
pipedal::ofstream_synced os;
|
||||||
@@ -653,17 +655,133 @@ void Storage::SaveCurrentPreset(const Pedalboard &pedalboard)
|
|||||||
item.preset(pedalboard);
|
item.preset(pedalboard);
|
||||||
SaveCurrentBank();
|
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;
|
Pedalboard newPedalboard = pedalboard;
|
||||||
newPedalboard.name(name);
|
newPedalboard.name(name);
|
||||||
|
|
||||||
int64_t newInstanceId = currentBank.addPreset(newPedalboard, saveAfterInstanceId);
|
if (bankInstanceId == this->bankIndex.selectedBank())
|
||||||
currentBank.selectedPreset(newInstanceId);
|
{
|
||||||
SaveCurrentBank();
|
int64_t newInstanceId = currentBank.addPreset(newPedalboard, saveAfterInstanceId);
|
||||||
return newInstanceId;
|
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)
|
void Storage::SetPresetIndex(const PresetIndex &presets)
|
||||||
{
|
{
|
||||||
// painful because we must move unique_ptrs.
|
// painful because we must move unique_ptrs.
|
||||||
@@ -826,25 +944,30 @@ int64_t Storage::CreateNewPreset()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
newPedalboard.name(name);
|
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)
|
int64_t Storage::CopyPreset(int64_t fromId, int64_t toId)
|
||||||
{
|
{
|
||||||
auto &fromItem = this->currentBank.getItem(fromId);
|
auto &fromItem = this->currentBank.getItem(fromId);
|
||||||
|
int64_t result;
|
||||||
if (toId == -1)
|
if (toId == -1)
|
||||||
{
|
{
|
||||||
Pedalboard newPedalboard = fromItem.preset();
|
Pedalboard newPedalboard = fromItem.preset();
|
||||||
std::string name = GetPresetCopyName(fromItem.preset().name());
|
std::string name = GetPresetCopyName(fromItem.preset().name());
|
||||||
newPedalboard.name(name);
|
newPedalboard.name(name);
|
||||||
return this->currentBank.addPreset(newPedalboard, fromId);
|
result = this->currentBank.addPreset(newPedalboard, fromId);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto &toItem = this->currentBank.getItem(toId);
|
auto &toItem = this->currentBank.getItem(toId);
|
||||||
toItem.preset(fromItem.preset());
|
toItem.preset(fromItem.preset());
|
||||||
return toId;
|
result = toId;
|
||||||
}
|
}
|
||||||
|
SaveCurrentBank();
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Storage::SetJackChannelSelection(const JackChannelSelection &channelSelection)
|
void Storage::SetJackChannelSelection(const JackChannelSelection &channelSelection)
|
||||||
@@ -2861,6 +2984,7 @@ std::string Storage::FromAbstractPathJson(const std::string &pathJson)
|
|||||||
return v.to_string();
|
return v.to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
JSON_MAP_BEGIN(UserSettings)
|
JSON_MAP_BEGIN(UserSettings)
|
||||||
JSON_MAP_REFERENCE(UserSettings, governor)
|
JSON_MAP_REFERENCE(UserSettings, governor)
|
||||||
JSON_MAP_REFERENCE(UserSettings, showStatusMonitor)
|
JSON_MAP_REFERENCE(UserSettings, showStatusMonitor)
|
||||||
|
|||||||
+5
-1
@@ -149,7 +149,7 @@ public:
|
|||||||
int64_t GetBankByMidiBankNumber(uint8_t bankNumber);
|
int64_t GetBankByMidiBankNumber(uint8_t bankNumber);
|
||||||
const Pedalboard& GetCurrentPreset();
|
const Pedalboard& GetCurrentPreset();
|
||||||
void SaveCurrentPreset(const Pedalboard&pedalboard);
|
void SaveCurrentPreset(const Pedalboard&pedalboard);
|
||||||
int64_t SaveCurrentPresetAs(const Pedalboard&pedalboard, const std::string&namne,int64_t saveAfterInstanceId = -1);
|
int64_t SaveCurrentPresetAs(const Pedalboard&pedalboard, int64_t bankInstanceId,const std::string&namne,int64_t saveAfterInstanceId = -1);
|
||||||
int64_t GetCurrentPresetId() const;
|
int64_t GetCurrentPresetId() const;
|
||||||
|
|
||||||
void GetPresetIndex(PresetIndex*pResult);
|
void GetPresetIndex(PresetIndex*pResult);
|
||||||
@@ -268,6 +268,10 @@ public:
|
|||||||
void LoadTone3000Auth();
|
void LoadTone3000Auth();
|
||||||
void SetTone3000Auth(const std::string&apiKey);
|
void SetTone3000Auth(const std::string&apiKey);
|
||||||
std::string GetTone3000Auth() const;
|
std::string GetTone3000Auth() const;
|
||||||
|
|
||||||
|
std::vector<PresetIndexEntry> RequestBankPresets(int64_t bankInstanceId);
|
||||||
|
int64_t ImportPresetsFromBank(int64_t bankInstanceId, const std::vector<int64_t> &presets);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,4 @@
|
|||||||
|
Did we fix Toob Tremolo stereo harmonic?
|
||||||
double select on double-click/ok in file dialog.
|
|
||||||
sudo apt install libbz2-dev
|
|
||||||
|
|
||||||
README, license files in file browser.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
ls
|
ls
|
||||||
|
|||||||
@@ -0,0 +1,248 @@
|
|||||||
|
// Copyright (c) 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
|
||||||
|
// the Software without restriction, including without limitation the rights to
|
||||||
|
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
// the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
// subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
import Divider from '@mui/material/Divider';
|
||||||
|
import Toolbar from "@mui/material/Toolbar";
|
||||||
|
import IconButtonEx from "./IconButtonEx";
|
||||||
|
import ArrowBackIcon from "@mui/icons-material/ArrowBack";
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
import List from '@mui/material/List';
|
||||||
|
import ListItem from '@mui/material/ListItem';
|
||||||
|
import ListItemIcon from '@mui/material/ListItemIcon';
|
||||||
|
import ListItemText from '@mui/material/ListItemText';
|
||||||
|
import Checkbox from '@mui/material/Checkbox';
|
||||||
|
import Select from '@mui/material/Select';
|
||||||
|
import Button from '@mui/material/Button';
|
||||||
|
import InputLabel from '@mui/material/InputLabel';
|
||||||
|
import DialogEx from './DialogEx';
|
||||||
|
import DialogTitle from '@mui/material/DialogTitle';
|
||||||
|
import MenuItem from '@mui/material/MenuItem';
|
||||||
|
import DialogActions from '@mui/material/DialogActions';
|
||||||
|
import DialogContent from '@mui/material/DialogContent';
|
||||||
|
import ResizeResponsiveComponent from './ResizeResponsiveComponent';
|
||||||
|
import { PiPedalModel, PresetIndexEntry } from './PiPedalModel';
|
||||||
|
import { BankIndexEntry } from './Banks';
|
||||||
|
|
||||||
|
|
||||||
|
export interface ImportPresetFromBankDialogProps {
|
||||||
|
open: boolean,
|
||||||
|
onOk: (bankInstanceId: number, presets: number[]) => void,
|
||||||
|
onClose: () => void
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface ImportPresetFromBankDialogState {
|
||||||
|
selectedBank: number;
|
||||||
|
banks: BankIndexEntry[];
|
||||||
|
bankPresets: PresetIndexEntry[] | null;
|
||||||
|
selectedPresets: number[];
|
||||||
|
|
||||||
|
fullScreen: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
function setSavedDefaultBankSelection(bankInstanceId: number) {
|
||||||
|
localStorage.setItem('pipedal_defaultBankSelection', bankInstanceId.toString());
|
||||||
|
}
|
||||||
|
function getSavedDefaultBankSelection() {
|
||||||
|
let defaultValue = localStorage.getItem('pipedal_defaultBankSelection');
|
||||||
|
if (defaultValue) {
|
||||||
|
return parseInt(defaultValue);
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
export default class ImportPresetFromBankDialog extends ResizeResponsiveComponent<ImportPresetFromBankDialogProps, ImportPresetFromBankDialogState> {
|
||||||
|
|
||||||
|
model: PiPedalModel;
|
||||||
|
constructor(props: ImportPresetFromBankDialogProps) {
|
||||||
|
super(props);
|
||||||
|
this.model = PiPedalModel.getInstance();
|
||||||
|
let banks = this.getBanks();
|
||||||
|
let selectedBank = this.getDefaultBankSelection(banks);
|
||||||
|
this.state = {
|
||||||
|
banks: banks,
|
||||||
|
selectedBank: selectedBank,
|
||||||
|
fullScreen: this.windowSize.height < 550,
|
||||||
|
bankPresets: null,
|
||||||
|
selectedPresets: []
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
private getBanks() {
|
||||||
|
let bankIndex = this.model.banks.get();
|
||||||
|
let result: BankIndexEntry[] = [];
|
||||||
|
for (let entry of bankIndex.entries) {
|
||||||
|
if (entry.instanceId === bankIndex.selectedBank) continue;
|
||||||
|
result.push(entry);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
private getDefaultBankSelection(banks: BankIndexEntry[]) {
|
||||||
|
if (banks.length === 0) return -1;
|
||||||
|
let defaultSelection = getSavedDefaultBankSelection();
|
||||||
|
for (let bank of banks) {
|
||||||
|
if (bank.instanceId === defaultSelection) return defaultSelection;
|
||||||
|
}
|
||||||
|
return banks[0].instanceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
private mounted: boolean = false;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
onWindowSizeChanged(width: number, height: number): void {
|
||||||
|
this.setState({ fullScreen: height < 550 })
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
requestPresets(bankInstanceId: number) {
|
||||||
|
this.model.requestBankPresets(bankInstanceId).then((presets) => {
|
||||||
|
if (!this.mounted) return;
|
||||||
|
this.setState({ bankPresets: presets, selectedPresets: [] });
|
||||||
|
}).catch((error) => {
|
||||||
|
this.model.showAlert("Error requesting presets: " + error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
componentDidMount() {
|
||||||
|
super.componentDidMount();
|
||||||
|
this.mounted = true;
|
||||||
|
this.requestPresets(this.state.selectedBank);
|
||||||
|
}
|
||||||
|
componentWillUnmount() {
|
||||||
|
super.componentWillUnmount();
|
||||||
|
this.mounted = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate() {
|
||||||
|
}
|
||||||
|
checkForIllegalCharacters(filename: string) {
|
||||||
|
}
|
||||||
|
|
||||||
|
handleBankChanged(bankId: number) {
|
||||||
|
setSavedDefaultBankSelection(bankId);
|
||||||
|
this.setState({ selectedBank: bankId });
|
||||||
|
this.requestPresets(bankId);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleOk() {
|
||||||
|
if (this.state.selectedPresets.length === 0) return;
|
||||||
|
this.props.onOk(this.state.selectedBank, this.state.selectedPresets);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let props = this.props;
|
||||||
|
let { open, onClose } = props;
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
onClose();
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<DialogEx tag="importPreset" open={open} fullWidth maxWidth="xs" onClose={handleClose} aria-labelledby="Rename-dialog-title"
|
||||||
|
fullScreen={this.state.fullScreen}
|
||||||
|
style={{ userSelect: "none" }}
|
||||||
|
onEnterKey={() => { }}
|
||||||
|
>
|
||||||
|
<div style={{ display: "flex", flexFlow: "column nowrap", height: this.state.fullScreen ? "100vh" : "80vh" }}>
|
||||||
|
{!this.state.fullScreen && (
|
||||||
|
<DialogTitle style={{ paddingTop: 8, paddingBottom: 0 }}>
|
||||||
|
<Toolbar style={{ padding: 0 }}>
|
||||||
|
<IconButtonEx
|
||||||
|
tooltip="Close"
|
||||||
|
edge="start"
|
||||||
|
color="inherit"
|
||||||
|
aria-label="cancel"
|
||||||
|
style={{ opacity: 0.6 }}
|
||||||
|
onClick={() => { onClose(); }}
|
||||||
|
>
|
||||||
|
<ArrowBackIcon style={{ width: 24, height: 24 }} />
|
||||||
|
</IconButtonEx>
|
||||||
|
|
||||||
|
<Typography noWrap component="div" sx={{ flexGrow: 1 }}>
|
||||||
|
Import Presets from Bank
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
</Toolbar>
|
||||||
|
</DialogTitle>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<DialogContent style={{ flex: "0 0 auto", paddingTop: 8, paddingBottom: 8, }}>
|
||||||
|
<InputLabel style={{ fontSize: "0.75rem", fontWeight: 400, marginTop: 0 }}>Bank</InputLabel>
|
||||||
|
|
||||||
|
<Select variant="standard" fullWidth value={this.state.selectedBank} style={{ marginBottom: 8 }}
|
||||||
|
onChange={(e) => { this.handleBankChanged(e.target.value as number); }}>
|
||||||
|
{this.state.banks.map((bankEntry) => {
|
||||||
|
return (
|
||||||
|
<MenuItem key={bankEntry.instanceId} value={bankEntry.instanceId}
|
||||||
|
selected={bankEntry.instanceId === this.state.selectedBank}
|
||||||
|
>
|
||||||
|
{bankEntry.name}
|
||||||
|
</MenuItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Select>
|
||||||
|
</DialogContent>
|
||||||
|
<Divider />
|
||||||
|
<List sx={{ flex: "1 1 auto", width: '100%', bgColor: 'background.paper', overflowX: 'auto' }}>
|
||||||
|
{this.state.bankPresets?.map((presetEntry) => {
|
||||||
|
const labelId = `checkbox-list-label-${presetEntry.instanceId}`;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ListItem key={presetEntry.instanceId}
|
||||||
|
onClick={() => {
|
||||||
|
let selectedPresets = this.state.selectedPresets;
|
||||||
|
let index = selectedPresets.indexOf(presetEntry.instanceId);
|
||||||
|
if (index === -1) {
|
||||||
|
selectedPresets.push(presetEntry.instanceId);
|
||||||
|
} else {
|
||||||
|
selectedPresets.splice(index, 1);
|
||||||
|
}
|
||||||
|
this.setState({ selectedPresets: [...selectedPresets] });
|
||||||
|
}}>
|
||||||
|
<ListItemIcon style={{ marginLeft: 16 }}>
|
||||||
|
<Checkbox
|
||||||
|
edge="start"
|
||||||
|
checked={this.state.selectedPresets.indexOf(presetEntry.instanceId) !== -1}
|
||||||
|
tabIndex={-1}
|
||||||
|
disableRipple
|
||||||
|
inputProps={{ 'aria-labelledby': labelId }}
|
||||||
|
/>
|
||||||
|
</ListItemIcon>
|
||||||
|
<ListItemText id={labelId} primary={presetEntry.name} />
|
||||||
|
</ListItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</List>
|
||||||
|
|
||||||
|
<Divider />
|
||||||
|
<DialogActions style={{ flex: "0 0 auto" }}>
|
||||||
|
<Button onClick={handleClose} variant="dialogSecondary" >
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() => { this.handleOk(); }} variant="dialogPrimary"
|
||||||
|
disabled={this.state.selectedPresets.length === 0}
|
||||||
|
style={{ opacity: this.state.selectedPresets.length === 0 ? 0.5 : 1.0 }}
|
||||||
|
>
|
||||||
|
OK
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</div>
|
||||||
|
</DialogEx>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2049,13 +2049,14 @@ export class PiPedalModel //implements PiPedalModel
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
saveCurrentPresetAs(newName: string, saveAfterInstanceId = -1): Promise<number> {
|
saveCurrentPresetAs(bankInstanceId: number, newName: string, saveAfterInstanceId = -1): Promise<number> {
|
||||||
// default behaviour is to save after the currently selected preset.
|
// default behaviour is to save after the currently selected preset.
|
||||||
if (saveAfterInstanceId === -1) {
|
if (saveAfterInstanceId === -1) {
|
||||||
saveAfterInstanceId = this.presets.get().selectedInstanceId;
|
saveAfterInstanceId = this.presets.get().selectedInstanceId;
|
||||||
}
|
}
|
||||||
let request: any = {
|
let request: any = {
|
||||||
clientId: this.clientId,
|
clientId: this.clientId,
|
||||||
|
bankInstanceId: bankInstanceId,
|
||||||
name: newName,
|
name: newName,
|
||||||
saveAfterInstanceId: saveAfterInstanceId
|
saveAfterInstanceId: saveAfterInstanceId
|
||||||
|
|
||||||
@@ -2064,7 +2065,9 @@ export class PiPedalModel //implements PiPedalModel
|
|||||||
return nullCast(this.webSocket)
|
return nullCast(this.webSocket)
|
||||||
.request<number>("saveCurrentPresetAs", request)
|
.request<number>("saveCurrentPresetAs", request)
|
||||||
.then((newPresetId) => {
|
.then((newPresetId) => {
|
||||||
this.loadPreset(newPresetId);
|
if (bankInstanceId === this.banks.get().selectedBank) {
|
||||||
|
this.loadPreset(newPresetId);
|
||||||
|
}
|
||||||
return newPresetId;
|
return newPresetId;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -3539,6 +3542,13 @@ export class PiPedalModel //implements PiPedalModel
|
|||||||
this.webSocket?.send("setSelectedPedalboardPlugin", { clientId: this.clientId, pluginInstanceId: pluginId });
|
this.webSocket?.send("setSelectedPedalboardPlugin", { clientId: this.clientId, pluginInstanceId: pluginId });
|
||||||
|
|
||||||
}
|
}
|
||||||
|
requestBankPresets(bankInstanceId: number): Promise<PresetIndexEntry[]> {
|
||||||
|
return nullCast(this.webSocket).request<PresetIndexEntry[]>("requestBankPresets", {bankInstanceId: bankInstanceId});
|
||||||
|
}
|
||||||
|
|
||||||
|
importPresetsFromBank(bankInstanceId: number, presets: number[]): Promise<number> {
|
||||||
|
return nullCast(this.webSocket).request<number>("importPresetsFromBank", {bankInstanceId: bankInstanceId, presets: presets});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let instance: PiPedalModel | undefined = undefined;
|
let instance: PiPedalModel | undefined = undefined;
|
||||||
|
|||||||
@@ -17,9 +17,12 @@
|
|||||||
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
import React, { SyntheticEvent,Component } from 'react';
|
import React, { SyntheticEvent, Component } from 'react';
|
||||||
|
import ImportPresetFromBankDialog from './ImportPresetFromBankDialog';
|
||||||
|
import Icon from '@mui/material/Icon';
|
||||||
|
import Divider from '@mui/material/Divider';
|
||||||
import { css } from '@emotion/react';
|
import { css } from '@emotion/react';
|
||||||
import {isDarkMode} from './DarkMode';
|
import { isDarkMode } from './DarkMode';
|
||||||
import IconButtonEx from './IconButtonEx';
|
import IconButtonEx from './IconButtonEx';
|
||||||
import Typography from '@mui/material/Typography';
|
import Typography from '@mui/material/Typography';
|
||||||
import { PiPedalModel, PiPedalModelFactory, PresetIndexEntry, PresetIndex } from './PiPedalModel';
|
import { PiPedalModel, PiPedalModelFactory, PresetIndexEntry, PresetIndex } from './PiPedalModel';
|
||||||
@@ -43,8 +46,8 @@ import ArrowBackIcon from '@mui/icons-material/ArrowBack';
|
|||||||
import EditIcon from '@mui/icons-material/Edit';
|
import EditIcon from '@mui/icons-material/Edit';
|
||||||
import RenameDialog from './RenameDialog';
|
import RenameDialog from './RenameDialog';
|
||||||
|
|
||||||
import Slide, {SlideProps} from '@mui/material/Slide';
|
import Slide, { SlideProps } from '@mui/material/Slide';
|
||||||
import {createStyles} from './WithStyles';
|
import { createStyles } from './WithStyles';
|
||||||
|
|
||||||
import { Theme } from '@mui/material/styles';
|
import { Theme } from '@mui/material/styles';
|
||||||
import WithStyles from './WithStyles';
|
import WithStyles from './WithStyles';
|
||||||
@@ -66,9 +69,11 @@ interface PresetDialogState {
|
|||||||
|
|
||||||
showActionBar: boolean;
|
showActionBar: boolean;
|
||||||
|
|
||||||
selectedItem: number;
|
currentItem: number;
|
||||||
|
selectedItems: Set<number>;
|
||||||
|
|
||||||
renameOpen: boolean;
|
renameOpen: boolean;
|
||||||
|
importOpen: boolean;
|
||||||
|
|
||||||
moreMenuAnchorEl: HTMLElement | null;
|
moreMenuAnchorEl: HTMLElement | null;
|
||||||
openUploadPresetDialog: boolean;
|
openUploadPresetDialog: boolean;
|
||||||
@@ -79,7 +84,7 @@ interface PresetDialogState {
|
|||||||
|
|
||||||
const styles = (theme: Theme) => createStyles({
|
const styles = (theme: Theme) => createStyles({
|
||||||
listIcon: css({
|
listIcon: css({
|
||||||
width: 24, height: 24, opacity: 0.6, fill: theme.palette.text.primary
|
width: 24, height: 24, opacity: 0.6, fill: theme.palette.text.primary
|
||||||
}),
|
}),
|
||||||
dialogAppBar: css({
|
dialogAppBar: css({
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
@@ -146,8 +151,10 @@ const PresetDialog = withStyles(
|
|||||||
this.state = {
|
this.state = {
|
||||||
presets: presets,
|
presets: presets,
|
||||||
showActionBar: false,
|
showActionBar: false,
|
||||||
selectedItem: presets.selectedInstanceId,
|
currentItem: presets.selectedInstanceId,
|
||||||
|
selectedItems: new Set<number>(),
|
||||||
renameOpen: false,
|
renameOpen: false,
|
||||||
|
importOpen: false,
|
||||||
moreMenuAnchorEl: null,
|
moreMenuAnchorEl: null,
|
||||||
openUploadPresetDialog: false
|
openUploadPresetDialog: false
|
||||||
|
|
||||||
@@ -155,10 +162,16 @@ const PresetDialog = withStyles(
|
|||||||
this.handlePresetsChanged = this.handlePresetsChanged.bind(this);
|
this.handlePresetsChanged = this.handlePresetsChanged.bind(this);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
setSelection(instanceId: number) {
|
||||||
|
this.setState({
|
||||||
|
currentItem: instanceId,
|
||||||
|
selectedItems: new Set<number>([instanceId])
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
selectItemAtIndex(index: number) {
|
selectItemAtIndex(index: number) {
|
||||||
let instanceId = this.state.presets.presets[index].instanceId;
|
let instanceId = this.state.presets.presets[index].instanceId;
|
||||||
this.setState({ selectedItem: instanceId });
|
this.setSelection(instanceId);
|
||||||
}
|
}
|
||||||
isEditMode() {
|
isEditMode() {
|
||||||
return this.state.showActionBar || this.props.isEditDialog;
|
return this.state.showActionBar || this.props.isEditDialog;
|
||||||
@@ -170,7 +183,7 @@ const PresetDialog = withStyles(
|
|||||||
|
|
||||||
handleDownloadPreset() {
|
handleDownloadPreset() {
|
||||||
this.handleMoreClose();
|
this.handleMoreClose();
|
||||||
this.model.download("downloadPreset", this.state.selectedItem);
|
this.model.download("downloadPreset", this.state.currentItem);
|
||||||
}
|
}
|
||||||
handleUploadPreset() {
|
handleUploadPreset() {
|
||||||
this.handleMoreClose();
|
this.handleMoreClose();
|
||||||
@@ -189,8 +202,10 @@ const PresetDialog = withStyles(
|
|||||||
if (!presets.areEqual(this.state.presets, false)) // avoid a bunch of peculiar effects if we update while a drag is in progress
|
if (!presets.areEqual(this.state.presets, false)) // avoid a bunch of peculiar effects if we update while a drag is in progress
|
||||||
{
|
{
|
||||||
// if we don't have a valid selection, then use the current preset.
|
// if we don't have a valid selection, then use the current preset.
|
||||||
if (this.state.presets.getItem(this.state.selectedItem) == null) {
|
if (this.state.presets.getItem(this.state.currentItem) == null) {
|
||||||
this.setState({ presets: presets, selectedItem: presets.selectedInstanceId });
|
this.setState({ presets: presets, currentItem: presets.selectedInstanceId,
|
||||||
|
selectedItems: new Set<number>([presets.selectedInstanceId])
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
this.setState({ presets: presets });
|
this.setState({ presets: presets });
|
||||||
}
|
}
|
||||||
@@ -208,7 +223,7 @@ const PresetDialog = withStyles(
|
|||||||
}
|
}
|
||||||
|
|
||||||
getSelectedIndex() {
|
getSelectedIndex() {
|
||||||
let instanceId = this.isEditMode() ? this.state.selectedItem : this.state.presets.selectedInstanceId;
|
let instanceId = this.isEditMode() ? this.state.currentItem : this.state.presets.selectedInstanceId;
|
||||||
let presets = this.state.presets;
|
let presets = this.state.presets;
|
||||||
for (let i = 0; i < presets.presets.length; ++i) {
|
for (let i = 0; i < presets.presets.length; ++i) {
|
||||||
if (presets.presets[i].instanceId === instanceId) return i;
|
if (presets.presets[i].instanceId === instanceId) return i;
|
||||||
@@ -217,12 +232,12 @@ const PresetDialog = withStyles(
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleDeleteClick() {
|
handleDeleteClick() {
|
||||||
if (!this.state.selectedItem) return;
|
if (this.state.currentItem === -1) return;
|
||||||
let selectedItem = this.state.selectedItem;
|
let currentItem = this.state.currentItem;
|
||||||
if (selectedItem !== -1) {
|
if (currentItem !== -1) {
|
||||||
this.model.deletePresetItem(selectedItem)
|
this.model.deletePresetItem(currentItem)
|
||||||
.then((selectedItem: number) => {
|
.then((currentItem: number) => {
|
||||||
this.setState({ selectedItem: selectedItem });
|
this.setSelection(currentItem);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
this.model.showAlert(error);
|
this.model.showAlert(error);
|
||||||
@@ -236,7 +251,7 @@ const PresetDialog = withStyles(
|
|||||||
|
|
||||||
handleItemClick(instanceId: number): void {
|
handleItemClick(instanceId: number): void {
|
||||||
if (this.isEditMode()) {
|
if (this.isEditMode()) {
|
||||||
this.setState({ selectedItem: instanceId });
|
this.setSelection(instanceId);
|
||||||
} else {
|
} else {
|
||||||
this.model.loadPreset(instanceId);
|
this.model.loadPreset(instanceId);
|
||||||
this.props.onDialogClose();
|
this.props.onDialogClose();
|
||||||
@@ -251,19 +266,21 @@ const PresetDialog = withStyles(
|
|||||||
mapElement(el: any): React.ReactNode {
|
mapElement(el: any): React.ReactNode {
|
||||||
let presetEntry = el as PresetIndexEntry;
|
let presetEntry = el as PresetIndexEntry;
|
||||||
const classes = withStyles.getClasses(this.props);
|
const classes = withStyles.getClasses(this.props);
|
||||||
let selectedItem = this.isEditMode() ? this.state.selectedItem : this.state.presets.selectedInstanceId;
|
let selected =
|
||||||
|
this.isEditMode() ? this.state.selectedItems.has(presetEntry.instanceId) :
|
||||||
|
(presetEntry.instanceId === this.state.presets.selectedInstanceId);
|
||||||
return (
|
return (
|
||||||
<div key={presetEntry.instanceId} className="itemBackground">
|
<div key={presetEntry.instanceId} id={"psetdlgItem_" + presetEntry.instanceId} className="itemBackground">
|
||||||
|
|
||||||
<ButtonBase style={{ width: "100%", height: 48 }}
|
<ButtonBase style={{ width: "100%", height: 48 }}
|
||||||
onClick={() => this.handleItemClick(presetEntry.instanceId)}
|
onClick={() => this.handleItemClick(presetEntry.instanceId)}
|
||||||
>
|
>
|
||||||
<SelectHoverBackground selected={presetEntry.instanceId === selectedItem} showHover={true} />
|
<SelectHoverBackground selected={selected} showHover={true} />
|
||||||
<div className={classes.itemFrame}>
|
<div className={classes.itemFrame}>
|
||||||
<div className={classes.iconFrame}>
|
<div className={classes.iconFrame}>
|
||||||
<img
|
<img
|
||||||
src={isDarkMode()? "img/ic_presets_white.svg": "img/ic_presets.svg"}
|
src={isDarkMode() ? "img/ic_presets_white.svg" : "img/ic_presets.svg"}
|
||||||
className={classes.itemIcon} alt="" />
|
className={classes.itemIcon} alt="" />
|
||||||
</div>
|
</div>
|
||||||
<div className={classes.itemLabel}>
|
<div className={classes.itemLabel}>
|
||||||
<Typography>
|
<Typography>
|
||||||
@@ -287,30 +304,33 @@ const PresetDialog = withStyles(
|
|||||||
moveElement(from: number, to: number): void {
|
moveElement(from: number, to: number): void {
|
||||||
let newPresets = this.state.presets.clone();
|
let newPresets = this.state.presets.clone();
|
||||||
newPresets.movePreset(from, to);
|
newPresets.movePreset(from, to);
|
||||||
|
let toInstanceId = newPresets.presets[to].instanceId;
|
||||||
this.setState({
|
this.setState({
|
||||||
presets: newPresets,
|
presets: newPresets,
|
||||||
selectedItem: newPresets.presets[to].instanceId
|
currentItem: toInstanceId,
|
||||||
|
selectedItems: new Set<number>([toInstanceId])
|
||||||
|
|
||||||
});
|
});
|
||||||
this.updateServerPresets(newPresets);
|
this.updateServerPresets(newPresets);
|
||||||
}
|
}
|
||||||
|
|
||||||
getSelectedName(): string {
|
getSelectedName(): string {
|
||||||
let item = this.state.presets.getItem(this.state.selectedItem);
|
let item = this.state.presets.getItem(this.state.currentItem);
|
||||||
if (item) return item.name;
|
if (item) return item.name;
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
handleRenameClick() {
|
handleRenameClick() {
|
||||||
let item = this.state.presets.getItem(this.state.selectedItem);
|
let item = this.state.presets.getItem(this.state.currentItem);
|
||||||
if (item) {
|
if (item) {
|
||||||
this.setState({ renameOpen: true });
|
this.setState({ renameOpen: true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
handleRenameOk(text: string) {
|
handleRenameOk(text: string) {
|
||||||
let item = this.state.presets.getItem(this.state.selectedItem);
|
let item = this.state.presets.getItem(this.state.currentItem);
|
||||||
if (!item) return;
|
if (!item) return;
|
||||||
if (item.name !== text) {
|
if (item.name !== text) {
|
||||||
this.model.renamePresetItem(this.state.selectedItem, text)
|
this.model.renamePresetItem(this.state.currentItem, text)
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
this.onError(error);
|
this.onError(error);
|
||||||
});
|
});
|
||||||
@@ -319,11 +339,14 @@ const PresetDialog = withStyles(
|
|||||||
this.setState({ renameOpen: false });
|
this.setState({ renameOpen: false });
|
||||||
}
|
}
|
||||||
handleCopy() {
|
handleCopy() {
|
||||||
let item = this.state.presets.getItem(this.state.selectedItem);
|
let item = this.state.presets.getItem(this.state.currentItem);
|
||||||
if (!item) return;
|
if (!item) return;
|
||||||
this.model.duplicatePreset(this.state.selectedItem)
|
this.model.duplicatePreset(this.state.currentItem)
|
||||||
.then((newId) => {
|
.then((newId) => {
|
||||||
this.setState({ selectedItem: newId });
|
this.setState({
|
||||||
|
currentItem: newId,
|
||||||
|
selectedItems: new Set<number>([newId])
|
||||||
|
});
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
this.onError(error);
|
this.onError(error);
|
||||||
});
|
});
|
||||||
@@ -333,6 +356,29 @@ const PresetDialog = withStyles(
|
|||||||
this.model?.showAlert(error);
|
this.model?.showAlert(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleImportPresetsFromBank()
|
||||||
|
{
|
||||||
|
this.handleMoreClose();
|
||||||
|
this.setState({ importOpen: true });
|
||||||
|
}
|
||||||
|
handleImportDialogOk(bankInstanceId: number, presets: number[]): void {
|
||||||
|
this.setState({ importOpen: false });
|
||||||
|
this.model.importPresetsFromBank(bankInstanceId, presets)
|
||||||
|
.then((instanceId) => {
|
||||||
|
if (instanceId !== -1) {
|
||||||
|
this.setSelection(instanceId);
|
||||||
|
setTimeout(() => {
|
||||||
|
let el = document.getElementById("psetdlgItem_" + instanceId);
|
||||||
|
if (el) {
|
||||||
|
el.scrollIntoView({ behavior: "smooth", block: "center" });
|
||||||
|
}
|
||||||
|
},0);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
this.model.showAlert(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@@ -344,9 +390,9 @@ const PresetDialog = withStyles(
|
|||||||
return (
|
return (
|
||||||
<DialogEx tag="preset" fullScreen open={this.props.show}
|
<DialogEx tag="preset" fullScreen open={this.props.show}
|
||||||
onClose={() => { this.handleDialogClose() }} TransitionComponent={Transition}
|
onClose={() => { this.handleDialogClose() }} TransitionComponent={Transition}
|
||||||
style={{userSelect: "none"}}
|
style={{ userSelect: "none" }}
|
||||||
onEnterKey={()=>{}}
|
onEnterKey={() => { }}
|
||||||
>
|
>
|
||||||
<div style={{ display: "flex", flexDirection: "column", flexWrap: "nowrap", width: "100%", height: "100%", overflow: "hidden" }}>
|
<div style={{ display: "flex", flexDirection: "column", flexWrap: "nowrap", width: "100%", height: "100%", overflow: "hidden" }}>
|
||||||
<div style={{ flex: "0 0 auto" }}>
|
<div style={{ flex: "0 0 auto" }}>
|
||||||
<AppBar className={classes.dialogAppBar} style={{ display: this.isEditMode() ? "none" : "block" }} >
|
<AppBar className={classes.dialogAppBar} style={{ display: this.isEditMode() ? "none" : "block" }} >
|
||||||
@@ -359,7 +405,7 @@ const PresetDialog = withStyles(
|
|||||||
<Typography variant="h6" className={classes.dialogTitle}>
|
<Typography variant="h6" className={classes.dialogTitle}>
|
||||||
Presets
|
Presets
|
||||||
</Typography>
|
</Typography>
|
||||||
<IconButtonEx tooltip="Edit"color="inherit" onClick={(e) => this.showActionBar(true)} >
|
<IconButtonEx tooltip="Edit" color="inherit" onClick={(e) => this.showActionBar(true)} >
|
||||||
<EditIcon />
|
<EditIcon />
|
||||||
</IconButtonEx>
|
</IconButtonEx>
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
@@ -382,12 +428,13 @@ const PresetDialog = withStyles(
|
|||||||
<Typography variant="h6" className={classes.dialogTitle}>
|
<Typography variant="h6" className={classes.dialogTitle}>
|
||||||
Presets
|
Presets
|
||||||
</Typography>
|
</Typography>
|
||||||
{(this.state.presets.getItem(this.state.selectedItem) != null)
|
{(this.state.presets.getItem(this.state.currentItem) != null)
|
||||||
&& (
|
&& (
|
||||||
|
|
||||||
<div style={{ flex: "0 0 auto", display: "flex", flexFlow: "row nowrap", alignItems: "center"
|
<div style={{
|
||||||
|
flex: "0 0 auto", display: "flex", flexFlow: "row nowrap", alignItems: "center"
|
||||||
|
|
||||||
}}>
|
}}>
|
||||||
<Button color="inherit" onClick={(e) => this.handleCopy()}>
|
<Button color="inherit" onClick={(e) => this.handleCopy()}>
|
||||||
Copy
|
Copy
|
||||||
</Button>
|
</Button>
|
||||||
@@ -419,6 +466,26 @@ const PresetDialog = withStyles(
|
|||||||
onClose={() => this.handleMoreClose()}
|
onClose={() => this.handleMoreClose()}
|
||||||
TransitionComponent={Fade}
|
TransitionComponent={Fade}
|
||||||
>
|
>
|
||||||
|
<MenuItem onClick={() => { this.handleDownloadPreset(); }} >
|
||||||
|
<ListItemIcon>
|
||||||
|
<Icon />
|
||||||
|
</ListItemIcon>
|
||||||
|
<ListItemText>
|
||||||
|
Copy to bank...
|
||||||
|
</ListItemText>
|
||||||
|
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem onClick={() => { this.handleImportPresetsFromBank(); }} >
|
||||||
|
<ListItemIcon>
|
||||||
|
<Icon />
|
||||||
|
</ListItemIcon>
|
||||||
|
<ListItemText>
|
||||||
|
Import presets from bank...
|
||||||
|
</ListItemText>
|
||||||
|
|
||||||
|
</MenuItem>
|
||||||
|
|
||||||
|
<Divider />
|
||||||
<MenuItem onClick={() => { this.handleDownloadPreset(); }} >
|
<MenuItem onClick={() => { this.handleDownloadPreset(); }} >
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<DownloadIcon className={classes.listIcon} />
|
<DownloadIcon className={classes.listIcon} />
|
||||||
@@ -465,13 +532,20 @@ const PresetDialog = withStyles(
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<UploadPresetDialog
|
<UploadPresetDialog
|
||||||
title='Upload preset'
|
title='Upload preset'
|
||||||
extension='.piPreset'
|
extension='.piPreset'
|
||||||
uploadPage='uploadPreset'
|
uploadPage='uploadPreset'
|
||||||
onUploaded={(instanceId) => this.setState({selectedItem: instanceId}) }
|
onUploaded={(instanceId) => this.setSelection(instanceId)}
|
||||||
uploadAfter={this.state.selectedItem}
|
uploadAfter={this.state.currentItem }
|
||||||
open={this.state.openUploadPresetDialog}
|
open={this.state.openUploadPresetDialog}
|
||||||
onClose={() => { this.setState({ openUploadPresetDialog: false }) }} />
|
onClose={() => { this.setState({ openUploadPresetDialog: false }) }} />
|
||||||
|
{this.state.importOpen && (
|
||||||
|
<ImportPresetFromBankDialog
|
||||||
|
open={this.state.importOpen}
|
||||||
|
onClose={() => this.setState({ importOpen: false })}
|
||||||
|
onOk={(bankInstanceId, presets) => this.handleImportDialogOk(bankInstanceId, presets)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
</DialogEx>
|
</DialogEx>
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import MoreVertIcon from '@mui/icons-material/MoreVert';
|
|||||||
import { Theme } from '@mui/material/styles';
|
import { Theme } from '@mui/material/styles';
|
||||||
import WithStyles from './WithStyles';
|
import WithStyles from './WithStyles';
|
||||||
import { withStyles } from "tss-react/mui";
|
import { withStyles } from "tss-react/mui";
|
||||||
import {createStyles} from './WithStyles';
|
import { createStyles } from './WithStyles';
|
||||||
|
|
||||||
import PresetDialog from './PresetDialog';
|
import PresetDialog from './PresetDialog';
|
||||||
import Menu from '@mui/material/Menu';
|
import Menu from '@mui/material/Menu';
|
||||||
@@ -33,9 +33,12 @@ import MenuItem from '@mui/material/MenuItem';
|
|||||||
import Fade from '@mui/material/Fade';
|
import Fade from '@mui/material/Fade';
|
||||||
import Divider from '@mui/material/Divider';
|
import Divider from '@mui/material/Divider';
|
||||||
import RenameDialog from './RenameDialog'
|
import RenameDialog from './RenameDialog'
|
||||||
|
import SavePresetAsDialog from './SavePresetAsDialog';
|
||||||
|
import ImportPresetFromBankDialog from './ImportPresetFromBankDialog';
|
||||||
|
|
||||||
import Select from '@mui/material/Select';
|
import Select from '@mui/material/Select';
|
||||||
import UploadPresetDialog from './UploadPresetDialog';
|
import UploadPresetDialog from './UploadPresetDialog';
|
||||||
import {isDarkMode} from './DarkMode';
|
import { isDarkMode } from './DarkMode';
|
||||||
|
|
||||||
interface PresetSelectorProps extends WithStyles<typeof styles> {
|
interface PresetSelectorProps extends WithStyles<typeof styles> {
|
||||||
|
|
||||||
@@ -49,6 +52,9 @@ interface PresetSelectorState {
|
|||||||
showEditPresetsDialog: boolean;
|
showEditPresetsDialog: boolean;
|
||||||
presetsMenuAnchorRef: HTMLElement | null;
|
presetsMenuAnchorRef: HTMLElement | null;
|
||||||
|
|
||||||
|
saveAsDialogOpen: boolean;
|
||||||
|
importDialogOpen: boolean;
|
||||||
|
|
||||||
renameDialogOpen: boolean;
|
renameDialogOpen: boolean;
|
||||||
renameDialogTitle: string;
|
renameDialogTitle: string;
|
||||||
renameDialogDefaultName: string;
|
renameDialogDefaultName: string;
|
||||||
@@ -59,7 +65,7 @@ interface PresetSelectorState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const selectColor = isDarkMode()? "#888": "#FFFFFF";
|
const selectColor = isDarkMode() ? "#888" : "#FFFFFF";
|
||||||
|
|
||||||
const styles = (theme: Theme) => createStyles({
|
const styles = (theme: Theme) => createStyles({
|
||||||
select: { // fu fu fu.Overrides for white selector on dark background.
|
select: { // fu fu fu.Overrides for white selector on dark background.
|
||||||
@@ -96,6 +102,8 @@ const PresetSelector =
|
|||||||
showEditPresetsDialog: false,
|
showEditPresetsDialog: false,
|
||||||
presetsMenuAnchorRef: null,
|
presetsMenuAnchorRef: null,
|
||||||
renameDialogOpen: false,
|
renameDialogOpen: false,
|
||||||
|
saveAsDialogOpen: false,
|
||||||
|
importDialogOpen: false,
|
||||||
renameDialogTitle: "",
|
renameDialogTitle: "",
|
||||||
renameDialogDefaultName: "",
|
renameDialogDefaultName: "",
|
||||||
renameDialogActionName: "",
|
renameDialogActionName: "",
|
||||||
@@ -117,6 +125,12 @@ const PresetSelector =
|
|||||||
this.setState({ presetsMenuAnchorRef: null });
|
this.setState({ presetsMenuAnchorRef: null });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handlePresetsMenuImport(e: SyntheticEvent): void {
|
||||||
|
this.handlePresetsMenuClose();
|
||||||
|
e.stopPropagation();
|
||||||
|
this.setState({ importDialogOpen: true });
|
||||||
|
}
|
||||||
|
|
||||||
handleDownloadPreset(e: SyntheticEvent) {
|
handleDownloadPreset(e: SyntheticEvent) {
|
||||||
this.handlePresetsMenuClose();
|
this.handlePresetsMenuClose();
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@@ -144,21 +158,8 @@ const PresetSelector =
|
|||||||
let currentPresets = this.model.presets.get();
|
let currentPresets = this.model.presets.get();
|
||||||
let item = currentPresets.getItem(currentPresets.selectedInstanceId);
|
let item = currentPresets.getItem(currentPresets.selectedInstanceId);
|
||||||
if (item == null) return;
|
if (item == null) return;
|
||||||
let name = item.name;
|
|
||||||
|
|
||||||
this.renameDialogOpen(name, "Save Preset As", "OK")
|
|
||||||
.then((newName) => {
|
|
||||||
return this.model.saveCurrentPresetAs(newName);
|
|
||||||
})
|
|
||||||
.then((newInstanceId) => {
|
|
||||||
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
this.showError(error);
|
|
||||||
})
|
|
||||||
;
|
|
||||||
|
|
||||||
|
|
||||||
|
this.setState({ saveAsDialogOpen: true });
|
||||||
}
|
}
|
||||||
handlePresetsMenuRename(e: SyntheticEvent): void {
|
handlePresetsMenuRename(e: SyntheticEvent): void {
|
||||||
this.handlePresetsMenuClose();
|
this.handlePresetsMenuClose();
|
||||||
@@ -198,7 +199,7 @@ const PresetSelector =
|
|||||||
showError(error: string) {
|
showError(error: string) {
|
||||||
this.model.showAlert(error);
|
this.model.showAlert(error);
|
||||||
}
|
}
|
||||||
renameDialogOpen(defaultText: string, title: string,acceptButtonText: string): Promise<string> {
|
renameDialogOpen(defaultText: string, title: string, acceptButtonText: string): Promise<string> {
|
||||||
let result = new Promise<string>(
|
let result = new Promise<string>(
|
||||||
(resolve, reject) => {
|
(resolve, reject) => {
|
||||||
this.setState(
|
this.setState(
|
||||||
@@ -219,6 +220,30 @@ const PresetSelector =
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleSaveAsDialogOk(bankInstanceId: number, name: string): void {
|
||||||
|
this.setState({ saveAsDialogOpen: false });
|
||||||
|
|
||||||
|
this.model.saveCurrentPresetAs(bankInstanceId, name)
|
||||||
|
.then((instanceId) => {
|
||||||
|
this.model.loadPreset(instanceId);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
this.showError(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
handleImportDialogOk(bankInstanceId: number, presets: number[]): void {
|
||||||
|
this.setState({ importDialogOpen: false });
|
||||||
|
this.model.importPresetsFromBank(bankInstanceId, presets)
|
||||||
|
.then((instanceId) => {
|
||||||
|
if (instanceId !== -1) {
|
||||||
|
this.model.loadPreset(instanceId);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
this.showError(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
handleRenameDialogClose(): void {
|
handleRenameDialogClose(): void {
|
||||||
this.setState({
|
this.setState({
|
||||||
renameDialogOpen: false,
|
renameDialogOpen: false,
|
||||||
@@ -318,7 +343,7 @@ const PresetSelector =
|
|||||||
onChange={(e, extra) => this.handleChange(e, extra)}
|
onChange={(e, extra) => this.handleChange(e, extra)}
|
||||||
onClose={(e) => this.handleSelectClose(e)}
|
onClose={(e) => this.handleSelectClose(e)}
|
||||||
displayEmpty
|
displayEmpty
|
||||||
value={presets.selectedInstanceId === 0? '' : presets.selectedInstanceId}
|
value={presets.selectedInstanceId === 0 ? '' : presets.selectedInstanceId}
|
||||||
inputProps={{
|
inputProps={{
|
||||||
classes: { icon: classes.icon },
|
classes: { icon: classes.icon },
|
||||||
'aria-label': "Select preset"
|
'aria-label': "Select preset"
|
||||||
@@ -339,13 +364,13 @@ const PresetSelector =
|
|||||||
}
|
}
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
<div style={{ flex: "0 0 auto"}}>
|
<div style={{ flex: "0 0 auto" }}>
|
||||||
<IconButtonEx
|
<IconButtonEx
|
||||||
tooltip="More..."
|
tooltip="More..."
|
||||||
style={{ flex: "0 0 auto", color: "#FFFFFF" }}
|
style={{ flex: "0 0 auto", color: "#FFFFFF" }}
|
||||||
onClick={(e) => this.handlePresetMenuClick(e)}
|
onClick={(e) => this.handlePresetMenuClick(e)}
|
||||||
size="large"
|
size="large"
|
||||||
>
|
>
|
||||||
<MoreVertIcon style={{ opacity: 0.75 }} color="inherit" />
|
<MoreVertIcon style={{ opacity: 0.75 }} color="inherit" />
|
||||||
</IconButtonEx>
|
</IconButtonEx>
|
||||||
<Menu
|
<Menu
|
||||||
@@ -358,6 +383,7 @@ const PresetSelector =
|
|||||||
<MenuItem onClick={(e) => this.handlePresetsMenuSave(e)}>Save preset</MenuItem>
|
<MenuItem onClick={(e) => this.handlePresetsMenuSave(e)}>Save preset</MenuItem>
|
||||||
<MenuItem onClick={(e) => this.handlePresetsMenuSaveAs(e)}>Save preset as...</MenuItem>
|
<MenuItem onClick={(e) => this.handlePresetsMenuSaveAs(e)}>Save preset as...</MenuItem>
|
||||||
<MenuItem onClick={(e) => this.handlePresetsMenuRename(e)}>Rename...</MenuItem>
|
<MenuItem onClick={(e) => this.handlePresetsMenuRename(e)}>Rename...</MenuItem>
|
||||||
|
<MenuItem onClick={(e) => this.handlePresetsMenuImport(e)}>Import from bank...</MenuItem>
|
||||||
<MenuItem onClick={(e) => this.handlePresetsMenuNew(e)}>New...</MenuItem>
|
<MenuItem onClick={(e) => this.handlePresetsMenuNew(e)}>New...</MenuItem>
|
||||||
<Divider />
|
<Divider />
|
||||||
<MenuItem onClick={(e) => { this.handleDownloadPreset(e); }} >Download preset</MenuItem>
|
<MenuItem onClick={(e) => { this.handleDownloadPreset(e); }} >Download preset</MenuItem>
|
||||||
@@ -366,7 +392,23 @@ const PresetSelector =
|
|||||||
<MenuItem onClick={(e) => this.handleMenuEditPresets()}>Manage presets...</MenuItem>
|
<MenuItem onClick={(e) => this.handleMenuEditPresets()}>Manage presets...</MenuItem>
|
||||||
</Menu>
|
</Menu>
|
||||||
</div>
|
</div>
|
||||||
<PresetDialog show={this.state.showPresetsDialog} isEditDialog={this.state.showEditPresetsDialog} onDialogClose={() => this.handleDialogClose()} />
|
<PresetDialog show={this.state.showPresetsDialog} isEditDialog={this.state.showEditPresetsDialog} onDialogClose={() => this.handleDialogClose()}
|
||||||
|
/>
|
||||||
|
{this.state.saveAsDialogOpen && (
|
||||||
|
<SavePresetAsDialog open={this.state.saveAsDialogOpen}
|
||||||
|
defaultName={presets.getItem(presets.selectedInstanceId)?.name ?? "My Preset"}
|
||||||
|
onClose={() => { this.setState({ saveAsDialogOpen: false }) }}
|
||||||
|
onOk={(bankInstanceId, name) => {
|
||||||
|
this.handleSaveAsDialogOk(bankInstanceId, name);
|
||||||
|
}} />
|
||||||
|
)}
|
||||||
|
{this.state.importDialogOpen && (
|
||||||
|
<ImportPresetFromBankDialog open={this.state.importDialogOpen}
|
||||||
|
onClose={() => { this.setState({ importDialogOpen: false }) }}
|
||||||
|
onOk={(bankInstanceId, presets) => {
|
||||||
|
this.handleImportDialogOk(bankInstanceId, presets);
|
||||||
|
}} />
|
||||||
|
)}
|
||||||
<RenameDialog open={this.state.renameDialogOpen}
|
<RenameDialog open={this.state.renameDialogOpen}
|
||||||
title={this.state.renameDialogTitle}
|
title={this.state.renameDialogTitle}
|
||||||
defaultName={this.state.renameDialogDefaultName}
|
defaultName={this.state.renameDialogDefaultName}
|
||||||
@@ -387,6 +429,6 @@ const PresetSelector =
|
|||||||
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
styles);
|
styles);
|
||||||
|
|
||||||
export default PresetSelector;
|
export default PresetSelector;
|
||||||
@@ -136,7 +136,16 @@ export default class RenameDialog extends ResizeResponsiveComponent<RenameDialog
|
|||||||
autoFocus={!isTouchUi()}
|
autoFocus={!isTouchUi()}
|
||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
variant="standard"
|
variant="standard"
|
||||||
|
autoComplete="off"
|
||||||
|
autoCorrect="on"
|
||||||
|
autoCapitalize="on"
|
||||||
|
spellCheck={false}
|
||||||
|
|
||||||
slotProps={{
|
slotProps={{
|
||||||
|
inputLabel: {
|
||||||
|
shrink: true
|
||||||
|
},
|
||||||
|
|
||||||
input: {
|
input: {
|
||||||
style: { scrollMargin: 24 }
|
style: { scrollMargin: 24 }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,189 @@
|
|||||||
|
// Copyright (c) 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
|
||||||
|
// the Software without restriction, including without limitation the rights to
|
||||||
|
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
// the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
// subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import Select from '@mui/material/Select';
|
||||||
|
import Button from '@mui/material/Button';
|
||||||
|
import InputLabel from '@mui/material/InputLabel';
|
||||||
|
import DialogEx from './DialogEx';
|
||||||
|
import DialogTitle from '@mui/material/DialogTitle';
|
||||||
|
import MenuItem from '@mui/material/MenuItem';
|
||||||
|
import DialogActions from '@mui/material/DialogActions';
|
||||||
|
import DialogContent from '@mui/material/DialogContent';
|
||||||
|
import { nullCast } from './Utility';
|
||||||
|
import ResizeResponsiveComponent from './ResizeResponsiveComponent';
|
||||||
|
import {PiPedalModel,PiPedalModelFactory} from './PiPedalModel';
|
||||||
|
//import TextFieldEx from './TextFieldEx';
|
||||||
|
import TextField from '@mui/material/TextField';
|
||||||
|
import { BankIndex } from './Banks';
|
||||||
|
|
||||||
|
|
||||||
|
export interface SavePresetAsDialogProps {
|
||||||
|
open: boolean,
|
||||||
|
defaultName: string,
|
||||||
|
onOk: (bankInstanceId: number, text: string) => void,
|
||||||
|
onClose: () => void
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface SavePresetAsDialogState {
|
||||||
|
selectedBank: number;
|
||||||
|
banks: BankIndex;
|
||||||
|
|
||||||
|
fullScreen: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
function isTouchUi()
|
||||||
|
{
|
||||||
|
return 'ontouchstart' in window || navigator.maxTouchPoints > 0;
|
||||||
|
}
|
||||||
|
export default class SavePresetAsDialog extends ResizeResponsiveComponent<SavePresetAsDialogProps, SavePresetAsDialogState> {
|
||||||
|
|
||||||
|
refText: React.RefObject<HTMLInputElement|null>;
|
||||||
|
|
||||||
|
model: PiPedalModel;
|
||||||
|
constructor(props: SavePresetAsDialogProps) {
|
||||||
|
super(props);
|
||||||
|
this.model = PiPedalModel.getInstance();
|
||||||
|
this.state = {
|
||||||
|
banks: this.model.banks.get(),
|
||||||
|
selectedBank: this.model.banks.get().selectedBank,
|
||||||
|
fullScreen: false
|
||||||
|
|
||||||
|
};
|
||||||
|
this.refText = React.createRef<HTMLInputElement>();
|
||||||
|
}
|
||||||
|
mounted: boolean = false;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
onWindowSizeChanged(width: number, height: number): void
|
||||||
|
{
|
||||||
|
this.setState({fullScreen: height < 200})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
componentDidMount()
|
||||||
|
{
|
||||||
|
super.componentDidMount();
|
||||||
|
this.mounted = true;
|
||||||
|
}
|
||||||
|
componentWillUnmount()
|
||||||
|
{
|
||||||
|
super.componentWillUnmount();
|
||||||
|
this.mounted = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
checkForIllegalCharacters(filename: string) {
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let props = this.props;
|
||||||
|
let { open, defaultName, onClose, onOk } = props;
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
onClose();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleOk = () => {
|
||||||
|
let text = nullCast(this.refText.current).value;
|
||||||
|
text = text.trim();
|
||||||
|
try {
|
||||||
|
this.checkForIllegalCharacters(text);
|
||||||
|
} catch (e:any)
|
||||||
|
{
|
||||||
|
let model:PiPedalModel = PiPedalModelFactory.getInstance();
|
||||||
|
model.showAlert(e.toString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (text.length === 0) return;
|
||||||
|
onOk(this.state.selectedBank,text);
|
||||||
|
}
|
||||||
|
const handleKeyDown = (event: React.KeyboardEvent<HTMLDivElement>): void => {
|
||||||
|
// 'keypress' event misbehaves on mobile so we track 'Enter' key via 'keydown' event
|
||||||
|
if (event.key === 'Enter') {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
handleOk();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<DialogEx tag="savePresetAs" open={open} fullWidth maxWidth="xs" onClose={handleClose} aria-labelledby="Rename-dialog-title"
|
||||||
|
fullScreen={this.state.fullScreen}
|
||||||
|
style={{userSelect: "none"}}
|
||||||
|
onEnterKey={()=>{}}
|
||||||
|
>
|
||||||
|
<DialogTitle>
|
||||||
|
Save Preset As
|
||||||
|
</DialogTitle>
|
||||||
|
|
||||||
|
<DialogContent >
|
||||||
|
<InputLabel style={{ fontSize: "0.75rem", fontWeight: 400, marginTop: 16 }}>Bank</InputLabel>
|
||||||
|
|
||||||
|
<Select variant="standard" fullWidth value={this.state.selectedBank} style={{marginBottom: 16}}
|
||||||
|
onChange={(e) => this.setState({ selectedBank: e.target.value as number })}>
|
||||||
|
{this.state.banks.entries.map((bankEntry) => {
|
||||||
|
return (
|
||||||
|
<MenuItem key={bankEntry.instanceId} value={bankEntry.instanceId}
|
||||||
|
selected={bankEntry.instanceId === this.state.selectedBank}
|
||||||
|
>
|
||||||
|
{bankEntry.name}
|
||||||
|
</MenuItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Select>
|
||||||
|
|
||||||
|
<TextField
|
||||||
|
autoFocus={!isTouchUi()}
|
||||||
|
onKeyDown={handleKeyDown}
|
||||||
|
autoComplete="off"
|
||||||
|
autoCorrect="on"
|
||||||
|
autoCapitalize="on"
|
||||||
|
spellCheck={false}
|
||||||
|
variant="standard"
|
||||||
|
slotProps={{
|
||||||
|
inputLabel: {
|
||||||
|
shrink: true
|
||||||
|
},
|
||||||
|
input: {
|
||||||
|
style: { scrollMargin: 24 }
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
id="name"
|
||||||
|
type="text"
|
||||||
|
label={"Name"}
|
||||||
|
fullWidth
|
||||||
|
defaultValue={defaultName}
|
||||||
|
inputRef={this.refText}
|
||||||
|
/>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions style={{flexShrink: 1}}>
|
||||||
|
<Button onClick={handleClose} variant="dialogSecondary" >
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button onClick={handleOk} variant="dialogPrimary" >
|
||||||
|
OK
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</DialogEx>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user