spectrograms 0.1.0

A focused Rust library for computing spectrograms with a simple, unified API
Documentation
# 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, &params, None)?;
```

**Batch processing (reuse plan):**
```rust
let planner = SpectrogramPlanner::new();
let mut plan = planner.linear_plan::<Power>(&params, None)?;
for signal in signals {
    let spec = plan.compute(&signal)?;
}
```

**Streaming/online:**
```rust
let mut plan = planner.linear_plan::<Power>(&params, 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)