Initial release — InstaGrain granular synthesizer v1.0
8-voice polyphonic granular synth (VST3/AU/LV2) with: - 128 grain pool per voice, Hann windowing, linear interpolation - Root note selector, sample rate correction, sustain pedal (CC64) - Scatter controls, direction modes (Fwd/Rev/PingPong), freeze - ADSR envelope, global filter (LP/HP/BP), reverb - Waveform display with grain visualization - Drag & drop sample loading, full state save/restore - CI/CD for Windows/macOS/Linux Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
83
Source/GrainControlPanel.cpp
Normal file
83
Source/GrainControlPanel.cpp
Normal file
@@ -0,0 +1,83 @@
|
||||
#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);
|
||||
}
|
||||
Reference in New Issue
Block a user