feat: add presets, Randomize button, menu highlight, finer LFO depth, preset-switch fade-out

- PresetManager: add ~40 new presets including a new Techno category with
  LFO-to-OSC-pitch movement, plus Bass/Lead/Pad/Keys/Brass/Strings/Wind/
  Arp/Pluck/FX expansions
- UI: add RANDOM button; highlight the currently selected entry in all
  dropdown menus (incl. ticking the active preset in the preset browser)
- LFO depth: finer 0.001 step with a skew curve so the lower range resolves
  much finer than the upper range (shared by LFO1 and LFO2)
- DSP: fade out active voices over 250ms on preset change / randomize so long
  release tails from the previous sound are silenced
- Update bg.png
This commit is contained in:
Armin 2026-07-14 13:54:17 +02:00
commit 6f63b403e3
7 changed files with 881 additions and 7 deletions

View file

@ -44,6 +44,11 @@ juce::AudioProcessorValueTreeState::ParameterLayout ChromaFlockProcessor::create
auto zeroOne = juce::NormalisableRange<float>(0.0f, 1.0f, 0.01f);
// LFO1 depth: fine 0.001 step, with a skew of 2.0 (value = p^2) so the
// lower knob range resolves much finer and the 0.001 step only applies
// in the upper range.
juce::NormalisableRange<float> lfoDepthRange(0.0f, 1.0f, 0.001f, 2.0f);
// OSC 1
layout.add(std::make_unique<juce::AudioParameterChoice>(
juce::ParameterID{"osc1Wave", 1}, "OSC1 Wave",
@ -181,7 +186,7 @@ juce::AudioProcessorValueTreeState::ParameterLayout ChromaFlockProcessor::create
juce::ParameterID{"lfo1Rate", 1}, "LFO1 Rate",
juce::NormalisableRange<float>(0.05f, 20.0f, 0.01f, 0.4f), 2.0f));
layout.add(std::make_unique<juce::AudioParameterFloat>(
juce::ParameterID{"lfo1Depth", 1}, "LFO1 Depth", zeroOne, 0.0f));
juce::ParameterID{"lfo1Depth", 1}, "LFO1 Depth", lfoDepthRange, 0.0f));
layout.add(std::make_unique<juce::AudioParameterChoice>(
juce::ParameterID{"lfo1Shape", 1}, "LFO1 Shape",
juce::StringArray{"Sine", "Triangle", "Saw", "Square"}, 0));
@ -202,7 +207,7 @@ juce::AudioProcessorValueTreeState::ParameterLayout ChromaFlockProcessor::create
juce::ParameterID{"lfo2Rate", 1}, "LFO2 Rate",
juce::NormalisableRange<float>(0.05f, 20.0f, 0.01f, 0.4f), 2.0f));
layout.add(std::make_unique<juce::AudioParameterFloat>(
juce::ParameterID{"lfo2Depth", 1}, "LFO2 Depth", zeroOne, 0.0f));
juce::ParameterID{"lfo2Depth", 1}, "LFO2 Depth", lfoDepthRange, 0.0f));
layout.add(std::make_unique<juce::AudioParameterChoice>(
juce::ParameterID{"lfo2Shape", 1}, "LFO2 Shape",
juce::StringArray{"Sine", "Triangle", "Saw", "Square"}, 0));
@ -259,6 +264,15 @@ void ChromaFlockProcessor::prepareToPlay(double sampleRate, int samplesPerBlock)
void ChromaFlockProcessor::releaseResources() {}
void ChromaFlockProcessor::fadeOutActiveVoices(float seconds) {
for (int i = 0; i < synth.getNumVoices(); ++i) {
if (auto* v = dynamic_cast<SubtractiveVoice*>(synth.getVoice(i))) {
if (v->isVoiceActive())
v->triggerQuickRelease(seconds);
}
}
}
void ChromaFlockProcessor::updateVoiceParameters() {
auto getParam = [&](const juce::String& id) -> float {
auto* p = apvts.getRawParameterValue(id);