active_call/media/
volume_control.rs1use super::processor::Processor;
2use crate::media::AudioFrame;
3use anyhow::Result;
4use std::sync::{
5 Arc,
6 atomic::{AtomicBool, AtomicU32, Ordering},
7};
8
9#[derive(Debug, Clone)]
11pub struct VolumeControlProcessor {
12 volume_level: Arc<AtomicU32>,
14 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 !self.muted.fetch_xor(true, Ordering::Relaxed)
53 }
54}
55
56impl Processor for VolumeControlProcessor {
57 fn process_frame(&self, frame: &mut AudioFrame) -> Result<()> {
58 if self.is_muted() {
60 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 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#[derive(Debug, Clone)]
86pub struct HoldProcessor {
87 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 !self.on_hold.fetch_xor(true, Ordering::Relaxed)
115 }
116}
117
118impl Processor for HoldProcessor {
119 fn process_frame(&self, frame: &mut AudioFrame) -> Result<()> {
120 if self.is_on_hold() {
121 if let crate::media::Samples::PCM { samples } = &mut frame.samples {
123 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 assert!((processor.get_volume() - 1.0).abs() < f32::EPSILON);
145 assert!(!processor.is_muted());
146
147 processor.set_volume(0.5);
149 assert!((processor.get_volume() - 0.5).abs() < f32::EPSILON);
150
151 processor.set_muted(true);
153 assert!(processor.is_muted());
154 }
155
156 #[test]
157 fn test_volume_processing() {
158 let 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 };
169
170 processor.process_frame(&mut frame).unwrap();
171
172 if let Samples::PCM { samples } = frame.samples {
173 assert_eq!(samples, vec![500, -500, 250, -250]);
174 } else {
175 panic!("Expected PCM samples");
176 }
177 }
178
179 #[test]
180 fn test_mute_processing() {
181 let processor = VolumeControlProcessor::new();
182 processor.set_muted(true);
183
184 let mut frame = AudioFrame {
185 track_id: "test".to_string(),
186 samples: Samples::PCM {
187 samples: vec![1000, -1000, 500, -500],
188 },
189 timestamp: 0,
190 sample_rate: 16000,
191 };
192
193 processor.process_frame(&mut frame).unwrap();
194
195 if let Samples::PCM { samples } = frame.samples {
196 assert_eq!(samples, vec![0, 0, 0, 0]);
197 } else {
198 panic!("Expected PCM samples");
199 }
200 }
201
202 #[test]
203 fn test_hold_processor() {
204 let processor = HoldProcessor::new();
205
206 assert!(!processor.is_on_hold());
208
209 processor.set_hold(true);
211 assert!(processor.is_on_hold());
212
213 let new_state = processor.toggle_hold();
215 assert!(!new_state);
216 assert!(!processor.is_on_hold());
217 }
218}