use std::sync::Arc;
use std::sync::atomic::{AtomicU32, Ordering};
use std::sync::mpsc::{Receiver, SyncSender, TryRecvError, TrySendError};
use fixed_resample::ResamplingCons;
const CHUNK: usize = 1024;
const RAMP: f32 = 0.003;
pub(super) const BUS_CHANNELS: usize = 2;
pub(super) const MAX_SINKS: usize = 64;
#[derive(Debug)]
pub(super) struct Gain {
target: AtomicU32,
peak: AtomicU32,
}
impl Gain {
pub(super) fn new() -> Self {
Self {
target: AtomicU32::new(1.0f32.to_bits()),
peak: AtomicU32::new(0),
}
}
pub(super) fn set_volume(&self, volume: f32) {
if !volume.is_finite() {
return;
}
self.target.store(volume.clamp(0.0, 1.0).to_bits(), Ordering::Relaxed);
}
pub(super) fn volume(&self) -> f32 {
f32::from_bits(self.target.load(Ordering::Relaxed))
}
pub(super) fn peak(&self) -> f32 {
f32::from_bits(self.peak.swap(0, Ordering::Relaxed))
}
fn record(&self, peak: f32) {
let current = f32::from_bits(self.peak.load(Ordering::Relaxed));
if peak > current {
self.peak.store(peak.to_bits(), Ordering::Relaxed);
}
}
}
pub(super) enum Command {
Add {
id: u64,
cons: ResamplingCons<f32>,
gain: Arc<Gain>,
},
Remove { id: u64 },
}
pub(super) struct Entry {
id: u64,
cons: ResamplingCons<f32>,
gain: Arc<Gain>,
applied: f32,
}
pub(super) struct Mixer {
entries: Vec<Entry>,
commands: Receiver<Command>,
retired: SyncSender<Entry>,
channels: usize,
step: f32,
bus: Vec<f32>,
scratch: Vec<f32>,
}
impl Mixer {
pub(super) fn new(commands: Receiver<Command>, retired: SyncSender<Entry>, rate: u32, channels: usize) -> Self {
Self {
entries: Vec::with_capacity(MAX_SINKS),
commands,
retired,
channels,
step: 1.0 / (rate as f32 * RAMP),
bus: vec![0.0; CHUNK * BUS_CHANNELS],
scratch: vec![0.0; CHUNK * BUS_CHANNELS],
}
}
fn retire(&mut self, entry: Entry) {
if let Err(err) = self.retired.try_send(entry) {
let (TrySendError::Full(entry) | TrySendError::Disconnected(entry)) = err;
drop(entry);
}
}
pub(super) fn fill(&mut self, out: &mut [f32]) {
loop {
match self.commands.try_recv() {
Ok(Command::Add { id, cons, gain }) => {
let entry = Entry {
id,
cons,
gain,
applied: 0.0,
};
debug_assert!(self.entries.len() < MAX_SINKS, "more sinks than the driver allows");
if self.entries.len() < MAX_SINKS {
self.entries.push(entry);
} else {
self.retire(entry);
}
}
Ok(Command::Remove { id }) => {
if let Some(index) = self.entries.iter().position(|e| e.id == id) {
let entry = self.entries.swap_remove(index);
self.retire(entry);
}
}
Err(TryRecvError::Empty | TryRecvError::Disconnected) => break,
}
}
let Self {
entries,
channels,
step,
bus,
scratch,
..
} = self;
let channels = *channels;
let total = out.len() / channels;
let mut done = 0;
while done < total {
let frames = (total - done).min(CHUNK);
let samples = frames * BUS_CHANNELS;
bus[..samples].fill(0.0);
for entry in entries.iter_mut() {
let _ = entry.cons.read_interleaved(&mut scratch[..samples], false);
let target = entry.gain.volume();
let mut applied = entry.applied;
let mut peak = 0.0f32;
for frame in 0..frames {
applied += (target - applied).clamp(-*step, *step);
let left = scratch[frame * 2] * applied;
let right = scratch[frame * 2 + 1] * applied;
peak = peak.max(left.abs()).max(right.abs());
bus[frame * 2] += left;
bus[frame * 2 + 1] += right;
}
entry.applied = applied;
entry.gain.record(peak);
}
for sample in &mut bus[..samples] {
*sample = sample.clamp(-1.0, 1.0);
}
let out = &mut out[done * channels..(done + frames) * channels];
match channels {
1 => {
for (frame, out) in out.iter_mut().enumerate() {
*out = (bus[frame * 2] + bus[frame * 2 + 1]) * 0.5;
}
}
_ => {
for (frame, out) in out.chunks_exact_mut(channels).enumerate() {
out[0] = bus[frame * 2];
out[1] = bus[frame * 2 + 1];
out[2..].fill(0.0);
}
}
}
done += frames;
}
}
}
#[cfg(test)]
mod tests {
use std::sync::mpsc::{Receiver, SyncSender, sync_channel};
use fixed_resample::{PushStatus, ResamplingChannelConfig, ResamplingProd, resampling_channel};
use super::*;
const RATE: u32 = 48_000;
const FRAMES: usize = RATE as usize / 10;
struct Harness {
mixer: Mixer,
commands: SyncSender<Command>,
retired: Receiver<Entry>,
next: u64,
}
impl Harness {
fn new(channels: usize) -> Self {
Self::with_depth(channels, 8)
}
fn with_depth(channels: usize, depth: usize) -> Self {
let (commands, rx) = sync_channel(depth);
let (retired_tx, retired) = sync_channel(MAX_SINKS);
Self {
mixer: Mixer::new(rx, retired_tx, RATE, channels),
commands,
retired,
next: 0,
}
}
fn add(&mut self, gain: Arc<Gain>) -> (u64, ResamplingProd<f32>) {
let (prod, cons) = resampling_channel::<f32>(
BUS_CHANNELS,
RATE,
RATE,
true,
ResamplingChannelConfig {
latency_seconds: 0.01,
capacity_seconds: 1.0,
..Default::default()
},
);
let id = self.next;
self.next += 1;
self.commands.send(Command::Add { id, cons, gain }).unwrap();
(id, prod)
}
fn fill(&mut self, out: &mut [f32]) {
self.mixer.fill(out);
}
fn settle(&mut self, out: &mut [f32]) {
self.fill(out);
self.fill(out);
}
}
fn push(prod: &mut ResamplingProd<f32>, value: f32, frames: usize) {
prod.push_interleaved(&vec![value; frames * BUS_CHANNELS]);
}
#[test]
fn discards_writes_until_the_device_reads() {
let mut harness = Harness::new(2);
let (_, mut prod) = harness.add(Arc::new(Gain::new()));
assert_eq!(prod.push_interleaved(&[1.0; 64]), PushStatus::OutputNotReady);
let mut out = vec![0.0f32; 512];
harness.fill(&mut out);
assert!(out.iter().all(|s| *s == 0.0));
assert_ne!(prod.push_interleaved(&[1.0; 64]), PushStatus::OutputNotReady);
}
#[test]
fn sums_sinks_and_clips() {
let mut harness = Harness::new(2);
let mut out = vec![0.0f32; 2048];
let mut prods: Vec<_> = (0..3).map(|_| harness.add(Arc::new(Gain::new())).1).collect();
harness.fill(&mut out);
for prod in &mut prods {
push(prod, 0.5, FRAMES);
}
harness.settle(&mut out);
let tail = &out[out.len() - 64..];
assert!(
tail.iter().all(|s| (*s - 1.0).abs() < 1e-5),
"expected clipped 1.0, got {:?}",
&tail[..4]
);
}
#[test]
fn volume_ramps_instead_of_stepping() {
let mut harness = Harness::new(2);
let mut out = vec![0.0f32; 2048];
let gain = Arc::new(Gain::new());
let (_, mut prod) = harness.add(gain.clone());
harness.fill(&mut out);
push(&mut prod, 1.0, FRAMES);
harness.settle(&mut out);
assert!((out[out.len() - 1] - 1.0).abs() < 1e-5, "gain never reached unity");
gain.set_volume(0.0);
harness.fill(&mut out);
assert!(out[0] > 0.9, "gain jumped: {}", out[0]);
assert!(out[out.len() - 1].abs() < 1e-5, "gain never reached zero");
let ramp = (RATE as f32 * RAMP) as usize;
assert!(ramp < out.len() / 2, "test buffer is shorter than the ramp");
for frame in out[..ramp * BUS_CHANNELS]
.chunks_exact(BUS_CHANNELS)
.collect::<Vec<_>>()
.windows(2)
{
assert!(frame[1][0] <= frame[0][0] + 1e-6, "ramp was not monotonic");
}
}
#[test]
fn a_non_finite_volume_is_ignored() {
let gain = Gain::new();
for bad in [f32::NAN, f32::INFINITY, f32::NEG_INFINITY] {
gain.set_volume(bad);
assert_eq!(gain.volume(), 1.0, "{bad} changed the volume");
}
gain.set_volume(0.5);
gain.set_volume(f32::NAN);
assert_eq!(gain.volume(), 0.5, "NaN clobbered a good volume");
}
#[test]
fn output_stays_finite_after_a_non_finite_volume() {
let mut harness = Harness::new(2);
let mut out = vec![0.0f32; 2048];
let gain = Arc::new(Gain::new());
let (_, mut prod) = harness.add(gain.clone());
harness.fill(&mut out);
push(&mut prod, 0.5, FRAMES);
gain.set_volume(f32::NAN);
harness.settle(&mut out);
assert!(out.iter().all(|s| s.is_finite()), "NaN reached the device buffer");
}
#[test]
fn peak_reports_the_loudest_sample_then_resets() {
let mut harness = Harness::new(2);
let mut out = vec![0.0f32; 2048];
let gain = Arc::new(Gain::new());
let (_, mut prod) = harness.add(gain.clone());
harness.fill(&mut out);
push(&mut prod, 0.25, FRAMES);
harness.settle(&mut out);
let peak = gain.peak();
assert!((peak - 0.25).abs() < 1e-3, "peak was {peak}");
assert_eq!(gain.peak(), 0.0, "peak did not reset");
}
#[test]
fn removed_sinks_stop_mixing() {
let mut harness = Harness::new(2);
let mut out = vec![0.0f32; 2048];
let (id, mut prod) = harness.add(Arc::new(Gain::new()));
harness.fill(&mut out);
push(&mut prod, 1.0, FRAMES);
harness.settle(&mut out);
assert!(out[out.len() - 1] > 0.9);
harness.commands.send(Command::Remove { id }).unwrap();
harness.fill(&mut out);
assert!(out.iter().all(|s| *s == 0.0), "removed sink still audible");
}
#[test]
fn removed_sinks_are_handed_back_rather_than_dropped() {
let mut harness = Harness::new(2);
let mut out = vec![0.0f32; 512];
let (id, _prod) = harness.add(Arc::new(Gain::new()));
harness.fill(&mut out);
harness.commands.send(Command::Remove { id }).unwrap();
harness.fill(&mut out);
let entry = harness
.retired
.try_recv()
.expect("the removed sink was dropped in the callback");
assert_eq!(entry.id, id);
}
#[test]
fn the_entry_list_never_grows() {
let mut harness = Harness::with_depth(2, MAX_SINKS);
let mut out = vec![0.0f32; 512];
let capacity = harness.mixer.entries.capacity();
assert_eq!(capacity, MAX_SINKS);
let _prods: Vec<_> = (0..MAX_SINKS).map(|_| harness.add(Arc::new(Gain::new())).1).collect();
harness.fill(&mut out);
assert_eq!(harness.mixer.entries.len(), MAX_SINKS);
assert_eq!(harness.mixer.entries.capacity(), capacity, "the entry list reallocated");
}
#[test]
fn silence_when_no_sink_is_registered() {
let mut harness = Harness::new(2);
let mut out = vec![1.0f32; 512];
harness.fill(&mut out);
assert!(out.iter().all(|s| *s == 0.0), "callback buffer was not overwritten");
}
#[test]
fn mono_device_gets_the_stereo_average() {
let mut harness = Harness::new(1);
let mut out = vec![0.0f32; 1024];
let (_, mut prod) = harness.add(Arc::new(Gain::new()));
harness.fill(&mut out);
let mut samples = vec![0.0f32; FRAMES * BUS_CHANNELS];
for frame in samples.chunks_exact_mut(BUS_CHANNELS) {
frame[0] = 1.0;
}
prod.push_interleaved(&samples);
harness.settle(&mut out);
assert!((out[out.len() - 1] - 0.5).abs() < 1e-5, "got {}", out[out.len() - 1]);
}
#[test]
fn surround_devices_get_silence_past_the_front_pair() {
let mut harness = Harness::new(6);
let mut out = vec![0.0f32; 6 * 512];
let (_, mut prod) = harness.add(Arc::new(Gain::new()));
harness.fill(&mut out);
push(&mut prod, 1.0, FRAMES);
harness.settle(&mut out);
let last = &out[out.len() - 6..];
assert!(last[0] > 0.9 && last[1] > 0.9, "front pair was silent");
assert!(last[2..].iter().all(|s| *s == 0.0), "rear channels were not silent");
}
#[test]
fn underflow_reads_as_silence_rather_than_stale_samples() {
let mut harness = Harness::new(2);
let mut out = vec![0.0f32; 8192];
let (_, mut prod) = harness.add(Arc::new(Gain::new()));
harness.fill(&mut out);
push(&mut prod, 1.0, 64);
harness.settle(&mut out);
assert!(
out[out.len() - 1].abs() < 1e-5,
"expected silence after underflow, got {}",
out[out.len() - 1]
);
}
}