docs/audio-source-snapshots/codex-rs/voice-host/src/processing_tests.rs

Текстовый файл из учебного комплекта, не свидетельство установленного runtime. Вернуться к учебнику · Исходный файл

   1 use super::*;
   2 use pretty_assertions::assert_eq;
   3 
   4 #[test]
   5 fn rendered_reference_changes_decoded_echo_capture() {
   6     let mut processors = [
   7         Processor::new(/*input_rate*/ 48_000, /*output_rate*/ 44_100).unwrap(),
   8         Processor::new(/*input_rate*/ 48_000, /*output_rate*/ 44_100).unwrap(),
   9     ];
  10     for processor in &mut processors {
  11         // Isolate echo cancellation from independent noise/gain adaptation.
  12         processor.apm.apply_config(sonora::Config {
  13             echo_canceller: Some(EchoCanceller::default()),
  14             ..Default::default()
  15         });
  16     }
  17     let start = Instant::now();
  18     let signal = |seconds: f32| {
  19         [311.0, 719.0, 1_423.0]
  20             .into_iter()
  21             .map(|frequency| (std::f32::consts::TAU * frequency * seconds).sin() * 0.1)
  22             .sum::<f32>()
  23     };
  24     let mut packets = [Vec::new(), Vec::new()];
  25     // Two seconds of deterministic samples allow AEC startup, without sleeping.
  26     for block in 0..200 {
  27         for offset in [0, 256] {
  28             let mut frame = Frame {
  29                 samples: [0.0; 256],
  30                 len: 256.min(441 - offset),
  31                 at: start - Duration::from_secs(/*secs*/ 10)
  32                     + Duration::from_secs_f64((block * 441 + offset) as f64 / 44_100.0),
  33                 generation: 0,
  34             };
  35             for (index, sample) in frame.samples[..frame.len].iter_mut().enumerate() {
  36                 *sample = signal((block * 441 + offset + index) as f32 / 44_100.0);
  37             }
  38             processors[0].render(&frame).unwrap();
  39         }
  40         // Keep delay metadata identical: only consuming reference samples may
  41         // explain the difference in the decoded capture signal.
  42         processors[1].render_delay = processors[0].render_delay;
  43         for offset in [0, 256] {
  44             let mut frame = Frame {
  45                 samples: [0.0; 256],
  46                 len: 256.min(480 - offset),
  47                 at: start + Duration::from_secs_f64((block * 480 + offset) as f64 / 48_000.0),
  48                 generation: 2,
  49             };
  50             for (index, sample) in frame.samples[..frame.len].iter_mut().enumerate() {
  51                 *sample = signal((block * 480 + offset + index) as f32 / 48_000.0 - 0.05);
  52             }
  53             for (processor, packets) in processors.iter_mut().zip(&mut packets) {
  54                 packets.extend(processor.capture(&frame, || frame.at).unwrap());
  55             }
  56         }
  57     }
  58     let decoded = packets.map(|packets| {
  59         let mut decoder = opus::Decoder::new(/*sample_rate*/ 48_000, opus::Channels::Mono).unwrap();
  60         let mut decoded = Vec::new();
  61         for packet in packets {
  62             let mut samples = [0.0; 960];
  63             assert_eq!(
  64                 decoder
  65                     .decode_float(&packet.data, &mut samples, /*fec*/ false)
  66                     .unwrap(),
  67                 960
  68             );
  69             assert!(samples.iter().all(|sample| sample.is_finite()));
  70             decoded.extend(samples);
  71         }
  72         decoded
  73     });
  74     assert_eq!(decoded[0].len(), decoded[1].len());
  75     assert!(decoded[0].len() > 48_000);
  76     let energy = decoded.map(|samples| {
  77         samples[48_000..]
  78             .iter()
  79             .map(|sample| sample * sample)
  80             .sum::<f32>()
  81     });
  82     assert!(
  83         energy[1] > 1.0,
  84         "control capture must contain the echo signal"
  85     );
  86     assert!(
  87         energy[0] < energy[1] * 0.5,
  88         "reference/control echo energy: {energy:?}"
  89     );
  90 }
  91 
  92 #[test]
  93 fn real_encoder_produces_twenty_millisecond_opus_packets() {
  94     for rate in [44_100, 48_000] {
  95         let mut processor = Processor::new(rate, /*output_rate*/ 48_000).unwrap();
  96         let mut packets = Vec::new();
  97         for block in 0..40 {
  98             let mut frame = Frame {
  99                 samples: [0.0; 256],
 100                 len: 256,
 101                 at: Instant::now(),
 102                 generation: 2,
 103             };
 104             for (index, sample) in frame.samples.iter_mut().enumerate() {
 105                 *sample = (std::f32::consts::TAU * 440.0 * (block as usize * 256 + index) as f32
 106                     / rate as f32)
 107                     .sin()
 108                     * 0.1;
 109             }
 110             packets.extend(processor.capture(&frame, || frame.at).unwrap());
 111         }
 112         assert!(!packets.is_empty());
 113         let mut decoder = opus::Decoder::new(/*sample_rate*/ 48_000, opus::Channels::Mono).unwrap();
 114         let mut energy = 0.0_f32;
 115         for packet in packets {
 116             let mut output = [0.0; 960];
 117             assert_eq!(
 118                 decoder
 119                     .decode_float(&packet.data, &mut output, /*fec*/ false)
 120                     .unwrap(),
 121                 960
 122             );
 123             assert!(output.iter().all(|sample| sample.is_finite()));
 124             energy += output.iter().map(|sample| sample * sample).sum::<f32>();
 125         }
 126         assert!(
 127             energy > 0.01,
 128             "decoded capture must contain signal at {rate} Hz"
 129         );
 130     }
 131 }
 132 
 133 #[test]
 134 fn unmute_reset_rejects_delayed_pre_unmute_audio_and_partial_history() {
 135     let mut processor = Processor::new(/*input_rate*/ 48_000, /*output_rate*/ 48_000).unwrap();
 136     let frame = Frame {
 137         samples: [0.25; 256],
 138         len: 256,
 139         at: Instant::now(),
 140         generation: 2,
 141     };
 142     assert!(processor.capture(&frame, || frame.at).unwrap().is_empty());
 143     assert_eq!(
 144         (0..10).find_map(|_| processor
 145             .capture(&frame, || frame.at + Duration::from_secs(1))
 146             .err()),
 147         Some("voice processing fell behind")
 148     );
 149     processor.reset().unwrap();
 150     for _ in 0..10 {
 151         assert!(processor.capture(&frame, || frame.at).unwrap().is_empty());
 152     }
 153     assert!(processor.pending.is_empty());
 154     assert!(processor.capture.input.is_empty());
 155     assert!(processor.capture.output.is_empty());
 156 }
 157 
 158 #[test]
 159 fn speaker_rate_change_discards_old_reference_but_keeps_capture_history() {
 160     for rate in [48_000, 24_000, 44_100] {
 161         let mut processor = Processor::new(/*input_rate*/ 48_000, /*output_rate*/ 48_000).unwrap();
 162         let mut fresh = Processor::new(/*input_rate*/ 48_000, rate).unwrap();
 163         let start = Instant::now();
 164         let mut frame = Frame {
 165             samples: [0.75; 256],
 166             len: 256,
 167             at: start,
 168             generation: 2,
 169         };
 170         processor.render.push(&frame).unwrap();
 171         processor.render.output.push_back(0.75);
 172         processor.capture.input.extend([0.25; 256]);
 173         processor.pending.extend([0.25; 480]);
 174         processor.render_delay = 123;
 175         processor.set_render_rate(rate).unwrap();
 176         assert_eq!(processor.capture.input, VecDeque::from(vec![0.25; 256]));
 177         assert_eq!(processor.pending, vec![0.25; 480]);
 178         assert_eq!(processor.render_delay, 0);
 179 
 180         frame.samples.fill(0.0);
 181         let mut actual = Vec::new();
 182         let mut expected = Vec::new();
 183         for index in 0..5 {
 184             frame.at = start + Duration::from_secs_f64(index as f64 * 256.0 / f64::from(rate));
 185             processor.render.push(&frame).unwrap();
 186             fresh.render.push(&frame).unwrap();
 187             actual.extend(std::iter::from_fn(|| processor.render.next()));
 188             expected.extend(std::iter::from_fn(|| fresh.render.next()));
 189         }
 190         assert!(!actual.is_empty());
 191         assert_eq!(actual, expected);
 192     }
 193 }
 194 
 195 #[test]
 196 fn capture_gap_discards_old_partial_packet_and_preserves_unmute_cutoff() {
 197     let mut processor = Processor::new(/*input_rate*/ 48_000, /*output_rate*/ 48_000).unwrap();
 198     let mut fresh = Processor::new(/*input_rate*/ 48_000, /*output_rate*/ 48_000).unwrap();
 199     let cutoff = processor.cutoff;
 200     let start = Instant::now();
 201     let mut frame = Frame {
 202         samples: [0.25; 256],
 203         len: 240,
 204         at: start,
 205         generation: 2,
 206     };
 207     for index in 0..5 {
 208         frame.at = start + Duration::from_millis(index * 5);
 209         assert!(processor.capture(&frame, || frame.at).unwrap().is_empty());
 210     }
 211     assert!(!processor.pending.is_empty());
 212     assert!(!processor.capture.input.is_empty());
 213 
 214     let resumed = start + Duration::from_millis(/*millis*/ 100);
 215     frame.samples.fill(0.0);
 216     let mut packets = Vec::new();
 217     let mut expected = Vec::new();
 218     for index in 0..12 {
 219         frame.at = resumed + Duration::from_millis(index * 5);
 220         let emitted = processor.capture(&frame, || frame.at).unwrap();
 221         if index < 2 {
 222             assert!(emitted.is_empty());
 223         }
 224         packets.extend(emitted.into_iter().map(|packet| (packet.at, packet.data)));
 225         expected.extend(
 226             fresh
 227                 .capture(&frame, || frame.at)
 228                 .unwrap()
 229                 .into_iter()
 230                 .map(|packet| (packet.at, packet.data)),
 231         );
 232     }
 233     assert!(!packets.is_empty());
 234     assert_eq!(packets, expected);
 235     assert_eq!(processor.cutoff, cutoff);
 236 }
 237 
 238 #[test]
 239 fn ordinary_capture_timestamp_jitter_keeps_partial_audio() {
 240     for jitter_ms in [-2_i64, 0, 2] {
 241         let mut processor = Processor::new(/*input_rate*/ 48_000, /*output_rate*/ 48_000).unwrap();
 242         let start = Instant::now();
 243         let mut frame = Frame {
 244             samples: [0.1; 256],
 245             len: 240,
 246             at: start,
 247             generation: 2,
 248         };
 249         for index in 0..5 {
 250             frame.at = start + Duration::from_millis(index * 5);
 251             assert!(processor.capture(&frame, || frame.at).unwrap().is_empty());
 252         }
 253         frame.at = start + Duration::from_millis((25 + jitter_ms) as u64);
 254         assert_eq!(processor.capture(&frame, || frame.at).unwrap().len(), 1);
 255     }
 256 }
 257 
 258 #[test]
 259 fn callback_gap_cannot_hide_in_a_packed_partial_block() {
 260     let mut processor = Processor::new(/*input_rate*/ 48_000, /*output_rate*/ 48_000).unwrap();
 261     let mut packer = super::super::buffers::FramePacker::default();
 262     let queue = crossbeam_queue::ArrayQueue::new(/*capacity*/ 8);
 263     let start = Instant::now();
 264     // Leave both an incomplete callback block and a processed half-packet.
 265     for index in 0..6 {
 266         let at = start + Duration::from_millis(index * 5);
 267         packer.discard_capture_gap(at, /*rate*/ 48_000.0);
 268         assert!(packer.push(
 269             Frame {
 270                 samples: [0.25; 256],
 271                 len: 240,
 272                 at,
 273                 generation: 2
 274             },
 275             /*rate*/ 48_000.0,
 276             &queue
 277         ));
 278         while let Some(frame) = queue.pop() {
 279             assert!(processor.capture(&frame, || frame.at).unwrap().is_empty());
 280         }
 281     }
 282     assert!(!processor.pending.is_empty());
 283     let resumed = start + Duration::from_millis(/*millis*/ 100);
 284     packer.discard_capture_gap(resumed, /*rate*/ 48_000.0);
 285     assert!(packer.push(
 286         Frame {
 287             samples: [0.0; 256],
 288             len: 256,
 289             at: resumed,
 290             generation: 2
 291         },
 292         /*rate*/ 48_000.0,
 293         &queue
 294     ));
 295     let frame = queue.pop().unwrap();
 296     assert_eq!(
 297         (frame.samples, frame.len, frame.at, frame.generation),
 298         ([0.0; 256], 256, resumed, 2)
 299     );
 300     assert!(processor.capture(&frame, || resumed).unwrap().is_empty());
 301     assert!(processor.pending.is_empty());
 302 }
 303 
 304 #[test]
 305 fn stale_echo_reference_cannot_cancel_capture_age() {
 306     for render_delay in [0, -750] {
 307         let mut processor = Processor::new(/*input_rate*/ 48_000, /*output_rate*/ 48_000).unwrap();
 308         processor.render_delay = render_delay;
 309         let start = Instant::now();
 310         let now = start + Duration::from_millis(/*millis*/ 750);
 311         let failure = (0..8).find_map(|index| {
 312             let frame = Frame {
 313                 samples: [0.25; 256],
 314                 len: 256,
 315                 at: start + Duration::from_secs_f64((index * 256) as f64 / 48_000.0),
 316                 generation: 2,
 317             };
 318             processor.capture(&frame, || now).err()
 319         });
 320         assert_eq!(failure, Some("voice processing fell behind"));
 321     }
 322 }