#dsp #stereo #binaural #audio-dsp #crossfeed

no-std bs2b

Bauer stereophonic-to-binaural (bs2b) DSP library in Rust

4 releases

Uses new Rust 2024

0.2.0 Feb 9, 2026
0.1.2 Feb 9, 2026
0.1.1 Feb 9, 2026
0.1.0 Feb 8, 2026

#241 in Audio

MIT license

41KB
896 lines

bs2b

A modern Rust implementation of the Bauer stereophonic-to-binaural (bs2b) crossfeed DSP.

This crate ports the reference bs2b algorithm into an idiomatic, type-safe API for real-time and offline audio processing.

Features

  • bs2b-compatible filter math and crossfeed behavior.
  • Stateful processor suitable for streaming audio.
  • Interleaved and planar stereo processing APIs.
  • Supports common PCM sample formats: f32, f64, i32, u32, i16, u16, i8, u8.
  • Built-in preset levels: DEFAULT, CMOY, JMEIER.
  • Benchmarks (Criterion) and tests included.

Feature flags:

  • std (default): enables std::error::Error integration for Bs2bError.
  • streaming: enables optional adapters for callback/iterator streaming APIs.
  • disabling default features (default-features = false) builds in no_std mode.

Installation

cargo add bs2b

no_std mode:

[dependencies]
bs2b = { version = "0.2", default-features = false }

Quick Start

use bs2b::{Bs2b, Level};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut dsp = Bs2b::new(48_000, Level::DEFAULT)?;

    // Interleaved stereo samples: L, R, L, R, ...
    let mut interleaved = vec![0.1_f32, -0.1, 0.25, -0.2, 0.3, -0.15];
    dsp.process_interleaved(&mut interleaved)?;

    Ok(())
}

API Guidelines

  • Use one Bs2b instance per stereo stream. The processor is stateful.
  • Call set_sample_rate if stream sample rate changes. This clears internal history.
  • Call clear after seeks, discontinuities, or stream restarts.
  • Use process_interleaved for contiguous LR data; use process_planar for split channels.
  • If your pipeline uses unsigned PCM, this crate applies the same signed-bias handling as the C reference implementation.

Choosing a Level

  • Level::DEFAULT: Closest to virtual speakers at ±30° azimuth, ~3 meters away. A moderate crossfeed.
  • Level::CMOY: Most popular setting. Matches the parameters of Chu Moy's well-known analog crossfeed circuit.
  • Level::JMEIER: Most subtle — makes the smallest changes to the original signal. Intended for relaxed listening. Matches Jan Meier's CORDA amplifier crossfeed.
  • Custom profile:
use bs2b::Level;

let level = Level::new(700, 45)?; // cut_frequency_hz, feed_db_tenths

Valid ranges:

  • cut_frequency_hz: 300..=2000
  • feed_db_tenths: 10..=150

Real-Time Usage Notes

  • process_frame is available for callback-style per-frame processing.
  • streaming::CallbackAdapter can process interleaved callback buffers (cpal-style).
  • streaming::StereoSourceAdapter can wrap interleaved sample iterators (rodio-style).
  • The library does no allocations while processing audio.
  • The processor is Clone if you need independent state branches.

Streaming Adapters

Enable the feature:

[dependencies]
bs2b = { version = "0.2", features = ["streaming"] }

cpal-style callback buffer processing:

use bs2b::{Bs2b, Level, streaming::CallbackAdapter};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let processor = Bs2b::new(48_000, Level::DEFAULT)?;
    let mut adapter = CallbackAdapter::new(processor, 2)?;
    let mut data = vec![0.0_f32; 512];

    // In your audio callback:
    // fn callback(data: &mut [f32]) {
    adapter.process(&mut data)?;
    // }
    Ok(())
}

rodio-style interleaved iterator processing:

use bs2b::{Bs2b, Level, streaming::StereoSourceAdapter};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let processor = Bs2b::new(48_000, Level::DEFAULT)?;
    let input = vec![0.1_f32, -0.1, 0.2, -0.2];

    let output: Vec<f32> = StereoSourceAdapter::new(input.into_iter(), processor).collect();
    let _ = output;
    Ok(())
}

Testing and Benchmarks

cargo test
cargo bench

To regenerate C-reference golden vectors used by integration tests:

./scripts/generate_golden_vectors.sh

References

License

MIT

Dependencies

~585KB
~12K SLoC