# pyminidsp

**[Documentation](https://wooters.github.io/pyminidsp/)**

Python bindings to the [miniDSP](https://github.com/wooters/miniDSP) C library.

A comprehensive DSP toolkit providing signal generation, spectral analysis,
filtering, effects, and more. All functions accept and return NumPy arrays.

## Installation

```bash
pip install pyminidsp
```

Pre-built wheels are available for:

- **Linux** x86-64
- **macOS** ARM64 (Apple Silicon)
- Python 3.9, 3.10, 3.11, 3.12, 3.13

### Building from Source

If you need to build from source (e.g. unsupported platform or development):

**Prerequisites:**
- [FFTW3](http://www.fftw.org/) development headers
  - Ubuntu/Debian: `sudo apt install libfftw3-dev`
  - macOS: `brew install fftw`
- A C compiler (gcc or clang)

```bash
# Clone the miniDSP C library
git clone https://github.com/wooters/miniDSP.git

# Create virtual environment
uv venv

# Install pyminidsp (set MINIDSP_SRC to point to the C library)
MINIDSP_SRC=./miniDSP uv sync
```

For development (includes test dependencies):

```bash
MINIDSP_SRC=./miniDSP uv sync --extra dev
```

## Quick Start

```python
import pyminidsp as md

# Generate a 440 Hz sine wave (1 second at 44.1 kHz)
signal = md.sine_wave(44100, amplitude=1.0, freq=440.0, sample_rate=44100.0)

# Compute the magnitude spectrum
mag = md.magnitude_spectrum(signal)

# Compute MFCCs from a frame
coeffs = md.mfcc(signal[:512], sample_rate=44100.0, num_mels=26, num_coeffs=13)

# Apply a low-pass biquad filter
lpf = md.BiquadFilter(md.LPF, freq=1000.0, sample_rate=44100.0)
filtered = lpf.process_array(signal)

# Clean up FFT caches when done
md.shutdown()
```

## API Overview

### Signal Generators

| Function | Description |
|----------|-------------|
| `sine_wave(n, amplitude, freq, sample_rate)` | Pure sine tone |
| `white_noise(n, amplitude, seed)` | Gaussian white noise |
| `impulse(n, amplitude, position)` | Kronecker delta |
| `chirp_linear(n, amplitude, f_start, f_end, sample_rate)` | Linear frequency sweep |
| `chirp_log(n, amplitude, f_start, f_end, sample_rate)` | Logarithmic frequency sweep |
| `square_wave(n, amplitude, freq, sample_rate)` | Square wave |
| `sawtooth_wave(n, amplitude, freq, sample_rate)` | Sawtooth wave |
| `shepard_tone(n, amplitude, base_freq, sample_rate, rate, num_octaves)` | Shepard tone illusion |

### Spectral Analysis

| Function | Description |
|----------|-------------|
| `magnitude_spectrum(signal)` | One-sided magnitude spectrum via FFT |
| `power_spectral_density(signal)` | Periodogram PSD estimate |
| `phase_spectrum(signal)` | Phase spectrum in radians |
| `stft(signal, n, hop)` | Short-Time Fourier Transform |
| `stft_num_frames(signal_len, n, hop)` | Number of STFT frames |
| `mel_filterbank(n, sample_rate, num_mels)` | Mel-spaced triangular filterbank |
| `mel_energies(signal, sample_rate, num_mels)` | Mel-band energies |
| `mfcc(signal, sample_rate, num_mels, num_coeffs)` | Mel-frequency cepstral coefficients |

### Window Functions

| Function | Description |
|----------|-------------|
| `hann_window(n)` | Hann window |
| `hamming_window(n)` | Hamming window |
| `blackman_window(n)` | Blackman window |
| `rect_window(n)` | Rectangular window |
| `kaiser_window(n, beta)` | Kaiser window (configurable shape) |

### Math Utilities

| Function | Description |
|----------|-------------|
| `bessel_i0(x)` | Zeroth-order modified Bessel function I₀ |
| `sinc(x)` | Normalized sinc function |

### Signal Measurement

| Function | Description |
|----------|-------------|
| `dot(a, b)` | Dot product of two vectors |
| `entropy(signal)` | Normalised entropy of a distribution |
| `energy(signal)` | Signal energy |
| `power(signal)` / `power_db(signal)` | Signal power (linear / dB) |
| `rms(signal)` | Root mean square |

### Signal Analysis

| Function | Description |
|----------|-------------|
| `zero_crossing_rate(signal)` | Zero-crossing rate |
| `autocorrelation(signal, max_lag)` | Normalised autocorrelation |
| `peak_detect(signal, threshold, min_distance)` | Local maxima detection |
| `f0_autocorrelation(signal, sample_rate, min_freq, max_freq)` | F0 via autocorrelation |
| `f0_fft(signal, sample_rate, min_freq, max_freq)` | F0 via FFT peak picking |
| `mix(signal_a, signal_b, weight_a, weight_b)` | Weighted sum of two signals |

### Signal Scaling

| Function | Description |
|----------|-------------|
| `scale(value, old_min, old_max, new_min, new_max)` | Map a value from one range to another |
| `scale_vec(signal, old_min, old_max, new_min, new_max)` | Map vector elements between ranges |
| `fit_within_range(signal, new_min, new_max)` | Fit signal values within a range |
| `adjust_dblevel(signal, target_db)` | Automatic gain control |

### Effects & Filters

| Function | Description |
|----------|-------------|
| `delay_echo(signal, delay_samples, feedback, dry, wet)` | Echo effect |
| `tremolo(signal, rate_hz, depth, sample_rate)` | Amplitude modulation |
| `comb_reverb(signal, delay_samples, feedback, dry, wet)` | Comb-filter reverb |
| `convolution_time(signal, kernel)` | Time-domain convolution |
| `convolution_fft_ola(signal, kernel)` | FFT overlap-add convolution |
| `convolution_num_samples(signal_len, kernel_len)` | Output length of linear convolution |
| `moving_average(signal, window_len)` | Moving average filter |
| `fir_filter(signal, coeffs)` | FIR filter |
| `design_lowpass_fir(num_taps, cutoff, sr, beta)` | Design Kaiser-windowed lowpass FIR |
| `lowpass_brickwall(signal, cutoff_hz, sr)` | FFT-based ideal lowpass filter |
| `BiquadFilter(type, freq, sample_rate)` | IIR biquad filter (LPF/HPF/BPF/etc.) |

### Resampling

| Function | Description |
|----------|-------------|
| `resample(signal, in_rate, out_rate)` | Polyphase sinc resampler |
| `resample_output_len(input_len, in_rate, out_rate)` | Compute resampled output length |

### DTMF

| Function | Description |
|----------|-------------|
| `dtmf_generate(digits, sample_rate, tone_ms, pause_ms)` | Generate DTMF tones |
| `dtmf_detect(signal, sample_rate)` | Detect DTMF digits |
| `dtmf_signal_length(num_digits, sample_rate, tone_ms, pause_ms)` | Calculate samples needed for DTMF |

### Delay Estimation (GCC-PHAT)

| Function | Description |
|----------|-------------|
| `get_delay(sig_a, sig_b, margin, weighting)` | Delay between two signals |
| `get_multiple_delays(signals, margin, weighting)` | Delays from reference to M-1 signals |
| `gcc(sig_a, sig_b, weighting)` | Full cross-correlation |

### Audio Steganography

| Function | Description |
|----------|-------------|
| `steg_encode(host, message, sample_rate, method)` | Hide text in audio |
| `steg_decode(stego, sample_rate, method)` | Recover hidden text |
| `steg_encode_bytes(host, data, sample_rate, method)` | Hide binary data |
| `steg_decode_bytes(stego, sample_rate, method)` | Recover binary data |
| `steg_detect(signal, sample_rate)` | Detect steganography method |
| `steg_capacity(host, sample_rate, method)` | Maximum message length |

### Spectrogram Art

| Function | Description |
|----------|-------------|
| `spectrogram_text(text, freq_lo, freq_hi, duration, sample_rate)` | Text visible in spectrogram |

### Constants

| Constant | Description |
|----------|-------------|
| `LPF`, `HPF`, `BPF`, `NOTCH`, `PEQ`, `LSH`, `HSH` | Biquad filter types |
| `STEG_LSB`, `STEG_FREQ_BAND`, `STEG_SPECTEXT` | Steganography methods |
| `STEG_TYPE_TEXT`, `STEG_TYPE_BINARY` | Steganography payload types |
| `GCC_SIMP`, `GCC_PHAT` | GCC weighting modes |

## Running Tests

```bash
MINIDSP_SRC=./miniDSP uv run pytest tests/ -v
```

## License

MIT
