active_call/media/
volume_control.rs

1use super::processor::Processor;
2use crate::media::AudioFrame;
3use anyhow::Result;
4use std::sync::{
5    Arc,
6    atomic::{AtomicBool, AtomicU32, Ordering},
7};
8
9/// Volume control processor for audio streams
10#[derive(Debug, Clone)]
11pub struct VolumeControlProcessor {
12    /// Volume level stored as bits of f32 (0.0 to 2.0, where 1.0 is normal volume)
13    volume_level: Arc<AtomicU32>,
14    /// Whether audio is muted
15    muted: Arc<AtomicBool>,
16}
17
18impl Default for VolumeControlProcessor {
19    fn default() -> Self {
20        Self::new()
21    }
22}
23
24impl VolumeControlProcessor {
25    pub fn new() -> Self {
26        Self {
27            volume_level: Arc::new(AtomicU32::new(1.0_f32.to_bits())),
28            muted: Arc::new(AtomicBool::new(false)),
29        }
30    }
31
32    pub fn set_volume(&self, level: f32) {
33        let clamped_level = level.clamp(0.0, 2.0);
34        self.volume_level
35            .store(clamped_level.to_bits(), Ordering::Relaxed);
36    }
37
38    pub fn get_volume(&self) -> f32 {
39        f32::from_bits(self.volume_level.load(Ordering::Relaxed))
40    }
41
42    pub fn set_muted(&self, muted: bool) {
43        self.muted.store(muted, Ordering::Relaxed);
44    }
45
46    pub fn is_muted(&self) -> bool {
47        self.muted.load(Ordering::Relaxed)
48    }
49
50    pub fn toggle_mute(&self) -> bool {
51        // Use fetch_xor to atomically toggle the boolean
52        !self.muted.fetch_xor(true, Ordering::Relaxed)
53    }
54}
55
56impl Processor for VolumeControlProcessor {
57    fn process_frame(&mut self, frame: &mut AudioFrame) -> Result<()> {
58        // Check if muted
59        if self.is_muted() {
60            // Mute the audio by zeroing out samples
61            if let crate::media::Samples::PCM { samples } = &mut frame.samples {
62                for sample in samples.iter_mut() {
63                    *sample = 0;
64                }
65            }
66            return Ok(());
67        }
68
69        // Apply volume control
70        let volume = self.get_volume();
71        if (volume - 1.0).abs() > f32::EPSILON {
72            if let crate::media::Samples::PCM { samples } = &mut frame.samples {
73                for sample in samples.iter_mut() {
74                    let adjusted = (*sample as f32 * volume) as i16;
75                    *sample = adjusted.clamp(i16::MIN, i16::MAX);
76                }
77            }
78        }
79
80        Ok(())
81    }
82}
83
84/// Hold/Unhold processor for audio streams
85#[derive(Debug, Clone)]
86pub struct HoldProcessor {
87    /// Whether the call is on hold
88    on_hold: Arc<AtomicBool>,
89}
90
91impl Default for HoldProcessor {
92    fn default() -> Self {
93        Self::new()
94    }
95}
96
97impl HoldProcessor {
98    pub fn new() -> Self {
99        Self {
100            on_hold: Arc::new(AtomicBool::new(false)),
101        }
102    }
103
104    pub fn set_hold(&self, hold: bool) {
105        self.on_hold.store(hold, Ordering::Relaxed);
106    }
107
108    pub fn is_on_hold(&self) -> bool {
109        self.on_hold.load(Ordering::Relaxed)
110    }
111
112    pub fn toggle_hold(&self) -> bool {
113        // Use fetch_xor to atomically toggle the boolean
114        !self.on_hold.fetch_xor(true, Ordering::Relaxed)
115    }
116}
117
118impl Processor for HoldProcessor {
119    fn process_frame(&mut self, frame: &mut AudioFrame) -> Result<()> {
120        if self.is_on_hold() {
121            // When on hold, replace audio with silence or hold music
122            if let crate::media::Samples::PCM { samples } = &mut frame.samples {
123                // Replace with silence for now
124                // TODO: Could be enhanced to play hold music
125                for sample in samples.iter_mut() {
126                    *sample = 0;
127                }
128            }
129        }
130        Ok(())
131    }
132}
133
134#[cfg(test)]
135mod tests {
136    use super::*;
137    use crate::media::Samples;
138
139    #[test]
140    fn test_volume_control() {
141        let processor = VolumeControlProcessor::new();
142
143        // Test default volume
144        assert!((processor.get_volume() - 1.0).abs() < f32::EPSILON);
145        assert!(!processor.is_muted());
146
147        // Test volume setting
148        processor.set_volume(0.5);
149        assert!((processor.get_volume() - 0.5).abs() < f32::EPSILON);
150
151        // Test mute
152        processor.set_muted(true);
153        assert!(processor.is_muted());
154    }
155
156    #[test]
157    fn test_volume_processing() {
158        let mut processor = VolumeControlProcessor::new();
159        processor.set_volume(0.5);
160
161        let mut frame = AudioFrame {
162            track_id: "test".to_string(),
163            samples: Samples::PCM {
164                samples: vec![1000, -1000, 500, -500],
165            },
166            timestamp: 0,
167            sample_rate: 16000,
168            channels: 1,
169        };
170
171        processor.process_frame(&mut frame).unwrap();
172
173        if let Samples::PCM { samples } = frame.samples {
174            assert_eq!(samples, vec![500, -500, 250, -250]);
175        } else {
176            panic!("Expected PCM samples");
177        }
178    }
179
180    #[test]
181    fn test_mute_processing() {
182        let mut processor = VolumeControlProcessor::new();
183        processor.set_muted(true);
184
185        let mut frame = AudioFrame {
186            track_id: "test".to_string(),
187            samples: Samples::PCM {
188                samples: vec![1000, -1000, 500, -500],
189            },
190            timestamp: 0,
191            sample_rate: 16000,
192            channels: 1,
193        };
194
195        processor.process_frame(&mut frame).unwrap();
196
197        if let Samples::PCM { samples } = frame.samples {
198            assert_eq!(samples, vec![0, 0, 0, 0]);
199        } else {
200            panic!("Expected PCM samples");
201        }
202    }
203
204    #[test]
205    fn test_hold_processor() {
206        let processor = HoldProcessor::new();
207
208        // Test default state
209        assert!(!processor.is_on_hold());
210
211        // Test hold setting
212        processor.set_hold(true);
213        assert!(processor.is_on_hold());
214
215        // Test toggle
216        let new_state = processor.toggle_hold();
217        assert!(!new_state);
218        assert!(!processor.is_on_hold());
219    }
220}