#include "GrainControlPanel.h" GrainControlPanel::GrainControlPanel() { titleLabel.setText ("GRAIN", juce::dontSendNotification); titleLabel.setJustificationType (juce::Justification::centredLeft); titleLabel.setColour (juce::Label::textColourId, InstaGrainLookAndFeel::textPrimary); addAndMakeVisible (titleLabel); setupKnob (positionKnob, posLabel, "Position", 0.0, 1.0, 0.5); setupKnob (sizeKnob, sizeLabel, "Size", 10.0, 500.0, 100.0, " ms"); setupKnob (densityKnob, densityLabel, "Density", 1.0, 100.0, 10.0, " g/s"); setupKnob (pitchKnob, pitchLabel, "Pitch", -24.0, 24.0, 0.0, " st"); setupKnob (panKnob, panLabel, "Pan", -1.0, 1.0, 0.0); // Root Note selector (MIDI 0-127) const char* noteNames[] = { "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B" }; for (int i = 0; i <= 127; ++i) { int octave = (i / 12) - 1; juce::String name = juce::String (noteNames[i % 12]) + juce::String (octave); rootNoteBox.addItem (name, i + 1); // ComboBox IDs start at 1 } rootNoteBox.setSelectedId (60 + 1); // Default: C4 addAndMakeVisible (rootNoteBox); rootNoteLabel.setText ("Root", juce::dontSendNotification); rootNoteLabel.setJustificationType (juce::Justification::centred); rootNoteLabel.setColour (juce::Label::textColourId, InstaGrainLookAndFeel::textSecondary); addAndMakeVisible (rootNoteLabel); } void GrainControlPanel::setupKnob (juce::Slider& knob, juce::Label& label, const juce::String& name, double min, double max, double def, const juce::String& suffix) { knob.setSliderStyle (juce::Slider::RotaryHorizontalVerticalDrag); knob.setTextBoxStyle (juce::Slider::TextBoxBelow, false, 60, 14); knob.setRange (min, max); knob.setValue (def); knob.setTextValueSuffix (suffix); knob.getProperties().set (InstaGrainLookAndFeel::knobTypeProperty, "orange"); addAndMakeVisible (knob); label.setText (name, juce::dontSendNotification); label.setJustificationType (juce::Justification::centred); label.setColour (juce::Label::textColourId, InstaGrainLookAndFeel::textSecondary); addAndMakeVisible (label); } void GrainControlPanel::resized() { auto bounds = getLocalBounds().reduced (4); titleLabel.setBounds (bounds.removeFromTop (20)); int knobW = bounds.getWidth() / 6; int knobH = bounds.getHeight() - 16; auto row = bounds.removeFromTop (knobH); juce::Slider* knobs[] = { &positionKnob, &sizeKnob, &densityKnob, &pitchKnob, &panKnob }; juce::Label* labels[] = { &posLabel, &sizeLabel, &densityLabel, &pitchLabel, &panLabel }; for (int i = 0; i < 5; ++i) { auto col = row.removeFromLeft (knobW); knobs[i]->setBounds (col.reduced (2)); labels[i]->setBounds (col.getX(), col.getBottom() - 2, col.getWidth(), 16); } // Root Note combo in the remaining space auto rootCol = row; rootNoteLabel.setBounds (rootCol.removeFromTop (14)); rootNoteBox.setBounds (rootCol.reduced (4, 2).removeFromTop (24)); } void GrainControlPanel::paint (juce::Graphics& g) { auto bounds = getLocalBounds().toFloat(); g.setColour (InstaGrainLookAndFeel::bgMedium.withAlpha (0.5f)); g.fillRoundedRectangle (bounds, 4.0f); g.setColour (InstaGrainLookAndFeel::bgLight.withAlpha (0.3f)); g.drawRoundedRectangle (bounds, 4.0f, 1.0f); }