Add user preset save/load to the preset menu

This commit is contained in:
Armin 2026-08-05 00:21:50 +02:00
commit 143e0bac76
4 changed files with 212 additions and 28 deletions

View file

@ -13,6 +13,7 @@ TrommelkisteProcessor::TrommelkisteProcessor()
formatManager.registerBasicFormats();
scanSamples();
loadKit(0);
refreshUserPresets();
}
TrommelkisteProcessor::~TrommelkisteProcessor() {}
@ -605,19 +606,12 @@ static const TrommelkisteProcessor::Kit kits[] = {
const TrommelkisteProcessor::Kit* TrommelkisteProcessor::getKitList() { return kits; }
int TrommelkisteProcessor::getNumKits() const { return 4; }
juce::String TrommelkisteProcessor::getCurrentKitName() const
{
if (currentKitIndex >= 0 && currentKitIndex < getNumKits())
return kits[currentKitIndex].name;
return {};
}
void TrommelkisteProcessor::loadKit(int index)
{
if (index < 0 || index >= getNumKits())
return;
currentKitIndex = index;
const auto& kit = kits[index];
for (int t = 0; t < NUM_TRACKS; ++t)
@ -647,18 +641,161 @@ void TrommelkisteProcessor::loadKit(int index)
}
}
void TrommelkisteProcessor::nextKit()
juce::String TrommelkisteProcessor::getPresetName(int index) const
{
int idx = currentKitIndex + 1;
if (idx >= getNumKits()) idx = 0;
loadKit(idx);
if (index < 0 || index >= getNumPresets())
return {};
if (index < getNumKits())
return kits[index].name;
return userPresets[index - getNumKits()].name;
}
void TrommelkisteProcessor::prevKit()
void TrommelkisteProcessor::loadPreset(int index)
{
int idx = currentKitIndex - 1;
if (idx < 0) idx = getNumKits() - 1;
loadKit(idx);
if (index < 0 || index >= getNumPresets())
return;
currentPresetIndex = index;
if (index < getNumKits())
loadKit(index);
else
loadUserPreset(index - getNumKits());
}
void TrommelkisteProcessor::nextPreset()
{
const int n = getNumPresets();
if (n <= 0) return;
loadPreset((currentPresetIndex + 1) % n);
}
void TrommelkisteProcessor::prevPreset()
{
const int n = getNumPresets();
if (n <= 0) return;
loadPreset((currentPresetIndex + n - 1) % n);
}
// ── User presets ───────────────────────────────────────────────
static juce::File getUserPresetDirectory()
{
return juce::File::getSpecialLocation(juce::File::userApplicationDataDirectory)
.getChildFile("Trommelkiste").getChildFile("Presets");
}
void TrommelkisteProcessor::refreshUserPresets()
{
userPresets.clear();
auto dir = getUserPresetDirectory();
if (! dir.isDirectory())
return;
juce::Array<juce::File> files =
dir.findChildFiles(juce::File::findFiles, false, "*.trommelpreset");
files.sort();
for (const auto& f : files)
{
auto xml = juce::XmlDocument::parse(f);
if (xml == nullptr)
continue;
UserPreset p;
p.name = xml->getStringAttribute("name");
if (p.name.isEmpty())
p.name = f.getFileNameWithoutExtension();
p.file = f;
userPresets.add(p);
}
}
void TrommelkisteProcessor::saveUserPreset(const juce::String& name)
{
auto dir = getUserPresetDirectory();
dir.createDirectory();
juce::String safeName = name;
safeName = safeName.replaceCharacter('\\', '_').replaceCharacter('/', '_');
auto file = dir.getChildFile(safeName + ".trommelpreset");
auto state = apvts.copyState();
state.setProperty("name", name, nullptr);
juce::ValueTree samples("Samples");
for (int i = 0; i < NUM_TRACKS; ++i)
{
juce::ValueTree tr("Track" + juce::String(i));
tr.setProperty("path", tracks[i].filePath, nullptr);
tr.setProperty("sampleIndex", currentSampleIndex[i], nullptr);
if (currentSampleIndex[i] >= 0 && currentSampleIndex[i] < trackSamples[i].size())
tr.setProperty("sampleName", trackSamples[i][currentSampleIndex[i]].name, nullptr);
samples.appendChild(tr, nullptr);
}
state.appendChild(samples, nullptr);
std::unique_ptr<juce::XmlElement> xml(state.createXml());
if (! xml->writeToFile(file, {}))
return;
refreshUserPresets();
for (int i = 0; i < userPresets.size(); ++i)
{
if (userPresets[i].file == file)
{
currentPresetIndex = getNumKits() + i;
break;
}
}
}
void TrommelkisteProcessor::loadUserPreset(int index)
{
if (index < 0 || index >= userPresets.size())
return;
std::unique_ptr<juce::XmlElement> xml = juce::XmlDocument::parse(userPresets[index].file);
if (xml == nullptr)
return;
auto state = juce::ValueTree::fromXml(*xml);
apvts.replaceState(state);
auto samples = state.getChildWithName("Samples");
if (samples.isValid())
{
for (int i = 0; i < NUM_TRACKS; ++i)
{
auto tr = samples.getChildWithName("Track" + juce::String(i));
if (! tr.isValid())
continue;
auto path = tr.getProperty("path", "").toString();
auto sampleName = tr.getProperty("sampleName", "").toString();
if (path.isNotEmpty())
{
loadSample(i, juce::File(path));
currentSampleIndex[i] = (int)tr.getProperty("sampleIndex", -1);
}
else if (sampleName.isNotEmpty())
{
int found = -1;
for (int s = 0; s < trackSamples[i].size(); ++s)
{
if (trackSamples[i][s].name.equalsIgnoreCase(sampleName))
{
found = s;
break;
}
}
if (found >= 0)
loadSampleByIndex(i, found);
}
}
}
}
juce::AudioProcessorEditor* TrommelkisteProcessor::createEditor()
@ -670,7 +807,7 @@ void TrommelkisteProcessor::getStateInformation(juce::MemoryBlock& destData)
{
auto state = apvts.copyState();
state.setProperty("kitIndex", currentKitIndex, nullptr);
state.setProperty("presetIndex", currentPresetIndex, nullptr);
juce::ValueTree samples("Samples");
for (int i = 0; i < NUM_TRACKS; ++i)
@ -697,7 +834,8 @@ void TrommelkisteProcessor::setStateInformation(const void* data, int sizeInByte
auto state = juce::ValueTree::fromXml(*xml);
apvts.replaceState(state);
currentKitIndex = (int)state.getProperty("kitIndex", 0);
currentPresetIndex = (int)state.getProperty("presetIndex",
state.getProperty("kitIndex", 0));
auto samples = state.getChildWithName("Samples");
if (samples.isValid())