diff --git a/OB-Xd.jucer b/OB-Xd.jucer index d1c57d3..2df16f6 100644 --- a/OB-Xd.jucer +++ b/OB-Xd.jucer @@ -1,6 +1,6 @@ - shiftDragCallback; private: Image kni; int fh, numFr; int w2, h2; + bool shouldResetOnShiftClick{ false }; + String resetActionMessage{}; AudioProcessorParameter* parameter {nullptr}; + KnobLookAndFeel lookAndFeel; }; diff --git a/Source/PluginEditor.cpp b/Source/PluginEditor.cpp index deb5cb1..95c5981 100755 --- a/Source/PluginEditor.cpp +++ b/Source/PluginEditor.cpp @@ -210,6 +210,14 @@ void ObxdAudioProcessorEditor::loadSkin (ObxdAudioProcessor& ownerFilter) } if (name == "osc1PitchKnob"){ osc1PitchKnob = addKnob (x, y, d, ownerFilter, OSC1P, "Osc1Pitch", 0); + osc1PitchKnob->shiftDragCallback = [](double value) + { + if (value < 0.125) return 0.0; + if (value < 0.375) return 0.25; + if (value < 0.625) return 0.5; + if (value < 0.875) return 0.75; + return 1.0; + }; mappingComps["osc1PitchKnob"] = osc1PitchKnob; } if (name == "pulseWidthKnob"){ @@ -218,6 +226,14 @@ void ObxdAudioProcessorEditor::loadSkin (ObxdAudioProcessor& ownerFilter) } if (name == "osc2PitchKnob"){ osc2PitchKnob = addKnob (x, y, d, ownerFilter, OSC2P, "Osc2Pitch", 0); + osc2PitchKnob->shiftDragCallback = [](double value) + { + if (value < 0.125) return 0.0; + if (value < 0.375) return 0.25; + if (value < 0.625) return 0.5; + if (value < 0.875) return 0.75; + return 1.0; + }; mappingComps["osc2PitchKnob"] = osc2PitchKnob; } @@ -420,35 +436,51 @@ void ObxdAudioProcessorEditor::loadSkin (ObxdAudioProcessor& ownerFilter) if (name == "pan1Knob"){ pan1Knob = addKnob (x, y, d, ownerFilter, PAN1, "1", 0.5); + pan1Knob->resetOnShiftClick(true, Action::panReset); + pan1Knob->addActionListener(this); mappingComps["pan1Knob"] = pan1Knob; } if (name == "pan2Knob"){ pan2Knob = addKnob (x, y, d, ownerFilter, PAN2, "2", 0.5); + pan2Knob->resetOnShiftClick(true, Action::panReset); + pan2Knob->addActionListener(this); mappingComps["pan2Knob"] = pan2Knob; } if (name == "pan3Knob"){ pan3Knob = addKnob (x, y, d, ownerFilter, PAN3, "3", 0.5); + pan3Knob->resetOnShiftClick(true, Action::panReset); + pan3Knob->addActionListener(this); mappingComps["pan3Knob"] = pan3Knob; } if (name == "pan4Knob"){ pan4Knob = addKnob (x, y, d, ownerFilter, PAN4, "4", 0.5); + pan4Knob->resetOnShiftClick(true, Action::panReset); + pan4Knob->addActionListener(this); mappingComps["pan4Knob"] = pan4Knob; } if (name == "pan5Knob"){ pan5Knob = addKnob (x, y, d, ownerFilter, PAN5, "5", 0.5); + pan5Knob->resetOnShiftClick(true, Action::panReset); + pan5Knob->addActionListener(this); mappingComps["pan5Knob"] = pan5Knob; } if (name == "pan6Knob"){ pan6Knob = addKnob (x, y, d, ownerFilter, PAN6, "6", 0.5); + pan6Knob->resetOnShiftClick(true, Action::panReset); + pan6Knob->addActionListener(this); mappingComps["pan6Knob"] = pan6Knob; } if (name == "pan7Knob"){ pan7Knob = addKnob (x, y, d, ownerFilter, PAN7, "7", 0.5); + pan7Knob->resetOnShiftClick(true, Action::panReset); + pan7Knob->addActionListener(this); mappingComps["pan7Knob"] = pan7Knob; } if (name == "pan8Knob"){ pan8Knob = addKnob (x, y, d, ownerFilter, PAN8, "8", 0.5); + pan8Knob->resetOnShiftClick(true, Action::panReset); + pan8Knob->addActionListener(this); mappingComps["pan8Knob"] = pan8Knob; } @@ -730,6 +762,22 @@ TooglableButton* ObxdAudioProcessorEditor::addButton (int x, int y, int w, int h return button; } + +void ObxdAudioProcessorEditor::actionListenerCallback(const String& message) +{ + if (message.equalsIgnoreCase(Action::panReset)) + { + const StringArray parameters{ "pan1Knob", "pan2Knob", "pan3Knob", "pan4Knob", "pan5Knob", "pan6Knob", "pan7Knob", "pan8Knob" }; + for (const auto& parameter : parameters) + { + if (auto* knob = dynamic_cast(mappingComps[parameter])) + { + knob->setValue(knob->getDoubleClickReturnValue()); + } + } + } +} + Rectangle ObxdAudioProcessorEditor::transformBounds(int x, int y, int w, int h) { if (getScaleFactor() == 1.0f) @@ -1449,6 +1497,8 @@ void ObxdAudioProcessorEditor::filesDropped(const StringArray& files, int x, int //createMenu(); } } + +const String ObxdAudioProcessorEditor::Action::panReset{ "panReset" }; /* bool ObxdAudioProcessorEditor::keyPressed(const KeyPress & press) { if (press.getKeyCode() == '+' || press.getKeyCode() == KeyPress::numberPadAdd) diff --git a/Source/PluginEditor.h b/Source/PluginEditor.h index 06e20ae..5efb1dd 100644 --- a/Source/PluginEditor.h +++ b/Source/PluginEditor.h @@ -55,6 +55,7 @@ class ObxdAudioProcessorEditor : public AudioProcessorEditor // , public Slider::Listener , public Button::Listener // , public ComboBox::Listener + , public ActionListener , public ApplicationCommandTarget , public Timer , public FileDragAndDropTarget @@ -177,6 +178,7 @@ public: { return processor.physicalPixelScaleFactor > 1.0; } + void actionListenerCallback(const String& message) override; private: Rectangle transformBounds(int x, int y, int w, int h); Knob* addKnob (int x, int y, int d, ObxdAudioProcessor& filter, int parameter, String name, float defval); @@ -304,6 +306,10 @@ private: int menuScaleNum; int countTimerForLed = 0; + struct Action + { + static const String panReset; + }; }; #endif // PLUGINEDITOR_H_INCLUDED diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp index c8380a5..02a0f61 100644 --- a/Source/PluginProcessor.cpp +++ b/Source/PluginProcessor.cpp @@ -39,10 +39,12 @@ AudioProcessorValueTreeState::ParameterLayout createParameterLayout() auto name = TRANS (id); auto range = NormalisableRange {0.0f, 1.0f}; auto defaultValue = defaultParams.values[i]; - auto parameter = std::make_unique (id, - name, - range, - defaultValue); + auto parameter = std::make_unique ( + id, name, range, defaultValue, String{}, AudioProcessorParameter::genericParameter, + [=](float value, int /*maxStringLength*/) + { + return ObxdAudioProcessor::getTrueParameterValueFromNormalizedRange(i, value); + }); params.push_back (std::move (parameter)); } @@ -794,7 +796,7 @@ File ObxdAudioProcessor::getBanksFolder() const File ObxdAudioProcessor::getMidiFolder() const { - return getDocumentFolder().getChildFile("Midi"); + return getDocumentFolder().getChildFile("MIDI"); } @@ -914,6 +916,110 @@ String ObxdAudioProcessor::getEngineParameterId (size_t index) return "Undefined"; } +String ObxdAudioProcessor::getTrueParameterValueFromNormalizedRange(size_t index, float value) +{ + switch (index) + { + // case SELF_OSC_PUSH: return "SelfOscPush"; + // case ENV_PITCH_BOTH: return "EnvPitchBoth"; + // case FENV_INVERT: return "FenvInvert"; + // case PW_OSC2_OFS: return "PwOfs"; + // case LEVEL_DIF: return "LevelDif"; + // case PW_ENV_BOTH: return "PwEnvBoth"; + // case PW_ENV: return "PwEnv"; + // case LFO_SYNC: return "LfoSync"; + // case ECONOMY_MODE: return "EconomyMode"; + // case UNLEARN: return "MidiUnlearn"; + // case MIDILEARN: return "MidiLearn"; + // case VAMPENV: return "VAmpFactor"; + // case VFLTENV: return "VFltFactor"; + // case ASPLAYEDALLOCATION: return "AsPlayedAllocation"; + case BENDLFORATE: return String{ logsc(value, 3, 10), 2 } + " Hz"; + // case FOURPOLE: return "FourPole"; + // case LEGATOMODE: return "LegatoMode"; + // case ENVPITCH: return "EnvelopeToPitch"; + // case OSCQuantize: return "PitchQuant"; + // case VOICE_COUNT: return "VoiceCount"; + // case BANDPASS: return "BandpassBlend"; + // case FILTER_WARM: return "Filter_Warm"; + // case BENDRANGE: return "BendRange"; + // case BENDOSC2: return "BendOsc2Only"; + case OCTAVE: return String{ (roundToInt(value * 4) - 2) * 12.f, 0 } + " Semitones"; + case TUNE: return String{ value * 200 - 100, 1 } + " Cents"; + // case BRIGHTNESS: return "Brightness"; + case NOISEMIX: { + const auto decibels = Decibels::gainToDecibels(logsc(value, 0, 1, 35)); + if (decibels < -80) return "-Inf"; + return String{ decibels, 2 } + " dB"; + } + case OSC1MIX: + case OSC2MIX: { + const auto decibels = Decibels::gainToDecibels(value); + if (decibels < -80) return "-Inf"; + return String{ decibels, 2 } + " dB"; + } + // case MULTIMODE: return "Multimode"; + // case LFOSHWAVE: return "LfoSampleHoldWave"; + // case LFOSINWAVE: return "LfoSineWave"; + // case LFOSQUAREWAVE: return "LfoSquareWave"; + // case LFO1AMT: return "LfoAmount1"; + // case LFO2AMT: return "LfoAmount2"; + // case LFOFILTER: return "LfoFilter"; + // case LFOOSC1: return "LfoOsc1"; + // case LFOOSC2: return "LfoOsc2"; + case LFOFREQ: return String{ logsc(value, 0, 50, 120), 2 } + " Hz"; + // case LFOPW1: return "LfoPw1"; + // case LFOPW2: return "LfoPw2"; + // case PORTADER: return "PortamentoDetune"; + // case FILTERDER: return "FilterDetune"; + // case ENVDER: return "EnvelopeDetune"; + case PAN1: + case PAN2: + case PAN3: + case PAN4: + case PAN5: + case PAN6: + case PAN7: + case PAN8: { + const auto pan = value - 0.5f; + if (pan < 0.f) return String{ pan, 2 } + " (Left)"; + if (pan > 0.f) return String{ pan, 2 } + " (Right)"; + return String{ pan, 2 } + " (Center)"; + } + // case XMOD: return "Xmod"; + // case OSC2HS: return "Osc2HardSync"; + // case OSC1P: return String{ getPitch(value * 48), 2 }; + // case OSC2P: return String{ getPitch(value * 48), 2 }; + // case PORTAMENTO: return "Portamento"; + // case UNISON: return "Unison"; + // case FLT_KF: return "FilterKeyFollow"; + // case PW: return "PulseWidth"; + // case OSC2Saw: return "Osc2Saw"; + // case OSC1Saw: return "Osc1Saw"; + // case OSC1Pul: return "Osc1Pulse"; + // case OSC2Pul: return "Osc2Pulse"; + //case VOLUME: return String{ Decibels::gainToDecibels(linsc(value, 0, 0.30)), 2 }; + // case UDET: return "VoiceDetune"; + // case OSC2_DET: return "Oscillator2detune"; + // case CUTOFF: return "Cutoff"; + // case RESONANCE: return "Resonance"; + // case ENVELOPE_AMT: return "FilterEnvAmount"; + // case LATK: return String{ logsc(value, 4, 60000, 900) / 1000.f, 2}; + // case LDEC: return String{ logsc(value, 4, 60000, 900) / 1000.f, 2}; + // case LSUS: return String{ value, 2}; + // case LREL: return String{ logsc(value, 8, 60000, 900) / 1000.f, 2}; + // case FATK: return String{ logsc(value, 1, 60000, 900) / 1000.f, 2}; + // case FDEC: return String{ logsc(value, 1, 60000, 900) / 1000.f, 2}; + // case FSUS: return String{ value, 2}; + // case FREL: return String{ logsc(value, 1, 60000, 900) / 1000.f, 2 }; + + default: + break; + } + + return String{ value, 2 }; +} + int ObxdAudioProcessor::getParameterIndexFromId (String paramId) { for (size_t i = 0; i < PARAM_COUNT; ++i) diff --git a/Source/PluginProcessor.h b/Source/PluginProcessor.h index cbb0d5f..0656600 100644 --- a/Source/PluginProcessor.h +++ b/Source/PluginProcessor.h @@ -198,6 +198,8 @@ public: void setGuiSize(const int gui_size); //============================================================================== static String getEngineParameterId (size_t); + static String getTrueParameterValueFromNormalizedRange(size_t, float normalizedValue); + int getParameterIndexFromId (String); void setEngineParameterValue (int, float, bool notifyToHost= false); void parameterChanged (const String&, float) override;