mirror of
https://codeberg.org/armin/chromaflock.git
synced 2026-09-01 04:10:47 +02:00
280 lines
11 KiB
C++
280 lines
11 KiB
C++
#pragma once
|
|
#include <juce_audio_processors/juce_audio_processors.h>
|
|
#include <juce_gui_basics/juce_gui_basics.h>
|
|
#include <juce_dsp/juce_dsp.h>
|
|
#include "PluginProcessor.h"
|
|
#include <vector>
|
|
|
|
class KnobLookAndFeel : public juce::LookAndFeel_V4 {
|
|
public:
|
|
KnobLookAndFeel();
|
|
void drawRotarySlider(juce::Graphics& g, int x, int y, int width, int height,
|
|
float sliderPos, float rotaryStartAngle,
|
|
float rotaryEndAngle, juce::Slider& slider) override;
|
|
private:
|
|
juce::Image knobOverlay;
|
|
};
|
|
|
|
class KnobSlider : public juce::Slider {
|
|
public:
|
|
using juce::Slider::Slider;
|
|
juce::String getTextFromValue(double v) override {
|
|
return juce::String::formatted("%.1f", v);
|
|
}
|
|
};
|
|
|
|
class ComboBoxLookAndFeel : public juce::LookAndFeel_V4 {
|
|
public:
|
|
void drawComboBox(juce::Graphics& g, int width, int height,
|
|
bool isButtonDown, int buttonX, int buttonY,
|
|
int buttonW, int buttonH, juce::ComboBox& box) override;
|
|
void drawPopupMenuItem(juce::Graphics& g, const juce::Rectangle<int>& area,
|
|
bool isSeparator, bool isActive, bool isHighlighted,
|
|
bool isTicked, bool hasSubMenu, const juce::String& text,
|
|
const juce::String& shortcutKeyText,
|
|
const juce::Drawable* icon, const juce::Colour* textColour) override;
|
|
};
|
|
|
|
class VuMeter : public juce::Component, public juce::Timer {
|
|
public:
|
|
explicit VuMeter(ChromaFlockProcessor& p) : processor(p) { startTimerHz(30); }
|
|
void paint(juce::Graphics& g) override;
|
|
void timerCallback() override { repaint(); }
|
|
private:
|
|
ChromaFlockProcessor& processor;
|
|
};
|
|
|
|
class WaveformDisplay : public juce::Component, public juce::Timer {
|
|
public:
|
|
explicit WaveformDisplay(ChromaFlockProcessor& p) : processor(p) {
|
|
fft = std::make_unique<juce::dsp::FFT>(ChromaFlockProcessor::fftOrder);
|
|
startTimerHz(30);
|
|
}
|
|
void paint(juce::Graphics& g) override;
|
|
void timerCallback() override { repaint(); }
|
|
private:
|
|
ChromaFlockProcessor& processor;
|
|
std::unique_ptr<juce::dsp::FFT> fft;
|
|
std::vector<float> specSmooth;
|
|
};
|
|
|
|
class PatchLCD : public juce::Component {
|
|
public:
|
|
std::function<void()> onClick;
|
|
void setText(const juce::String& t) { text = t; repaint(); }
|
|
const juce::String& getText() const { return text; }
|
|
void paint(juce::Graphics& g) override;
|
|
void mouseEnter(const juce::MouseEvent&) override { setMouseCursor(juce::MouseCursor::PointingHandCursor); }
|
|
void mouseDown(const juce::MouseEvent&) override { if (onClick) onClick(); }
|
|
private:
|
|
juce::String text = "INIT";
|
|
};
|
|
|
|
class MidiLed : public juce::Component, public juce::Timer {
|
|
public:
|
|
explicit MidiLed(ChromaFlockProcessor& p) : processor(p) { startTimerHz(30); }
|
|
void paint(juce::Graphics& g) override;
|
|
void timerCallback() override { repaint(); }
|
|
private:
|
|
ChromaFlockProcessor& processor;
|
|
};
|
|
|
|
class LedToggle : public juce::Component, public juce::Timer {
|
|
public:
|
|
LedToggle(juce::AudioProcessorValueTreeState& apvts, const juce::String& paramId,
|
|
const juce::String& onText, const juce::String& offText);
|
|
void paint(juce::Graphics& g) override;
|
|
void mouseDown(const juce::MouseEvent&) override;
|
|
void mouseEnter(const juce::MouseEvent&) override;
|
|
void timerCallback() override;
|
|
bool isLit() const { return lit; }
|
|
std::function<void()> onChange;
|
|
private:
|
|
juce::AudioProcessorValueTreeState& apvts;
|
|
juce::String paramId;
|
|
juce::String onText, offText;
|
|
bool lit = false;
|
|
};
|
|
|
|
class PianoRollComponent : public juce::Component, public juce::Timer {
|
|
public:
|
|
explicit PianoRollComponent(ChromaFlockProcessor& p) : processor(p) { startTimerHz(60); }
|
|
void paint(juce::Graphics& g) override;
|
|
void timerCallback() override;
|
|
void mouseDown(const juce::MouseEvent& e) override;
|
|
void mouseUp(const juce::MouseEvent& e) override;
|
|
void mouseDrag(const juce::MouseEvent& e) override;
|
|
|
|
private:
|
|
ChromaFlockProcessor& processor;
|
|
|
|
static constexpr int firstMidiNote = 48; // C3
|
|
static constexpr int numKeys = 49; // C3..C7
|
|
|
|
int whiteKeyWidth = 54;
|
|
int whiteKeyHeight = 130;
|
|
int blackKeyWidth = 35;
|
|
int blackKeyHeight = 80;
|
|
int pitchBendWidth = 40;
|
|
|
|
int midiNoteForKey(int index) const;
|
|
bool isBlackKey(int midiNote) const;
|
|
int keyAtPosition(juce::Point<int> pos) const;
|
|
void triggerNote(int note, bool on);
|
|
|
|
int lastTriggeredNote = -1;
|
|
int currentPitchBend = 64; // 0..127, 64 = center
|
|
bool pitchBendDragging = false;
|
|
int pitchBendStartY = 0;
|
|
|
|
bool pitchBendGliding = false;
|
|
float pitchBendGlideStart = 0.0f;
|
|
juce::int64 pitchBendGlideStartMs = 0;
|
|
static constexpr int pitchBendGlideMs = 50;
|
|
};
|
|
|
|
class MainContentComponent : public juce::Component {
|
|
public:
|
|
explicit MainContentComponent(ChromaFlockProcessor& p);
|
|
void paint(juce::Graphics& g) override;
|
|
void resized() override;
|
|
|
|
std::function<void(float)> onScaleChanged;
|
|
|
|
private:
|
|
ChromaFlockProcessor& processorRef;
|
|
KnobLookAndFeel knobLaf;
|
|
ComboBoxLookAndFeel comboLaf;
|
|
juce::Image bgImage;
|
|
bool bgImageLoaded = false;
|
|
juce::Image logoImage;
|
|
bool logoLoaded = false;
|
|
|
|
VuMeter vuMeter;
|
|
WaveformDisplay waveformDisplay;
|
|
PianoRollComponent pianoRoll;
|
|
|
|
juce::Label osc1Label, osc2Label, filterLabel, envLabel, fEnvLabel, globalLabel;
|
|
juce::Label fxLabel, lfoLabel, fx2Label;
|
|
juce::Label scaleLabel;
|
|
juce::Label midiLabel;
|
|
juce::Label octaveTransposeLabel, semitoneTransposeLabel;
|
|
|
|
juce::ComboBox octaveTransposeBox, semitoneTransposeBox;
|
|
juce::ComboBox osc1WaveBox;
|
|
KnobSlider osc1OctKnob, osc1SemiKnob, osc1FineKnob, osc1LevelKnob;
|
|
|
|
juce::ComboBox osc2WaveBox;
|
|
KnobSlider osc2OctKnob, osc2SemiKnob, osc2FineKnob, osc2LevelKnob, phaseOffsetKnob;
|
|
|
|
juce::ComboBox filterTypeBox;
|
|
KnobSlider filterCutoffKnob, filterResKnob, filterEnvAmtKnob, keyTrackKnob;
|
|
|
|
KnobSlider envAttackKnob, envDecayKnob, envSustainKnob, envReleaseKnob;
|
|
KnobSlider fEnvAttackKnob, fEnvDecayKnob, fEnvSustainKnob, fEnvReleaseKnob;
|
|
|
|
KnobSlider panKnob, driveKnob, masterKnob, pbRangeKnob;
|
|
|
|
// FX controls
|
|
juce::ComboBox distTypeBox;
|
|
KnobSlider distAmountKnob, distSymmetryKnob, distToneKnob;
|
|
KnobSlider compThresholdKnob, compRatioKnob, compAttackKnob, compReleaseKnob, compMakeupKnob;
|
|
KnobSlider autoPanRateKnob, autoPanDepthKnob, autoPanPhaseKnob;
|
|
KnobSlider limiterThresholdKnob, limiterCeilingKnob, limiterReleaseKnob;
|
|
|
|
// LFO controls
|
|
KnobSlider lfo1RateKnob, lfo1DepthKnob;
|
|
juce::ComboBox lfo1ShapeBox, lfo1DestBox;
|
|
KnobSlider lfo2RateKnob, lfo2DepthKnob;
|
|
juce::ComboBox lfo2ShapeBox, lfo2DestBox;
|
|
LedToggle lfo1SyncLed, lfo2SyncLed, delaySyncLed;
|
|
|
|
// FX2 controls
|
|
KnobSlider delayTimeKnob, delayFbKnob, delayMixKnob;
|
|
KnobSlider reverbSizeKnob, reverbDampKnob, reverbMixKnob;
|
|
|
|
// Arpeggiator controls
|
|
juce::Label arpEnabledLabel, arpPatternLabel, arpOctavesLabel, arpDirectionLabel, arpRateLabel;
|
|
juce::ComboBox arpPatternBox, arpOctavesBox, arpRateBox;
|
|
LedToggle arpEnabledLed, arpDirectionLed, delayPingPongLed;
|
|
|
|
// Preset menu
|
|
juce::TextButton presetButton{"PRESET"};
|
|
PatchLCD patchLCD;
|
|
MidiLed midiLed;
|
|
juce::TextButton prevPresetButton{"<"}, nextPresetButton{">"};
|
|
juce::TextButton randomizeButton{"RANDOM"};
|
|
int currentPresetIndex = 0;
|
|
|
|
juce::ComboBox uiScaleBox;
|
|
|
|
using SliderAttachment = juce::AudioProcessorValueTreeState::SliderAttachment;
|
|
using ComboBoxAttachment = juce::AudioProcessorValueTreeState::ComboBoxAttachment;
|
|
|
|
std::unique_ptr<SliderAttachment> osc1OctAttach, osc1SemiAttach, osc1FineAttach, osc1LevelAttach;
|
|
std::unique_ptr<SliderAttachment> osc2OctAttach, osc2SemiAttach, osc2FineAttach, osc2LevelAttach, phaseOffsetAttach;
|
|
std::unique_ptr<ComboBoxAttachment> osc1WaveAttach, osc2WaveAttach, filterTypeAttach;
|
|
std::unique_ptr<ComboBoxAttachment> octaveTransposeAttach, semitoneTransposeAttach;
|
|
std::unique_ptr<SliderAttachment> filterCutoffAttach, filterResAttach, filterEnvAmtAttach, keyTrackAttach;
|
|
std::unique_ptr<SliderAttachment> envAttackAttach, envDecayAttach, envSustainAttach, envReleaseAttach;
|
|
std::unique_ptr<SliderAttachment> fEnvAttackAttach, fEnvDecayAttach, fEnvSustainAttach, fEnvReleaseAttach;
|
|
std::unique_ptr<SliderAttachment> panAttach, driveAttach, masterAttach, pbRangeAttach;
|
|
|
|
// FX attachments
|
|
std::unique_ptr<ComboBoxAttachment> distTypeAttach;
|
|
std::unique_ptr<SliderAttachment> distAmountAttach, distSymmetryAttach, distToneAttach;
|
|
std::unique_ptr<SliderAttachment> compThresholdAttach, compRatioAttach, compAttackAttach;
|
|
std::unique_ptr<SliderAttachment> compReleaseAttach, compMakeupAttach;
|
|
std::unique_ptr<SliderAttachment> autoPanRateAttach, autoPanDepthAttach, autoPanPhaseAttach;
|
|
std::unique_ptr<SliderAttachment> limiterThresholdAttach, limiterCeilingAttach, limiterReleaseAttach;
|
|
|
|
// LFO attachments
|
|
std::unique_ptr<SliderAttachment> lfo1RateAttach, lfo1DepthAttach, lfo1BeatAttach;
|
|
std::unique_ptr<ComboBoxAttachment> lfo1ShapeAttach, lfo1DestAttach;
|
|
std::unique_ptr<SliderAttachment> lfo2RateAttach, lfo2DepthAttach, lfo2BeatAttach;
|
|
std::unique_ptr<ComboBoxAttachment> lfo2ShapeAttach, lfo2DestAttach;
|
|
|
|
// FX2 attachments
|
|
std::unique_ptr<SliderAttachment> delayTimeAttach, delayFbAttach, delayMixAttach, delayBeatAttach;
|
|
std::unique_ptr<SliderAttachment> reverbSizeAttach, reverbDampAttach, reverbMixAttach;
|
|
|
|
// Arpeggiator attachments
|
|
std::unique_ptr<ComboBoxAttachment> arpPatternAttach, arpOctavesAttach, arpRateAttach;
|
|
|
|
void setupLabel(juce::Label& label, const juce::String& text);
|
|
void setupKnob(juce::Slider& knob, const juce::String& name);
|
|
void setupCombo(juce::ComboBox& combo);
|
|
void setupSyncKnob(juce::Slider& knob,
|
|
std::unique_ptr<SliderAttachment>& rateAttach,
|
|
std::unique_ptr<SliderAttachment>& beatAttach,
|
|
const juce::String& rateId,
|
|
const juce::String& beatId,
|
|
bool syncOn);
|
|
void showPresetMenu();
|
|
void showFileMenu();
|
|
void loadPresetFile();
|
|
void savePresetFile();
|
|
juce::String currentPresetName() const;
|
|
void applyCurrentPreset();
|
|
void randomizeParameters();
|
|
|
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(MainContentComponent)
|
|
};
|
|
|
|
class ChromaFlockEditor : public juce::AudioProcessorEditor {
|
|
public:
|
|
ChromaFlockEditor(ChromaFlockProcessor& p);
|
|
~ChromaFlockEditor() override;
|
|
|
|
void paint(juce::Graphics&) override;
|
|
void resized() override;
|
|
void setUIScale(float newScale);
|
|
|
|
private:
|
|
MainContentComponent content;
|
|
float currentScale = 1.0f;
|
|
static constexpr int baseWidth = 1660;
|
|
static constexpr int baseHeight = 1028;
|
|
|
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ChromaFlockEditor)
|
|
};
|