🎛️ Steverator — "Le Saturateur à Poisson" 🐟
📥 Téléchargement / Download
🚀 ➡️ Télécharger la dernière version / Download Latest Release
Visitez la page des releases pour télécharger le plugin pour macOS ou Windows.
🇫🇷 MANUEL UTILISATEUR (Français) — Cliquez pour ouvrir
🎉 Bienvenue dans le Steverator !
Le Steverator est un saturateur multi-bandes fun et puissant qui va ajouter chaleur , caractère et grain à tous vos sons ! 🔥
Que vous vouliez :
🎸 Réchauffer une basse molle
🎤 Donner du mordant à une voix plate
🥁 Ajouter du punch à vos drums
🎹 Colorer un synthé trop propre
...le Steverator est là pour vous ! Avec sa mascotte Steve le poisson 🐟 qui bouge au rythme de votre musique, mixer devient encore plus fun !
🎮 L'Interface en un Coup d'Œil
L'interface est divisée en 4 zones principales super intuitives :
┌─────────────┬─────────────────────┬─────────────────┐
│ 🐟 STEVE │ 🎚️ BANDES FREQ │ 🔥 SATURATION │
│ + INPUT │ Low/Mid/High │ + MASTER │
│ + PRESETS │ Crossovers │ Wave/Shape │
└─────────────┴─────────────────────┴─────────────────┘
🎚️ Section 1 : Entrée & Steve (Gauche)
Contrôle
Description
🔊 Input
Contrôle le niveau d'entrée (-24dB à +24dB). Plus c'est fort, plus ça sature !
🐟 Steve
Notre mascotte animée ! Il danse avec votre musique 💃
📁 Presets
Des préréglages tout faits pour démarrer rapidement
💡 Astuce : Montez l'Input pour attaquer fort la saturation, ou baissez-le pour un effet plus subtil !
📊 Section 2 : Bandes de Fréquences (Centre)
Le Steverator divise votre son en 3 bandes indépendantes :
🔈 BASSES ──────┬────── MÉDIUMS ──────┬────── AIGUS 🔊
Low Freq High Freq
Contrôle
Ce que ça fait
Low Freq
Point de séparation Basses/Médiums (en Hz)
High Freq
Point de séparation Médiums/Aigus (en Hz)
🔥 Low Warmth
Ajoute une saturation ronde et chaleureuse aux basses
✨ High Softness
Saturation douce pour des aigus non-agressifs
📊 Level Low/High
Volume de sortie de chaque bande
💡 Astuce : Voulez-vous des basses massives sans toucher aux aigus ? Montez le Low Warmth et gardez le High Softness bas !
🔥 Section 3 : Saturation Globale (Droite)
C'est ici que la magie opère ! ✨
Contrôle
Ce que ça fait
🌊 Wave
Le type de distorsion (Tube, Tape, Diode, Fold, etc.)
🎛️ Saturation
LE gros bouton ! L'intensité de l'effet
🔧 Shape
La texture : plus tranchant ou plus rond ?
🌊 Les différents types de Waves :
┌──────────────────────────────────────────────────┐
│ 🎸 Tube → Chaleureux, vintage, rock │
│ 📼 Tape → Doux, compression naturelle │
│ 💡 Diode → Agressif, mordant, rock/metal │
│ 🔄 Fold → Créatif, synthwave, expérimental │
│ 📐 Clip → Digital, brutal, EDM │
│ ... et plein d'autres ! │
└──────────────────────────────────────────────────┘
🎯 Section 4 : Master (Droite & Bas)
Contrôle
Description
🔀 Mix
Mélange Wet/Dry (0% = original, 100% = saturé)
🔊 Output
Volume de sortie final
👁️ Delta Monitor
Écoutez UNIQUEMENT la distorsion ajoutée !
📈 Delta Gain
Volume du signal Delta
🛡️ Limiter
Protection contre le clipping numérique
💡 Pro tip : Utilisez le Delta Monitor pour entendre exactement ce que vous ajoutez au mix. Super utile pour régler finement !
🚀 Quick Start : Vos Premiers Pas
Chargez le plugin sur une piste audio
Choisissez un preset ou partez de zéro
Sélectionnez un Wave (commencez par "Tube" !)
Montez la Saturation petit à petit
Ajustez le Mix à votre goût
Profitez du son chaud et fuzzy ! 🔥
🇬🇧 USER MANUAL (English) — Click to expand
Steverator is a fun and powerful multi-band saturator that adds warmth , character , and grit to all your sounds! 🔥
Whether you want to:
🎸 Warm up a thin bass
🎤 Add bite to a flat vocal
🥁 Punch up your drums
🎹 Color a too-clean synth
...Steverator's got you covered! With our dancing mascot Steve the Fish 🐟 moving to the beat, mixing becomes even more fun!
🚀 Quick Start:
Load the plugin → 2. Pick a Wave (try "Tube"!) → 3. Turn up Saturation → 4. Adjust Mix → 5. Enjoy! 🔥
🛠️ DEVELOPER DOCUMENTATION
Senior-level technical reference for AI agents and developers working on Steverator
Project Overview
Architecture Deep Dive
Complete File Reference
Parameter System (20 Parameters)
DSP Signal Flow
Waveshape Algorithms (28 Types)
UI System & Rendering
Build System & Deployment
Common Tasks & Patterns
┌─────────────────────────────────────────────────────────────────┐
│ STEVERATOR — Multi-Band Saturation VST3 Plugin │
├─────────────────────────────────────────────────────────────────┤
│ Language: C++17 │
│ Framework: JUCE 7.x │
│ Format: VST3, Standalone │
│ Platforms: macOS (Universal: ARM64 + x86_64), Windows x64 │
│ Architecture: MVC (Model-View-Controller) │
│ Lines of Code: ~2,500 (Source only) │
└─────────────────────────────────────────────────────────────────┘
3-Band Crossover using Linkwitz-Riley filters (flat phase response)
58 Waveshape Algorithms (Tube, Tape, Diode, Fuzz, Chebyshev, etc.)
4x Oversampling for alias-free saturation
Delta Monitor for isolating added harmonics
Smooth Crossfades (10ms) for click-free parameter changes
Animated Mascot reacting to audio RMS level
Procedural UI (no image-based knobs)
Real-time Visualizers with 5 analysis panels
Model-View-Controller Pattern
┌──────────────────────────────────────────────────────────────────────┐
│ JUCE PLUGIN ARCHITECTURE │
├──────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ MODEL: AudioProcessorValueTreeState (APVTS) │ │
│ │ ══════════════════════════════════════════════════════════ │ │
│ │ • Single source of truth for 20 parameters │ │
│ │ • Thread-safe atomic access │ │
│ │ • Automatic DAW automation support │ │
│ │ • XML serialization for presets │ │
│ │ • Location: PluginProcessor.h → `apvts` member │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌───────────────┴───────────────┐ │
│ ▼ ▼ │
│ ┌─────────────────────┐ ┌─────────────────────┐ │
│ │ CONTROLLER (DSP) │ │ VIEW (UI) │ │
│ │ PluginProcessor │ │ PluginEditor │ │
│ │ ═══════════════════│ │ ════════════════════│ │
│ │ • processBlock() │◄───────►│ • paint() │ │
│ │ • prepareToPlay() │ APVTS │ • resized() │ │
│ │ • 3-Band Crossover │ Link │ • timerCallback() │ │
│ │ • Oversampling │ │ • Attachments │ │
│ │ • Waveshaping │ │ • CustomLookAndFeel│ │
│ └─────────────────────┘ └─────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────────────┘
// AUDIO THREAD (Real-time, ~44100 calls/sec)
processBlock () {
// ✅ ALLOWED: Atomic reads, stack allocation, SIMD ops
float value = *apvts.getRawParameterValue (" drive" );
// ❌ FORBIDDEN: new, malloc, mutex, file I/O, logging
}
// MESSAGE THREAD (UI, ~30-60 fps)
timerCallback () {
// ✅ ALLOWED: Atomic reads from processor
float rms = processor.currentRMSLevel .load ();
// ✅ ALLOWED: Heap allocation for display
repaint ();
}
📁 Complete File Reference
File
Size
Purpose
CMakeLists.txt
4.7 KB
Build config : JUCE setup, plugin metadata, linking
build_and_deploy.sh
3.9 KB
Master script : Build, sign, deploy, cache clear
build_and_test.sh
1.8 KB
Quick test build without deployment
README.md
~13 KB
This documentation
AGENTS.md
9.7 KB
AI agent instructions
TUTORIAL.md
3.9 KB
Learning guide
LICENSE
1.1 KB
MIT License
Source Directory (Source/)
📄 PluginProcessor.h (127 lines)
class Vst_saturatorAudioProcessor : public juce ::AudioProcessor {
public:
// === LIFECYCLE ===
Vst_saturatorAudioProcessor ();
~Vst_saturatorAudioProcessor () override ;
// === DSP CORE ===
void prepareToPlay (double sampleRate, int samplesPerBlock) override ;
void processBlock (juce::AudioBuffer<float >&, juce::MidiBuffer&) override ;
void releaseResources () override ;
// === STATE ===
void getStateInformation (juce::MemoryBlock& destData) override ;
void setStateInformation (const void * data, int sizeInBytes) override ;
// === PUBLIC DATA ===
juce::AudioProcessorValueTreeState apvts; // Parameter tree
// Visualizer circular buffer (512 samples)
static const int visualizerBufferSize = 512 ;
std::array<float , visualizerBufferSize> visualizerBuffer;
std::atomic<int > visualizerWriteIndex{0 };
// Envelope follower for Steve animation
std::atomic<float > currentRMSLevel{0 .0f };
private:
// === DSP MEMBERS ===
using Filter = juce::dsp::LinkwitzRileyFilter<float >;
Filter lp1, hp1, lp2, hp2; // 4 filters for 3-band split
juce::AudioBuffer<float > lowBuffer, midBuffer, highBuffer;
juce::dsp::Limiter<float > limiter;
juce::dsp::Oversampling<float > oversampling; // 4x, polyphase IIR
// Delta crossfade state
float deltaSmoothed = 0 .0f ;
float deltaCrossfadeStep = 0 .0f ; // Calculated for 10ms fade
};
📄 PluginProcessor.cpp (707 lines)
Key Functions:
Function
Lines
Description
createParameterLayout()
42-148
Defines all 20 parameters with ranges
prepareToPlay()
204-244
Initialize DSP, filters, oversampling
processBlock()
265-669
MAIN DSP LOOP - All audio processing
getStateInformation()
681-689
Serialize state to XML
setStateInformation()
691-700
Deserialize state from XML
processBlock() Detailed Flow:
Line 265-669: processBlock()
├── 276-311: Read all 20 parameter values (atomic)
├── 317-323: Input gain staging + dry buffer copy
├── 325-335: Update crossover filter coefficients (if changed)
├── 343-403: processBands() lambda - 3-band split & per-band processing
├── 406-540: applyWaveshape() lambda - 28 waveshape algorithms
├── 542-584: Pre/Post routing (Saturation→EQ or EQ→Saturation)
├── 596-627: Delta monitor crossfade mixing
├── 630-635: Output gain + optional limiter
├── 637-668: Visualizer buffer write + RMS calculation
📄 PluginEditor.h (287 lines)
class Vst_saturatorAudioProcessorEditor : public juce ::AudioProcessorEditor,
private juce::Timer {
public:
Vst_saturatorAudioProcessorEditor (Vst_saturatorAudioProcessor&);
~Vst_saturatorAudioProcessorEditor () override ;
void paint (juce::Graphics&) override ;
void resized () override ;
void timerCallback () override ;
private:
// === DESIGN CONSTANTS ===
static constexpr int DESIGN_WIDTH = 1300 ;
static constexpr int DESIGN_HEIGHT = 850 ;
float scaleFactor = 1 .0f ;
// === UI COMPONENTS ===
// A. Global Saturation
juce::Slider saturationSlider, shapeSlider;
juce::ComboBox waveshapeCombo;
// B. Low Band
juce::ToggleButton lowEnableButton;
juce::Slider lowFreqSlider, lowWarmthSlider, lowLevelSlider;
// C. High Band
juce::ToggleButton highEnableButton;
juce::Slider highFreqSlider, highSoftnessSlider, highLevelSlider;
// D. Gain & Routing
juce::Slider inputGainSlider, mixSlider, outputGainSlider;
juce::ToggleButton prePostButton, limiterButton, bypassButton;
// E. Delta Monitoring
juce::ToggleButton deltaButton;
juce::Slider deltaGainSlider;
// F. Preset System
juce::ComboBox presetsCombo;
juce::TextButton presetLeftBtn{" <" }, presetRightBtn{" >" };
std::vector<PresetData> presets;
// G. Waveshape navigation
juce::TextButton waveLeftBtn{" <" };
juce::TextButton waveRightBtn{" >" };
// H. Tab System
juce::TextButton knobsTabButton{" KNOBS" };
juce::TextButton page1TabButton{" VISUALIZERS" };
juce::TextButton page2TabButton{" 2" };
juce::TextButton page3TabButton{" 3" };
juce::TextButton page4TabButton{" 4" };
// I. Styling
CustomLookAndFeel customLookAndFeel;
juce::Image steveImage, steve2Image;
// J. DevTools
juce::TextButton devToolsButton{" 🐞" };
DevToolsPopover devToolsPopover;
// K. Visualizer Tab
VisualizerTabComponent visualizerTab;
// L. Build hash
juce::String buildHash;
};
📄 PluginEditor.cpp (main UI file, ~1000+ lines)
Key Functions:
Function
Lines
Description
Constructor
~14-350
Initialize all 25+ UI components, attachments, tooltips, tabs
scaleDesignBounds()
~350-365
Scale fixed design to any window size
paint()
~365-520
Draw background, Steve, labels, tabs
resized()
~520-700
Position all components using scaled coordinates
timerCallback()
~700-780
Update Steve animation, DevTools metrics (~30fps)
setActiveTab()
~780-800
Switch between Knobs and Visualizers tabs
initializePresets()
~800-1050
Define 76 factory presets
applyPreset()
~1050-1100
Apply preset values to all sliders
navigatePreset()
~1100-1120
Handle preset navigation with arrow buttons
navigateWaveshape()
~1120-1140
Handle waveshape navigation with arrow buttons
refreshDevTools()
~1140-1200
Update DevTools metrics display
loadImage()
~1200-1220
Load images from BinaryData or filesystem
📄 CustomLookAndFeel.h (123 lines)
class CustomLookAndFeel : public juce ::LookAndFeel_V4 {
public:
CustomLookAndFeel ();
~CustomLookAndFeel () override = default ;
// Resource loading
void ensureImageLoaded ();
void ensureFontLoaded ();
juce::Font getCustomFont (float height, int style = juce::Font::plain);
// Component drawing overrides
void drawRotarySlider (...) override ; // Procedural knobs
void getSliderLayout (...) override ; // Custom slider layout
void createSliderTextBox (...) override ; // Custom text box
void hitTestRotarySlider (...) override ; // Larger hit area
void drawToggleButton (...) override ; // Glowy toggle buttons
void drawComboBox (...) override ; // Styled dropdowns
void positionComboBoxText (...) override ; // Combo text positioning
void drawTooltip (...) override ; // Orange tooltips
void getTooltipBounds (...) override ; // Scrollable tooltip bounds
void drawPopupMenuItem (...) override ; // Menu styling
void drawPopupMenuBackground (...) override ; // Menu background
void drawPopupMenuSectionHeader (...) override ; // Section headers
void getIdealPopupMenuItemSize (...) override ; // Menu item sizing
void drawButtonBackground (...) override ; // Button backgrounds
void drawButtonText (...) override ; // Navigation arrows
void getComboBoxFont (...) override ; // Font styling
void getLabelFont (...) override ; // Font styling
void getPopupMenuFont (...) override ; // Font styling
private:
juce::Image indicatorImage;
juce::Typeface::Ptr customTypeface;
};
📄 CustomLookAndFeel.cpp (~700+ lines)
Key Functions:
Function
Lines
Description
Constructor
~20-29
Set popup colors, load font immediately
ensureFontLoaded()
~57-85
Load NanumPenScript from BinaryData
ensureImageLoaded()
~31-55
Load indicator image from multiple paths
drawRotarySlider()
~100-300
Main knob rendering - concentric rings, gradient arc
getSliderLayout()
~300-340
Custom layout to center text box
createSliderTextBox()
~340-380
Custom text box with hit testing
drawToggleButton()
~400-460
Glowing orange toggle buttons
drawComboBox()
~460-510
Styled dropdown with custom border
drawTooltip()
~550-600
Orange tooltip with scroll support
drawPopupMenuItem()
~620-670
Custom menu item colors
drawPopupMenuSectionHeader()
~670-690
Orange separator headers
drawButtonBackground()
~700-750
Navigation arrow button styling
Assets Directory (Assets/)
File
Size
Format
Usage
NanumPenScript-Regular.ttf
3.2 MB
TrueType
Custom handwriting font
steve.png
2.3 MB
PNG
Mascot (closed mouth)
steve2.png
2.2 MB
PNG
Mascot (open mouth - talking state)
indicator.png
2 KB
PNG
Knob position dot
background.png
2.4 MB
PNG
(Legacy, not currently used)
version.txt
26 B
Text
Build hash + timestamp
📄 VisualizerAnalysis.h (75 lines)
// Data structure for visualizer frame
struct VisualizerFrameData {
std::vector<float > preWaveform;
std::vector<float > postWaveform;
std::vector<float > deltaWaveform;
std::vector<float > preSpectrum;
std::vector<float > postSpectrum;
std::vector<float > deltaSpectrum;
float crestPre = 0 .0f ;
float crestPost = 0 .0f ;
float crestDelta = 0 .0f ;
float crestChange = 0 .0f ;
float peakPre = 0 .0f ;
float rmsPre = 0 .0f ;
float peakPost = 0 .0f ;
float rmsPost = 0 .0f ;
float peakDelta = 0 .0f ;
float rmsDelta = 0 .0f ;
float lowHighBalance = 0 .5f ;
bool hasData = false ;
};
// Ring buffer for audio capture (thread-safe)
class AnalyzerTap {
public:
explicit AnalyzerTap (int bufferSize = 8192 );
void prepare (double newSampleRate);
void setEnabled (bool shouldEnable);
bool isEnabled () const ;
void pushSamples (const juce::AudioBuffer<float > &preBuffer,
const juce::AudioBuffer<float > &postBuffer);
void readLatest (std::vector<float > &preOut, std::vector<float > &postOut,
int numSamples) const ;
private:
std::vector<float > preRingBuffer;
std::vector<float > postRingBuffer;
std::atomic<int > writeIndex{0 };
std::atomic<bool > enabled{false };
double sampleRate = 44100.0 ;
};
// Analysis engine that processes ring buffer into visualizer data
class VisualizerAnalysisEngine {
public:
explicit VisualizerAnalysisEngine (AnalyzerTap &tapToUse);
void setFftSize (int newFftSize);
void setScopeSize (int newScopeSize);
void updateFrame (VisualizerFrameData &frame);
private:
void ensureBuffers ();
void computeSpectrum (const std::vector<float > &timeDomain,
std::vector<float > &spectrumOut);
void computeCrestMetrics (const std::vector<float > &buffer, float &peakOut,
float &rmsOut, float &crestOut) const ;
AnalyzerTap &tap;
int fftSize = 2048 ;
int scopeSize = 512 ;
std::unique_ptr<juce::dsp::FFT> fft;
std::unique_ptr<juce::dsp::WindowingFunction<float >> window;
std::vector<float > fftBuffer;
std::vector<float > preTemp;
std::vector<float > postTemp;
std::vector<float > deltaTemp;
};
📄 VisualizerAnalysis.cpp (193 lines)
Key Functions:
Function
Lines
Description
pushSamples()
~21-47
Write audio samples to ring buffer (thread-safe)
readLatest()
~49-71
Read latest samples from ring buffer
updateFrame()
~88-132
Main analysis loop - FFT, metrics, spectrum
ensureBuffers()
~134-148
Resize buffers when FFT/scope size changes
computeSpectrum()
~150-170
FFT analysis with Hann windowing
computeCrestMetrics()
~172-192
Calculate peak/RMS/crest factor
📄 VisualizerComponents.h (128 lines)
// Visualizer display modes
enum class VisualizerMode { Waveform, Bars, Line, Heat, Harmonics };
// Panel state (persisted)
struct VisualizerPanelState {
VisualizerMode mode = VisualizerMode::Waveform;
bool showPre = true ;
bool peakHold = false ;
int smoothingIndex = 2 ;
};
// Single visualizer panel with header + display area
class VisualizerPanelComponent final : public juce::Component {
public:
VisualizerPanelComponent (int panelIndex, const juce::String &panelTitle,
juce::Colour panelTint);
void setPanelState (const VisualizerPanelState &newState);
VisualizerPanelState getPanelState () const ;
void setFrameData (const VisualizerFrameData &newFrame);
void setExpanded (bool shouldExpand);
void setExpandCallback (ExpandCallback callback);
void setModeAvailability (const std::array<bool , 5 > &availability);
void setToggleVisibility (bool showPreToggle, bool showHoldToggle,
bool showSmoothingToggle);
void paint (juce::Graphics &g) override ;
void resized () override ;
private:
void configureHeader ();
void updateExpandButton ();
void updateModeAvailability ();
void drawWaveform (juce::Graphics &g, juce::Rectangle<float > area,
const std::vector<float > &wave, juce::Colour colour) const ;
void drawSpectrumBars (juce::Graphics &g, juce::Rectangle<float > area,
const std::vector<float > &spectrum,
juce::Colour colour) const ;
void drawSpectrumLine (juce::Graphics &g, juce::Rectangle<float > area,
const std::vector<float > &spectrum,
juce::Colour colour) const ;
void drawHeatmap (juce::Graphics &g, juce::Rectangle<float > area);
void drawCrestMeter (juce::Graphics &g, juce::Rectangle<float > area);
void drawHarmonicBalance (juce::Graphics &g, juce::Rectangle<float > area);
VisualizerPanelState state;
VisualizerFrameData frame;
ExpandCallback expandCallback;
bool isExpanded = false ;
std::deque<std::vector<float >> heatHistory;
int maxHeatHistory = 24 ;
};
// Grid layout for multiple panels
class VisualizerGridComponent final : public juce::Component {
public:
explicit VisualizerGridComponent (
std::array<VisualizerPanelComponent *, 5 > panelComponents);
void setExpandedPanelIndex (int index);
void resized () override ;
private:
std::array<VisualizerPanelComponent *, 5 > panels;
int expandedPanelIndex = -1 ;
};
// Main visualizer tab (timer-driven)
class VisualizerTabComponent final : public juce::Component,
private juce::Timer {
public:
VisualizerTabComponent (AnalyzerTap &tapToUse, juce::ValueTree stateRoot);
~VisualizerTabComponent () override ;
void setActive (bool shouldBeActive);
double getLastFrameTimeMs () const ;
double getRefreshIntervalMs () const ;
bool isActiveNow () const ;
void resized () override ;
void paint (juce::Graphics &g) override ;
private:
void timerCallback () override ;
void updateFrameRate (double frameTimeMs);
void setExpandedPanel (int index);
void restorePanelState ();
void storePanelState ();
VisualizerPanelComponent &panelForIndex (int index);
const VisualizerPanelComponent &panelForIndex (int index) const ;
void configurePanelModes ();
AnalyzerTap &tap;
VisualizerAnalysisEngine analysis;
VisualizerFrameData frame;
VisualizerPanelComponent deltaPanel;
VisualizerPanelComponent shaperPanel;
VisualizerPanelComponent dynamicsPanel;
VisualizerPanelComponent balancePanel;
VisualizerPanelComponent utilityPanel;
std::array<VisualizerPanelComponent *, 5 > panels;
VisualizerGridComponent grid;
int expandedPanelIndex = -1 ;
bool isActive = false ;
juce::ValueTree stateTree;
double lastFrameTimeMs = 0.0 ;
double fpsTimerMs = 16.0 ;
int stableHighFpsFrames = 0 ;
};
📄 VisualizerComponents.cpp (~500+ lines)
Key Functions:
Function
Lines
Description
configureHeader()
~44-87
Set up mode selector, toggles, buttons
setPanelState()
~89-97
Apply state to UI controls
drawWaveform()
~200-230
Time-domain oscilloscope rendering
drawSpectrumBars()
~232-260
Frequency-domain bar graph
drawSpectrumLine()
~262-290
Frequency-domain line graph
drawHeatmap()
~292-340
Historical spectrum heatmap
drawCrestMeter()
~342-370
Crest factor visualization
drawHarmonicBalance()
~372-400
Low/High frequency balance
timerCallback()
~450-480
Update loop - read tap, analyze, repaint all panels
configurePanelModes()
~482-510
Set mode availability per panel
### Parameter Access Pattern
```cpp
// In processBlock() - Real-time safe
float saturation = *apvts.getRawParameterValue("drive");
bool bypass = *apvts.getRawParameterValue("bypass");
// In Editor - Via Attachments
saturationAttachment = std::make_unique<SliderAttachment>(
processor.apvts, "drive", saturationSlider);
┌─────────────────────────────────────────────────────────────────────────┐
│ AUDIO INPUT (Stereo) │
└─────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ 1. BYPASS CHECK │
│ if (bypass) return; // Early exit, no processing │
└─────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ 2. DRY BUFFER COPY │
│ dryBuffer.makeCopyOf(buffer); // For wet/dry mix later │
└─────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ 3. INPUT GAIN │
│ buffer.applyGain(inputGain); // -24dB to +24dB │
└─────────────────────────────────────────────────────────────────────────┘
│
┌───────────────┴───────────────┐
│ │
prePost = false prePost = true
(Pre: Sat→EQ) (Post: EQ→Sat)
│ │
▼ ▼
┌────────────────────────────┐ ┌────────────────────────────┐
│ 4A. OVERSAMPLED SATURATION│ │ 4B. 3-BAND CROSSOVER │
│ ┌──────────────────────┐ │ │ ┌──────────────────────┐ │
│ │ 4x Upsample │ │ │ │ Linkwitz-Riley Split │ │
│ │ Apply Waveshape │ │ │ │ ┌─────┬─────┬─────┐ │ │
│ │ 4x Downsample │ │ │ │ │ LOW │ MID │HIGH │ │ │
│ └──────────────────────┘ │ │ │ └──┬──┴──┬──┴──┬──┘ │ │
└────────────────────────────┘ │ │ ▼ │ ▼ │ │
│ │ │ Warmth │ Softness │ │
▼ │ │ Process │ Process │ │
┌────────────────────────────┐ │ │ └─────┴─────┘ │ │
│ 5A. 3-BAND CROSSOVER │ │ │ SUM │ │
│ (Same as 4B) │ │ └──────────────────────┘ │
└────────────────────────────┘ └────────────────────────────┘
│ │
└───────────────┬───────────────┘
│ ▼
┌────────────────────────────┐
│ 5B. OVERSAMPLED SATURATION│
│ (Same as 4A) │
└────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ 6. DELTA MONITOR / MIX / OUTPUT │
│ ┌───────────────────────────────────────────────────────────────────┐ │
│ │ if (deltaEnabled): │ │
│ │ output = tanh((wet - dry) * deltaGain) // Harmonics only │ │
│ │ else: │ │
│ │ output = dry * (1 - mix) + wet * mix // Dry/Wet blend │ │
│ │ │ │
│ │ Crossfade: ~10ms smooth transition (deltaCrossfadeStep) │ │
│ └───────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ 7. OUTPUT GAIN │
│ buffer.applyGain(outputGain); // -24dB to +24dB │
└─────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ 8. LIMITER (Optional) │
│ if (limiterEnable) limiter.process(block); // Prevent clipping │
└─────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ 9. VISUALIZER + RMS UPDATE │
│ Write to circular buffer (512 samples) │
│ Update currentRMSLevel atomic for Steve animation │
└─────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ AUDIO OUTPUT (Stereo) │
└─────────────────────────────────────────────────────────────────────────┘
INPUT AUDIO
│
┌───────────────┼───────────────┐
│ │ │
▼ ▼ ▼
┌───────────┐ ┌───────────┐ ┌───────────┐
│ LP1 │ │ HP1 │ │ HP2 │
│ (lowFreq) │ │ (lowFreq) │ │ (highFreq)│
└─────┬─────┘ └─────┬─────┘ └─────┬─────┘
│ │ │
│ ▼ │
│ ┌───────────┐ │
│ │ LP2 │ │
│ │ (highFreq)│ │
│ └─────┬─────┘ │
│ │ │
▼ ▼ ▼
┌───────────┐ ┌───────────┐ ┌───────────┐
│ LOW BAND │ │ MID BAND │ │ HIGH BAND │
│ Warmth │ │ (Pass- │ │ Softness │
│ Process │ │ through) │ │ Process │
│ Level │ │ │ │ Level │
└─────┬─────┘ └─────┬─────┘ └─────┬─────┘
│ │ │
└───────────────┼───────────────┘
│
▼
SUM (Recombine)
Filter Types: juce::dsp::LinkwitzRileyFilter<float> - 4th order (24dB/octave), flat summed response
🌊 Waveshape Algorithms (58 Types)
Complete Algorithm Reference
Index
Name
Formula
Character
0
Tube
soft*(1-s) + hard*s
Warm, vintage
1
SoftClip
tanh(x * (1 + s*2))
Smooth, musical
2
HardClip
clamp(x * (1 + s*3), -1, 1)
Digital, aggressive
3
Diode 1
x>0 ? tanh(x*(1+s)) : x*0.5
Asymmetric, rectified
4
Diode 2
x>0 ? x*0.7 : tanh(x*(1+s*2))
Reverse asymmetric
5
Linear Fold
Threshold-based folding
Creative, metallic
6
Sin Fold
sin(x * π * (1+s*2))
Wavetable-like
7
Zero-Square
x² * sign(x) * (1+s)
Harsh, edgy
8
Downsample
tanh(x*(1+s))
Lo-fi feel
9
Asym
x>0 ? tanh(x*(1+s*2)) : x*0.3
Tube-like asymmetry
Index
Name
Formula
Character
10
Rectify
abs(x) * (1-s*0.5)
Octave-up effect
11
X-Shaper
x*(1+s) / (1+s*abs(x))
Soft limiting
12
X-Shaper Asym
Asymmetric X-Shaper
Warm edges
13
Sine Shaper
sin(tanh(x)*π*0.5*(1+s))
Rounded harmonics
14
Stomp Box
atan(x*(1+s*5)) / π
Pedal-like
15
Tape Sat.
x*(1-s) + tanh(x*1.5)*s
Analog warmth
16
Overdrive
(2/π) * atan(x*(1+s*10))
Classic drive
17
Soft Sat.
x / (1 + abs(x)*s)
Gentle saturation
18
Bit-Crush
round(x*levels) / levels
Digital artifacts
19
Glitch Fold
x * sin(x*s*π)
Experimental
Index
Name
Formula
Character
20
Valve
(x+bias) / (1+abs(x+bias))
Vintage tube
21
Fuzz Fac
sign(x) * (1-exp(-abs(x*(1+s*10))))
Fuzz Face style
22
Cheby 3
(4x³ - 3x) * (0.5+s*0.5)
3rd harmonic
23
Cheby 5
(16x⁵ - 20x³ + 5x) * (0.5+s*0.5)
5th harmonic
24
Log Sat
sign(x) * log(1+k*abs(x)) / log(1+k)
Logarithmic
25
Half Wave
x>0 ? tanh(x*(1+s)) : x
Half rectifier
26
Cubic
x_scaled - x_scaled³/3
Classic soft clip
27
Octaver Sat
(abs(x)*2 - 1) * (0.5+s*0.5)
Octave generator
🔥 TUBE TYPES (28-33) — Inspired by Decapitator
Index
Name
Formula
Character
28
Triode
12AX7 model with mu factor
Classic warmth
29
Pentode
EL34 with screen grid
Power tube push
30
Class A
Single-ended bias
Smooth warmth
31
Class AB
Push-pull threshold
Punchy attack
32
Class B
Crossover distortion
Gritty character
33
Germanium
Temperature-dependent
Vintage fuzz
📼 TAPE MODES (34-38) — Inspired by Saturn
Index
Name
Formula
Character
34
Tape 15ips
Fast speed, high headroom
Bright, open
35
Tape 7.5ips
Slow speed, warmth boost
Warm, round
36
Tape Cassette
HF loss + saturation
Lo-fi vibes
37
Tape 456
Ampex 456 hysteresis
Punchy low end
38
Tape SM900
Modern tape blend
Clean + vintage
Index
Name
Formula
Character
39
Transformer
Iron core saturation
Harmonic warmth
40
Console
Neve-style harmonics
Rich coloring
41
API Style
Punchy clipping
Attack + presence
42
SSL Style
Clean compression
Subtle sheen
Index
Name
Formula
Character
43
Silicon
Modern transistor clip
Crisp, defined
44
FET Clean
1176-style limiting
Transparent control
45
FET Dirty
1176 all-buttons-in
Aggressive pump
46
OpAmp
IC-style clipping
Harsh but musical
47
CMOS
Digital/analog hybrid
Unique texture
🎨 CREATIVE (48-52) — Inspired by Trash 2
Index
Name
Formula
Character
48
Scream
Extreme harmonic boost
Aggressive screamer
49
Buzz
Sine modulation
Buzzy texture
50
Crackle
Random noise injection
Vinyl character
51
Wrap
Wrap-around modulo
Experimental
52
Density
Dual-tanh stacking
Thick, dense
Index
Name
Formula
Character
53
Cheby 7
7th order Chebyshev polynomial
Rich harmonics
54
Hyperbolic
sinh(x) / cosh(2x)
Smooth curves
55
Exponential
1 - exp(-abs(x))
Fast attack
56
Parabolic
Parabolic limiting curve
Soft knee
57
Wavelet
Mexican hat wavelet
Unique texture
s=0.0: Mild effect, original character preserved
s=0.5: Balanced, typical use
s=1.0: Maximum effect, extreme character
🎵 Factory Presets (76 Presets)
Preset
Waveshape
Character
Warm Tape
Tape Sat.
Vintage analog warmth
Tube Glow
SoftClip
Classic tube saturation
Soft Clip
HardClip
Digital but musical
Vintage Console
SoftClip
Old desk coloring
Analog Warmth
Soft Sat.
Subtle harmonics
Classic Overdrive
Overdrive
Rock amp simulation
Preset
Best For
Hip-Hop Low End
808s, bass
EDM Punch
Transients
Rock Crunch
Guitars
Jazz Warmth
Horns, piano
Lo-Fi Beats
Chill vibes
Metal Aggression
Heavy guitars
Preset
Target
Bass Growl
Bass guitar
Vocal Warmth
Lead vocals
Drums Punch
Drum bus
Guitar Amp
Electric guitar
Synth Edge
Synth leads
Piano Glue
Acoustic piano
Strings Silk
Strings
Horns Presence
Brass
Preset
Effect
Bitcrushed
Digital destruction
Fuzz Box
Vintage fuzz pedal
Wave Folder
Metallic textures
Sin Fold
Wavetable-like
Rectifier
Octave-up effect
Extreme Destroy
Complete destruction
Preset
Character
Digital Grit
Modern edge
Glitchy Bass
Experimental
Valve Master
Tube preamp
Hard Fuzz
Heavy fuzz
Harmonic Filter
Chebyshev
Polished Sat.
Clean saturation
Log Deep
Logarithmic depth
Half Vintage
Half-wave character
Octave Dirt
Octave + distortion
Pentode Drive
Power tube
🎚️ MASTERING / SUBTLE (37-40)
Preset
Use Case
Master Glue
Mix bus glue
Parallel Crush
Parallel processing
Subtle Harmonics
Mastering gentle
Bus Warmth
Subgroup saturation
🔥 DECAPITATOR STYLE (41-48)
Preset
Emulation
Punish (A)
Aggressive Triode
Pentode Power
EL34 power section
Class A Warmth
Single-ended amp
Push-Pull Punch
Class AB power
Germanium Fuzz
Vintage transistor
Triode Clean
Subtle 12AX7
Hot Pentode
Pushed hard
Class B Grit
Crossover distortion
📼 SATURN TAPE STYLE (49-56)
Preset
Mode
Tape Machine 15
15 ips bright
Tape Machine 7.5
7.5 ips warm
Lo-Fi Cassette
Cassette vibes
Ampex 456
Punchy tape
Modern Tape
SM900 clean
Tape Slam
Driven tape
Tape + Tube
Combined
Vintage Deck
Reel-to-reel
🎛️ CONSOLE / TRANSFORMER (57-62)
Preset
Emulation
Neve Console
1073-style warmth
API Punch
API 2500 punch
SSL Sheen
G-Series clean
Iron Saturator
Transformer iron
Console Crunch
Pushed preamp
Vintage Desk
Old console
⚡ MODERN PRODUCTION (63-68)
Preset
Style
FET Vocal
1176 on vocals
All Buttons In
1176 slammed
Silicon Bass
Transistor bass
OpAmp Drive
IC character
Digital Hybrid
CMOS blend
Parallel FET
Parallel compression
Preset
Effect
Screamer
Aggressive scream
Buzz Saw
Buzzy texture
Vinyl Crackle
Lo-fi crackle
Wrap Around
Wrap distortion
Dense Stack
Thick density
Harmonic 7
Chebyshev 7th
Hyperbolic
Sinh curves
Wavelet FX
Wavelet texture
// Background
juce::Colour::fromFloatRGBA (0 .93f , 0 .90f , 0 .82f , 1 .0f ) // #EDE6D1 Warm beige
// Knob Ring
juce::Colour::fromFloatRGBA(0 .6f , 0 .35f , 0 .1f , 1 .0f ) // #994D1A Dark brown-orange
// Text Labels
juce::Colour::fromFloatRGBA(0 .5f , 0 .3f , 0 .1f , 1 .0f ) // #7D4C1A Dark brown
// Active Controls
juce::Colour::fromFloatRGBA(1 .0f , 0 .5f , 0 .1f , 1 .0f ) // #FF8019 Bright orange
// Inactive Controls
juce::Colour::fromFloatRGBA(0 .6f , 0 .35f , 0 .1f , 1 .0f ) // #994D1A Subtle brown
Knob Rendering (drawRotarySlider)
┌─────────────────────┐
│ Outer Ring │ ← Dark stroke circle
│ ┌───────────────┐ │
│ │ Gradient Arc │ │ ← HSV gradient (gold→orange)
│ │ ┌─────────┐ │ │
│ │ │ Inner │ │ │ ← Lighter center
│ │ │ Fill │ │ │
│ │ │ ● │ │ │ ← Indicator dot (rotated)
│ │ └─────────┘ │ │
│ └───────────────┘ │
└─────────────────────┘
VALUE
(editable)
30 FPS refresh rate via startTimerHz(30)
Waveform : Reads 512 samples from circular buffer
Steve : Switches images based on RMS threshold
🔨 Build System & Deployment
# Key settings from CMakeLists.txt
project (steverator VERSION 0.0.1)
set (CMAKE_OSX_ARCHITECTURES "arm64;x86_64" ) # Universal Binary
juce_add_plugin (steverator
COMPANY_NAME "NeiXXa"
PLUGIN_CODE "STEV"
MANUFACTURER_CODE "NXXA"
BUNDLE_ID "com.neixxa.steverator"
FORMATS VST3 Standalone
PRODUCT_NAME "Steverator"
)
# Embedded binary data
juce_add_binary_data (Assets SOURCES
Assets/NanumPenScript-Regular.ttf
Assets/steve.png
Assets/steve2.png
Assets/indicator.png
)
Build Script Flow (build_and_deploy.sh)
1. Generate BUILD_HASH (5 chars, e.g., " 09FA1" )
2. Create version.txt with hash + timestamp
3. cmake -B build -DCMAKE_BUILD_TYPE=Release
4. cmake --build build --config Release
5. Copy assets to Standalone.app/Contents/Resources/
6. sudo: Clean old VST3, copy new, fix permissions
7. codesign --force --deep --sign -
8. Clear Ableton caches
9. Launch Standalone for testing
VST3 : /Library/Audio/Plug-Ins/VST3/steverator.vst3
Standalone : build/steverator_artefacts/Release/Standalone/Steverator.app
📝 Common Tasks & Patterns
// 1. PluginProcessor.cpp - createParameterLayout()
layout.add(std::make_unique<juce::AudioParameterFloat>(
" myParam" , // ID (used in code)
" My Parameter" , // Display name
0 .0f , 1 .0f , // Min, Max
0 .5f // Default
));
// 2. PluginProcessor.cpp - processBlock()
float myValue = *apvts.getRawParameterValue(" myParam" );
// 3. PluginEditor.h - Add members
juce::Slider mySlider;
std::unique_ptr<SliderAttachment> myAttachment;
// 4. PluginEditor.cpp - Constructor
mySlider.setSliderStyle(juce::Slider::RotaryHorizontalVerticalDrag);
addAndMakeVisible (mySlider);
myAttachment = std::make_unique<SliderAttachment>(
processor.apvts, " myParam" , mySlider);
// 5. PluginEditor.cpp - resized()
mySlider.setBounds(scaleDesignBounds(x, y, w, h));
// 6. Run ./build_and_deploy.sh
// 1. PluginProcessor.cpp - createParameterLayout()
// Add to StringArray in waveshape parameter
juce::StringArray{" Tube" , ..., " My New Shape" },
// 2. PluginProcessor.cpp - applyWaveshape lambda
case 28 : // My New Shape
{
float myOutput = /* your algorithm */ ;
output = myOutput;
break ;
}
// CustomLookAndFeel.cpp
// For knobs: drawRotarySlider() near line 150
g.setColour(juce::Colour::fromFloatRGBA(R, G, B, A));
// For buttons: drawToggleButton() near line 400
auto fillColor = button.getToggleState()
? juce::Colour(0xFF , 0x80 , 0x19 ) // Active
: juce::Colour(0x99 , 0x4D , 0x1A ); // Inactive
Real-Time Safety Checklist
// ✅ SAFE in processBlock()
float x = *apvts.getRawParameterValue(" param" );
float result = std::tanh(x);
buffer.applyGain(gain);
atomicVar.store(value, std::memory_order_relaxed);
// ❌ FORBIDDEN in processBlock()
new float [size];
std::vector<float > temp;
std::mutex lock;
DBG (" debug message" );
juce::File::exists ();
Issue
Solution
Plugin not appearing in DAW
Run ./build_and_deploy.sh, restart DAW, rescan plugins
Audio glitches/clicks
Check for allocations in processBlock, verify buffer sizes
Knobs not responding
Verify attachment IDs match parameter IDs exactly
Font not loading
Check BinaryData namespace, verify file embedded in CMake
Steve not animating
Check RMS threshold in timerCallback (~0.1f)
Build hash not updating
Delete build/ folder, rebuild from scratch
Last Updated: January 2026 | Version: 0.0.1 | Maintainer: NeiXXa