active_call/media/
ambiance.rs1use super::processor::Processor;
2use crate::media::{AudioFrame, INTERNAL_SAMPLERATE, Samples};
3use anyhow::Result;
4use serde::{Deserialize, Serialize};
5use tracing::info;
6
7#[derive(Debug, Clone, Serialize, Deserialize, Default)]
8#[serde(rename_all = "camelCase")]
9pub struct AmbianceOption {
10 pub path: Option<String>,
11 pub duck_level: Option<f32>,
12 pub normal_level: Option<f32>,
13 pub transition_speed: Option<f32>,
14 pub enabled: Option<bool>,
15}
16
17impl AmbianceOption {
18 pub fn merge(&mut self, other: &AmbianceOption) {
19 if self.path.is_none() {
20 self.path = other.path.clone();
21 }
22 if self.duck_level.is_none() {
23 self.duck_level = other.duck_level;
24 }
25 if self.normal_level.is_none() {
26 self.normal_level = other.normal_level;
27 }
28 if self.transition_speed.is_none() {
29 self.transition_speed = other.transition_speed;
30 }
31 if self.enabled.is_none() {
32 self.enabled = other.enabled;
33 }
34 }
35}
36
37pub struct AmbianceProcessor {
38 samples: Vec<i16>,
39 cursor: usize,
40 duck_level: f32,
41 normal_level: f32,
42 enabled: bool,
43 current_level: f32,
44 transition_speed: f32,
45 resample_phase: u32,
46 resample_step: u32,
47}
48
49impl AmbianceProcessor {
50 pub async fn new(option: AmbianceOption) -> Result<Self> {
51 let path = option
52 .path
53 .ok_or_else(|| anyhow::anyhow!("Ambiance path required"))?;
54
55 let samples =
56 crate::media::loader::load_audio_as_pcm(&path, INTERNAL_SAMPLERATE, true).await?;
57
58 info!("Loading ambiance {}: samples={}", path, samples.len());
59
60 let normal_level = option.normal_level.unwrap_or(0.3);
61 Ok(Self {
62 samples,
63 cursor: 0,
64 duck_level: option.duck_level.unwrap_or(0.1),
65 normal_level,
66 enabled: option.enabled.unwrap_or(true),
67 current_level: normal_level,
68 transition_speed: option.transition_speed.unwrap_or(0.01),
69 resample_phase: 0,
70 resample_step: 1 << 16,
71 })
72 }
73
74 pub fn set_enabled(&mut self, enabled: bool) {
75 self.enabled = enabled;
76 }
77
78 pub fn set_levels(&mut self, normal: f32, duck: f32) {
79 self.normal_level = normal;
80 self.duck_level = duck;
81 }
82
83 #[inline]
84 fn get_ambient_sample_with_rate(&mut self, target_sample_rate: u32) -> i16 {
85 if self.samples.is_empty() {
86 return 0;
87 }
88
89 self.resample_step =
90 (((INTERNAL_SAMPLERATE as u64) << 16) / target_sample_rate as u64) as u32;
91 let sample = self.samples[self.cursor];
92
93 self.resample_phase += self.resample_step;
94 while self.resample_phase >= (1 << 16) {
95 self.resample_phase -= 1 << 16;
96 self.cursor = (self.cursor + 1) % self.samples.len();
97 }
98
99 sample
100 }
101
102 #[inline]
103 fn soft_mix(signal: i16, ambient: i16, level: f32) -> i16 {
104 let ambient_scaled = (ambient as i32 * (level * 256.0) as i32) >> 8;
105 let signal_i32 = signal as i32;
106 let mixed = signal_i32 + ambient_scaled;
107
108 if mixed > 32767 {
109 let over = mixed - 32767;
110 (32767 - (over >> 2)) as i16
111 } else if mixed < -32768 {
112 let under = -32768 - mixed;
113 (-32768 + (under >> 2)) as i16
114 } else {
115 mixed as i16
116 }
117 }
118}
119
120impl Processor for AmbianceProcessor {
121 fn process_frame(&mut self, frame: &mut AudioFrame) -> Result<()> {
122 if !self.enabled || self.samples.is_empty() {
123 return Ok(());
124 }
125
126 let is_server_side_speaking = match &frame.samples {
127 Samples::PCM { samples } => !samples.is_empty(),
128 Samples::RTP { .. } => true,
129 Samples::Empty => false,
130 };
131
132 let target_level = if is_server_side_speaking {
133 self.duck_level
134 } else {
135 self.normal_level
136 };
137
138 if (self.current_level - target_level).abs() > 0.001 {
139 if self.current_level < target_level {
140 self.current_level = (self.current_level + self.transition_speed).min(target_level);
141 } else {
142 self.current_level = (self.current_level - self.transition_speed).max(target_level);
143 }
144 }
145
146 let sample_rate = if frame.sample_rate > 0 {
147 frame.sample_rate
148 } else {
149 INTERNAL_SAMPLERATE
150 };
151 let channels = frame.channels.max(1) as usize;
152
153 match &mut frame.samples {
154 Samples::PCM { samples } => {
155 let frame_sample_count = samples.len() / channels;
156 for i in 0..frame_sample_count {
157 let ambient = self.get_ambient_sample_with_rate(sample_rate);
158 for c in 0..channels {
159 let idx = i * channels + c;
160 if idx < samples.len() {
161 samples[idx] =
162 Self::soft_mix(samples[idx], ambient, self.current_level);
163 }
164 }
165 }
166 }
167 Samples::Empty => {
168 let frame_size = (sample_rate as usize * 20) / 1000;
169 let mut ambient_samples = Vec::with_capacity(frame_size * channels);
170 for _ in 0..frame_size {
171 let ambient = self.get_ambient_sample_with_rate(sample_rate);
172 let ambient_scaled =
173 ((ambient as i32 * (self.current_level * 256.0) as i32) >> 8) as i16;
174 for _ in 0..channels {
175 ambient_samples.push(ambient_scaled);
176 }
177 }
178 frame.samples = Samples::PCM {
179 samples: ambient_samples,
180 };
181 frame.sample_rate = sample_rate;
182 frame.channels = channels as u16;
183 }
184 _ => {}
185 }
186
187 Ok(())
188 }
189}