Files
op-pedal/src/Banks.hpp
T
2026-06-04 13:46:11 -04:00

389 lines
12 KiB
C++

// 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.
#pragma once
#include "json.hpp"
#include "Pedalboard.hpp"
#include "PiPedalException.hpp"
namespace pipedal
{
#define GETTER_SETTER_REF(name) \
decltype(name##_) &name() { return name##_; } \
const decltype(name##_) &name() const { return name##_; } \
void name(const decltype(name##_) &value) { name##_ = value; }
#define GETTER_SETTER_VEC(name) \
decltype(name##_) &name() { return name##_; } \
const decltype(name##_) &name() const { return name##_; }
#define GETTER_SETTER(name) \
decltype(name##_) name() const { return name##_; } \
void name(decltype(name##_) value) { name##_ = value; }
class PresetIndexEntry
{
int64_t instanceId_ = 0;
std::string name_;
public:
PresetIndexEntry() {}
PresetIndexEntry(uint64_t instanceId, const std::string &name)
: 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)
{
*pResult = presets_[i].name();
return true;
}
}
return false;
}
GETTER_SETTER(selectedInstanceId);
GETTER_SETTER_VEC(presets);
GETTER_SETTER(presetChanged);
DECLARE_JSON_MAP(PresetIndex);
};
class BankFileEntry
{
int64_t instanceId_;
Pedalboard preset_;
public:
GETTER_SETTER(instanceId);
GETTER_SETTER_REF(preset);
DECLARE_JSON_MAP(BankFileEntry);
};
class BankFile
{
std::string name_;
int64_t nextInstanceId_ = 0;
int64_t selectedPreset_ = -1;
std::vector<std::unique_ptr<BankFileEntry>> presets_;
public:
GETTER_SETTER(name);
GETTER_SETTER(nextInstanceId);
GETTER_SETTER(selectedPreset);
GETTER_SETTER_VEC(presets);
void clear()
{
nextInstanceId_ = 0;
presets_.clear();
selectedPreset_ = -1;
}
void move(size_t from, size_t to)
{
if (from >= this->presets_.size())
{
throw std::invalid_argument("Argument out of range.");
}
if (to >= this->presets_.size())
{
throw std::invalid_argument("Argument out of range.");
}
std::unique_ptr<BankFileEntry> t = std::move(this->presets_[from]);
presets_.erase(presets_.begin() + from);
presets_.insert(presets_.begin() + to, std::move(t));
}
void updateNextIndex()
{
int64_t t = 0;
for (size_t i = 0; i < this->presets_.size(); ++i)
{
int64_t instanceId = this->presets_[i]->instanceId();
if (instanceId > t)
t = instanceId;
}
this->nextInstanceId_ = t;
}
int64_t addPreset(const Pedalboard &preset, int64_t afterItem = -1)
{
if (hasName(preset.name()))
{
throw PiPedalStateException("A preset by that name already exists.");
}
std::unique_ptr<BankFileEntry> entry = std::make_unique<BankFileEntry>();
entry->instanceId(++nextInstanceId_);
entry->preset(preset);
int64_t instanceId = entry->instanceId();
if (afterItem == -1)
{
this->presets_.push_back(std::move(entry));
}
else
{
for (auto it = this->presets_.begin(); it != this->presets_.end(); ++it)
{
if ((*it)->instanceId() == afterItem)
{
++it;
this->presets_.insert(it, std::move(entry));
break;
}
}
}
return instanceId;
}
bool hasItem(int64_t instanceId)
{
for (size_t i = 0; i < presets_.size(); ++i)
{
if (presets_[i]->instanceId() == instanceId)
{
return true;
}
}
return false;
}
bool hasName(const std::string &name)
{
for (size_t i = 0; i < presets_.size(); ++i)
{
if (presets_[i]->preset().name() == name)
{
return true;
}
}
return false;
}
BankFileEntry* getPresetByName(const std::string&presetName) {
for (std::unique_ptr<BankFileEntry> &preset: presets_)
{
if (preset->preset().name() == presetName)
{
return &(*preset);
}
}
return nullptr;
}
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;
}
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 (i == presets_.size() && i != 0) {
newSelection = presets_[i-1]->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_);
if (afterId == -2)
{
entries_.insert(entries_.begin(), bank);
return bank.instanceId();
}
for (size_t i = 0; i < this->entries_.size(); ++i)
{
if (entries_[i].instanceId() == afterId)
{
entries_.insert(entries_.begin() + (i + 1), bank);
return bank.instanceId();
}
}
// else at the end.
entries_.push_back(bank);
return bank.instanceId();
}
BankIndexEntry *getEntryByName(const std::string &name)
{
for (size_t i = 0; i < entries_.size(); ++i)
{
if (entries_[i].name() == name)
{
return &entries_[i];
}
}
return nullptr;
}
BankIndexEntry &getBankIndexEntry(int64_t instanceId)
{
for (size_t i = 0; i < entries_.size(); ++i)
{
if (entries_[i].instanceId() == instanceId)
{
return entries_[i];
}
}
throw PiPedalArgumentException("Bank not found.");
}
const BankIndexEntry &getBankIndexEntry(int64_t instanceId) const
{
for (size_t i = 0; i < entries_.size(); ++i)
{
if (entries_[i].instanceId() == instanceId)
{
return entries_[i];
}
}
throw PiPedalArgumentException("Bank not found.");
}
};
#undef GETTER_SETTER
#undef GETTER_SETTER_VEC
#undef GETTER_SETTER_REF
}