@@ -165,15 +165,25 @@ async fn run_dual_loop(
165165 while let Some ( ( raw_mic, raw_speaker) ) = joiner. pop_pair ( ) {
166166 let raw_mic = Arc :: < [ f32 ] > :: from ( raw_mic) ;
167167 let raw_speaker = Arc :: < [ f32 ] > :: from ( raw_speaker) ;
168- let aec_reference_speaker = aec_reference
168+ let aligned = aec_reference
169169 . as_mut ( )
170170 . map ( |aligner| aligner. align ( & raw_speaker, & raw_mic) )
171- . unwrap_or_else ( || Arc :: clone ( & raw_speaker) ) ;
171+ . unwrap_or_else ( || AecAlignedPair {
172+ mic : Arc :: clone ( & raw_mic) ,
173+ speaker : Arc :: clone ( & raw_speaker) ,
174+ alignment_changed : false ,
175+ } ) ;
176+ if aligned. alignment_changed {
177+ if let Some ( processor) = aec. as_mut ( ) {
178+ processor. reset ( ) ;
179+ }
180+ linear_echo_gain = None ;
181+ }
172182 let aec_mic = process_aec (
173183 & mut aec,
174184 & mut linear_echo_gain,
175- & raw_mic ,
176- & aec_reference_speaker ,
185+ & aligned . mic ,
186+ & aligned . speaker ,
177187 ) ;
178188 if tx
179189 . send ( Ok ( CaptureFrame {
@@ -199,13 +209,51 @@ async fn run_dual_loop(
199209
200210struct AecReferenceAligner {
201211 probe : SyncProbe ,
202- delay_line : SampleDelayLine ,
203- last_delay_samples : usize ,
212+ mic_delay_line : SampleDelayLine ,
213+ speaker_delay_line : SampleDelayLine ,
214+ last_alignment : AecAlignment ,
204215 last_logged_state : Option < SyncProbeState > ,
205216 last_logged_stable_lag_samples : Option < isize > ,
206217 log_probe_events : bool ,
207218}
208219
220+ struct AecAlignedPair {
221+ mic : Arc < [ f32 ] > ,
222+ speaker : Arc < [ f32 ] > ,
223+ alignment_changed : bool ,
224+ }
225+
226+ #[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
227+ enum AecAlignment {
228+ None ,
229+ DelayMic ( usize ) ,
230+ DelaySpeaker ( usize ) ,
231+ }
232+
233+ impl AecAlignment {
234+ fn from_lag_samples ( lag_samples : Option < isize > ) -> Self {
235+ match lag_samples {
236+ Some ( lag) if lag > 0 => Self :: DelaySpeaker ( lag as usize ) ,
237+ Some ( lag) if lag < 0 => Self :: DelayMic ( lag. unsigned_abs ( ) ) ,
238+ _ => Self :: None ,
239+ }
240+ }
241+
242+ fn mic_delay_samples ( self ) -> usize {
243+ match self {
244+ Self :: DelayMic ( samples) => samples,
245+ Self :: None | Self :: DelaySpeaker ( _) => 0 ,
246+ }
247+ }
248+
249+ fn speaker_delay_samples ( self ) -> usize {
250+ match self {
251+ Self :: DelaySpeaker ( samples) => samples,
252+ Self :: None | Self :: DelayMic ( _) => 0 ,
253+ }
254+ }
255+ }
256+
209257impl AecReferenceAligner {
210258 fn new ( sample_rate : u32 ) -> Self {
211259 let max_lag_samples = ( ( sample_rate as usize ) * ( AEC_MAX_REFERENCE_LAG_MS as usize ) ) / 1000 ;
@@ -215,15 +263,16 @@ impl AecReferenceAligner {
215263
216264 Self {
217265 probe : SyncProbe :: new ( config) ,
218- delay_line : SampleDelayLine :: new ( max_delay_samples) ,
219- last_delay_samples : 0 ,
266+ mic_delay_line : SampleDelayLine :: new ( max_delay_samples) ,
267+ speaker_delay_line : SampleDelayLine :: new ( max_delay_samples) ,
268+ last_alignment : AecAlignment :: None ,
220269 last_logged_state : None ,
221270 last_logged_stable_lag_samples : None ,
222271 log_probe_events : std:: env:: var ( AUDIO_SYNC_PROBE_ENV ) . ok ( ) . as_deref ( ) == Some ( "1" ) ,
223272 }
224273 }
225274
226- fn align ( & mut self , raw_speaker : & [ f32 ] , raw_mic : & [ f32 ] ) -> Arc < [ f32 ] > {
275+ fn align ( & mut self , raw_speaker : & [ f32 ] , raw_mic : & [ f32 ] ) -> AecAlignedPair {
227276 let observed = catch_unwind ( AssertUnwindSafe ( || {
228277 self . probe . observe ( raw_speaker, raw_mic)
229278 } ) ) ;
@@ -235,44 +284,62 @@ impl AecReferenceAligner {
235284 }
236285 } ;
237286
238- if let Some ( event) = event {
239- self . update_delay ( & event) ;
287+ let alignment_changed = if let Some ( event) = event {
288+ let alignment_changed = self . update_alignment ( & event) ;
240289 if self . log_probe_events {
241290 self . log_probe_event ( event) ;
242291 }
243- }
292+ alignment_changed
293+ } else {
294+ false
295+ } ;
244296
245- Arc :: < [ f32 ] > :: from (
246- self . delay_line
247- . process ( raw_speaker, self . last_delay_samples ) ,
248- )
297+ let aligned_mic = self
298+ . mic_delay_line
299+ . process ( raw_mic, self . last_alignment . mic_delay_samples ( ) ) ;
300+ let aligned_speaker = self
301+ . speaker_delay_line
302+ . process ( raw_speaker, self . last_alignment . speaker_delay_samples ( ) ) ;
303+
304+ AecAlignedPair {
305+ mic : Arc :: from ( aligned_mic) ,
306+ speaker : Arc :: from ( aligned_speaker) ,
307+ alignment_changed,
308+ }
249309 }
250310
251- fn update_delay ( & mut self , event : & SyncProbeEvent ) {
311+ fn update_alignment ( & mut self , event : & SyncProbeEvent ) -> bool {
252312 let snapshot = event. snapshot ( ) ;
253- let next_delay = if matches ! (
313+ let next_alignment = if matches ! (
254314 snapshot. state,
255315 SyncProbeState :: Locked | SyncProbeState :: Holdover
256316 ) {
257- snapshot
258- . stable_lag_samples
259- . filter ( |lag| * lag > 0 )
260- . map ( |lag| lag as usize )
261- . unwrap_or ( 0 )
317+ AecAlignment :: from_lag_samples ( snapshot. stable_lag_samples )
262318 } else {
263- 0
319+ AecAlignment :: None
264320 } ;
265321
266- if next_delay != self . last_delay_samples {
322+ if next_alignment != self . last_alignment {
267323 tracing:: info!(
268- previous_delay_samples = self . last_delay_samples,
269- delay_samples = next_delay,
270- delay_ms = next_delay as f32 / self . probe. config( ) . sample_rate as f32 * 1000.0 ,
324+ previous_alignment = ?self . last_alignment,
325+ alignment = ?next_alignment,
326+ mic_delay_samples = next_alignment. mic_delay_samples( ) ,
327+ speaker_delay_samples = next_alignment. speaker_delay_samples( ) ,
328+ mic_delay_ms = next_alignment. mic_delay_samples( ) as f32
329+ / self . probe. config( ) . sample_rate as f32
330+ * 1000.0 ,
331+ speaker_delay_ms = next_alignment. speaker_delay_samples( ) as f32
332+ / self . probe. config( ) . sample_rate as f32
333+ * 1000.0 ,
334+ stable_lag_samples = snapshot. stable_lag_samples,
271335 state = ?snapshot. state,
272- "aec_reference_delay_changed "
336+ "aec_reference_alignment_changed "
273337 ) ;
274- self . last_delay_samples = next_delay;
338+ self . last_alignment = next_alignment;
339+ return true ;
275340 }
341+
342+ false
276343 }
277344
278345 fn log_probe_event ( & mut self , event : SyncProbeEvent ) {
@@ -636,4 +703,22 @@ mod tests {
636703 assert_eq ! ( first, vec![ 0.0 , 0.0 , 1.0 ] ) ;
637704 assert_eq ! ( second, vec![ 2.0 ] ) ;
638705 }
706+
707+ #[ test]
708+ fn aec_alignment_delays_speaker_for_positive_lag ( ) {
709+ let alignment = AecAlignment :: from_lag_samples ( Some ( 320 ) ) ;
710+
711+ assert_eq ! ( alignment, AecAlignment :: DelaySpeaker ( 320 ) ) ;
712+ assert_eq ! ( alignment. mic_delay_samples( ) , 0 ) ;
713+ assert_eq ! ( alignment. speaker_delay_samples( ) , 320 ) ;
714+ }
715+
716+ #[ test]
717+ fn aec_alignment_delays_mic_for_negative_lag ( ) {
718+ let alignment = AecAlignment :: from_lag_samples ( Some ( -320 ) ) ;
719+
720+ assert_eq ! ( alignment, AecAlignment :: DelayMic ( 320 ) ) ;
721+ assert_eq ! ( alignment. mic_delay_samples( ) , 320 ) ;
722+ assert_eq ! ( alignment. speaker_delay_samples( ) , 0 ) ;
723+ }
639724}
0 commit comments