use super::processor::Processor;
use crate::media::AudioFrame;
use anyhow::Result;
use std::sync::{
Arc,
atomic::{AtomicBool, AtomicU32, Ordering},
};
#[derive(Debug, Clone)]
pub struct VolumeControlProcessor {
volume_level: Arc<AtomicU32>,
muted: Arc<AtomicBool>,
}
impl Default for VolumeControlProcessor {
fn default() -> Self {
Self::new()
}
}
impl VolumeControlProcessor {
pub fn new() -> Self {
Self {
volume_level: Arc::new(AtomicU32::new(1.0_f32.to_bits())),
muted: Arc::new(AtomicBool::new(false)),
}
}
pub fn set_volume(&self, level: f32) {
let clamped_level = level.clamp(0.0, 2.0);
self.volume_level
.store(clamped_level.to_bits(), Ordering::Relaxed);
}
pub fn get_volume(&self) -> f32 {
f32::from_bits(self.volume_level.load(Ordering::Relaxed))
}
pub fn set_muted(&self, muted: bool) {
self.muted.store(muted, Ordering::Relaxed);
}
pub fn is_muted(&self) -> bool {
self.muted.load(Ordering::Relaxed)
}
pub fn toggle_mute(&self) -> bool {
!self.muted.fetch_xor(true, Ordering::Relaxed)
}
}
impl Processor for VolumeControlProcessor {
fn process_frame(&mut self, frame: &mut AudioFrame) -> Result<()> {
if self.is_muted() {
if let crate::media::Samples::PCM { samples } = &mut frame.samples {
for sample in samples.iter_mut() {
*sample = 0;
}
}
return Ok(());
}
let volume = self.get_volume();
if (volume - 1.0).abs() > f32::EPSILON {
if let crate::media::Samples::PCM { samples } = &mut frame.samples {
for sample in samples.iter_mut() {
let adjusted = (*sample as f32 * volume) as i16;
*sample = adjusted.clamp(i16::MIN, i16::MAX);
}
}
}
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct HoldProcessor {
on_hold: Arc<AtomicBool>,
}
impl Default for HoldProcessor {
fn default() -> Self {
Self::new()
}
}
impl HoldProcessor {
pub fn new() -> Self {
Self {
on_hold: Arc::new(AtomicBool::new(false)),
}
}
pub fn set_hold(&self, hold: bool) {
self.on_hold.store(hold, Ordering::Relaxed);
}
pub fn is_on_hold(&self) -> bool {
self.on_hold.load(Ordering::Relaxed)
}
pub fn toggle_hold(&self) -> bool {
!self.on_hold.fetch_xor(true, Ordering::Relaxed)
}
}
impl Processor for HoldProcessor {
fn process_frame(&mut self, frame: &mut AudioFrame) -> Result<()> {
if self.is_on_hold() {
if let crate::media::Samples::PCM { samples } = &mut frame.samples {
for sample in samples.iter_mut() {
*sample = 0;
}
}
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::media::Samples;
#[test]
fn test_volume_control() {
let processor = VolumeControlProcessor::new();
assert!((processor.get_volume() - 1.0).abs() < f32::EPSILON);
assert!(!processor.is_muted());
processor.set_volume(0.5);
assert!((processor.get_volume() - 0.5).abs() < f32::EPSILON);
processor.set_muted(true);
assert!(processor.is_muted());
}
#[test]
fn test_volume_processing() {
let mut processor = VolumeControlProcessor::new();
processor.set_volume(0.5);
let mut frame = AudioFrame {
track_id: "test".to_string(),
samples: Samples::PCM {
samples: vec![1000, -1000, 500, -500],
},
timestamp: 0,
sample_rate: 16000,
channels: 1,
..Default::default()
};
processor.process_frame(&mut frame).unwrap();
if let Samples::PCM { samples } = frame.samples {
assert_eq!(samples, vec![500, -500, 250, -250]);
} else {
panic!("Expected PCM samples");
}
}
#[test]
fn test_mute_processing() {
let mut processor = VolumeControlProcessor::new();
processor.set_muted(true);
let mut frame = AudioFrame {
track_id: "test".to_string(),
samples: Samples::PCM {
samples: vec![1000, -1000, 500, -500],
},
timestamp: 0,
sample_rate: 16000,
channels: 1,
..Default::default()
};
processor.process_frame(&mut frame).unwrap();
if let Samples::PCM { samples } = frame.samples {
assert_eq!(samples, vec![0, 0, 0, 0]);
} else {
panic!("Expected PCM samples");
}
}
#[test]
fn test_hold_processor() {
let processor = HoldProcessor::new();
assert!(!processor.is_on_hold());
processor.set_hold(true);
assert!(processor.is_on_hold());
let new_state = processor.toggle_hold();
assert!(!new_state);
assert!(!processor.is_on_hold());
}
}