diff --git a/Source/PluginEditor.cpp b/Source/PluginEditor.cpp index 7aa4480..1725ab3 100644 --- a/Source/PluginEditor.cpp +++ b/Source/PluginEditor.cpp @@ -622,8 +622,20 @@ MainContentComponent::MainContentComponent(ChromaFlockProcessor& p) setupParam(phaseOffsetKnob, phaseOffsetAttach, "phaseOffset", "PHASE"); setupLabel(filterLabel, "FILTER"); - setupCB(filterTypeBox, filterTypeAttach, "filterType", {"LP 12dB", "LP 24dB", "Band Pass", "High Pass", "Notch", - "Band Pass 24", "High Pass 24", "Notch 24", "LP 48dB"}); + // Display order differs from the parameter's index order: LP 48dB is shown + // directly under LP 24dB. itemIds are (paramIndex + 1) so the underlying + // choice index (and therefore saved state / presets) stays meaningful. + setupCombo(filterTypeBox); + filterTypeBox.addItem("LP 12dB", 1); + filterTypeBox.addItem("LP 24dB", 2); + filterTypeBox.addItem("LP 48dB", 9); + filterTypeBox.addItem("Band Pass", 3); + filterTypeBox.addItem("High Pass", 4); + filterTypeBox.addItem("Notch", 5); + filterTypeBox.addItem("Band Pass 24", 6); + filterTypeBox.addItem("High Pass 24", 7); + filterTypeBox.addItem("Notch 24", 8); + filterTypeAttach = std::make_unique(processorRef.apvts, "filterType", filterTypeBox); setupParam(filterCutoffKnob, filterCutoffAttach, "filterCutoff", "CUTOFF"); setupParam(filterResKnob, filterResAttach, "filterRes", "RES"); setupParam(filterEnvAmtKnob, filterEnvAmtAttach, "filterEnvAmt", "ENV AMT"); diff --git a/Source/PluginEditor.h b/Source/PluginEditor.h index 33ca245..4620adc 100644 --- a/Source/PluginEditor.h +++ b/Source/PluginEditor.h @@ -214,9 +214,53 @@ private: using SliderAttachment = juce::AudioProcessorValueTreeState::SliderAttachment; using ComboBoxAttachment = juce::AudioProcessorValueTreeState::ComboBoxAttachment; + // Maps a 0-based parameter choice index to a ComboBox itemId (index + 1) + // so the dropdown can be displayed in an order that differs from the + // parameter's index order (e.g. LP 48dB listed under LP 24dB). This keeps + // stored parameter indices meaningful while only reordering the menu. + class IndexedComboBoxAttachment : public juce::AudioProcessorValueTreeState::Listener, + private juce::ComboBox::Listener { + public: + IndexedComboBoxAttachment (juce::AudioProcessorValueTreeState& stateToUse, + const juce::String& paramID, + juce::ComboBox& comboToUse) + : state (stateToUse), paramId (paramID), combo (comboToUse) { + combo.addListener (this); + state.addParameterListener (paramId, this); + auto* p = state.getParameter (paramId); + combo.setSelectedId (1 + juce::roundToInt (p->getValue() * (combo.getNumItems() - 1)), + juce::dontSendNotification); + } + + ~IndexedComboBoxAttachment() override { + combo.removeListener (this); + state.removeParameterListener (paramId, this); + } + + void parameterChanged (const juce::String& parameterID, float newValue) override { + if (parameterID == paramId) + combo.setSelectedId (1 + juce::roundToInt (newValue * (combo.getNumItems() - 1)), + juce::dontSendNotification); + } + + void comboBoxChanged (juce::ComboBox* cb) override { + if (cb != &combo) + return; + auto* p = state.getParameter (paramId); + int index = combo.getSelectedId() - 1; + p->setValueNotifyingHost (static_cast (index) / (combo.getNumItems() - 1)); + } + + private: + juce::AudioProcessorValueTreeState& state; + juce::String paramId; + juce::ComboBox& combo; + }; + std::unique_ptr osc1OctAttach, osc1SemiAttach, osc1FineAttach, osc1LevelAttach; std::unique_ptr osc2OctAttach, osc2SemiAttach, osc2FineAttach, osc2LevelAttach, phaseOffsetAttach; - std::unique_ptr osc1WaveAttach, osc2WaveAttach, filterTypeAttach; + std::unique_ptr osc1WaveAttach, osc2WaveAttach; + std::unique_ptr filterTypeAttach; std::unique_ptr octaveTransposeAttach, semitoneTransposeAttach; std::unique_ptr filterCutoffAttach, filterResAttach, filterEnvAmtAttach, keyTrackAttach; std::unique_ptr envAttackAttach, envDecayAttach, envSustainAttach, envReleaseAttach;