use std::collections::HashMap;
use async_trait::async_trait;
use bytes::Bytes;
use crate::error::{AudioError, AudioResult};
use crate::frame::AudioFrame;
use crate::traits::AudioProcessor;
struct MixerTrack {
volume: f32,
buffer: Option<AudioFrame>,
}
pub struct Mixer {
tracks: HashMap<String, MixerTrack>,
output_sample_rate: u32,
}
impl Mixer {
pub fn new(output_sample_rate: u32) -> Self {
Self { tracks: HashMap::new(), output_sample_rate }
}
pub fn add_track(&mut self, name: impl Into<String>, volume: f32) {
self.tracks
.insert(name.into(), MixerTrack { volume: volume.clamp(0.0, 1.0), buffer: None });
}
pub fn set_volume(&mut self, name: &str, volume: f32) {
if let Some(track) = self.tracks.get_mut(name) {
track.volume = volume.clamp(0.0, 1.0);
}
}
pub fn push_frame(&mut self, track: &str, frame: AudioFrame) {
if let Some(t) = self.tracks.get_mut(track) {
t.buffer = Some(frame);
}
}
pub fn mix(&mut self) -> AudioResult<AudioFrame> {
if self.tracks.is_empty() {
return Err(AudioError::Fx("mixer has no tracks".into()));
}
let max_samples = self
.tracks
.values()
.filter_map(|t| t.buffer.as_ref())
.map(|f| f.data.len() / 2)
.max()
.unwrap_or(0);
if max_samples == 0 {
return Ok(AudioFrame::silence(self.output_sample_rate, 1, 0));
}
let mut mixed = vec![0i32; max_samples];
for track in self.tracks.values() {
let volume = track.volume;
if let Some(ref frame) = track.buffer {
let samples = frame.samples();
for (i, &s) in samples.iter().enumerate() {
if i < max_samples {
mixed[i] += (s as f32 * volume) as i32;
}
}
}
}
let pcm: Vec<u8> = mixed
.iter()
.flat_map(|&s| {
let clamped = s.clamp(-32768, 32767) as i16;
clamped.to_le_bytes()
})
.collect();
for track in self.tracks.values_mut() {
track.buffer = None;
}
Ok(AudioFrame::new(Bytes::from(pcm), self.output_sample_rate, 1))
}
}
#[async_trait]
impl AudioProcessor for Mixer {
async fn process(&self, frame: &AudioFrame) -> AudioResult<AudioFrame> {
let volume = self.tracks.values().next().map(|t| t.volume).unwrap_or(1.0);
let samples = frame.samples();
let pcm: Vec<u8> = samples
.iter()
.flat_map(|&s| {
let scaled = (s as f32 * volume) as i32;
let clamped = scaled.clamp(-32768, 32767) as i16;
clamped.to_le_bytes()
})
.collect();
Ok(AudioFrame::new(Bytes::from(pcm), frame.sample_rate, frame.channels))
}
}