First commit.
This commit is contained in:
+685
@@ -0,0 +1,685 @@
|
||||
#include "pch.h"
|
||||
#include "Storage.hpp"
|
||||
#include "PiPedalException.hpp"
|
||||
#include <sstream>
|
||||
#include "json.hpp"
|
||||
#include <fstream>
|
||||
#include "Lv2Log.hpp"
|
||||
|
||||
using namespace pipedal;
|
||||
|
||||
const char *BANK_EXTENSION = ".bank";
|
||||
const char *BANKS_FILENAME = "index.banks";
|
||||
|
||||
Storage::Storage()
|
||||
{
|
||||
SetDataRoot("~/var/PiPedal");
|
||||
}
|
||||
|
||||
inline bool isSafeCharacter(char c)
|
||||
{
|
||||
return (c >= '0' && c <= '9') | (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '-';
|
||||
}
|
||||
|
||||
static int fromhexStrict(char c)
|
||||
{
|
||||
if (c >= '0' && c <= '9')
|
||||
return c - '0';
|
||||
if (c >= 'A' && c <= 'F')
|
||||
return c - 'A' + 10;
|
||||
// lowercase letters won't round-trip properly.
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::string Storage::SafeDecodeName(const std::string &name)
|
||||
{
|
||||
std::stringstream s;
|
||||
for (int i = 0; i < name.length(); /**/)
|
||||
{
|
||||
char c = name[i++];
|
||||
if (c < ' ')
|
||||
throw PiPedalArgumentException("Unsafe file name.");
|
||||
|
||||
if (c == '+')
|
||||
{
|
||||
s << ' ';
|
||||
}
|
||||
else if (c == '%')
|
||||
{
|
||||
if (i + 2 > name.length())
|
||||
{
|
||||
throw PiPedalArgumentException("Unsafe file name.");
|
||||
}
|
||||
int v1 = fromhexStrict(name[i]);
|
||||
int v2 = fromhexStrict(name[i + 1]);
|
||||
i += 2;
|
||||
if (v1 == -1 || v2 == -1)
|
||||
throw PiPedalArgumentException("Unsafe file name.");
|
||||
char c = (char)((v1 << 4) + v2);
|
||||
if (isSafeCharacter(c))
|
||||
{
|
||||
// name won't round-trip because the escape will not be re-encoded.
|
||||
throw PiPedalArgumentException("Unsafe file name.");
|
||||
}
|
||||
s << c;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isSafeCharacter(c))
|
||||
{
|
||||
s << c;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw PiPedalArgumentException("Unsafe file name.");
|
||||
}
|
||||
}
|
||||
}
|
||||
return s.str();
|
||||
}
|
||||
|
||||
static const char *hex = "0123456789ABCDEF";
|
||||
|
||||
std::string Storage::SafeEncodeName(const std::string &name)
|
||||
{
|
||||
std::stringstream s;
|
||||
for (char c : name)
|
||||
{
|
||||
if (c < ' ')
|
||||
c = ' ';
|
||||
bool safe = isSafeCharacter(c);
|
||||
if (safe)
|
||||
{
|
||||
s << c;
|
||||
}
|
||||
else if (c == ' ')
|
||||
{
|
||||
s << '+';
|
||||
}
|
||||
else
|
||||
{
|
||||
s << '%' << hex[(c >> 4) & 0x0F] << hex[(c)&0x0F];
|
||||
}
|
||||
}
|
||||
return s.str();
|
||||
}
|
||||
|
||||
std::filesystem::path ResolveHomePath(std::filesystem::path path)
|
||||
{
|
||||
if (path.begin() == path.end())
|
||||
return path;
|
||||
// is the first element "~"?
|
||||
auto it = path.begin();
|
||||
auto el = (*it);
|
||||
if (el != "~")
|
||||
return path;
|
||||
const char *homeDirectory = nullptr;
|
||||
|
||||
homeDirectory = getenv("HOME");
|
||||
if (homeDirectory == nullptr)
|
||||
{
|
||||
homeDirectory = getenv("USERPROFILE");
|
||||
}
|
||||
std::filesystem::path result = homeDirectory;
|
||||
|
||||
bool first = true;
|
||||
for (auto i = path.begin(); i != path.end(); ++i)
|
||||
{
|
||||
if (!first)
|
||||
{
|
||||
result /= *i;
|
||||
}
|
||||
first = false;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
void Storage::SetDataRoot(const char *path)
|
||||
{
|
||||
this->dataRoot = ResolveHomePath(path);
|
||||
}
|
||||
|
||||
void Storage::Initialize()
|
||||
{
|
||||
try
|
||||
{
|
||||
std::filesystem::create_directories(this->GetPresetsDirectory());
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
throw PiPedalStateException("Can't create presets directory. (" + (std::string)this->GetPresetsDirectory() + ") (" + e.what() + ")");
|
||||
}
|
||||
LoadBankIndex();
|
||||
LoadCurrentBank();
|
||||
try
|
||||
{
|
||||
LoadChannelSelection();
|
||||
}
|
||||
catch (const std::exception &)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
void Storage::LoadBank(int64_t instanceId)
|
||||
{
|
||||
auto indexEntry = this->bankIndex.getBankIndexEntry(instanceId);
|
||||
|
||||
LoadBankFile(indexEntry.name(), &(this->currentBank));
|
||||
if (this->bankIndex.selectedBank() != instanceId)
|
||||
{
|
||||
this->bankIndex.selectedBank(instanceId);
|
||||
SaveBankIndex();
|
||||
}
|
||||
this->LoadPreset(this->bankIndex.selectedBank());
|
||||
}
|
||||
|
||||
void Storage::LoadCurrentBank()
|
||||
{
|
||||
LoadBank(bankIndex.selectedBank());
|
||||
}
|
||||
|
||||
std::filesystem::path Storage::GetPresetsDirectory() const
|
||||
{
|
||||
return this->dataRoot / "presets";
|
||||
}
|
||||
|
||||
std::filesystem::path Storage::GetChannelSelectionFileName()
|
||||
{
|
||||
return this->dataRoot / "JackChannelSelection.json";
|
||||
}
|
||||
std::filesystem::path Storage::GetIndexFileName()
|
||||
{
|
||||
return this->GetPresetsDirectory() / BANKS_FILENAME;
|
||||
}
|
||||
std::filesystem::path Storage::GetBankFileName(const std::string &name)
|
||||
{
|
||||
std::string fileName = SafeEncodeName(name) + BANK_EXTENSION;
|
||||
return this->GetPresetsDirectory() / fileName;
|
||||
}
|
||||
|
||||
void Storage::LoadBankIndex()
|
||||
{
|
||||
try
|
||||
{
|
||||
auto path = GetIndexFileName();
|
||||
std::ifstream s;
|
||||
s.open(path);
|
||||
json_reader reader(s);
|
||||
reader.read(&bankIndex);
|
||||
}
|
||||
catch (const std::exception &)
|
||||
{
|
||||
bankIndex.clear();
|
||||
ReIndex();
|
||||
|
||||
if (bankIndex.entries().size() == 0)
|
||||
{
|
||||
currentBank.clear();
|
||||
PedalBoard defaultPedalBoard = PedalBoard::MakeDefault();
|
||||
int64_t instanceId = currentBank.addPreset(defaultPedalBoard);
|
||||
currentBank.selectedPreset(instanceId);
|
||||
|
||||
std::string name = "Default Bank";
|
||||
SaveBankFile(name, currentBank);
|
||||
int64_t selectedBank = bankIndex.addBank(-1,name);
|
||||
bankIndex.selectedBank(selectedBank);
|
||||
}
|
||||
SaveBankIndex();
|
||||
}
|
||||
}
|
||||
void Storage::SaveBankIndex()
|
||||
{
|
||||
std::ofstream os;
|
||||
os.open(GetIndexFileName(), std::ios_base::trunc);
|
||||
json_writer writer(os);
|
||||
writer.write(this->bankIndex);
|
||||
}
|
||||
|
||||
void Storage::ReIndex()
|
||||
{
|
||||
for (const auto &dirEntry : std::filesystem::directory_iterator(GetPresetsDirectory()))
|
||||
{
|
||||
if (!dirEntry.is_directory())
|
||||
{
|
||||
auto path = dirEntry.path();
|
||||
if (path.extension() == BANK_EXTENSION)
|
||||
{
|
||||
std::string name = SafeDecodeName(path.stem());
|
||||
bankIndex.addBank(-1,name);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bankIndex.entries().size() == 0)
|
||||
{
|
||||
CreateBank("Default Bank");
|
||||
}
|
||||
bankIndex.selectedBank(bankIndex.entries()[0].instanceId());
|
||||
}
|
||||
|
||||
void Storage::CreateBank(const std::string &name)
|
||||
{
|
||||
BankFile bankFile;
|
||||
PedalBoard defaultPreset = PedalBoard::MakeDefault();
|
||||
defaultPreset.name(std::string("Default Preset"));
|
||||
|
||||
bankFile.addPreset(defaultPreset);
|
||||
|
||||
int64_t instanceId = bankFile.presets()[0]->instanceId();
|
||||
bankFile.selectedPreset(instanceId);
|
||||
SaveBankFile(name, bankFile);
|
||||
this->bankIndex.addBank(-1,name);
|
||||
this->SaveBankIndex();
|
||||
}
|
||||
|
||||
void Storage::LoadBankFile(const std::string &name, BankFile *pBank)
|
||||
{
|
||||
std::filesystem::path fileName = GetBankFileName(name);
|
||||
std::ifstream is(fileName);
|
||||
json_reader reader(is);
|
||||
reader.read(pBank);
|
||||
|
||||
}
|
||||
|
||||
void Storage::SaveBankFile(const std::string &name, const BankFile &bankFile)
|
||||
{
|
||||
std::filesystem::path fileName = GetBankFileName(name);
|
||||
std::filesystem::path backupFile = ((std::string)fileName) + ".$$$";
|
||||
if (std::filesystem::exists(backupFile))
|
||||
{
|
||||
std::filesystem::remove(backupFile);
|
||||
}
|
||||
if (std::filesystem::exists(fileName))
|
||||
{
|
||||
std::filesystem::rename(fileName, backupFile);
|
||||
}
|
||||
try
|
||||
{
|
||||
std::ofstream s;
|
||||
s.open(fileName, std::ios_base::trunc);
|
||||
json_writer writer(s);
|
||||
writer.write(bankFile);
|
||||
if (std::filesystem::exists(backupFile))
|
||||
{
|
||||
std::filesystem::remove(backupFile);
|
||||
}
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
std::filesystem::remove(fileName);
|
||||
if (std::filesystem::exists(backupFile))
|
||||
{
|
||||
std::filesystem::rename(backupFile, fileName);
|
||||
}
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
void Storage::SaveCurrentBank()
|
||||
{
|
||||
auto indexEntry = this->bankIndex.getBankIndexEntry(this->bankIndex.selectedBank());
|
||||
SaveBankFile(indexEntry.name(), this->currentBank);
|
||||
}
|
||||
|
||||
const PedalBoard &Storage::GetCurrentPreset()
|
||||
{
|
||||
auto &item = currentBank.getItem(currentBank.selectedPreset());
|
||||
return item.preset();
|
||||
}
|
||||
|
||||
bool Storage::LoadPreset(int64_t instanceId)
|
||||
{
|
||||
if (!currentBank.hasItem(instanceId))
|
||||
return false;
|
||||
currentBank.selectedPreset(instanceId);
|
||||
SaveCurrentBank();
|
||||
return true;
|
||||
}
|
||||
void Storage::saveCurrentPreset(const PedalBoard &pedalBoard)
|
||||
{
|
||||
auto &item = currentBank.getItem(currentBank.selectedPreset());
|
||||
item.preset(pedalBoard);
|
||||
SaveCurrentBank();
|
||||
}
|
||||
int64_t Storage::saveCurrentPresetAs(const PedalBoard &pedalBoard, const std::string &name, int64_t saveAfterInstanceId)
|
||||
{
|
||||
PedalBoard newPedalBoard = pedalBoard;
|
||||
newPedalBoard.name(name);
|
||||
|
||||
int64_t newInstanceId = currentBank.addPreset(newPedalBoard, saveAfterInstanceId);
|
||||
currentBank.selectedPreset(newInstanceId);
|
||||
SaveCurrentBank();
|
||||
return newInstanceId;
|
||||
}
|
||||
|
||||
void Storage::SetPresetIndex(const PresetIndex &presets)
|
||||
{
|
||||
// painful because we must move unique_ptrs.
|
||||
std::map<int64_t, std::unique_ptr<BankFileEntry> *> entries;
|
||||
for (int i = 0; i < this->currentBank.presets().size(); ++i)
|
||||
{
|
||||
auto & preset = this->currentBank.presets()[i];
|
||||
entries[preset->instanceId()] = &(this->currentBank.presets()[i]);
|
||||
}
|
||||
std::vector<std::unique_ptr<BankFileEntry>*> newPresets;
|
||||
for (size_t i = 0; i < presets.presets().size(); ++i)
|
||||
{
|
||||
std::unique_ptr<BankFileEntry>* p = entries[presets.presets()[i].instanceId()];
|
||||
if (p == nullptr)
|
||||
{
|
||||
throw PiPedalStateException("Presets do not match the currently loaded bank.");
|
||||
}
|
||||
newPresets.push_back(p);
|
||||
}
|
||||
// we made it this far. So now we know we can rebuild a new BankFile.
|
||||
BankFile bankFile;
|
||||
bankFile.presets().resize(newPresets.size());
|
||||
for (int i = 0; i < presets.presets().size(); ++i)
|
||||
{
|
||||
std::unique_ptr<BankFileEntry> *oldBankFilePreset = newPresets[i];
|
||||
|
||||
bankFile.presets()[i] = std::move((*oldBankFilePreset));
|
||||
}
|
||||
bankFile.nextInstanceId(currentBank.nextInstanceId());
|
||||
bankFile.selectedPreset(currentBank.selectedPreset());
|
||||
|
||||
this->currentBank = std::move(bankFile); // deleting any stray presets while we're at it.
|
||||
this->SaveCurrentBank();
|
||||
}
|
||||
void Storage::GetPresetIndex(PresetIndex *pResult)
|
||||
{
|
||||
*pResult = PresetIndex();
|
||||
pResult->selectedInstanceId(this->currentBank.selectedPreset());
|
||||
for (auto &item : this->currentBank.presets())
|
||||
{
|
||||
PresetIndexEntry entry;
|
||||
entry.instanceId(item->instanceId());
|
||||
entry.name(item->preset().name());
|
||||
pResult->presets().push_back(entry);
|
||||
}
|
||||
}
|
||||
PedalBoard Storage::GetPreset(int64_t instanceId) const
|
||||
{
|
||||
for (size_t i = 0; i < currentBank.presets().size(); ++i)
|
||||
{
|
||||
if (currentBank.presets()[i]->instanceId() == instanceId)
|
||||
{
|
||||
return currentBank.presets()[i]->preset();
|
||||
}
|
||||
}
|
||||
throw PiPedalException("Not found.");
|
||||
}
|
||||
|
||||
int64_t Storage::DeletePreset(int64_t presetId)
|
||||
{
|
||||
int64_t newSelection = currentBank.deletePreset(presetId);
|
||||
SaveCurrentBank();
|
||||
return newSelection;
|
||||
}
|
||||
|
||||
bool Storage::RenamePreset(int64_t presetId, const std::string &name)
|
||||
{
|
||||
if (this->currentBank.renamePreset(presetId, name))
|
||||
{
|
||||
SaveCurrentBank();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static int lastIndexOf(const char *sz, char c)
|
||||
{
|
||||
size_t len = strlen(sz);
|
||||
for (size_t i = len - 1; i >= 0; --i)
|
||||
{
|
||||
if (sz[i] == c)
|
||||
{
|
||||
if (i > 0 && sz[i - 1] == ' ')
|
||||
--i;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
std::string Storage::GetPresetCopyName(const std::string &name)
|
||||
{
|
||||
const char *str = name.c_str();
|
||||
std::string stem;
|
||||
if (name.length() != 0 && name[name.length() - 1] == ')')
|
||||
{
|
||||
const char *str = name.c_str();
|
||||
size_t pos = lastIndexOf(str, '(');
|
||||
if (pos == -1)
|
||||
{
|
||||
stem = name;
|
||||
}
|
||||
else
|
||||
{
|
||||
stem = name.substr(0, pos);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
stem = name;
|
||||
}
|
||||
int copyNumber = 1;
|
||||
while (true)
|
||||
{
|
||||
std::string candidate;
|
||||
if (copyNumber == 1)
|
||||
{
|
||||
candidate = stem + " (Copy)";
|
||||
}
|
||||
else
|
||||
{
|
||||
std::stringstream s;
|
||||
s << stem << " (Copy " << copyNumber << ")";
|
||||
candidate = s.str();
|
||||
}
|
||||
if (!this->currentBank.hasName(candidate))
|
||||
{
|
||||
return candidate;
|
||||
}
|
||||
++copyNumber;
|
||||
}
|
||||
}
|
||||
int64_t Storage::CopyPreset(int64_t fromId, int64_t toId)
|
||||
{
|
||||
auto &fromItem = this->currentBank.getItem(fromId);
|
||||
if (toId == -1)
|
||||
{
|
||||
PedalBoard newPedalBoard = fromItem.preset();
|
||||
std::string name = GetPresetCopyName(fromItem.preset().name());
|
||||
newPedalBoard.name(name);
|
||||
return this->currentBank.addPreset(newPedalBoard, fromId);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto &toItem = this->currentBank.getItem(toId);
|
||||
toItem.preset(fromItem.preset());
|
||||
return toId;
|
||||
}
|
||||
}
|
||||
|
||||
void Storage::SetJackChannelSelection(const JackChannelSelection &channelSelection)
|
||||
{
|
||||
this->jackChannelSelection = channelSelection;
|
||||
this->isJackChannelSelectionValid = true;
|
||||
SaveChannelSelection();
|
||||
}
|
||||
const JackChannelSelection &Storage::GetJackChannelSelection(const JackConfiguration &jackConfiguration)
|
||||
{
|
||||
if (!this->isJackChannelSelectionValid)
|
||||
{
|
||||
if (jackConfiguration.isValid())
|
||||
{
|
||||
auto defaultSelection = JackChannelSelection::MakeDefault(jackConfiguration);
|
||||
SetJackChannelSelection(defaultSelection);
|
||||
return this->jackChannelSelection;
|
||||
}
|
||||
}
|
||||
return jackChannelSelection;
|
||||
}
|
||||
|
||||
void Storage::LoadChannelSelection()
|
||||
{
|
||||
auto fileName = this->GetChannelSelectionFileName();
|
||||
if (std::filesystem::exists(fileName))
|
||||
{
|
||||
try
|
||||
{
|
||||
std::ifstream s(fileName);
|
||||
json_reader reader(s);
|
||||
reader.read(&this->jackChannelSelection);
|
||||
this->isJackChannelSelectionValid = true;
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
Lv2Log::error("I/O error reading %s: %s", fileName.c_str(), e.what());
|
||||
throw PiPedalStateException("Unexpected error reading Jack settings file.");
|
||||
}
|
||||
}
|
||||
}
|
||||
void Storage::SaveChannelSelection()
|
||||
{
|
||||
auto fileName = this->GetChannelSelectionFileName();
|
||||
try
|
||||
{
|
||||
std::ofstream s(fileName);
|
||||
json_writer writer(s);
|
||||
writer.write(this->jackChannelSelection);
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
Lv2Log::error("I/O error writing %s: %s", fileName.c_str(), e.what());
|
||||
throw PiPedalStateException("Unexpected error writing Jack settings file.");
|
||||
}
|
||||
}
|
||||
|
||||
void Storage::RenameBank(int64_t bankId, const std::string &newName)
|
||||
{
|
||||
auto existingBank = this->bankIndex.getEntryByName(newName);
|
||||
if (existingBank != nullptr)
|
||||
{
|
||||
throw PiPedalStateException("A bank by that name already exists.");
|
||||
}
|
||||
auto &entry = this->bankIndex.getBankIndexEntry(bankId);
|
||||
std::filesystem::path oldPath = this->GetBankFileName(entry.name());
|
||||
std::filesystem::path newPath = this->GetBankFileName(newName);
|
||||
try
|
||||
{
|
||||
std::filesystem::rename(oldPath, newPath);
|
||||
}
|
||||
catch (std::exception &e)
|
||||
{
|
||||
std::stringstream s;
|
||||
s << "Unable to rename the bank. (" << e.what() << ")";
|
||||
throw PiPedalException(s.str());
|
||||
}
|
||||
entry.name(newName);
|
||||
SaveBankIndex();
|
||||
}
|
||||
|
||||
int64_t Storage::SaveBankAs(int64_t bankId, const std::string &newName)
|
||||
{
|
||||
auto existingBank = this->bankIndex.getEntryByName(newName);
|
||||
if (existingBank != nullptr)
|
||||
{
|
||||
throw PiPedalStateException("A bank by that name already exists.");
|
||||
}
|
||||
auto &entry = this->bankIndex.getBankIndexEntry(bankId);
|
||||
std::filesystem::path oldPath = this->GetBankFileName(entry.name());
|
||||
std::filesystem::path newPath = this->GetBankFileName(newName);
|
||||
try
|
||||
{
|
||||
std::filesystem::copy(oldPath,newPath);
|
||||
}
|
||||
catch (std::exception &e)
|
||||
{
|
||||
std::stringstream s;
|
||||
s << "Unable to save the bank. (" << e.what() << ")";
|
||||
throw PiPedalException(s.str());
|
||||
}
|
||||
int64_t newId = this->bankIndex.addBank(bankId,newName);
|
||||
SaveBankIndex();
|
||||
return newId;
|
||||
}
|
||||
|
||||
void Storage::MoveBank(int from, int to)
|
||||
{
|
||||
this->bankIndex.move(from,to);
|
||||
this->SaveBankIndex();
|
||||
}
|
||||
int64_t Storage::DeleteBank(int64_t bankId)
|
||||
{
|
||||
auto & entries = this->bankIndex.entries();
|
||||
|
||||
for (size_t i = 0; i < entries.size(); ++i)
|
||||
{
|
||||
auto & entry = entries[i];
|
||||
|
||||
if (entry.instanceId() == bankId) {
|
||||
std::filesystem::path fileName = this->GetBankFileName(entry.name());
|
||||
entries.erase(entries.begin()+i);
|
||||
|
||||
int64_t newSelection;
|
||||
if (i < entries.size())
|
||||
{
|
||||
newSelection = entries[i].instanceId();
|
||||
} else if (entries.size() > 0)
|
||||
{
|
||||
newSelection = entries[entries.size()-1].instanceId();
|
||||
} else {
|
||||
// zero entries?
|
||||
// Create a default empty bank.
|
||||
BankIndexEntry newEntry;
|
||||
|
||||
BankFile defaultBank;
|
||||
PedalBoard defaultPedalBoard = PedalBoard::MakeDefault();
|
||||
int64_t instanceId = defaultBank.addPreset(defaultPedalBoard);
|
||||
defaultBank.selectedPreset(instanceId);
|
||||
|
||||
std::string name = "Default Bank";
|
||||
SaveBankFile(name, defaultBank);
|
||||
int64_t selectedBank = bankIndex.addBank(-1,name);
|
||||
newSelection = selectedBank;
|
||||
}
|
||||
if (this->bankIndex.selectedBank() == bankId)
|
||||
{
|
||||
this->bankIndex.selectedBank(newSelection);
|
||||
}
|
||||
this->SaveBankIndex();
|
||||
std::filesystem::remove(fileName);
|
||||
return newSelection;
|
||||
}
|
||||
}
|
||||
throw PiPedalStateException("Bank not found.");
|
||||
}
|
||||
|
||||
int64_t Storage::UploadPreset(const BankFile&bankFile,int64_t uploadAfter)
|
||||
{
|
||||
int64_t lastPreset = this->currentBank.selectedPreset();
|
||||
if (uploadAfter != -1)
|
||||
{
|
||||
lastPreset = uploadAfter;
|
||||
}
|
||||
|
||||
if (bankFile.presets().size() == 0) {
|
||||
throw PiPedalException("Invalid preset.");
|
||||
}
|
||||
for (size_t i = 0; i < bankFile.presets().size(); ++i)
|
||||
{
|
||||
PedalBoard preset = bankFile.presets()[i]->preset();
|
||||
|
||||
int n = 2;
|
||||
std::string baseName = preset.name();
|
||||
while (this->currentBank.hasName(preset.name()))
|
||||
{
|
||||
std::stringstream s;
|
||||
s << baseName << "(" << n++ << ")";
|
||||
preset.name(s.str());
|
||||
}
|
||||
|
||||
lastPreset = this->currentBank.addPreset(preset,lastPreset);
|
||||
}
|
||||
this->SaveCurrentBank();
|
||||
return lastPreset;
|
||||
}
|
||||
Reference in New Issue
Block a user