feat: beat-sync LFOs and delay to host BPM

This commit is contained in:
Armin 2026-07-14 01:18:46 +02:00
commit 5cc704c1d1
5 changed files with 250 additions and 110 deletions

View file

@ -467,17 +467,33 @@ MainContentComponent::MainContentComponent(ChromaFlockProcessor& p)
setupParam(autoPanDepthKnob, autoPanDepthAttach, "autoPanDepth", "DEPTH");
// LFO
setupParam(lfo1RateKnob, lfo1RateAttach, "lfo1Rate", "RATE");
setupKnob(lfo1RateKnob, "RATE");
setupParam(lfo1DepthKnob, lfo1DepthAttach, "lfo1Depth", "DEPTH");
setupCB(lfo1ShapeBox, lfo1ShapeAttach, "lfo1Shape", {"Sine", "Triangle", "Saw", "Square"});
setupCB(lfo1DestBox, lfo1DestAttach, "lfo1Dest", {"Filter", "OSC1", "OSC2", "Both OSC"});
setupParam(lfo2RateKnob, lfo2RateAttach, "lfo2Rate", "RATE");
setupCB(lfo1SyncBox, lfo1SyncAttach, "lfo1Sync", {"Sync Off", "Sync On"});
lfo1SyncBox.onChange = [this]() {
setupSyncKnob(lfo1RateKnob, lfo1RateAttach, lfo1BeatAttach, "lfo1Rate", "lfo1Beat", lfo1SyncBox);
};
setupSyncKnob(lfo1RateKnob, lfo1RateAttach, lfo1BeatAttach, "lfo1Rate", "lfo1Beat", lfo1SyncBox);
setupKnob(lfo2RateKnob, "RATE");
setupParam(lfo2DepthKnob, lfo2DepthAttach, "lfo2Depth", "DEPTH");
setupCB(lfo2ShapeBox, lfo2ShapeAttach, "lfo2Shape", {"Sine", "Triangle", "Saw", "Square"});
setupCB(lfo2DestBox, lfo2DestAttach, "lfo2Dest", {"Filter", "OSC1", "OSC2", "Both OSC"});
setupCB(lfo2SyncBox, lfo2SyncAttach, "lfo2Sync", {"Sync Off", "Sync On"});
lfo2SyncBox.onChange = [this]() {
setupSyncKnob(lfo2RateKnob, lfo2RateAttach, lfo2BeatAttach, "lfo2Rate", "lfo2Beat", lfo2SyncBox);
};
setupSyncKnob(lfo2RateKnob, lfo2RateAttach, lfo2BeatAttach, "lfo2Rate", "lfo2Beat", lfo2SyncBox);
// FX2
setupParam(delayTimeKnob, delayTimeAttach, "delayTime", "TIME");
setupKnob(delayTimeKnob, "TIME");
setupCB(delaySyncBox, delaySyncAttach, "delaySync", {"Sync Off", "Sync On"});
delaySyncBox.onChange = [this]() {
setupSyncKnob(delayTimeKnob, delayTimeAttach, delayBeatAttach, "delayTime", "delayBeat", delaySyncBox);
};
setupSyncKnob(delayTimeKnob, delayTimeAttach, delayBeatAttach, "delayTime", "delayBeat", delaySyncBox);
setupParam(delayFbKnob, delayFbAttach, "delayFeedback", "FBACK");
setupParam(delayMixKnob, delayMixAttach, "delayMix", "MIX");
setupParam(reverbSizeKnob, reverbSizeAttach, "reverbSize", "SIZE");
@ -582,6 +598,21 @@ void MainContentComponent::setupCombo(juce::ComboBox& combo) {
addAndMakeVisible(combo);
}
void MainContentComponent::setupSyncKnob(juce::Slider& knob,
std::unique_ptr<SliderAttachment>& rateAttach,
std::unique_ptr<SliderAttachment>& beatAttach,
const juce::String& rateId,
const juce::String& beatId,
juce::ComboBox& syncBox) {
bool on = syncBox.getSelectedId() == 2;
rateAttach.reset();
beatAttach.reset();
if (on)
beatAttach = std::make_unique<SliderAttachment>(processorRef.apvts, beatId, knob);
else
rateAttach = std::make_unique<SliderAttachment>(processorRef.apvts, rateId, knob);
}
void MainContentComponent::showPresetMenu() {
juce::PopupMenu menu;
std::vector<juce::String> menuOrder;
@ -619,6 +650,12 @@ void MainContentComponent::applyCurrentPreset() {
currentPresetIndex = juce::jlimit(0, static_cast<int>(presets.size()) - 1, currentPresetIndex);
processorRef.presetManager.applyPreset(presets[currentPresetIndex].name, processorRef.apvts);
patchLCD.setText(presets[currentPresetIndex].name);
// The ComboBoxAttachment updates the sync boxes without firing onChange,
// so re-sync the rate/beat knob attachments to match.
setupSyncKnob(lfo1RateKnob, lfo1RateAttach, lfo1BeatAttach, "lfo1Rate", "lfo1Beat", lfo1SyncBox);
setupSyncKnob(lfo2RateKnob, lfo2RateAttach, lfo2BeatAttach, "lfo2Rate", "lfo2Beat", lfo2SyncBox);
setupSyncKnob(delayTimeKnob, delayTimeAttach, delayBeatAttach, "delayTime", "delayBeat", delaySyncBox);
}
void MainContentComponent::paint(juce::Graphics& g) {
@ -820,12 +857,14 @@ void MainContentComponent::resized() {
lfo1DepthKnob.setBounds(132, lfoKnobY, lfoKnobSize, lfoKnobSize);
lfo1ShapeBox.setBounds(242, lfoKnobY + 2, 130, 36);
lfo1DestBox.setBounds(382, lfoKnobY + 2, 155, 36);
lfo1SyncBox.setBounds(22, lfoKnobY + lfoKnobSize + 6, 200, 28);
// LFO 2 sub-section (X=560, Y=642, W=530, H=110)
lfo2RateKnob.setBounds(568, lfoKnobY, lfoKnobSize, lfoKnobSize);
lfo2DepthKnob.setBounds(678, lfoKnobY, lfoKnobSize, lfoKnobSize);
lfo2ShapeBox.setBounds(788, lfoKnobY + 2, 130, 36);
lfo2DestBox.setBounds(928, lfoKnobY + 2, 155, 36);
lfo2SyncBox.setBounds(568, lfoKnobY + lfoKnobSize + 6, 200, 28);
// --- FX2 SECTION ---
int fx2KnobY = lfoKnobY;
@ -835,6 +874,7 @@ void MainContentComponent::resized() {
delayTimeKnob.setBounds(1110, fx2KnobY, lfoKnobSize, lfoKnobSize);
delayFbKnob.setBounds(1198, fx2KnobY, lfoKnobSize, lfoKnobSize);
delayMixKnob.setBounds(1286, fx2KnobY, lfoKnobSize, lfoKnobSize);
delaySyncBox.setBounds(1110, fx2KnobY + lfoKnobSize + 6, 240, 28);
// Reverb sub-section (X=1375, Y=690, W=265, H=130)
reverbSizeKnob.setBounds(1383, fx2KnobY, lfoKnobSize, lfoKnobSize);