# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Common Commands
### Building and Testing
```bash
# Check code (fast, no build)
cargo check
# Build with default features (FFTW backend)
cargo build
# Build with RealFFT backend (pure Rust, no system dependencies)
cargo build --no-default-features --features realfft
# Run all tests (default FFTW backend)
cargo test
# Run tests with RealFFT backend
cargo test --no-default-features --features realfft
# Run a specific test
cargo test <test_name>
# Run doc tests
cargo test --doc
# Build examples
cargo build --examples
# Run a specific example
cargo run --example basic_linear
```
### Linting and Formatting
```bash
# Run clippy with default features
cargo clippy --all-targets --all-features -- -D warnings
# Run clippy with RealFFT backend
cargo clippy --all-targets --no-default-features --features realfft -- -D warnings
# Check formatting
cargo fmt --all -- --check
# Auto-format code
cargo fmt
```
### Documentation
```bash
# Build documentation
cargo doc --no-deps --all-features
# Build and open documentation
cargo doc --no-deps --all-features --open
```
## Architecture Overview
### Core Design Philosophy
This library is built around a **plan-based computation model** for spectrograms. The key insight is that FFT plans can be expensive to create but cheap to reuse, so the library separates plan creation from execution.
### Type-Level Architecture
The library uses Rust's type system to guarantee correctness at compile-time. The main type is:
```rust
Spectrogram<FreqScale, AmpScale>
```
Where:
- `FreqScale` determines frequency representation: `LinearHz`, `Mel`, `Erb`, `LogHz`, `Cqt`
- `AmpScale` determines amplitude scaling: `Power`, `Magnitude`, `Decibels`
Type aliases like `LinearPowerSpectrogram = Spectrogram<LinearHz, Power>` provide ergonomic names.
### Key Components
1. **FFT Backend Layer** (`src/fft_backend.rs`)
- Trait-based abstraction over FFT implementations
- Two backends: FFTW (C library, fastest) or RealFFT (pure Rust)
- Enforced at compile-time via mutually exclusive feature flags
- Key traits: `R2cPlan` (real-to-complex), `C2rPlan` (complex-to-real)
- Thread-safe plan caching via `HashMap<usize, Arc<Plan>>`
2. **STFT Engine** (in `src/spectrogram.rs`)
- `StftPlan`: Handles windowing, framing, and FFT execution
- Owns the FFT plan, window samples, and frame buffer
- Zero-copy design with pre-allocated workspace buffers
- Supports centered framing (padding) and various window functions
3. **Frequency Mapping** (in `src/spectrogram.rs`)
- `FrequencyMapping<FreqScale>`: Transforms FFT bins to desired frequency scale
- `Identity`: Linear frequency (direct FFT bins)
- `Mel`: Mel-scale filterbank using triangular filters
- `Erb`: ERB-scale (gammatone) filterbank
- `Cqt`: Constant-Q Transform (requires inverse FFT)
- Each mapping knows its output dimensionality
4. **Amplitude Scaling** (in `src/spectrogram.rs`)
- `AmplitudeScaling<AmpScale>`: Converts complex spectrum to desired amplitude
- Power: |X|²
- Magnitude: |X|
- Decibels: 10·log₁₀(power) with configurable floor
5. **Plan + Compute Pattern** (in `src/spectrogram.rs`)
- `SpectrogramPlanner`: Factory for creating reusable plans
- `SpectrogramPlan<FreqScale, AmpScale>`: The compiled, reusable execution object
- Plans own all resources: FFT plan, window, mapping matrices, workspace buffers
- `compute()`: One-shot computation (creates plan internally)
- `compute_frame()`: For streaming/online processing
- `compute_into()`: For pre-allocated output buffers
6. **Workspace Management**
- Pre-allocated buffers to avoid per-frame allocation
- `Workspace` struct contains: `frame` (windowed input), `fft_out` (complex FFT), `spectrum` (magnitude/power), `mapped` (after frequency mapping)
- Dynamically resized only when needed
### Data Flow
```
Input samples
↓
STFT: framing + windowing + FFT per frame
↓
Complex spectrum (frequency bins × frames)
↓
Frequency mapping (identity/mel/erb/cqt)
↓
Amplitude scaling (power/magnitude/dB)
↓
Spectrogram<FreqScale, AmpScale>
```
### Module Structure
- `src/lib.rs`: Public API, feature flag enforcement, module exports
- `src/spectrogram.rs`: Core spectrogram types, planner, plan, STFT logic, frequency mappings
- `src/fft_backend.rs`: FFT abstraction traits and backend implementations (FFTW, RealFFT)
- `src/window.rs`: Window function implementations (Hanning, Hamming, Blackman, Kaiser, Gaussian)
- `src/error.rs`: Error types using `thiserror`
- `src/mfcc.rs`: Mel-Frequency Cepstral Coefficients computation
- `src/chroma.rs`: Chromagram (pitch class profiles) computation
- `src/cqt.rs`: Constant-Q Transform implementation
- `src/erb.rs`: ERB/Gammatone filterbank parameters
### Feature Flags
The library **requires exactly one FFT backend**:
- `fftw` (default): Uses system FFTW library, fastest but requires C dependency
- `realfft`: Pure Rust, slower but works everywhere
Additional features:
- `serde`: Enable serialization support for parameters
This is enforced via `compile_error!` in `src/lib.rs`.
## Development Guidelines
### FFT Backend Considerations
- Always test with **both** backends when making changes to FFT-related code
- FFTW requires system library: `libfftw3-dev` on Ubuntu, `brew install fftw` on macOS
- FFTW plan creation is **not thread-safe** (uses global mutex `FFTW_PLANNER_LOCK`)
- Both backends normalize inverse FFT by dividing by `n_fft`
### Performance Characteristics
- FFT sizes that are powers of 2 are significantly faster
- Plan reuse is critical for batch processing (can be 10-100x faster)
- Workspace buffers eliminate per-frame allocation overhead
- CQT is the most expensive operation (requires inverse FFT per bin)
### Testing Strategy
- Tests are in `tests/` directory, organized by feature:
- `spectrogram_tests.rs`: Core spectrogram computation
- `params_tests.rs`: Parameter validation
- `window_tests.rs`: Window function correctness
- `mfcc_tests.rs`, `chroma_tests.rs`, `cqt_tests.rs`: Feature-specific tests
- `streaming_tests.rs`: Online/frame-by-frame processing
- `builder_tests.rs`: Builder pattern tests
- CI tests both backends on Linux, macOS, Windows
- Doc tests serve as executable examples
### Common Patterns
**One-shot computation:**
```rust
let spec = LinearPowerSpectrogram::compute(&samples, ¶ms, None)?;
```
**Batch processing (reuse plan):**
```rust
let planner = SpectrogramPlanner::new();
let mut plan = planner.linear_plan::<Power>(¶ms, None)?;
for signal in signals {
let spec = plan.compute(&signal)?;
}
```
**Streaming/online:**
```rust
let mut plan = planner.linear_plan::<Power>(¶ms, None)?;
for frame_idx in 0..n_frames {
let frame_data = plan.compute_frame(&samples, frame_idx)?;
}
```
### Error Handling
Errors are defined in `src/error.rs` using `thiserror`:
- `InvalidInput`: Bad parameters (negative values, zero FFT size, etc.)
- `DimensionMismatch`: Array size mismatches
- `FftBackend`: FFT backend errors
- `InvalidFrequency`: Frequency range issues
### Adding New Features
When adding new frequency scales or amplitude scales:
1. Implement the appropriate marker trait (e.g., `FreqScaleSpec`, `AmpScaleSpec`)
2. Add frequency mapping logic to `FrequencyMapping`
3. Add type aliases to `src/lib.rs` for ergonomics
4. Update `SpectrogramPlanner` with a new plan creation method
5. Add comprehensive tests and examples
## Notes
- The library focuses solely on computation, not visualization or I/O
- All audio is assumed to be mono f64 samples; users must handle channel conversion
- Time axis uses seconds, frequency axis uses Hz (or scale-specific units for Mel/ERB)
- The library uses `ndarray::Array2` for matrix storage (row-major, Rust-native)