mirror of
https://codeberg.org/armin/chromaflock.git
synced 2026-09-01 04:10:47 +02:00
add arpeggiator, rename presets, move spectrum analyzer into waveform
This commit is contained in:
parent
a806e1a8b5
commit
3331b4cc16
9 changed files with 1126 additions and 395 deletions
104
AGENTS.md
104
AGENTS.md
|
|
@ -86,7 +86,7 @@ Osc1 + Osc2 → Amplitude Envelope × Velocity → Filter (cutoff modulated by F
|
|||
- `processBlock()`: clears buffer, calls `updateVoiceParameters()`, then `synth.renderNextBlock()`
|
||||
- State save/restore via XML binary copy
|
||||
|
||||
### Parameters (27 total)
|
||||
### Parameters (32 total)
|
||||
|
||||
| ID | Name | Range | Default |
|
||||
|---------------------|------------------|--------------------|---------|
|
||||
|
|
@ -117,6 +117,11 @@ Osc1 + Osc2 → Amplitude Envelope × Velocity → Filter (cutoff modulated by F
|
|||
| pan | Pan | -1..1 | 0 |
|
||||
| drive | Drive | 1..5 (log) | 1.5 |
|
||||
| masterLevel | Master Level | 0..1 | 0.8 |
|
||||
| arpEnabled | Arp On | Choice(2) Off/On | 0 (Off) |
|
||||
| arpPattern | Arp Pattern | Choice(7) | 0 (Up) |
|
||||
| arpOctaves | Arp Octaves | Choice(3) 1/2/3 | 1 (2) |
|
||||
| arpDirection | Arp Direction | Choice(2) Up/Down | 0 (Up) |
|
||||
| arpRate | Arp Rate | Choice(6) 1/16..2 | 1 (1/8) |
|
||||
|
||||
## UI Architecture
|
||||
|
||||
|
|
@ -137,22 +142,31 @@ ChromaFlockEditor (juce::AudioProcessorEditor)
|
|||
|
||||
### Layout Dimensions (base coordinates, 1× scale)
|
||||
- **baseWidth**: 1660
|
||||
- **baseHeight**: 480 (needs to increase to ~620 for visualizers)
|
||||
- **Section Y origin**: 56px (below header line at y=48)
|
||||
- **Knob size**: 112×112
|
||||
- **Knob spacing**: 10px
|
||||
- **Label height**: 18px
|
||||
- **Combo box height**: 48px
|
||||
- **baseHeight**: 1028
|
||||
- **Header**: y 0..80 (logo + preset buttons + transpose + scale)
|
||||
- **Knob size**: 112×112 (main rows), 80×80 (FX/LFO rows)
|
||||
- **Visualizer row**: y=746, h=128 (VU meter, waveform w/ spectrum overlay, arpeggiator)
|
||||
- **Piano roll**: y=884, h=136
|
||||
|
||||
### Section Layout (from paint/drawSection calls)
|
||||
| Section | X | Y | W | H |
|
||||
|------------|------|-----|------|------|
|
||||
| OSC 1 | 10 | 52 | 498 | 200 |
|
||||
| OSC 2 | 520 | 52 | 620 | 200 |
|
||||
| FILTER | 1152 | 52 | 498 | 200 |
|
||||
| AMP ENV | 10 | 262 | 498 | 150 |
|
||||
| FILTER ENV | 520 | 262 | 498 | 150 |
|
||||
| MASTER | 1030 | 262 | 376 | 150 |
|
||||
| Section | X | Y | W | H |
|
||||
|--------------|------|-----|------|------|
|
||||
| OSC 1 | 10 | 80 | 498 | 200 |
|
||||
| OSC 2 | 518 | 80 | 624 | 200 |
|
||||
| FILTER | 1152 | 80 | 498 | 200 |
|
||||
| AMP ENV | 10 | 290 | 563 | 160 |
|
||||
| FILTER ENV | 583 | 290 | 502 | 160 |
|
||||
| MASTER | 1095 | 290| 555 | 160 |
|
||||
| DISTORTION | 10 | 460 | 510 | 115 |
|
||||
| COMPRESSION | 528 | 460 | 468 | 115 |
|
||||
| PANNING | 1004 | 460 | 268 | 115 |
|
||||
| LIMITER | 1280 | 460 | 370 | 115 |
|
||||
| LFO 1 | 10 | 586 | 540 | 150 |
|
||||
| LFO 2 | 560 | 586 | 530 | 150 |
|
||||
| DELAY | 1015 | 586 | 375 | 150 |
|
||||
| REVERB | 1395 | 586 | 255 | 150 |
|
||||
| ARPEGGIATOR | 860 | 746 | 790 | 128 |
|
||||
| PIANO ROLL | 10 | 884 | 1640 | 136 |
|
||||
|
||||
### Look-and-Feel (KnobLookAndFeel)
|
||||
- Inherits `juce::LookAndFeel_V4`
|
||||
|
|
@ -166,8 +180,9 @@ ChromaFlockEditor (juce::AudioProcessorEditor)
|
|||
- Knob labels: `0xff888888`, 10pt font
|
||||
- Text boxes: `0xffcccccc` text on `0xff1a1a1a` background
|
||||
|
||||
### ComboBox LAF (not yet implemented — currently uses default V4 look)
|
||||
- Needs custom 3D LAF similar to knobs (gradient, shadow, highlight)
|
||||
### ComboBox LAF (implemented — `ComboBoxLookAndFeel`)
|
||||
- Custom 3D LAF (gradient spotlight, drop shadow, gold arrow, highlighted popup items)
|
||||
- `drawPopupMenuItem` highlights the selected entry persistently and inverts text color on hover
|
||||
|
||||
### Header
|
||||
- Title: "CHROMAFLOCK" at (20, 8), 22pt bold gold
|
||||
|
|
@ -178,47 +193,28 @@ ChromaFlockEditor (juce::AudioProcessorEditor)
|
|||
- Scale label at (1520, 10), 42×20
|
||||
- Scale combo at (1566, 8), 80×24
|
||||
|
||||
## Planned / In-Progress Work
|
||||
## Implemented Features
|
||||
|
||||
### Filter Crash Fix (HIGH PRIORITY)
|
||||
**Problem**: Single `Filter filter` in `SubtractiveVoice` is called for both L and R in sequence (lines 87-88 of Voice.h). The filter's internal state (`stage[4]`) is shared/corrupted between channels, causing instability when filter env amount > 0.
|
||||
### Filter Crash Fix
|
||||
`SubtractiveVoice` now has separate `filter` (L) and `filterR` (R) instances; `startNote()` prepares both. Resolves the state-sharing crash when filter env amount > 0.
|
||||
|
||||
**Fix**: Add `Filter filterR` member. In `renderNextBlock()`:
|
||||
- Process left channel through `filter`
|
||||
- Process right channel through `filterR`
|
||||
- In `startNote()`, call `filter.prepare(sr)` AND `filterR.prepare(sr)`
|
||||
### Arpeggiator
|
||||
- New class `DSP/Arpeggiator.h` — tempo-synced, pure note-logic (no audio). Tracks held notes (press order for the As Played pattern), builds a pitch pool from held notes × octave range (1–3, clamped to MIDI range), and sequences it per pattern.
|
||||
- Patterns: Up, Down, UpDown, DownUp, Random, As Played, Chord.
|
||||
- `arpDirection` (Up/Down) controls whether octave copies extend above or below the root; sequential patterns always order by ascending pitch.
|
||||
- `arpRate` step divisions (beats): 1/16, 1/8, 1/4, 1/2, 1, 2 — tick period = `rateBeats * 60 / bpm`.
|
||||
- Routing (`PluginProcessor.cpp::processBlock`): when `arpEnabled`, MIDI note events feed the arp instead of the synth; the arp drives `synth.noteOn/noteOff` (with transpose) on each tick via `arpSampleCount` accumulation. Piano-roll `noteOn/noteOff` route through the same logic. When disabled, `arp.reset()` stops any sounding arp note and notes play normally.
|
||||
- UI: bottom-right section at (860, 746) titled ARPEGGIATOR with 5 combos (ON, PATTERN, OCTAVES, DIR, RATE).
|
||||
|
||||
### Scope Buffer for Visualizers (HIGH PRIORITY)
|
||||
**Need to add to `ChromaFlockProcessor`**:
|
||||
- `std::array<float, 1024> scopeBuffer` — ring buffer for waveform data
|
||||
- `std::atomic<int> scopeWritePos` — write position index
|
||||
- Write to buffer in `processBlock()` after `synth.renderNextBlock()`, interleaving L+R samples
|
||||
- Add getter method: `const float* getScopeData() const`
|
||||
- Add `juce::juce_dsp` to CMakeLists.txt for FFT
|
||||
### Visualizers
|
||||
- `WaveformDisplay` (bottom-left, 60×746, 790×128) draws the scope ring buffer waveform AND a semi-transparent `juce::dsp::FFT` spectrum overlay behind it (30 fps). Requires `juce::juce_dsp` linked and `#include <juce_dsp/juce_dsp.h>`.
|
||||
- Standalone `SpectrumAnalyzer` component was removed.
|
||||
|
||||
### Visualizer Components (HIGH PRIORITY)
|
||||
Two new classes to add to `PluginEditor.h`:
|
||||
|
||||
**WaveformDisplay** (juce::Component)
|
||||
- Reads from processor scope buffer
|
||||
- Draws waveform as line graph on dark background
|
||||
- Timer callback at 30fps
|
||||
- Positioned at bottom-left of UI (~190px tall, ~830px wide)
|
||||
|
||||
**SpectrumDisplay** (juce::Component)
|
||||
- Uses `juce::dsp::FFT` to compute spectrum from scope buffer
|
||||
- Draws vertical bars with HSV color gradient (cool→warm based on frequency)
|
||||
- Timer callback at 30fps
|
||||
- Positioned at bottom-right of UI (~190px tall, ~830px wide)
|
||||
|
||||
### Height Expansion
|
||||
- `baseHeight` must increase from 480 to ~620
|
||||
- Bottom sections (AMP ENV, FILTER ENV, MASTER) at y=262 currently end at y=412
|
||||
- Visualizers fill the remaining space (y=412 to y=612)
|
||||
- Editor window resized accordingly
|
||||
|
||||
### ComboBox 3D LAF (MEDIUM PRIORITY)
|
||||
Custom look-and-feel for combo boxes matching the 3D knob aesthetic.
|
||||
### Preset Level Normalization (`Tools/LevelNormalizer.cpp`)
|
||||
- Standalone JUCE console tool that renders every preset offline (C4, 8s, 44.1 kHz) and derives a new `masterLevel` for consistent loudness: peak normalized to 0.80, RMS-normalized to 0.15, final value = `min(gainPeak, gainRms) * current`, clamped to 0..1.
|
||||
- Build target: `cmake --build build --target level_normalizer`; run `./build/level_normalizer --apply` to rewrite the `masterLevel` entries in `Source/PresetManager.h` (reports a count mismatch and aborts if preset order/count ever drifts).
|
||||
- Applied 2026: all 136 presets re-balanced; `masterLevel` now ranges 0.325..1.0. Sustained/washy presets (heavy reverb/delay) get the largest cuts; quiet transient presets clamp at 1.0.
|
||||
- Caveat: noise-based presets render with slight run-to-run variance (`juce::Random` reseeds per process), so re-running can shift borderline values by a few percent.
|
||||
|
||||
## Common Pitfalls & Gotchas
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue