#stereo #downmixer

downmixer

A downmixer that can do 2, Dolby 2.1, 3.1, 4.1, 5.1, 6.1, and 7.1 channels audio downmix to 2 or 1 channels. Written in Rust.

3 releases

Uses new Rust 2024

new 0.0.5 May 16, 2025
0.0.2 May 15, 2025
0.0.1 May 14, 2025

#935 in Audio

Download history 178/week @ 2025-05-10

178 downloads per month
Used in 3 crates

Custom license

23KB
360 lines

Downmixer

The downmixer to downmix multi-channels audio to stereo

Overview

The downmixer needs a DownmixerParams to specify the modifier of each channels by dB values.

If you have no idea about the dB modifying, use DownmixerParams::default().

The default value is:

impl DownmixerParams {
    /// * Setup default parameters
    pub fn new() -> Self {
        Self {
            front_lr_db: 0.0,
            front_center_db: -3.0,
            lowfreq_db: -6.0,
            back_lr_db: -3.0,
            front_center_lr_db: -1.5,
            back_center_db: -4.5,
            side_lr_db: -3.0,
            top_center_db: -4.5,
            top_front_lr_db: -3.0,
            top_front_center_db: -4.5,
            top_back_lr_db: -3.0,
            top_back_center_db: -4.5,
        }
    }
}

The implementation of Downmixer

#[derive(Debug, Clone, Copy)]
pub struct Downmixer {
    /// Num channels
    pub channels: u16,

    /// The channel mask indicates which channel is for which speaker.
    pub channel_mask: u32,

    /// The weights for downmixing, the `u32` is the bitmask indicating which speaker the channel is, and the `f64` is the weight.
    pub gains: CopiableBuffer<(u32, f64), 18>,
}

impl Downmixer {
    /// * Create a new `Downmixer` by specifying the channel mask and the `DownmixerParams` to compute gains for each channel.
    pub fn new(channel_mask: u32, params: DownmixerParams) -> Self {
        let mut ret = Self {
            channels: 0,
            channel_mask,
            gains: CopiableBuffer::new(),
        };
        ret.gains = params.gains_from_channel_mask(channel_mask).into_iter().collect();
        ret.channels = ret.gains.len() as u16;
        ret
    }

    /// * Downmix an audio frame to a stereo frame.
    pub fn downmix_frame_to_stereo<S>(&self, frame: &[S]) -> (S, S)
    where
        S: SampleType {
        use speaker_positions::*;
        let lmax: f64 = self.gains.iter().map(|&(b, g)| if is_lcenter(b) {g} else {0.0}).sum();
        let rmax: f64 = self.gains.iter().map(|&(b, g)| if is_rcenter(b) {g} else {0.0}).sum();
        let lmix: f64 = self.gains.iter().enumerate().map(|(i, &(b, g))| if is_lcenter(b) {frame[i].to_f64() * g} else {0.0}).sum();
        let rmix: f64 = self.gains.iter().enumerate().map(|(i, &(b, g))| if is_rcenter(b) {frame[i].to_f64() * g} else {0.0}).sum();
        (S::scale_from(lmix / lmax), S::scale_from(rmix / rmax))
    }

    /// Downmix multiple audio frames to stereo frames
    pub fn downmix_frame_to_stereos<S>(&self, frames: &[Vec<S>]) -> Vec<(S, S)>
    where
        S: SampleType {
        frames.iter().map(|frame|self.downmix_frame_to_stereo(frame)).collect()
    }

    /// * Downmix an audio frame to a mono frame.
    pub fn downmix_frame_to_mono<S>(&self, frame: &[S]) -> S
    where
        S: SampleType {
        let (l, r) = self.downmix_frame_to_stereo(frame);
        S::average(l, r)
    }
}

Dependencies

~120KB