diff --git a/README.md b/README.md
index ab1c336..d131ad8 100644
--- a/README.md
+++ b/README.md
@@ -1,39 +1,59 @@
-```
-T E X T S T E P
-```
+
+
+# T E X T S T E P
+
+**A terminal-based step sequencer, drum machine, and synthesizer built entirely in Rust.**
-A terminal-based step sequencer, drum machine, and synthesizer built in Rust.
All DSP from scratch — no samples, no external audio libraries. Just your terminal and your speakers.
+[](https://www.rust-lang.org/)
+[](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
+[](https://www.apple.com/macos/)
+[](https://developer.apple.com/documentation/coreaudio)
+[](https://ratatui.rs/)
+[]()
+[]()
+

-## What Is This
+
+
+---
-TextStep is a TUI music production tool with:
+## Features
-- **8 drum tracks** — Kick, Snare, Closed HiHat, Open HiHat, Ride, Clap, Cowbell, Tom — each fully synthesized with 8 tweakable sound parameters
-- **Polyphonic synth** — 2 oscillators + sub, 2 ADSR envelopes, resonant filter, LFO with 6 waveforms, collapsible UI section
-- **32-step sequencer** with 10 patterns and 8 kit slots
-- **Send effects chain** — Schroeder reverb, tempo-synced filtered delay, tube saturator, SSL-style glue compressor
-- **Live performance** — drum pads, real-time recording, pattern queuing, per-pattern BPM
-- **Mouse support** — click the grid, drag parameters Ableton-style, audition sounds from the activity bar
-- **Project system** — save/load `.tsp` files, standalone kit export, preset browser
+- **8 Drum Tracks** — Kick, Snare, Closed HiHat, Open HiHat, Ride, Clap, Cowbell, Tom — each fully synthesized with 8 tweakable sound parameters
+- **Polyphonic Synth** — 2 oscillators + sub, 2 ADSR envelopes, resonant filter, LFO with 6 waveforms, collapsible UI section
+- **32-Step Sequencer** — 10 patterns and 8 kit slots with per-pattern BPM and swing
+- **Send Effects Chain** — Schroeder reverb, tempo-synced filtered delay, tube saturator, SSL-style glue compressor
+- **Live Performance** — drum pads, real-time recording, pattern queuing, per-pattern BPM
+- **Mouse Support** — click the grid, drag parameters Ableton-style, audition sounds from the activity bar
+- **Project System** — save/load `.tsp` files, standalone kit export, preset browser
+- **Spectrum Analyzer** — real-time FFT spectrum display and VU meter with 90s Hi-Fi LED aesthetic
Ships with **10 demo patterns** ready to play: House, Chicago House, Brit House, French House, Dirty House, Trance, Techno, Drum & Bass, Trap, and Moombahton.
-### Color Palette
+## Getting Started
-Hardware/synthwave aesthetic — amber for data, cyan for transport, pink for focus, gold for queued state. All rendered with UTF-8 block characters on a dark background.
+### Prerequisites
-## Build & Run
+- [Rust](https://www.rust-lang.org/tools/install) 1.70 or later
+- macOS with CoreAudio (primary target)
+### Build & Run
Requires Rust (1.70+). macOS with CoreAudio is the primary target; Linux with ALSA is also supported.
```bash
+# Clone the repository
+git clone https://github.com/illobo/textStep.git
+cd textStep
+
+# Build and run (release mode recommended for audio performance)
cargo build --release
cargo run --release
```
+### Run Tests
On Linux, install ALSA development headers first:
```bash
@@ -43,7 +63,7 @@ sudo apt-get install libasound2-dev # Debian/Ubuntu
Run the tests:
```bash
-cargo test
+cargo test # 23 tests, runs in <1s
```
### Pre-built Binaries
@@ -145,7 +165,28 @@ Projects are stored as JSON in `~/Library/Application Support/textstep/projects/
## Architecture
-Two-thread model:
+```
+┌──────────────────────────────────────────────────┐
+│ UI Thread │
+│ ┌────────────┐ ┌──────────┐ ┌──────────────┐ │
+│ │ ratatui │ │ crossterm│ │ App State │ │
+│ │ renderer │ │ events │ │ (app.rs) │ │
+│ └────────────┘ └──────────┘ └──────────────┘ │
+│ │
+│ crossbeam channels (lock-free, bounded) │
+│ ▼ UiToAudio ▲ AudioToUi │
+│ │
+│ Audio Thread │
+│ ┌────────────┐ ┌──────────┐ ┌──────────────┐ │
+│ │ Sequencer │ │ Voices │ │ Effects │ │
+│ │ Clock │ │ Drum+Syn │ │ Rev/Dly/Comp │ │
+│ └────────────┘ └──────────┘ └──────────────┘ │
+│ │ │
+│ cpal/CoreAudio │
+└──────────────────────────────────────────────────┘
+```
+
+**Two-thread model:**
- **UI thread** — ratatui + crossterm for rendering and input at ~60fps
- **Audio thread** — cpal/CoreAudio callback running all DSP per-sample
@@ -156,25 +197,112 @@ Communication is lock-free via bounded crossbeam channels. The audio thread neve
Every sound is synthesized in real-time with no external DSP dependencies:
-- **Drum voices**: TR-808/909-inspired kicks (sine + pitch envelope + resonant impulse), noise-blended snares, 6-oscillator metallic banks for hats and rides (using Mutable Instruments Plaits-style inharmonic ratios), ring-modulated open hats, bandpass claps, detuned pulse cowbells, FM toms
-- **Synth voice**: dual oscillators, sub, noise, two ADSR envelopes, resonant SVF filter, 6-waveform LFO
-- **Effects**: Schroeder/Freeverb reverb (4 comb + 2 allpass), tempo-synced filtered delay, asymmetric tube saturator, feedforward RMS glue compressor with soft knee
-- **Primitives**: 1-pole HP/LP filters, state-variable filter, xorshift32 noise, tanh waveshaping
+- **Drum voices** — TR-808/909-inspired kicks (sine + pitch envelope + resonant impulse), noise-blended snares, 6-oscillator metallic banks for hats and rides (Mutable Instruments Plaits-style inharmonic ratios), ring-modulated open hats, bandpass claps, detuned pulse cowbells, FM toms
+- **Synth voice** — dual oscillators, sub, noise, two ADSR envelopes, resonant SVF filter, 6-waveform LFO
+- **Effects** — Schroeder/Freeverb reverb (4 comb + 2 allpass), tempo-synced filtered delay, asymmetric tube saturator, feedforward RMS glue compressor with soft knee
+- **Primitives** — 1-pole HP/LP filters, state-variable filter, xorshift32 noise, tanh waveshaping
+
+### Source Map
+
+| Directory | Purpose |
+|-----------|---------|
+| `src/` | Core: entry point, app state, input handling, messages |
+| `src/ui/` | Rendering: layout, theme, grids, knobs, transport, spectrum |
+| `src/audio/` | DSP: engine, clock, drum/synth voices, effects, mixer, FFT |
+| `src/sequencer/` | Data: patterns, transport, project serialization |
+| `src/presets/` | Preset browser: drum/synth sounds and patterns by genre |
+
+### Color Palette
+
+Hardware/synthwave aesthetic — all rendered with UTF-8 block characters on a dark background.
+
+| Color | Hex | Usage |
+|-------|-----|-------|
+| Amber | `#e8a838` | Active steps, gauge fills, primary data |
+| Cyan | `#61dafb` | Transport state, beat LEDs, playhead, focused borders |
+| Pink | `#ff6b9d` | Focus/selection, current track, record |
+| Gold | `#ffd700` | Queued patterns, warnings |
+
+## Dependencies
+
+| Crate | Version | Purpose |
+|-------|---------|---------|
+| `ratatui` | 0.29 | TUI rendering framework |
+| `crossterm` | 0.28 | Terminal backend (events, raw mode) |
+| `cpal` | 0.15 | Cross-platform audio I/O (CoreAudio) |
+| `crossbeam-channel` | 0.5 | Lock-free bounded MPSC channels |
+| `serde` + `serde_json` | 1 | Project serialization (JSON) |
+
+No other runtime dependencies.
-### Dependencies
+## Project Structure
```
-ratatui 0.29 TUI rendering
-crossterm 0.28 Terminal backend
-cpal 0.15 Audio I/O (CoreAudio)
-crossbeam-channel 0.5 Lock-free channels
-serde + serde_json Project serialization
+textStep/
+├── src/
+│ ├── main.rs # Entry point, thread spawning, event loop
+│ ├── app.rs # Application state, focus, modals
+│ ├── keys.rs # Keyboard input handler
+│ ├── mouse.rs # Mouse input handler (mirrors UI layout)
+│ ├── messages.rs # Cross-thread message enums
+│ ├── params.rs # Shared effect parameters
+│ ├── ui/
+│ │ ├── mod.rs # Top-level render dispatch
+│ │ ├── layout.rs # Layout constants (single source of truth)
+│ │ ├── theme.rs # Color palette and styling
+│ │ ├── transport_bar.rs # Transport controls rendering
+│ │ ├── drum_grid.rs # 8×32 drum step grid
+│ │ ├── synth_grid.rs # Synth step grid with note display
+│ │ ├── knobs.rs # Drum parameter sliders
+│ │ ├── synth_knobs.rs # Synth parameter groups
+│ │ ├── waveform.rs # Spectrum analyzer / VU meter
+│ │ ├── splash.rs # Boot animation
+│ │ └── help_overlay.rs # Keyboard shortcut reference
+│ ├── audio/
+│ │ ├── engine.rs # Audio callback and voice management
+│ │ ├── clock.rs # Beat/step timing with swing
+│ │ ├── drum_voice.rs # 8 drum synth voices (all DSP)
+│ │ ├── synth_voice.rs # Polyphonic synth DSP
+│ │ ├── effects.rs # Reverb, delay, compressor, saturator
+│ │ ├── mixer.rs # Channel mixing, mute/solo
+│ │ ├── display_buffer.rs # Lock-free audio→UI waveform buffer
+│ │ └── fft.rs # FFT for spectrum analyzer
+│ ├── sequencer/
+│ │ ├── drum_pattern.rs # Drum pattern data (8 tracks × 32 steps)
+│ │ ├── synth_pattern.rs # Synth pattern data and parameters
+│ │ ├── transport.rs # Transport state (BPM, play, swing)
+│ │ └── project.rs # Project serialization (.tsp JSON)
+│ └── presets/
+│ ├── mod.rs # Preset browser state machine
+│ ├── drum_presets.rs # Drum sound presets
+│ ├── synth_presets.rs # Synth sound presets
+│ ├── pattern_presets.rs # Drum pattern presets by genre
+│ └── synth_pattern_presets.rs # Synth pattern presets
+├── assets/
+│ └── demo.gif # Demo recording
+├── Cargo.toml
+├── BLUEPRINT.md # Full technical documentation
+└── LICENSE # GPL v2
```
-No other runtime dependencies.
+## Contributing
+
+Contributions are welcome! Please ensure:
-See [BLUEPRINT.md](BLUEPRINT.md) for full technical documentation.
+1. All tests pass: `cargo test`
+2. Code compiles without warnings: `cargo build --release`
+3. Follow the existing code style and comment conventions
+
+See [BLUEPRINT.md](BLUEPRINT.md) for full technical documentation and architecture details.
## License
-[GNU General Public License v2.0](LICENSE)
+This project is licensed under the [GNU General Public License v2.0](LICENSE).
+
+---
+
+
+
+**Built with Rust** · **All DSP from scratch** · **Zero audio dependencies**
+
+
diff --git a/src/audio/drum_voice.rs b/src/audio/drum_voice.rs
index 38dcda9..208f83f 100644
--- a/src/audio/drum_voice.rs
+++ b/src/audio/drum_voice.rs
@@ -1,5 +1,6 @@
-// Drum voice trait and all 8 voice implementations
-// Hand-rolled DSP — no external crate dependencies.
+//! Drum voice DSP implementations (8 tracks).
+//! All synthesis is hand-rolled: oscillators, filters, envelopes, noise.
+//! No external DSP crate dependencies.
use crate::sequencer::drum_pattern::DrumTrackParams;
diff --git a/src/audio/effects.rs b/src/audio/effects.rs
index 46a8da3..88dc5cc 100644
--- a/src/audio/effects.rs
+++ b/src/audio/effects.rs
@@ -1,14 +1,11 @@
-// Audio effects: Reverb, Delay, Glue Compressor
-// Reverb/Delay ported from zicbox applyReverb.h with adaptations for Rust.
+//! Send effects: Schroeder reverb, tempo-synced delay, tube saturator, RMS glue compressor.
+//! Reverb/delay ported from zicbox applyReverb.h with adaptations for Rust.
-// ---------------------------------------------------------------------------
-// Reverb: 4 parallel comb filters + 2 series allpass filters
-// ---------------------------------------------------------------------------
-
-// Base lengths tuned for 44100 Hz; scaled by sample_rate / 44100.0
+// Base comb/allpass lengths tuned for 44100 Hz; scaled by sample_rate / 44100.0
const BASE_COMB_LENGTHS: [usize; 4] = [1117, 1301, 1571, 1787];
const BASE_ALLPASS_LENGTHS: [usize; 2] = [557, 443];
+/// Schroeder reverb: 4 parallel comb filters feeding 2 series allpass filters.
pub struct ReverbEffect {
comb_buf: Vec,
comb_lengths: [usize; 4],
diff --git a/src/audio/engine.rs b/src/audio/engine.rs
index bf5c353..daadd02 100644
--- a/src/audio/engine.rs
+++ b/src/audio/engine.rs
@@ -1,4 +1,4 @@
-// AudioEngine: cpal callback, voice mixing, send effects, message handling
+//! Audio engine: cpal callback, voice management, LFO, message dispatch.
use std::sync::Arc;
@@ -16,9 +16,8 @@ use crate::sequencer::drum_pattern::{DrumPattern, DrumTrackId, NUM_DRUM_TRACKS};
use crate::sequencer::synth_pattern::{SynthPattern, LFO_DEST_FIELDS, lfo_division_multiplier};
use crate::sequencer::transport::{PlayState, Transport};
-// ---------------------------------------------------------------------------
-// LFO: tempo-synced, global (shared across synth voices)
-// ---------------------------------------------------------------------------
+/// Tempo-synced global LFO shared across synth voices.
+/// Supports sine, triangle, saw (up/down), square, and exponential decay waveforms.
struct Lfo {
phase: f64,
}
@@ -76,6 +75,8 @@ impl Lfo {
}
}
+/// Core audio engine running on the audio thread.
+/// Owns all voices, effects, the sequencer clock, and handles messages from the UI thread.
pub struct AudioEngine {
sample_rate: f64,
clock: SequencerClock,
diff --git a/src/audio/synth_voice.rs b/src/audio/synth_voice.rs
index 542da12..f0ea39c 100644
--- a/src/audio/synth_voice.rs
+++ b/src/audio/synth_voice.rs
@@ -1,5 +1,5 @@
-// Paraphonic 2-oscillator synthesizer voice with sub oscillator.
-// Hand-rolled DSP — no external crate dependencies.
+//! Polyphonic synth voice: dual oscillators, sub, noise, 2 ADSR envelopes,
+//! SVF filter, tempo-synced LFO. Hand-rolled DSP, no external crate dependencies.
use crate::sequencer::synth_pattern::SynthParams;
@@ -368,9 +368,8 @@ impl Filter24dB {
}
}
-// ---------------------------------------------------------------------------
-// SynthVoice — paraphonic 2-oscillator + sub
-// ---------------------------------------------------------------------------
+/// Paraphonic synthesizer voice with dual oscillators, sub oscillator,
+/// two ADSR envelopes (amplitude + filter), and a state-variable filter.
pub struct SynthVoice {
sample_rate: f32,
osc1: Oscillator,
diff --git a/src/keys.rs b/src/keys.rs
index 005aba4..f52364b 100644
--- a/src/keys.rs
+++ b/src/keys.rs
@@ -1,4 +1,4 @@
-// Keyboard input mapping
+//! Keyboard event handler: maps key events to app state changes.
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
@@ -10,6 +10,7 @@ use crate::sequencer::project::{NUM_KITS, NUM_PATTERNS};
use crate::sequencer::synth_pattern::{SynthControlField, MAX_STEPS as SYNTH_MAX_STEPS};
use crate::sequencer::transport::{PlayState, RecordMode};
+/// How much a parameter changes per arrow-key press (0.02 = 50 steps for full range).
const PARAM_INCREMENT: f32 = 0.02;
/// Map drum pad keys (bottom row) to track indices.
diff --git a/src/main.rs b/src/main.rs
index 05093c6..0c2491a 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,3 +1,5 @@
+//! Entry point: spawns audio thread, runs UI event loop with ~60fps rendering.
+
mod app;
mod audio;
mod keys;
@@ -18,6 +20,9 @@ use crossterm::terminal::{self, EnterAlternateScreen, LeaveAlternateScreen};
use ratatui::backend::CrosstermBackend;
use ratatui::Terminal;
+/// Sets up cross-thread channels, starts the audio stream, initializes the
+/// terminal, and enters the main UI loop. The audio stream is kept alive
+/// until this function returns.
fn main() -> io::Result<()> {
// Create channels for UI <-> Audio communication
let (tx_to_audio, rx_from_ui) = crossbeam_channel::bounded(64);
diff --git a/src/messages.rs b/src/messages.rs
index 0aae4bd..d141bde 100644
--- a/src/messages.rs
+++ b/src/messages.rs
@@ -1,10 +1,12 @@
-// UiToAudio and AudioToUi message enums
+//! Lock-free message types for cross-thread communication between UI and audio.
use crate::params::EffectParams;
use crate::sequencer::drum_pattern::{DrumPattern, DrumTrackId};
use crate::sequencer::synth_pattern::SynthPattern;
use crate::sequencer::transport::Transport;
+/// Commands sent from the UI thread to the audio thread.
+/// Sent via a bounded crossbeam channel (capacity 64).
pub enum UiToAudio {
SetTransport(Transport),
SetDrumPattern(DrumPattern),
@@ -15,6 +17,8 @@ pub enum UiToAudio {
ReleaseSynth, // release synth envelopes
}
+/// Notifications sent from the audio thread back to the UI.
+/// Sent via a bounded crossbeam channel (capacity 16).
pub enum AudioToUi {
PlaybackPosition {
global_step: usize,
diff --git a/src/mouse.rs b/src/mouse.rs
index 26effa6..d8a041a 100644
--- a/src/mouse.rs
+++ b/src/mouse.rs
@@ -1,4 +1,5 @@
-// Mouse input handling
+//! Mouse event handler. Hit-testing layout MUST mirror ui/mod.rs — uses shared
+//! constants from ui/layout.rs.
use std::time::Instant;
@@ -18,7 +19,8 @@ const DOUBLE_CLICK_MS: u128 = 300;
/// 0.04 = full range in ~25 rows of drag.
const DRAG_SENSITIVITY: f32 = 0.04;
-/// Main mouse event handler.
+/// Routes mouse events (click, scroll, drag) to the appropriate UI section.
+/// Layout rectangles are recomputed from `term_size` to match rendering.
pub fn handle_mouse(app: &mut App, event: MouseEvent, term_size: Rect) {
// Ignore mouse during splash or modals
if app.ui.splash.phase != crate::app::SplashPhase::Done {
diff --git a/src/params.rs b/src/params.rs
index da4bd6f..4732660 100644
--- a/src/params.rs
+++ b/src/params.rs
@@ -1,7 +1,9 @@
-// Shared parameter types
+//! Shared effect parameters used by both UI and audio engine.
use serde::{Deserialize, Serialize};
+/// Master effect and mix bus parameters, serialized with each project.
+/// All values are normalized to 0.0..1.0.
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub struct EffectParams {
pub reverb_amount: f32, // 0.0-1.0: feedback/decay
diff --git a/src/presets/drum_presets.rs b/src/presets/drum_presets.rs
index 8d0ebb0..d85b74a 100644
--- a/src/presets/drum_presets.rs
+++ b/src/presets/drum_presets.rs
@@ -1,3 +1,5 @@
+//! Hand-crafted drum sound presets organized by category (808, 909, Acoustic, Lo-Fi, etc.).
+
use crate::sequencer::drum_pattern::DrumTrackId;
use crate::sequencer::project::DrumSoundParams;
use super::DrumSoundPreset;
diff --git a/src/presets/mod.rs b/src/presets/mod.rs
index 7f5b4a8..d839a57 100644
--- a/src/presets/mod.rs
+++ b/src/presets/mod.rs
@@ -1,3 +1,6 @@
+//! Preset browser: categories, merge modes, and state machine for browsing
+//! drum/synth sound and pattern presets.
+
pub mod drum_presets;
pub mod pattern_presets;
pub mod synth_pattern_presets;
@@ -24,8 +27,7 @@ pub struct SynthSoundPreset {
pub params: SynthParams,
}
-// ── Preset Browser State ─────────────────────────────────────────────────────
-
+/// Which kind of preset the browser is targeting.
#[derive(Clone, Debug, PartialEq)]
pub enum PresetTarget {
DrumSound(usize), // track index
@@ -40,6 +42,7 @@ pub enum PatternMergeMode {
Layer,
}
+/// State machine for the preset browser modal: tracks target, category, and selection index.
#[derive(Clone, Debug, PartialEq)]
pub struct PresetBrowserState {
pub target: PresetTarget,
diff --git a/src/presets/pattern_presets.rs b/src/presets/pattern_presets.rs
index 49199c9..0e59b3a 100644
--- a/src/presets/pattern_presets.rs
+++ b/src/presets/pattern_presets.rs
@@ -1,3 +1,5 @@
+//! Drum pattern presets: genre-specific step patterns (House, Techno, DnB, etc.).
+
use crate::sequencer::drum_pattern::NUM_DRUM_TRACKS;
pub struct PatternPreset {
diff --git a/src/presets/synth_pattern_presets.rs b/src/presets/synth_pattern_presets.rs
index a58d53e..188c68a 100644
--- a/src/presets/synth_pattern_presets.rs
+++ b/src/presets/synth_pattern_presets.rs
@@ -1,3 +1,5 @@
+//! Synth pattern presets: melodic sequences organized by genre and style.
+
use crate::sequencer::synth_pattern::MAX_STEPS;
pub struct SynthPatternPreset {
diff --git a/src/presets/synth_presets.rs b/src/presets/synth_presets.rs
index e41afa2..c181a5e 100644
--- a/src/presets/synth_presets.rs
+++ b/src/presets/synth_presets.rs
@@ -1,3 +1,5 @@
+//! Synth sound presets: pads, leads, basses, keys, and FX organized by category.
+
use crate::sequencer::synth_pattern::SynthParams;
use super::SynthSoundPreset;
diff --git a/src/sequencer/drum_pattern.rs b/src/sequencer/drum_pattern.rs
index 01bb02f..22c2c3e 100644
--- a/src/sequencer/drum_pattern.rs
+++ b/src/sequencer/drum_pattern.rs
@@ -1,8 +1,9 @@
-// DrumPattern: 8 tracks x 32 steps
+//! Drum pattern data: 8 tracks x 32 steps with per-track synthesis parameters.
pub const NUM_DRUM_TRACKS: usize = 8;
pub const MAX_STEPS: usize = 32;
+/// Identifies one of the 8 drum tracks (Kick, Snare, CHH, OHH, Ride, Clap, Cowbell, Tom).
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum DrumTrackId {
Kick = 0,
@@ -135,6 +136,7 @@ pub const TRACK_IDS: [DrumTrackId; NUM_DRUM_TRACKS] = [
DrumTrackId::Tom,
];
+/// A drum pattern: 8 tracks of boolean step data plus per-track synthesis parameters.
#[derive(Clone, Copy, Debug)]
pub struct DrumPattern {
pub steps: [[bool; MAX_STEPS]; NUM_DRUM_TRACKS],
diff --git a/src/sequencer/project.rs b/src/sequencer/project.rs
index d19e6ee..a9ead3f 100644
--- a/src/sequencer/project.rs
+++ b/src/sequencer/project.rs
@@ -1,4 +1,5 @@
-// Project: bundles a drum kit + 10 patterns into a single saveable unit.
+//! Project serialization: bundles kit sounds, synth parameters, and 10 patterns
+//! as JSON (.tsp files).
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
diff --git a/src/sequencer/synth_pattern.rs b/src/sequencer/synth_pattern.rs
index dcb6b21..10ff9eb 100644
--- a/src/sequencer/synth_pattern.rs
+++ b/src/sequencer/synth_pattern.rs
@@ -1,11 +1,10 @@
-// SynthPattern: monophonic synth sequencer data model
+//! Synth pattern data: 32 steps with note/velocity/length, plus full synth parameter set.
use serde::{Serialize, Deserialize};
pub const MAX_STEPS: usize = 32;
-// --- SynthStep ---
-
+/// A single synth sequencer step: MIDI note, velocity (0 = off), and length in steps.
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub struct SynthStep {
pub note: u8, // MIDI note number (0-127). 60 = C4
@@ -42,8 +41,8 @@ impl SynthStep {
}
}
-// --- SynthParams ---
-
+/// Complete synth parameter set: dual oscillators, sub, two ADSR envelopes,
+/// filter with its own envelope, LFO routing, and send effect levels.
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub struct SynthParams {
// Oscillator 1
diff --git a/src/sequencer/transport.rs b/src/sequencer/transport.rs
index e7686e2..8e37174 100644
--- a/src/sequencer/transport.rs
+++ b/src/sequencer/transport.rs
@@ -1,5 +1,6 @@
-// Transport state, BPM, loop config
+//! Transport state: play/pause/stop, BPM, loop configuration, swing amount.
+/// Sequencer playback state.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum PlayState {
Stopped,
@@ -13,6 +14,7 @@ pub enum RecordMode {
On,
}
+/// Per-section loop length settings (8/16/24/32 steps for drum and synth independently).
#[derive(Clone, Copy, Debug)]
pub struct LoopConfig {
pub enabled: bool,
@@ -30,6 +32,7 @@ impl Default for LoopConfig {
}
}
+/// Master transport: play state, tempo (BPM), record mode, loop config, and swing.
#[derive(Clone, Copy, Debug)]
pub struct Transport {
pub state: PlayState,
diff --git a/src/ui/drum_grid.rs b/src/ui/drum_grid.rs
index 25ebcd8..74c5e1a 100644
--- a/src/ui/drum_grid.rs
+++ b/src/ui/drum_grid.rs
@@ -1,4 +1,4 @@
-// Drum step grid widget (8x32) with spaced squares and hardware borders
+//! Drum step grid: 8 tracks x 32 steps with playhead, cursor, and mute/solo indicators.
use ratatui::Frame;
use ratatui::layout::Rect;
@@ -14,6 +14,8 @@ use crate::ui::theme;
/// Track name column: ">Cowbell " or " Cowbell " = 9 chars (padded to longest name)
const NAME_WIDTH: usize = 9;
+/// Renders the 8-track drum grid with step indicators, track names,
+/// mute/solo buttons, playhead column, and cursor highlight.
pub fn render_drum_grid(f: &mut Frame, area: Rect, app: &App) {
let focused_grid = app.ui.focus == FocusSection::DrumGrid;
let border_style = theme::focus_border_style(focused_grid);
diff --git a/src/ui/help_overlay.rs b/src/ui/help_overlay.rs
index 1e6fed4..e7038e1 100644
--- a/src/ui/help_overlay.rs
+++ b/src/ui/help_overlay.rs
@@ -1,4 +1,4 @@
-// Key binding reference panel (3-column layout, renders inline below sequencer)
+//! Help overlay: 3-column keyboard shortcut reference panel.
use ratatui::Frame;
use ratatui::layout::Rect;
@@ -54,6 +54,8 @@ fn row3<'a>(
Line::from(v)
}
+/// Renders the help panel with three columns of key-binding entries
+/// covering transport, navigation, editing, and pattern management shortcuts.
pub fn render_help(f: &mut Frame, area: Rect) {
let block = Block::default()
.title(" Key Bindings (? to close) ")
diff --git a/src/ui/knobs.rs b/src/ui/knobs.rs
index c664ce8..19b5c3f 100644
--- a/src/ui/knobs.rs
+++ b/src/ui/knobs.rs
@@ -1,4 +1,4 @@
-// Knobs panel: vertical slider bars for the selected drum track's parameters
+//! Drum parameter panel: vertical slider bars for the selected track's sound parameters.
use ratatui::Frame;
use ratatui::layout::Rect;
@@ -19,6 +19,8 @@ const SLIDER_LABELS: [&str; 11] = [
/// Number of vertical bar rows in each slider.
const BAR_ROWS: usize = 5;
+/// Renders vertical slider columns for the currently selected drum track's
+/// parameters (Tune, Sweep, Color, Snap, Filter, Drive, Decay, Volume, etc.).
pub fn render_knobs(f: &mut Frame, area: Rect, app: &App) {
let focused = app.ui.focus == FocusSection::Knobs;
let border_style = theme::focus_border_style(focused);
diff --git a/src/ui/mod.rs b/src/ui/mod.rs
index 0e7b92b..2c369d9 100644
--- a/src/ui/mod.rs
+++ b/src/ui/mod.rs
@@ -1,3 +1,5 @@
+//! Top-level render dispatch: computes layout, renders all sections, handles modals.
+
pub mod drum_grid;
pub mod help_overlay;
pub mod knobs;
diff --git a/src/ui/splash.rs b/src/ui/splash.rs
index e68d878..3aa5a22 100644
--- a/src/ui/splash.rs
+++ b/src/ui/splash.rs
@@ -1,4 +1,4 @@
-// Splash screen: Hardware-themed boot sequence with amber/cyan palette
+//! Splash screen animation: typewriter logo, matrix rain reveal, boot sequence.
use ratatui::Frame;
use ratatui::layout::Rect;
@@ -54,6 +54,8 @@ fn logo_total_chars() -> usize {
line.chars().count() * LOGO_ROWS
}
+/// Draws the splash screen: animated block-letter logo with typewriter effect,
+/// matrix rain columns, and a simulated boot log sequence.
pub fn render_splash(f: &mut Frame, area: Rect, splash: &SplashState) {
match splash.phase {
SplashPhase::SlideIn | SplashPhase::Hold => {
diff --git a/src/ui/synth_grid.rs b/src/ui/synth_grid.rs
index 02f1ea3..c037df2 100644
--- a/src/ui/synth_grid.rs
+++ b/src/ui/synth_grid.rs
@@ -1,4 +1,4 @@
-// Synth step sequencer: 32-step row with note names
+//! Synth step grid: 32-step note sequencer with multi-step note visualization.
use ratatui::Frame;
use ratatui::layout::Rect;
@@ -14,6 +14,8 @@ use crate::ui::theme;
/// Track name column width, matching drum grid (padded to longest name "Cowbell")
const NAME_WIDTH: usize = 9;
+/// Renders the synth step row with note names, velocity shading,
+/// multi-step continuation bars, and playhead/cursor highlights.
pub fn render_synth_grid(f: &mut Frame, area: Rect, app: &App) {
let focused = app.ui.focus == FocusSection::SynthGrid;
let border_style = theme::focus_border_style(focused);
@@ -84,7 +86,10 @@ pub fn render_synth_grid(f: &mut Frame, area: Rect, app: &App) {
let playback_step = app.ui.synth_playback_step;
let is_playing = app.transport.state == PlayState::Playing;
- let mut covered_until: Option = None; // end step index of a multi-step note
+ // Multi-step note tracking: `covered_until` holds the last step index covered by
+ // the current note's length. Steps within that range render as continuation bars
+ // (`is_continuation`) instead of new note heads, with an end-cap on the final step.
+ let mut covered_until: Option = None;
let mut cover_bg: Option = None; // background color for continuation cells
for s in 0..MAX_STEPS {
diff --git a/src/ui/synth_knobs.rs b/src/ui/synth_knobs.rs
index 5563819..b3834fe 100644
--- a/src/ui/synth_knobs.rs
+++ b/src/ui/synth_knobs.rs
@@ -1,4 +1,4 @@
-// Synth knobs panel: grouped layout with vertical sliders and packed ADSR bars
+//! Synth parameter panel: grouped knobs for OSC, ENV, FILT, LFO, and AMP sections.
use ratatui::Frame;
use ratatui::layout::{Constraint, Direction, Layout, Rect};
@@ -14,6 +14,9 @@ use crate::ui::theme;
const SLIDER_ROWS: usize = 5;
// ── Row group definitions ────────────────────────────────────────────────────
+// Each constant below lists the `SynthControlField` variants for one sub-section.
+// These arrays drive both the rendering order in `render_synth_knobs` and the
+// keyboard/mouse navigation mapping in `mouse.rs`, so both files must stay in sync.
const OSC1_SLIDERS: &[SynthControlField] = &[
SynthControlField::Osc1Tune,
@@ -61,6 +64,8 @@ const ADSR_LABELS: &[&str] = &["A", "D", "S", "R"];
// ── Main render ──────────────────────────────────────────────────────────────
+/// Renders the synth parameter panel with grouped slider/ADSR sections
+/// laid out in four row groups: OSC, ENV+FILT, LFO, and AMP.
pub fn render_synth_knobs(f: &mut Frame, area: Rect, app: &App) {
let focused = app.ui.focus == FocusSection::SynthControls;
let border_style = theme::focus_border_style(focused);
diff --git a/src/ui/transport_bar.rs b/src/ui/transport_bar.rs
index ebe4ed7..447ae38 100644
--- a/src/ui/transport_bar.rs
+++ b/src/ui/transport_bar.rs
@@ -1,5 +1,4 @@
-// Play/Pause/Stop, BPM display, LED, loop controls, pattern indicator
-// Hardware/Synthwave themed transport bar
+//! Transport control bar: play/pause state, BPM, beat LEDs, swing, pattern/kit selectors.
use ratatui::Frame;
use ratatui::layout::Rect;
@@ -15,6 +14,8 @@ use crate::ui::theme;
const PATTERN_KEYS: [char; 10] = ['q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p'];
const KIT_KEYS: [char; 8] = ['1', '2', '3', '4', '5', '6', '7', '8'];
+/// Draws the transport bar: play state, BPM, beat LEDs, swing, record toggle,
+/// pattern/kit selectors, loop indicators, and master level gauges.
pub fn render_transport(f: &mut Frame, area: Rect, app: &App) {
let focused = app.ui.focus == FocusSection::Transport;
let border_style = theme::focus_border_style(focused);