1use crate::codecs::ambe_plus2;
49use crate::codecs::mbe_baseline::analysis::{
50 AnalysisError, AnalysisOutput, AnalysisState, ToneDetection,
51 detect_tone, encode as analysis_encode,
52 encode_ambe_plus2 as analysis_encode_ambe_plus2,
53 profile as analysis_profile,
54};
55use crate::codecs::mbe_baseline::{
56 FrameDisposition, FrameErrorContext, GAMMA_W, SynthState, UnvoicedNoiseGen, synthesize_frame,
57};
58use crate::enhancement::{self, EnhancementMode, EnhancementState};
59use crate::mbe_params::MbeParams;
60use crate::imbe_wire;
61use crate::ambe_plus2_wire;
62
63const SAMPLE_RATE_HZ: f32 = 8_000.0;
65
66pub const FRAME_SAMPLES: usize = 160;
69
70#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
78#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
79#[non_exhaustive]
80pub enum Rate {
81 Imbe7200x4400,
84 Imbe4400x4400,
94 AmbePlus2_3600x2450,
99 AmbePlus2_2450x2450,
115}
116
117impl Rate {
118 #[inline]
124 pub const fn fec_frame_bytes(self) -> usize {
125 match self {
126 Rate::Imbe7200x4400 => 18,
127 Rate::Imbe4400x4400 => 11,
128 Rate::AmbePlus2_3600x2450 => 9,
129 Rate::AmbePlus2_2450x2450 => 7,
130 }
131 }
132
133 #[inline]
136 pub const fn frame_samples(self) -> usize {
137 FRAME_SAMPLES
138 }
139}
140
141#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
159#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
160#[non_exhaustive]
161pub enum AmbePlus2Synth {
162 AmbePlus,
165 Baseline,
170}
171
172impl Default for AmbePlus2Synth {
173 fn default() -> Self {
174 Self::AmbePlus
175 }
176}
177
178#[derive(Clone, Debug, Default)]
184#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
185pub struct FrameStats {
186 pub analysis: Option<AnalysisStats>,
188 pub decode: Option<DecodeStats>,
190}
191
192#[derive(Clone, Debug)]
194#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
195pub struct AnalysisStats {
196 pub output: AnalysisOutputKind,
198 pub params: MbeParams,
202 pub tone_detect: Option<ToneDetection>,
212}
213
214#[derive(Clone, Copy, Debug, Eq, PartialEq)]
219#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
220pub enum AnalysisOutputKind {
221 Voice,
223 Silence,
225 Tone {
230 id: u8,
232 amplitude: u8,
234 },
235}
236
237#[derive(Clone, Debug)]
239#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
240pub struct DecodeStats {
241 pub epsilon_0: u8,
244 pub epsilon_t: u8,
246 pub disposition: Option<FrameDisposition>,
252}
253
254#[derive(Debug)]
257pub enum VocoderError {
258 WrongPcmLength {
260 expected: usize,
262 got: usize,
264 },
265 WrongBitsLength {
267 expected: usize,
269 got: usize,
271 },
272 Analysis(AnalysisError),
274 Quantize(String),
276 UnsupportedTranscode {
279 from: Rate,
281 to: Rate,
283 },
284}
285
286impl core::fmt::Display for VocoderError {
287 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
288 match self {
289 VocoderError::WrongPcmLength { expected, got } => {
290 write!(f, "expected {expected} PCM samples per frame, got {got}")
291 }
292 VocoderError::WrongBitsLength { expected, got } => {
293 write!(f, "expected {expected} FEC bytes per frame, got {got}")
294 }
295 VocoderError::Analysis(e) => write!(f, "analysis encoder error: {e:?}"),
296 VocoderError::Quantize(msg) => write!(f, "quantize error: {msg}"),
297 VocoderError::UnsupportedTranscode { from, to } => {
298 write!(f, "unsupported transcode direction: {from:?} -> {to:?}")
299 }
300 }
301 }
302}
303
304impl std::error::Error for VocoderError {}
305
306pub struct Vocoder {
318 rate: Rate,
319 analysis: AnalysisState,
320 imbe_dec: imbe_wire::dequantize::DecoderState,
321 ambe_plus2_dec: ambe_plus2_wire::dequantize::DecoderState,
322 synth: SynthState,
323 last_stats: FrameStats,
324 tone_detection: bool,
325 ambe_plus2_synth: AmbePlus2Synth,
326 enhancement: EnhancementMode,
327 enhancement_state: EnhancementState,
328 prev_disposition: Option<FrameDisposition>,
329}
330
331impl Vocoder {
332 pub fn new(rate: Rate) -> Self {
342 let noise_gen = match rate {
343 Rate::Imbe7200x4400 | Rate::Imbe4400x4400 => UnvoicedNoiseGen::SpecLcg,
344 Rate::AmbePlus2_3600x2450 | Rate::AmbePlus2_2450x2450 => UnvoicedNoiseGen::ChipLcg,
345 };
346 Self {
347 rate,
348 analysis: AnalysisState::new(),
349 imbe_dec: imbe_wire::dequantize::DecoderState::new(),
350 ambe_plus2_dec: ambe_plus2_wire::dequantize::DecoderState::new(),
351 synth: SynthState::with_unvoiced_gen(noise_gen),
352 last_stats: FrameStats::default(),
353 tone_detection: false,
354 ambe_plus2_synth: AmbePlus2Synth::AmbePlus,
355 enhancement: EnhancementMode::Classical(crate::enhancement::ClassicalConfig::default()),
362 enhancement_state: EnhancementState::default(),
363 prev_disposition: None,
364 }
365 }
366
367 pub fn set_ambe_plus2_synth(&mut self, gen: AmbePlus2Synth) {
372 self.ambe_plus2_synth = gen;
373 }
374
375 #[inline]
377 pub fn ambe_plus2_synth(&self) -> AmbePlus2Synth {
378 self.ambe_plus2_synth
379 }
380
381 pub fn set_tone_detection(&mut self, enabled: bool) {
405 self.tone_detection = enabled;
406 }
407
408 #[inline]
410 pub fn tone_detection(&self) -> bool {
411 self.tone_detection
412 }
413
414 pub fn set_repeat_reset_after(&mut self, n: Option<u32>) {
421 self.synth.set_repeat_reset_after(n);
422 }
423
424 #[inline]
427 pub fn repeat_reset_after(&self) -> Option<u32> {
428 self.synth.repeat_reset_after()
429 }
430
431 pub fn set_chip_compat(&mut self, on: bool) {
443 self.synth.set_chip_compat(on);
444 }
445
446 #[inline]
448 pub fn chip_compat(&self) -> bool {
449 self.synth.chip_compat()
450 }
451
452 pub fn set_chip_compat_spectral_clamp(&mut self, on: bool) {
467 self.synth.set_chip_compat_spectral_clamp(on);
468 }
469
470 #[inline]
474 pub fn chip_compat_spectral_clamp(&self) -> bool {
475 self.synth.chip_compat_spectral_clamp()
476 }
477
478 pub fn set_silence_dispatch(&mut self, on: bool) {
485 self.analysis.set_silence_detection(on);
486 }
487
488 #[inline]
490 pub fn silence_dispatch(&self) -> bool {
491 self.analysis.silence_detection_enabled()
492 }
493
494 pub fn set_pitch_silence_override(&mut self, on: bool) {
499 self.analysis.set_pitch_silence_override(on);
500 }
501
502 #[inline]
504 pub fn pitch_silence_override(&self) -> bool {
505 self.analysis.pitch_silence_override_enabled()
506 }
507
508 pub fn set_default_pitch_on_silence(&mut self, on: bool) {
519 self.analysis.set_default_pitch_on_silence(on);
520 }
521
522 #[inline]
524 pub fn default_pitch_on_silence(&self) -> bool {
525 self.analysis.default_pitch_on_silence_enabled()
526 }
527
528 pub fn set_pyin_pitch(&mut self, on: bool) {
535 self.analysis.set_pyin_pitch(on);
536 }
537
538 #[inline]
540 pub fn pyin_pitch(&self) -> bool {
541 self.analysis.pyin_pitch_enabled()
542 }
543
544 pub fn set_spectral_subtraction(&mut self, on: bool) {
551 self.analysis.set_spectral_subtraction(on);
552 }
553
554 #[inline]
556 pub fn spectral_subtraction(&self) -> bool {
557 self.analysis.spectral_subtraction_enabled()
558 }
559
560 pub fn set_amp_ema_alpha(&mut self, alpha: f64) {
567 self.analysis.set_amp_ema_alpha(alpha);
568 }
569
570 #[inline]
573 pub fn amp_ema_alpha(&self) -> f64 {
574 self.analysis.amp_ema_alpha()
575 }
576
577 pub fn set_enhancement(&mut self, mode: EnhancementMode) {
588 self.enhancement = mode;
589 self.enhancement_state = EnhancementState::default();
590 }
591
592 #[inline]
594 pub fn enhancement(&self) -> &EnhancementMode {
595 &self.enhancement
596 }
597
598 #[inline]
601 pub fn builder(rate: Rate) -> VocoderBuilder {
602 VocoderBuilder::new(rate)
603 }
604
605 #[inline]
609 pub fn rate(&self) -> Rate {
610 self.rate
611 }
612
613 #[inline]
616 pub fn frame_samples(&self) -> usize {
617 self.rate.frame_samples()
618 }
619
620 #[inline]
622 pub fn fec_frame_bytes(&self) -> usize {
623 self.rate.fec_frame_bytes()
624 }
625
626 #[inline]
629 pub fn last_stats(&self) -> &FrameStats {
630 &self.last_stats
631 }
632
633 #[inline]
640 pub fn last_disposition(&self) -> Option<FrameDisposition> {
641 self.synth.last_disposition()
642 }
643
644 pub fn reset(&mut self) {
649 self.analysis = AnalysisState::new();
650 self.imbe_dec = imbe_wire::dequantize::DecoderState::new();
651 self.ambe_plus2_dec = ambe_plus2_wire::dequantize::DecoderState::new();
652 self.synth = SynthState::new();
653 self.last_stats = FrameStats::default();
654 self.enhancement_state = EnhancementState::default();
655 self.prev_disposition = None;
656 }
658
659 pub fn encode_pcm(&mut self, pcm: &[i16]) -> Result<Vec<u8>, VocoderError> {
664 if pcm.len() != self.frame_samples() {
665 return Err(VocoderError::WrongPcmLength {
666 expected: self.frame_samples(),
667 got: pcm.len(),
668 });
669 }
670 let (bytes, stats) = match self.rate {
671 Rate::Imbe7200x4400 => imbe_pipeline::encode(pcm, self, true)?,
672 Rate::Imbe4400x4400 => imbe_pipeline::encode(pcm, self, false)?,
673 Rate::AmbePlus2_3600x2450 => ambe_plus2_pipeline::encode(pcm, self, true)?,
674 Rate::AmbePlus2_2450x2450 => ambe_plus2_pipeline::encode(pcm, self, false)?,
675 };
676 self.last_stats.analysis = Some(stats);
677 Ok(bytes)
678 }
679
680 pub fn decode_bits(&mut self, bits: &[u8]) -> Result<Vec<i16>, VocoderError> {
685 if bits.len() != self.fec_frame_bytes() {
686 return Err(VocoderError::WrongBitsLength {
687 expected: self.fec_frame_bytes(),
688 got: bits.len(),
689 });
690 }
691 let (mut pcm, stats) = match self.rate {
692 Rate::Imbe7200x4400 => imbe_pipeline::decode(bits, self, true),
693 Rate::Imbe4400x4400 => imbe_pipeline::decode(bits, self, false),
694 Rate::AmbePlus2_3600x2450 => ambe_plus2_pipeline::decode(bits, self, true),
695 Rate::AmbePlus2_2450x2450 => ambe_plus2_pipeline::decode(bits, self, false),
696 };
697 let prev_was_use = matches!(self.prev_disposition, Some(FrameDisposition::Use));
698 enhancement::apply(
699 &self.enhancement,
700 &mut self.enhancement_state,
701 &mut pcm,
702 SAMPLE_RATE_HZ,
703 prev_was_use,
704 );
705 self.prev_disposition = stats.disposition;
706 self.last_stats.decode = Some(stats);
707 Ok(pcm)
708 }
709
710 pub fn encode_stream<'a>(&'a mut self, pcm: &'a [i16]) -> EncodeStream<'a> {
730 EncodeStream { vocoder: self, pcm, pos: 0 }
731 }
732
733 pub fn extract_params(&mut self, pcm: &[i16]) -> Result<MbeParams, VocoderError> {
751 if pcm.len() != self.frame_samples() {
752 return Err(VocoderError::WrongPcmLength {
753 expected: self.frame_samples(),
754 got: pcm.len(),
755 });
756 }
757 let frame = pcm.try_into().expect("length already validated");
758 let analysis_out = match self.rate {
759 Rate::Imbe7200x4400 | Rate::Imbe4400x4400 => {
760 analysis_encode(frame, &mut self.analysis)
761 }
762 Rate::AmbePlus2_3600x2450 | Rate::AmbePlus2_2450x2450 => {
763 analysis_encode_ambe_plus2(frame, &mut self.analysis)
764 }
765 }
766 .map_err(VocoderError::Analysis)?;
767 Ok(match analysis_out {
768 AnalysisOutput::Voice(p) => p,
769 AnalysisOutput::Silence => match self.rate {
770 Rate::Imbe7200x4400 | Rate::Imbe4400x4400 => MbeParams::silence(),
771 Rate::AmbePlus2_3600x2450 | Rate::AmbePlus2_2450x2450 => {
772 MbeParams::silence_ambe_plus2()
773 }
774 },
775 })
776 }
777
778 pub fn synthesize_params(&mut self, params: &MbeParams) -> Vec<i16> {
799 let prev_err = self.synth.err;
803 self.synth.err = FrameErrorContext::default();
804 let err = self.synth.err;
805 let gamma_w = self.synth.gamma_w;
806 let pcm: [i16; FRAME_SAMPLES] = match self.rate {
807 Rate::Imbe7200x4400 | Rate::Imbe4400x4400 => {
808 synthesize_frame(params, &err, gamma_w, &mut self.synth)
809 }
810 Rate::AmbePlus2_3600x2450 | Rate::AmbePlus2_2450x2450 => match self.ambe_plus2_synth {
811 AmbePlus2Synth::AmbePlus => ambe_plus2::synthesize_frame(params, &mut self.synth),
812 AmbePlus2Synth::Baseline => synthesize_frame(params, &err, gamma_w, &mut self.synth),
813 },
814 };
815 self.synth.err = prev_err;
816 let mut pcm = pcm.to_vec();
817 let prev_was_use = matches!(self.prev_disposition, Some(FrameDisposition::Use));
818 enhancement::apply(
819 &self.enhancement,
820 &mut self.enhancement_state,
821 &mut pcm,
822 SAMPLE_RATE_HZ,
823 prev_was_use,
824 );
825 self.prev_disposition = self.synth.last_disposition();
826 pcm
827 }
828
829 pub fn decode_stream<'a>(&'a mut self, bits: &'a [u8]) -> DecodeStream<'a> {
844 DecodeStream { vocoder: self, bits, pos: 0 }
845 }
846}
847
848pub struct EncodeStream<'a> {
852 vocoder: &'a mut Vocoder,
853 pcm: &'a [i16],
854 pos: usize,
855}
856
857impl Iterator for EncodeStream<'_> {
858 type Item = Result<Vec<u8>, VocoderError>;
859 fn next(&mut self) -> Option<Self::Item> {
860 let n = self.vocoder.frame_samples();
861 if self.pos + n > self.pcm.len() {
862 return None;
863 }
864 let frame = &self.pcm[self.pos..self.pos + n];
865 self.pos += n;
866 Some(self.vocoder.encode_pcm(frame))
867 }
868
869 fn size_hint(&self) -> (usize, Option<usize>) {
870 let remaining = (self.pcm.len() - self.pos) / self.vocoder.frame_samples();
871 (remaining, Some(remaining))
872 }
873}
874
875impl ExactSizeIterator for EncodeStream<'_> {}
876
877pub struct DecodeStream<'a> {
881 vocoder: &'a mut Vocoder,
882 bits: &'a [u8],
883 pos: usize,
884}
885
886impl Iterator for DecodeStream<'_> {
887 type Item = Result<Vec<i16>, VocoderError>;
888 fn next(&mut self) -> Option<Self::Item> {
889 let n = self.vocoder.fec_frame_bytes();
890 if self.pos + n > self.bits.len() {
891 return None;
892 }
893 let frame = &self.bits[self.pos..self.pos + n];
894 self.pos += n;
895 Some(self.vocoder.decode_bits(frame))
896 }
897
898 fn size_hint(&self) -> (usize, Option<usize>) {
899 let remaining = (self.bits.len() - self.pos) / self.vocoder.fec_frame_bytes();
900 (remaining, Some(remaining))
901 }
902}
903
904impl ExactSizeIterator for DecodeStream<'_> {}
905
906#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
912#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
913pub struct TranscodeDirection {
914 pub from: Rate,
916 pub to: Rate,
918}
919
920impl TranscodeDirection {
921 #[inline]
923 pub const fn new(from: Rate, to: Rate) -> Self {
924 Self { from, to }
925 }
926
927 #[inline]
929 pub const fn input_frame_bytes(self) -> usize {
930 self.from.fec_frame_bytes()
931 }
932
933 #[inline]
935 pub const fn output_frame_bytes(self) -> usize {
936 self.to.fec_frame_bytes()
937 }
938}
939
940pub struct Transcoder {
961 direction: TranscodeDirection,
962 full_to_half: Option<crate::rate_conversion::FullToHalfConverter>,
963 half_to_full: Option<crate::rate_conversion::HalfToFullConverter>,
964}
965
966impl Transcoder {
967 pub fn new(from: Rate, to: Rate) -> Result<Self, VocoderError> {
983 let direction = TranscodeDirection { from, to };
984 match (from, to) {
985 (Rate::Imbe7200x4400, Rate::AmbePlus2_3600x2450) => Ok(Self {
986 direction,
987 full_to_half: Some(crate::rate_conversion::FullToHalfConverter::new()),
988 half_to_full: None,
989 }),
990 (Rate::AmbePlus2_3600x2450, Rate::Imbe7200x4400) => Ok(Self {
991 direction,
992 full_to_half: None,
993 half_to_full: Some(crate::rate_conversion::HalfToFullConverter::new()),
994 }),
995 (Rate::Imbe7200x4400, Rate::Imbe4400x4400)
998 | (Rate::Imbe4400x4400, Rate::Imbe7200x4400)
999 | (Rate::AmbePlus2_3600x2450, Rate::AmbePlus2_2450x2450)
1000 | (Rate::AmbePlus2_2450x2450, Rate::AmbePlus2_3600x2450) => Ok(Self {
1001 direction,
1002 full_to_half: None,
1003 half_to_full: None,
1004 }),
1005 _ => Err(VocoderError::UnsupportedTranscode { from, to }),
1006 }
1007 }
1008
1009 #[inline]
1011 pub fn direction(&self) -> TranscodeDirection {
1012 self.direction
1013 }
1014
1015 pub fn transcode(&mut self, bits: &[u8]) -> Result<Vec<u8>, VocoderError> {
1021 let in_n = self.direction.input_frame_bytes();
1022 if bits.len() != in_n {
1023 return Err(VocoderError::WrongBitsLength {
1024 expected: in_n,
1025 got: bits.len(),
1026 });
1027 }
1028 match (self.direction.from, self.direction.to) {
1029 (Rate::Imbe7200x4400, Rate::AmbePlus2_3600x2450) => {
1030 let dibits_in = unpack_dibits_n::<72>(bits);
1031 let dibits_out = self
1032 .full_to_half
1033 .as_mut()
1034 .expect("constructed with this direction")
1035 .convert(&dibits_in)
1036 .map_err(|e| VocoderError::Quantize(format!("{e:?}")))?;
1037 Ok(pack_dibits_n::<36, 9>(&dibits_out).to_vec())
1038 }
1039 (Rate::AmbePlus2_3600x2450, Rate::Imbe7200x4400) => {
1040 let dibits_in = unpack_dibits_n::<36>(bits);
1041 let dibits_out = self
1042 .half_to_full
1043 .as_mut()
1044 .expect("constructed with this direction")
1045 .convert(&dibits_in)
1046 .map_err(|e| VocoderError::Quantize(format!("{e:?}")))?;
1047 Ok(pack_dibits_n::<72, 18>(&dibits_out).to_vec())
1048 }
1049 (Rate::Imbe7200x4400, Rate::Imbe4400x4400) => {
1050 Ok(imbe_pipeline::fec_to_info_bytes(bits).to_vec())
1051 }
1052 (Rate::Imbe4400x4400, Rate::Imbe7200x4400) => {
1053 Ok(imbe_pipeline::info_to_fec_bytes(bits).to_vec())
1054 }
1055 (Rate::AmbePlus2_3600x2450, Rate::AmbePlus2_2450x2450) => {
1056 Ok(ambe_plus2_pipeline::fec_to_info_bytes(bits).to_vec())
1057 }
1058 (Rate::AmbePlus2_2450x2450, Rate::AmbePlus2_3600x2450) => {
1059 Ok(ambe_plus2_pipeline::info_to_fec_bytes(bits).to_vec())
1060 }
1061 (from, to) => Err(VocoderError::UnsupportedTranscode { from, to }),
1062 }
1063 }
1064
1065 pub fn reset(&mut self) {
1068 *self = Self::new(self.direction.from, self.direction.to)
1069 .expect("direction was validated at construction");
1070 }
1071}
1072
1073fn unpack_dibits_n<const N: usize>(bytes: &[u8]) -> [u8; N] {
1074 let mut out = [0u8; N];
1075 let mut bit = 0usize;
1076 for slot in &mut out {
1077 let mut d = 0u8;
1078 for _ in 0..2 {
1079 let b = (bytes[bit / 8] >> (7 - (bit % 8))) & 1;
1080 d = (d << 1) | b;
1081 bit += 1;
1082 }
1083 *slot = d;
1084 }
1085 out
1086}
1087
1088fn pack_dibits_n<const N: usize, const B: usize>(dibits: &[u8; N]) -> [u8; B] {
1089 let mut out = [0u8; B];
1090 let mut bit = 0usize;
1091 for &d in dibits {
1092 for pos in (0..2).rev() {
1093 let b = (d >> pos) & 1;
1094 out[bit / 8] |= b << (7 - (bit % 8));
1095 bit += 1;
1096 }
1097 }
1098 out
1099}
1100
1101pub struct LiveEncoder {
1122 vocoder: Vocoder,
1123 pcm_buf: Vec<i16>,
1124}
1125
1126impl LiveEncoder {
1127 pub fn new(rate: Rate) -> Self {
1129 Self {
1130 vocoder: Vocoder::new(rate),
1131 pcm_buf: Vec::new(),
1132 }
1133 }
1134
1135 #[inline]
1138 pub fn vocoder(&self) -> &Vocoder {
1139 &self.vocoder
1140 }
1141
1142 pub fn push(&mut self, pcm: &[i16]) -> Vec<Result<Vec<u8>, VocoderError>> {
1147 self.pcm_buf.extend_from_slice(pcm);
1148 let n = self.vocoder.frame_samples();
1149 let mut out = Vec::with_capacity(self.pcm_buf.len() / n);
1150 while self.pcm_buf.len() >= n {
1151 let result = self.vocoder.encode_pcm(&self.pcm_buf[..n]);
1155 self.pcm_buf.drain(..n);
1156 out.push(result);
1157 }
1158 out
1159 }
1160
1161 #[inline]
1164 pub fn pending_samples(&self) -> usize {
1165 self.pcm_buf.len()
1166 }
1167
1168 #[inline]
1172 pub fn discard_pending(&mut self) {
1173 self.pcm_buf.clear();
1174 }
1175
1176 pub fn flush(&mut self) -> Result<Option<Vec<u8>>, VocoderError> {
1185 if self.pcm_buf.is_empty() {
1186 return Ok(None);
1187 }
1188 let n = self.vocoder.frame_samples();
1189 self.pcm_buf.resize(n, 0);
1190 let bits = self.vocoder.encode_pcm(&self.pcm_buf)?;
1191 self.pcm_buf.clear();
1192 Ok(Some(bits))
1193 }
1194
1195 pub fn reset(&mut self) {
1198 self.vocoder.reset();
1199 self.pcm_buf.clear();
1200 }
1201
1202 #[inline]
1204 pub fn rate(&self) -> Rate {
1205 self.vocoder.rate()
1206 }
1207}
1208
1209pub struct LiveDecoder {
1222 vocoder: Vocoder,
1223 bits_buf: Vec<u8>,
1224}
1225
1226impl LiveDecoder {
1227 pub fn new(rate: Rate) -> Self {
1229 Self {
1230 vocoder: Vocoder::new(rate),
1231 bits_buf: Vec::new(),
1232 }
1233 }
1234
1235 #[inline]
1237 pub fn vocoder(&self) -> &Vocoder {
1238 &self.vocoder
1239 }
1240
1241 pub fn push(&mut self, bits: &[u8]) -> Vec<Result<Vec<i16>, VocoderError>> {
1245 self.bits_buf.extend_from_slice(bits);
1246 let n = self.vocoder.fec_frame_bytes();
1247 let mut out = Vec::with_capacity(self.bits_buf.len() / n);
1248 while self.bits_buf.len() >= n {
1249 let result = self.vocoder.decode_bits(&self.bits_buf[..n]);
1250 self.bits_buf.drain(..n);
1251 out.push(result);
1252 }
1253 out
1254 }
1255
1256 #[inline]
1259 pub fn pending_bytes(&self) -> usize {
1260 self.bits_buf.len()
1261 }
1262
1263 #[inline]
1265 pub fn discard_pending(&mut self) {
1266 self.bits_buf.clear();
1267 }
1268
1269 pub fn reset(&mut self) {
1271 self.vocoder.reset();
1272 self.bits_buf.clear();
1273 }
1274
1275 #[inline]
1277 pub fn rate(&self) -> Rate {
1278 self.vocoder.rate()
1279 }
1280}
1281
1282#[derive(Clone, Debug)]
1297pub struct VocoderBuilder {
1298 rate: Rate,
1299 tone_detection: bool,
1300 repeat_reset_after: Option<u32>,
1301 chip_compat: bool,
1302 silence_dispatch: bool,
1303 pitch_silence_override: bool,
1304 default_pitch_on_silence: bool,
1305 pyin_pitch: bool,
1306 spectral_subtraction: bool,
1307 amp_ema_alpha: f64,
1308 ambe_plus2_synth: AmbePlus2Synth,
1309 enhancement: EnhancementMode,
1310}
1311
1312impl VocoderBuilder {
1313 #[inline]
1321 pub fn new(rate: Rate) -> Self {
1322 Self {
1323 rate,
1324 tone_detection: false,
1325 repeat_reset_after: None,
1326 chip_compat: false,
1327 silence_dispatch: false,
1328 pitch_silence_override: false,
1329 default_pitch_on_silence: false,
1330 pyin_pitch: false,
1331 spectral_subtraction: true,
1332 amp_ema_alpha: 0.0,
1333 ambe_plus2_synth: AmbePlus2Synth::AmbePlus,
1334 enhancement: EnhancementMode::Classical(
1335 crate::enhancement::ClassicalConfig::default(),
1336 ),
1337 }
1338 }
1339
1340 #[inline]
1343 pub fn tone_detection(mut self, on: bool) -> Self {
1344 self.tone_detection = on;
1345 self
1346 }
1347
1348 #[inline]
1351 pub fn repeat_reset_after(mut self, n: Option<u32>) -> Self {
1352 self.repeat_reset_after = n;
1353 self
1354 }
1355
1356 #[inline]
1359 pub fn chip_compat(mut self, on: bool) -> Self {
1360 self.chip_compat = on;
1361 self
1362 }
1363
1364 #[inline]
1367 pub fn silence_dispatch(mut self, on: bool) -> Self {
1368 self.silence_dispatch = on;
1369 self
1370 }
1371
1372 #[inline]
1376 pub fn pitch_silence_override(mut self, on: bool) -> Self {
1377 self.pitch_silence_override = on;
1378 self
1379 }
1380
1381 #[inline]
1385 pub fn default_pitch_on_silence(mut self, on: bool) -> Self {
1386 self.default_pitch_on_silence = on;
1387 self
1388 }
1389
1390 #[inline]
1393 pub fn pyin_pitch(mut self, on: bool) -> Self {
1394 self.pyin_pitch = on;
1395 self
1396 }
1397
1398 #[inline]
1401 pub fn spectral_subtraction(mut self, on: bool) -> Self {
1402 self.spectral_subtraction = on;
1403 self
1404 }
1405
1406 #[inline]
1409 pub fn amp_ema_alpha(mut self, alpha: f64) -> Self {
1410 self.amp_ema_alpha = alpha;
1411 self
1412 }
1413
1414 #[inline]
1417 pub fn ambe_plus2_synth(mut self, gen: AmbePlus2Synth) -> Self {
1418 self.ambe_plus2_synth = gen;
1419 self
1420 }
1421
1422 #[inline]
1425 pub fn enhancement(mut self, mode: EnhancementMode) -> Self {
1426 self.enhancement = mode;
1427 self
1428 }
1429
1430 pub fn build(self) -> Vocoder {
1432 let mut v = Vocoder::new(self.rate);
1433 v.set_tone_detection(self.tone_detection);
1434 v.set_repeat_reset_after(self.repeat_reset_after);
1435 v.set_chip_compat(self.chip_compat);
1436 v.set_silence_dispatch(self.silence_dispatch);
1437 v.set_pitch_silence_override(self.pitch_silence_override);
1438 v.set_default_pitch_on_silence(self.default_pitch_on_silence);
1439 v.set_pyin_pitch(self.pyin_pitch);
1440 v.set_spectral_subtraction(self.spectral_subtraction);
1441 v.set_amp_ema_alpha(self.amp_ema_alpha);
1442 v.set_ambe_plus2_synth(self.ambe_plus2_synth);
1443 v.set_enhancement(self.enhancement);
1444 v
1445 }
1446}
1447
1448impl core::fmt::Debug for Vocoder {
1449 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1450 f.debug_struct("Vocoder")
1451 .field("rate", &self.rate)
1452 .field("frame_samples", &self.frame_samples())
1453 .field("fec_frame_bytes", &self.fec_frame_bytes())
1454 .finish_non_exhaustive()
1455 }
1456}
1457
1458mod imbe_pipeline {
1459 use super::*;
1460 use crate::imbe_wire::dequantize::{dequantize, quantize};
1461 use crate::imbe_wire::frame::{INFO_WIDTHS, decode_frame, encode_frame};
1462 use crate::imbe_wire::priority::{deprioritize, prioritize};
1463
1464 pub(super) fn encode(
1465 pcm: &[i16],
1466 vocoder: &mut Vocoder,
1467 apply_fec: bool,
1468 ) -> Result<(Vec<u8>, AnalysisStats), VocoderError> {
1469 let tone_detect = if vocoder.tone_detection {
1474 detect_tone(pcm)
1475 } else {
1476 None
1477 };
1478 let frame = pcm.try_into().expect("length already validated");
1479 let (kind, params) = match analysis_encode(frame, &mut vocoder.analysis)
1480 .map_err(VocoderError::Analysis)?
1481 {
1482 AnalysisOutput::Voice(p) => (AnalysisOutputKind::Voice, p),
1483 AnalysisOutput::Silence => (AnalysisOutputKind::Silence, MbeParams::silence()),
1484 };
1485 let mut snapshot = vocoder.imbe_dec.clone();
1486 let b = quantize(¶ms, &mut vocoder.imbe_dec)
1487 .map_err(|e| VocoderError::Quantize(format!("{e:?}")))?;
1488 let l = params.harmonic_count();
1492 let info = prioritize(&b, l);
1493 let _ = deprioritize; let _ = dequantize(&info, &mut snapshot);
1495 vocoder.imbe_dec = snapshot;
1496 let bytes: Vec<u8> = if apply_fec {
1497 let dibits = encode_frame(&info);
1498 pack_dibits_full(&dibits).to_vec()
1499 } else {
1500 pack_info_full(&info).to_vec()
1501 };
1502 Ok((bytes, AnalysisStats { output: kind, params, tone_detect }))
1503 }
1504
1505 pub(super) fn decode(
1506 bits: &[u8],
1507 vocoder: &mut Vocoder,
1508 apply_fec: bool,
1509 ) -> (Vec<i16>, DecodeStats) {
1510 let (info, stats_eps0, stats_epst, eps4) = if apply_fec {
1511 let dibits = analysis_profile::time(analysis_profile::Stage::DibitUnpack, || {
1512 unpack_dibits_full(bits)
1513 });
1514 let imbe = analysis_profile::time(analysis_profile::Stage::DecodeFrame, || {
1515 decode_frame(&dibits)
1516 });
1517 let s0: u8 = imbe.errors[0];
1518 let st: u8 = imbe.error_total().min(255) as u8;
1519 let e4: u8 = imbe.errors[4];
1520 (imbe.info, s0, st, e4)
1521 } else {
1522 let info = unpack_info_full(bits);
1524 (info, 0u8, 0u8, 0u8)
1525 };
1526 let dq = analysis_profile::time(analysis_profile::Stage::Dequantize, || {
1527 dequantize(&info, &mut vocoder.imbe_dec)
1528 });
1529 let pcm: [i16; FRAME_SAMPLES] = match dq {
1530 Ok(params) => {
1531 let err = FrameErrorContext {
1532 epsilon_0: stats_eps0,
1533 epsilon_4: eps4,
1534 epsilon_t: stats_epst,
1535 bad_pitch: false,
1536 };
1537 synthesize_frame(¶ms, &err, GAMMA_W, &mut vocoder.synth)
1538 }
1539 Err(_) => [0i16; FRAME_SAMPLES],
1540 };
1541 let disposition = vocoder.synth.last_disposition();
1542 (
1543 pcm.to_vec(),
1544 DecodeStats {
1545 epsilon_0: stats_eps0,
1546 epsilon_t: stats_epst,
1547 disposition,
1548 },
1549 )
1550 }
1551
1552 fn pack_info_full(info: &[u16; 8]) -> [u8; 11] {
1553 let mut out = [0u8; 11];
1554 super::pack_info_bits(info, &INFO_WIDTHS, &mut out);
1555 out
1556 }
1557
1558 fn unpack_info_full(bytes: &[u8]) -> [u16; 8] {
1559 let mut out = [0u16; 8];
1560 super::unpack_info_bits(bytes, &INFO_WIDTHS, &mut out);
1561 out
1562 }
1563
1564 pub(super) fn fec_to_info_bytes(fec_bytes: &[u8]) -> [u8; 11] {
1567 let dibits = unpack_dibits_full(fec_bytes);
1568 let frame = decode_frame(&dibits);
1569 pack_info_full(&frame.info)
1570 }
1571
1572 pub(super) fn info_to_fec_bytes(info_bytes: &[u8]) -> [u8; 18] {
1575 let info = unpack_info_full(info_bytes);
1576 let dibits = encode_frame(&info);
1577 pack_dibits_full(&dibits)
1578 }
1579
1580 fn pack_dibits_full(dibits: &[u8; 72]) -> [u8; 18] {
1581 let mut out = [0u8; 18];
1582 let mut bit = 0usize;
1583 for &d in dibits {
1584 for pos in (0..2).rev() {
1585 let b = (d >> pos) & 1;
1586 out[bit / 8] |= b << (7 - (bit % 8));
1587 bit += 1;
1588 }
1589 }
1590 out
1591 }
1592
1593 fn unpack_dibits_full(bytes: &[u8]) -> [u8; 72] {
1594 let mut out = [0u8; 72];
1595 let mut bit = 0usize;
1596 for slot in &mut out {
1597 let mut d = 0u8;
1598 for _ in 0..2 {
1599 let b = (bytes[bit / 8] >> (7 - (bit % 8))) & 1;
1600 d = (d << 1) | b;
1601 bit += 1;
1602 }
1603 *slot = d;
1604 }
1605 out
1606 }
1607}
1608
1609mod ambe_plus2_pipeline {
1610 use super::*;
1611 use crate::ambe_plus2_wire::dequantize::{
1612 Decoded, decode_to_params, encode_tone_frame_info, quantize, tone_to_mbe_params,
1613 };
1614 use crate::ambe_plus2_wire::frame::{INFO_WIDTHS, decode_frame, encode_frame};
1615
1616 pub(super) fn encode(
1617 pcm: &[i16],
1618 vocoder: &mut Vocoder,
1619 apply_fec: bool,
1620 ) -> Result<(Vec<u8>, AnalysisStats), VocoderError> {
1621 let tone_detect = if vocoder.tone_detection {
1626 detect_tone(pcm)
1627 } else {
1628 None
1629 };
1630 if let Some(ToneDetection { id, amplitude }) = tone_detect {
1631 let info = encode_tone_frame_info(id, amplitude);
1632 let bytes = pack_info_or_fec(&info, apply_fec);
1633 let params = tone_to_mbe_params(id, amplitude)
1639 .unwrap_or_else(MbeParams::silence_ambe_plus2);
1640 return Ok((
1641 bytes,
1642 AnalysisStats {
1643 output: AnalysisOutputKind::Tone { id, amplitude },
1644 params,
1645 tone_detect,
1646 },
1647 ));
1648 }
1649
1650 let frame = pcm.try_into().expect("length already validated");
1651 let (kind, params) = match analysis_encode_ambe_plus2(frame, &mut vocoder.analysis)
1652 .map_err(VocoderError::Analysis)?
1653 {
1654 AnalysisOutput::Voice(p) => (AnalysisOutputKind::Voice, p),
1655 AnalysisOutput::Silence => (AnalysisOutputKind::Silence, MbeParams::silence_ambe_plus2()),
1656 };
1657 let info = quantize(¶ms, &mut vocoder.ambe_plus2_dec)
1658 .map_err(|e| VocoderError::Quantize(format!("{e:?}")))?;
1659 let bytes = pack_info_or_fec(&info, apply_fec);
1660 Ok((bytes, AnalysisStats { output: kind, params, tone_detect }))
1661 }
1662
1663 fn pack_info_or_fec(info: &[u16; 4], apply_fec: bool) -> Vec<u8> {
1664 if apply_fec {
1665 let dibits = encode_frame(info);
1666 pack_dibits_half(&dibits).to_vec()
1667 } else {
1668 pack_info_half(info).to_vec()
1669 }
1670 }
1671
1672 pub(super) fn decode(
1673 bits: &[u8],
1674 vocoder: &mut Vocoder,
1675 apply_fec: bool,
1676 ) -> (Vec<i16>, DecodeStats) {
1677 let (info, stats_eps0, stats_epst, eps3) = if apply_fec {
1678 let dibits = analysis_profile::time(analysis_profile::Stage::DibitUnpack, || {
1679 unpack_dibits_half(bits)
1680 });
1681 let frame = analysis_profile::time(analysis_profile::Stage::DecodeFrame, || {
1682 decode_frame(&dibits)
1683 });
1684 const E0_UNCORRECTABLE_CAP: u8 = 4;
1700 let raw_e0 = frame.errors[0];
1701 let s0: u8 = if raw_e0 == u8::MAX { E0_UNCORRECTABLE_CAP } else { raw_e0 };
1702 let s1: u8 = frame.errors[1];
1703 let st: u8 = u16::from(s0)
1704 .saturating_add(u16::from(s1))
1705 .min(255) as u8;
1706 let e3: u8 = frame.errors[3];
1707 (frame.info, s0, st, e3)
1708 } else {
1709 let info = unpack_info_half(bits);
1711 (info, 0u8, 0u8, 0u8)
1712 };
1713 let err = FrameErrorContext {
1714 epsilon_0: stats_eps0,
1715 epsilon_4: eps3, epsilon_t: stats_epst,
1717 bad_pitch: false,
1718 };
1719 vocoder.synth.err = err;
1726 let dq = analysis_profile::time(analysis_profile::Stage::Dequantize, || {
1727 decode_to_params(&info, &mut vocoder.ambe_plus2_dec)
1728 });
1729 let pcm: [i16; FRAME_SAMPLES] = match dq {
1730 Ok(Decoded::Voice(p)) => match vocoder.ambe_plus2_synth {
1731 AmbePlus2Synth::AmbePlus => ambe_plus2::synthesize_frame(&p, &mut vocoder.synth),
1732 AmbePlus2Synth::Baseline => {
1733 let gamma_w = vocoder.synth.gamma_w;
1734 synthesize_frame(&p, &err, gamma_w, &mut vocoder.synth)
1735 }
1736 },
1737 Ok(Decoded::Tone { params, .. }) => match vocoder.ambe_plus2_synth {
1738 AmbePlus2Synth::AmbePlus => ambe_plus2::synthesize_tone(¶ms, &mut vocoder.synth),
1739 AmbePlus2Synth::Baseline => {
1740 let gamma_w = vocoder.synth.gamma_w;
1741 synthesize_frame(¶ms, &err, gamma_w, &mut vocoder.synth)
1742 }
1743 },
1744 Ok(Decoded::Erasure) | Err(_) => [0i16; FRAME_SAMPLES],
1745 };
1746 let disposition = vocoder.synth.last_disposition();
1747 (
1748 pcm.to_vec(),
1749 DecodeStats {
1750 epsilon_0: stats_eps0,
1751 epsilon_t: stats_epst,
1752 disposition,
1753 },
1754 )
1755 }
1756
1757 fn pack_dibits_half(dibits: &[u8; 36]) -> [u8; 9] {
1758 let mut out = [0u8; 9];
1759 let mut bit = 0usize;
1760 for &d in dibits {
1761 for pos in (0..2).rev() {
1762 let b = (d >> pos) & 1;
1763 out[bit / 8] |= b << (7 - (bit % 8));
1764 bit += 1;
1765 }
1766 }
1767 out
1768 }
1769
1770 fn unpack_dibits_half(bytes: &[u8]) -> [u8; 36] {
1771 let mut out = [0u8; 36];
1772 let mut bit = 0usize;
1773 for slot in &mut out {
1774 let mut d = 0u8;
1775 for _ in 0..2 {
1776 let b = (bytes[bit / 8] >> (7 - (bit % 8))) & 1;
1777 d = (d << 1) | b;
1778 bit += 1;
1779 }
1780 *slot = d;
1781 }
1782 out
1783 }
1784
1785 fn pack_info_half(info: &[u16; 4]) -> [u8; 7] {
1786 let mut out = [0u8; 7];
1787 super::pack_info_bits(info, &INFO_WIDTHS, &mut out);
1788 out
1789 }
1790
1791 fn unpack_info_half(bytes: &[u8]) -> [u16; 4] {
1792 let mut out = [0u16; 4];
1793 super::unpack_info_bits(bytes, &INFO_WIDTHS, &mut out);
1794 out
1795 }
1796
1797 pub(super) fn fec_to_info_bytes(fec_bytes: &[u8]) -> [u8; 7] {
1800 let dibits = unpack_dibits_half(fec_bytes);
1801 let frame = decode_frame(&dibits);
1802 pack_info_half(&frame.info)
1803 }
1804
1805 pub(super) fn info_to_fec_bytes(info_bytes: &[u8]) -> [u8; 9] {
1808 let info = unpack_info_half(info_bytes);
1809 let dibits = encode_frame(&info);
1810 pack_dibits_half(&dibits)
1811 }
1812}
1813
1814fn pack_info_bits<const N: usize>(info: &[u16; N], widths: &[u8; N], out: &mut [u8]) {
1819 let mut bit_idx = 0usize;
1820 for i in 0..N {
1821 let w = widths[i] as usize;
1822 let v = info[i];
1823 for k in (0..w).rev() {
1824 let b = ((v >> k) & 1) as u8;
1825 out[bit_idx / 8] |= b << (7 - (bit_idx % 8));
1826 bit_idx += 1;
1827 }
1828 }
1829}
1830
1831fn unpack_info_bits<const N: usize>(bytes: &[u8], widths: &[u8; N], out: &mut [u16; N]) {
1832 let mut bit_idx = 0usize;
1833 for i in 0..N {
1834 let w = widths[i] as usize;
1835 let mut v = 0u16;
1836 for _ in 0..w {
1837 let b = (bytes[bit_idx / 8] >> (7 - (bit_idx % 8))) & 1;
1838 v = (v << 1) | u16::from(b);
1839 bit_idx += 1;
1840 }
1841 out[i] = v;
1842 }
1843}
1844
1845#[cfg(test)]
1846mod tests {
1847 use super::*;
1848
1849 fn periodic_pcm(period: usize, amplitude: i16) -> [i16; FRAME_SAMPLES] {
1850 let mut out = [0i16; FRAME_SAMPLES];
1851 for (n, slot) in out.iter_mut().enumerate() {
1852 let phase = (n % period) as f32 / period as f32;
1853 *slot = (amplitude as f32 * (2.0 * core::f32::consts::PI * phase).sin()) as i16;
1854 }
1855 out
1856 }
1857
1858 #[test]
1859 fn rate_byte_sizes_match_wire_layouts() {
1860 assert_eq!(Rate::Imbe7200x4400.fec_frame_bytes(), 18);
1861 assert_eq!(Rate::Imbe4400x4400.fec_frame_bytes(), 11);
1862 assert_eq!(Rate::AmbePlus2_3600x2450.fec_frame_bytes(), 9);
1863 assert_eq!(Rate::AmbePlus2_2450x2450.fec_frame_bytes(), 7);
1864 assert_eq!(Rate::Imbe7200x4400.frame_samples(), 160);
1865 assert_eq!(Rate::AmbePlus2_3600x2450.frame_samples(), 160);
1866 }
1867
1868 #[test]
1869 fn no_fec_imbe_roundtrip_smoke() {
1870 let mut tx = Vocoder::new(Rate::Imbe4400x4400);
1871 let mut rx = Vocoder::new(Rate::Imbe4400x4400);
1872 for _ in 0..5 {
1873 let pcm = periodic_pcm(40, 8000);
1874 let bits = tx.encode_pcm(&pcm).expect("encode");
1875 assert_eq!(bits.len(), 11, "no-FEC IMBE wire frame is 11 bytes (88 info bits)");
1876 let out = rx.decode_bits(&bits).expect("decode");
1877 assert_eq!(out.len(), FRAME_SAMPLES);
1878 }
1879 }
1880
1881 #[test]
1882 fn no_fec_ambeplus2_roundtrip_smoke() {
1883 let mut tx = Vocoder::new(Rate::AmbePlus2_2450x2450);
1884 let mut rx = Vocoder::new(Rate::AmbePlus2_2450x2450);
1885 for _ in 0..5 {
1886 let pcm = periodic_pcm(40, 6000);
1887 let bits = tx.encode_pcm(&pcm).expect("encode");
1888 assert_eq!(
1889 bits.len(),
1890 7,
1891 "no-FEC AMBE+2 wire frame is 7 bytes (49 info bits + 7 pad)"
1892 );
1893 let out = rx.decode_bits(&bits).expect("decode");
1894 assert_eq!(out.len(), FRAME_SAMPLES);
1895 }
1896 }
1897
1898 #[test]
1903 fn no_fec_full_matches_fec_full_on_clean_channel() {
1904 let mut tx_fec = Vocoder::new(Rate::Imbe7200x4400);
1905 let mut rx_fec = Vocoder::new(Rate::Imbe7200x4400);
1906 let mut tx_raw = Vocoder::new(Rate::Imbe4400x4400);
1907 let mut rx_raw = Vocoder::new(Rate::Imbe4400x4400);
1908 for k in 0..6 {
1909 let pcm = periodic_pcm(40 + k, 8000);
1910 let pcm_fec = rx_fec
1911 .decode_bits(&tx_fec.encode_pcm(&pcm).unwrap())
1912 .unwrap();
1913 let pcm_raw = rx_raw
1914 .decode_bits(&tx_raw.encode_pcm(&pcm).unwrap())
1915 .unwrap();
1916 assert_eq!(
1917 pcm_fec, pcm_raw,
1918 "no-FEC and FEC paths must match on a clean channel (frame {k})"
1919 );
1920 }
1921 }
1922
1923 #[test]
1924 fn no_fec_half_matches_fec_half_on_clean_channel() {
1925 let mut tx_fec = Vocoder::new(Rate::AmbePlus2_3600x2450);
1926 let mut rx_fec = Vocoder::new(Rate::AmbePlus2_3600x2450);
1927 let mut tx_raw = Vocoder::new(Rate::AmbePlus2_2450x2450);
1928 let mut rx_raw = Vocoder::new(Rate::AmbePlus2_2450x2450);
1929 for k in 0..6 {
1930 let pcm = periodic_pcm(40 + k, 6000);
1931 let pcm_fec = rx_fec
1932 .decode_bits(&tx_fec.encode_pcm(&pcm).unwrap())
1933 .unwrap();
1934 let pcm_raw = rx_raw
1935 .decode_bits(&tx_raw.encode_pcm(&pcm).unwrap())
1936 .unwrap();
1937 assert_eq!(
1938 pcm_fec, pcm_raw,
1939 "no-FEC and FEC paths must match on a clean channel (frame {k})"
1940 );
1941 }
1942 }
1943
1944 #[test]
1945 fn imbe_roundtrip_smoke() {
1946 let mut tx = Vocoder::new(Rate::Imbe7200x4400);
1947 let mut rx = Vocoder::new(Rate::Imbe7200x4400);
1948 for _ in 0..5 {
1951 let pcm = periodic_pcm(40, 8000);
1952 let bits = tx.encode_pcm(&pcm).expect("encode");
1953 assert_eq!(bits.len(), 18);
1954 let out = rx.decode_bits(&bits).expect("decode");
1955 assert_eq!(out.len(), FRAME_SAMPLES);
1956 }
1957 let stats = tx.last_stats();
1958 assert!(stats.analysis.is_some(), "encoder didn't fill stats");
1959 }
1960
1961 #[test]
1962 fn ambe_plus2_roundtrip_smoke() {
1963 let mut tx = Vocoder::new(Rate::AmbePlus2_3600x2450);
1964 let mut rx = Vocoder::new(Rate::AmbePlus2_3600x2450);
1965 for _ in 0..5 {
1966 let pcm = periodic_pcm(40, 8000);
1967 let bits = tx.encode_pcm(&pcm).expect("encode");
1968 assert_eq!(bits.len(), 9);
1969 let out = rx.decode_bits(&bits).expect("decode");
1970 assert_eq!(out.len(), FRAME_SAMPLES);
1971 }
1972 let stats = rx.last_stats();
1973 assert!(stats.decode.is_some(), "decoder didn't fill stats");
1974 }
1975
1976 #[test]
1977 fn wrong_pcm_length_errors() {
1978 let mut v = Vocoder::new(Rate::Imbe7200x4400);
1979 let r = v.encode_pcm(&[0i16; 159]);
1980 assert!(matches!(r, Err(VocoderError::WrongPcmLength { expected: 160, got: 159 })));
1981 }
1982
1983 #[test]
1984 fn wrong_bits_length_errors_per_rate() {
1985 let mut a = Vocoder::new(Rate::Imbe7200x4400);
1986 assert!(matches!(
1987 a.decode_bits(&[0u8; 9]),
1988 Err(VocoderError::WrongBitsLength { expected: 18, got: 9 })
1989 ));
1990 let mut b = Vocoder::new(Rate::AmbePlus2_3600x2450);
1991 assert!(matches!(
1992 b.decode_bits(&[0u8; 18]),
1993 Err(VocoderError::WrongBitsLength { expected: 9, got: 18 })
1994 ));
1995 }
1996
1997 #[test]
1998 fn reset_clears_state_and_keeps_rate() {
1999 let mut v = Vocoder::new(Rate::AmbePlus2_3600x2450);
2000 let pcm = periodic_pcm(40, 8000);
2001 let _ = v.encode_pcm(&pcm).unwrap();
2002 assert!(v.last_stats().analysis.is_some());
2003 v.reset();
2004 assert!(v.last_stats().analysis.is_none());
2005 assert_eq!(v.rate(), Rate::AmbePlus2_3600x2450);
2006 }
2007
2008 #[cfg(feature = "serde")]
2013 #[test]
2014 fn frame_stats_round_trip_through_json() {
2015 let mut v = Vocoder::new(Rate::Imbe7200x4400);
2016 let pcm = periodic_pcm(40, 8000);
2017 for _ in 0..3 {
2018 let bits = v.encode_pcm(&pcm).unwrap();
2019 let _ = v.decode_bits(&bits).unwrap();
2020 }
2021 let stats = v.last_stats().clone();
2022 let s = serde_json::to_string(&stats).expect("serialize FrameStats");
2023 let back: FrameStats = serde_json::from_str(&s).expect("deserialize FrameStats");
2024 assert_eq!(stats.analysis.is_some(), back.analysis.is_some());
2028 assert_eq!(stats.decode.is_some(), back.decode.is_some());
2029 if let (Some(a), Some(b)) = (&stats.analysis, &back.analysis) {
2030 assert_eq!(a.output, b.output);
2031 assert_eq!(a.params, b.params);
2032 }
2033 if let (Some(a), Some(b)) = (&stats.decode, &back.decode) {
2034 assert_eq!(a.epsilon_0, b.epsilon_0);
2035 assert_eq!(a.epsilon_t, b.epsilon_t);
2036 assert_eq!(a.disposition, b.disposition);
2037 }
2038 }
2039
2040 #[cfg(feature = "serde")]
2043 #[test]
2044 fn rate_serializes_as_named_variant() {
2045 let s = serde_json::to_string(&Rate::Imbe7200x4400).unwrap();
2046 assert_eq!(s, "\"Imbe7200x4400\"");
2047 let back: Rate = serde_json::from_str(&s).unwrap();
2048 assert_eq!(back, Rate::Imbe7200x4400);
2049 }
2050
2051 #[test]
2054 fn encode_stream_yields_one_per_frame_drops_partial() {
2055 let mut v = Vocoder::new(Rate::Imbe7200x4400);
2056 let mut pcm: Vec<i16> = Vec::with_capacity(5 * FRAME_SAMPLES + 50);
2058 for f in 0..5 {
2059 pcm.extend_from_slice(&periodic_pcm(40, (1000 + f * 100) as i16));
2060 }
2061 pcm.extend(std::iter::repeat(0i16).take(50));
2062 let stream = v.encode_stream(&pcm);
2063 assert_eq!(stream.len(), 5);
2064 let bits: Vec<Vec<u8>> = stream.collect::<Result<Vec<_>, _>>().unwrap();
2065 assert_eq!(bits.len(), 5);
2066 for b in &bits {
2067 assert_eq!(b.len(), 18); }
2069 }
2070
2071 #[test]
2074 fn decode_stream_yields_one_per_frame_drops_partial() {
2075 let mut tx = Vocoder::new(Rate::AmbePlus2_3600x2450);
2076 let mut pcm: Vec<i16> = Vec::with_capacity(7 * FRAME_SAMPLES);
2077 for _ in 0..7 {
2078 pcm.extend_from_slice(&periodic_pcm(40, 5000));
2079 }
2080 let bits: Vec<u8> = tx
2081 .encode_stream(&pcm)
2082 .collect::<Result<Vec<_>, _>>()
2083 .unwrap()
2084 .into_iter()
2085 .flatten()
2086 .collect();
2087 let mut padded = bits.clone();
2089 padded.extend_from_slice(&[0; 4]);
2090
2091 let mut rx = Vocoder::new(Rate::AmbePlus2_3600x2450);
2092 let frames: Vec<Vec<i16>> = rx
2093 .decode_stream(&padded)
2094 .collect::<Result<Vec<_>, _>>()
2095 .unwrap();
2096 assert_eq!(frames.len(), 7);
2097 for f in &frames {
2098 assert_eq!(f.len(), FRAME_SAMPLES);
2099 }
2100 }
2101
2102 #[test]
2107 fn decode_stats_carry_disposition() {
2108 let mut tx = Vocoder::new(Rate::Imbe7200x4400);
2109 let mut rx = Vocoder::new(Rate::Imbe7200x4400);
2110 for _ in 0..5 {
2111 let pcm = periodic_pcm(40, 8000);
2112 let bits = tx.encode_pcm(&pcm).unwrap();
2113 let _ = rx.decode_bits(&bits).unwrap();
2114 }
2115 let disp = rx
2116 .last_stats()
2117 .decode
2118 .as_ref()
2119 .expect("decode stats populated after decode call")
2120 .disposition
2121 .expect("disposition surfaced");
2122 assert_eq!(disp, FrameDisposition::Use);
2124 assert_eq!(rx.last_disposition(), Some(FrameDisposition::Use));
2125 }
2126
2127 #[test]
2132 fn tone_detection_emits_tone_frame_and_decoder_recognises_it() {
2133 let mut tx = Vocoder::new(Rate::AmbePlus2_3600x2450);
2135 tx.set_tone_detection(true);
2136 let mut pcm = [0i16; FRAME_SAMPLES];
2139 let two_pi = 2.0 * core::f64::consts::PI;
2140 for (n, slot) in pcm.iter_mut().enumerate() {
2141 let s = 8000.0_f64 * (two_pi * 312.5 * n as f64 / 8000.0).sin();
2142 *slot = s.round() as i16;
2143 }
2144 let bits = tx.encode_pcm(&pcm).unwrap();
2145 assert_eq!(bits.len(), 9);
2146
2147 match tx.last_stats().analysis.as_ref().unwrap().output {
2149 AnalysisOutputKind::Tone { id, amplitude: _ } => assert_eq!(id, 10),
2150 other => panic!("expected Tone, got {other:?}"),
2151 }
2152
2153 use crate::ambe_plus2_wire::dequantize::{FrameKind, classify_ambe_plus2_frame};
2157 use crate::ambe_plus2_wire::frame::decode_frame;
2158 let mut dibits = [0u8; 36];
2159 let mut bit = 0;
2160 for slot in &mut dibits {
2161 let mut d = 0u8;
2162 for _ in 0..2 {
2163 let b = (bits[bit / 8] >> (7 - (bit % 8))) & 1;
2164 d = (d << 1) | b;
2165 bit += 1;
2166 }
2167 *slot = d;
2168 }
2169 let frame = decode_frame(&dibits);
2170 assert_eq!(classify_ambe_plus2_frame(&frame.info), FrameKind::Tone);
2171
2172 let mut rx = Vocoder::new(Rate::AmbePlus2_3600x2450);
2177 let out = rx.decode_bits(&bits).unwrap();
2178 assert_eq!(out.len(), FRAME_SAMPLES);
2179 }
2180
2181 #[test]
2185 fn tone_detection_off_means_voice_path_even_for_pure_sine() {
2186 let mut tx = Vocoder::new(Rate::AmbePlus2_3600x2450);
2187 let mut pcm = [0i16; FRAME_SAMPLES];
2189 let two_pi = 2.0 * core::f64::consts::PI;
2190 for (n, slot) in pcm.iter_mut().enumerate() {
2191 let s = 8000.0_f64 * (two_pi * 312.5 * n as f64 / 8000.0).sin();
2192 *slot = s.round() as i16;
2193 }
2194 let _ = tx.encode_pcm(&pcm).unwrap();
2195 let stats = tx.last_stats().analysis.as_ref().unwrap();
2196 assert!(matches!(
2197 stats.output,
2198 AnalysisOutputKind::Voice | AnalysisOutputKind::Silence
2199 ));
2200 assert!(!matches!(stats.output, AnalysisOutputKind::Tone { .. }));
2201 assert!(stats.tone_detect.is_none());
2202 }
2203
2204 #[test]
2210 fn tone_detection_on_phase1_surfaces_metadata_without_changing_wire() {
2211 let mut tx = Vocoder::new(Rate::Imbe7200x4400);
2212 tx.set_tone_detection(true);
2213 let mut pcm = [0i16; FRAME_SAMPLES];
2214 let two_pi = 2.0 * core::f64::consts::PI;
2215 for (n, slot) in pcm.iter_mut().enumerate() {
2216 let s = 8000.0_f64 * (two_pi * 312.5 * n as f64 / 8000.0).sin();
2217 *slot = s.round() as i16;
2218 }
2219 let bits_with_detect = tx.encode_pcm(&pcm).unwrap();
2220 let stats = tx.last_stats().analysis.as_ref().unwrap();
2221 assert!(matches!(
2223 stats.output,
2224 AnalysisOutputKind::Voice | AnalysisOutputKind::Silence
2225 ));
2226 let det = stats.tone_detect.expect("Phase 1 detection metadata");
2228 assert_eq!(det.id, 10); let mut tx_off = Vocoder::new(Rate::Imbe7200x4400);
2233 let bits_no_detect = tx_off.encode_pcm(&pcm).unwrap();
2234 assert_eq!(bits_with_detect, bits_no_detect);
2235 }
2236
2237 #[test]
2241 fn live_encoder_handles_arbitrary_chunk_sizes() {
2242 let mut total_pcm: Vec<i16> = Vec::with_capacity(7 * FRAME_SAMPLES);
2243 for _ in 0..7 {
2244 total_pcm.extend_from_slice(&periodic_pcm(40, 6000));
2245 }
2246 let mut ref_v = Vocoder::new(Rate::Imbe7200x4400);
2248 let mut ref_bits: Vec<u8> = Vec::new();
2249 for chunk in total_pcm.chunks_exact(FRAME_SAMPLES) {
2250 ref_bits.extend(ref_v.encode_pcm(chunk).unwrap());
2251 }
2252 let mut live = LiveEncoder::new(Rate::Imbe7200x4400);
2254 let mut live_bits: Vec<u8> = Vec::new();
2255 let splits = [250usize, 50, 333];
2256 let mut pos = 0;
2257 for &n in &splits {
2258 let end = (pos + n).min(total_pcm.len());
2259 for r in live.push(&total_pcm[pos..end]) {
2260 live_bits.extend(r.unwrap());
2261 }
2262 pos = end;
2263 }
2264 for r in live.push(&total_pcm[pos..]) {
2265 live_bits.extend(r.unwrap());
2266 }
2267 assert_eq!(live_bits, ref_bits);
2268 assert_eq!(live.pending_samples(), 0);
2270 }
2271
2272 #[test]
2275 fn live_encoder_residue_held_across_calls() {
2276 let mut live = LiveEncoder::new(Rate::Imbe7200x4400);
2277 let pcm: Vec<i16> = periodic_pcm(40, 6000)
2279 .iter()
2280 .copied()
2281 .chain(periodic_pcm(40, 6000)[..80].iter().copied())
2282 .collect();
2283 assert_eq!(pcm.len(), 240);
2284 let frames = live.push(&pcm[..120]);
2285 assert!(frames.is_empty());
2286 assert_eq!(live.pending_samples(), 120);
2287 let frames = live.push(&pcm[120..]);
2288 assert_eq!(frames.len(), 1);
2289 assert!(frames[0].is_ok());
2290 assert_eq!(live.pending_samples(), 80);
2291 }
2292
2293 #[test]
2296 fn live_decoder_handles_arbitrary_chunk_sizes() {
2297 let mut tx = Vocoder::new(Rate::AmbePlus2_3600x2450);
2298 let mut all_pcm: Vec<i16> = Vec::with_capacity(5 * FRAME_SAMPLES);
2299 for _ in 0..5 {
2300 all_pcm.extend_from_slice(&periodic_pcm(40, 5000));
2301 }
2302 let bits: Vec<u8> = tx
2303 .encode_stream(&all_pcm)
2304 .collect::<Result<Vec<_>, _>>()
2305 .unwrap()
2306 .into_iter()
2307 .flatten()
2308 .collect();
2309 let mut ref_v = Vocoder::new(Rate::AmbePlus2_3600x2450);
2310 let mut ref_pcm: Vec<i16> = Vec::new();
2311 for chunk in bits.chunks_exact(9) {
2312 ref_pcm.extend(ref_v.decode_bits(chunk).unwrap());
2313 }
2314 let mut live = LiveDecoder::new(Rate::AmbePlus2_3600x2450);
2315 let mut live_pcm: Vec<i16> = Vec::new();
2316 for chunk in bits.chunks(7) {
2318 for r in live.push(chunk) {
2319 live_pcm.extend(r.unwrap());
2320 }
2321 }
2322 assert_eq!(live_pcm, ref_pcm);
2323 assert_eq!(live.pending_bytes(), 0); }
2325
2326 #[test]
2328 fn live_encoder_discard_pending_clears_residue() {
2329 let mut live = LiveEncoder::new(Rate::Imbe7200x4400);
2330 let pcm = periodic_pcm(40, 6000);
2331 let frames = live.push(&pcm[..80]);
2332 assert!(frames.is_empty());
2333 assert_eq!(live.pending_samples(), 80);
2334 live.discard_pending();
2335 assert_eq!(live.pending_samples(), 0);
2336 }
2337
2338 #[test]
2341 fn transcoder_phase1_to_phase2_changes_frame_size() {
2342 let mut tx = Transcoder::new(Rate::Imbe7200x4400, Rate::AmbePlus2_3600x2450).unwrap();
2343 assert_eq!(
2344 tx.direction(),
2345 TranscodeDirection::new(Rate::Imbe7200x4400, Rate::AmbePlus2_3600x2450)
2346 );
2347 let mut enc = Vocoder::new(Rate::Imbe7200x4400);
2349 let pcm = periodic_pcm(40, 6000);
2350 for _ in 0..3 {
2351 let phase1 = enc.encode_pcm(&pcm).unwrap();
2352 assert_eq!(phase1.len(), 18);
2353 let phase2 = tx.transcode(&phase1).unwrap();
2354 assert_eq!(phase2.len(), 9, "P1→P2 transcode produces 9-byte frames");
2355 }
2356 }
2357
2358 #[test]
2359 fn transcoder_phase2_to_phase1_changes_frame_size() {
2360 let mut tx = Transcoder::new(Rate::AmbePlus2_3600x2450, Rate::Imbe7200x4400).unwrap();
2361 let mut enc = Vocoder::new(Rate::AmbePlus2_3600x2450);
2362 let pcm = periodic_pcm(40, 6000);
2363 for _ in 0..3 {
2364 let phase2 = enc.encode_pcm(&pcm).unwrap();
2365 assert_eq!(phase2.len(), 9);
2366 let phase1 = tx.transcode(&phase2).unwrap();
2367 assert_eq!(phase1.len(), 18);
2368 }
2369 }
2370
2371 #[test]
2372 fn transcoder_rejects_wrong_input_length() {
2373 let mut tx = Transcoder::new(Rate::Imbe7200x4400, Rate::AmbePlus2_3600x2450).unwrap();
2374 assert!(matches!(
2375 tx.transcode(&[0u8; 9]),
2376 Err(VocoderError::WrongBitsLength { expected: 18, got: 9 })
2377 ));
2378 }
2379
2380 #[test]
2383 fn transcoder_rejects_unsupported_pair() {
2384 let res = Transcoder::new(Rate::Imbe7200x4400, Rate::Imbe7200x4400);
2385 assert!(matches!(
2386 res.err(),
2387 Some(VocoderError::UnsupportedTranscode {
2388 from: Rate::Imbe7200x4400,
2389 to: Rate::Imbe7200x4400,
2390 })
2391 ));
2392 }
2393
2394 #[test]
2397 fn transcoder_full_fec_to_info_roundtrip_is_lossless() {
2398 let mut enc = Vocoder::new(Rate::Imbe7200x4400);
2399 let mut strip =
2400 Transcoder::new(Rate::Imbe7200x4400, Rate::Imbe4400x4400).unwrap();
2401 let mut add =
2402 Transcoder::new(Rate::Imbe4400x4400, Rate::Imbe7200x4400).unwrap();
2403 for k in 0..4 {
2404 let pcm = periodic_pcm(40 + k, 7000);
2405 let fec = enc.encode_pcm(&pcm).unwrap();
2406 assert_eq!(fec.len(), 18);
2407 let info = strip.transcode(&fec).unwrap();
2408 assert_eq!(info.len(), 11);
2409 let fec2 = add.transcode(&info).unwrap();
2410 assert_eq!(fec2, fec, "FEC strip + add round-trips byte-for-byte");
2411 }
2412 }
2413
2414 #[test]
2415 fn transcoder_half_fec_to_info_roundtrip_is_lossless() {
2416 let mut enc = Vocoder::new(Rate::AmbePlus2_3600x2450);
2417 let mut strip =
2418 Transcoder::new(Rate::AmbePlus2_3600x2450, Rate::AmbePlus2_2450x2450).unwrap();
2419 let mut add =
2420 Transcoder::new(Rate::AmbePlus2_2450x2450, Rate::AmbePlus2_3600x2450).unwrap();
2421 for k in 0..4 {
2422 let pcm = periodic_pcm(40 + k, 6000);
2423 let fec = enc.encode_pcm(&pcm).unwrap();
2424 assert_eq!(fec.len(), 9);
2425 let info = strip.transcode(&fec).unwrap();
2426 assert_eq!(info.len(), 7);
2427 let fec2 = add.transcode(&info).unwrap();
2428 assert_eq!(fec2, fec, "FEC strip + add round-trips byte-for-byte");
2429 }
2430 }
2431
2432 #[test]
2436 fn extract_params_returns_params_per_frame() {
2437 let mut v = Vocoder::new(Rate::Imbe7200x4400);
2438 let pcm = periodic_pcm(40, 6000);
2441 let p1 = v.extract_params(&pcm).unwrap();
2442 let p2 = v.extract_params(&pcm).unwrap();
2443 let p3 = v.extract_params(&pcm).unwrap();
2444 let any_voice = [&p1, &p2, &p3].iter().any(|p| {
2447 let amps = p.amplitudes_slice();
2448 amps.iter().any(|&a| a > 0.0)
2449 });
2450 assert!(any_voice, "no voice params after 3 frames of periodic PCM");
2451 }
2452
2453 #[test]
2460 fn extract_then_synthesize_roundtrips_through_params() {
2461 let mut a = Vocoder::new(Rate::Imbe7200x4400);
2462 let mut b = Vocoder::new(Rate::Imbe7200x4400);
2463 let pcm = periodic_pcm(40, 6000);
2464 for _ in 0..5 {
2465 let params = a.extract_params(&pcm).unwrap();
2466 let resynth = b.synthesize_params(¶ms);
2467 assert_eq!(resynth.len(), FRAME_SAMPLES);
2468 }
2469 }
2470
2471 #[test]
2476 fn synthesize_params_emits_one_frame_per_call() {
2477 let mut tx = Vocoder::new(Rate::Imbe7200x4400);
2478 let pcm = tx.synthesize_params(&MbeParams::silence());
2479 assert_eq!(pcm.len(), FRAME_SAMPLES);
2480 let peak = pcm.iter().map(|&s| s.unsigned_abs()).max().unwrap_or(0);
2482 assert!(peak < 5000, "silence params produced peak={peak}");
2483
2484 let mut rx = Vocoder::new(Rate::AmbePlus2_3600x2450);
2485 let pcm = rx.synthesize_params(&MbeParams::silence_ambe_plus2());
2486 assert_eq!(pcm.len(), FRAME_SAMPLES);
2487 let peak = pcm.iter().map(|&s| s.unsigned_abs()).max().unwrap_or(0);
2488 assert!(peak < 5000);
2489 }
2490
2491 #[test]
2494 fn synthesize_params_advances_synth_disposition() {
2495 let mut v = Vocoder::new(Rate::Imbe7200x4400);
2496 assert_eq!(v.last_disposition(), None);
2497 let _ = v.synthesize_params(&MbeParams::silence());
2498 assert_eq!(v.last_disposition(), Some(FrameDisposition::Use));
2500 }
2501
2502 #[test]
2504 fn builder_configures_all_knobs() {
2505 let v = Vocoder::builder(Rate::AmbePlus2_3600x2450)
2506 .tone_detection(true)
2507 .repeat_reset_after(Some(3))
2508 .silence_dispatch(true)
2509 .pitch_silence_override(true)
2510 .ambe_plus2_synth(AmbePlus2Synth::Baseline)
2511 .build();
2512 assert_eq!(v.rate(), Rate::AmbePlus2_3600x2450);
2513 assert!(v.tone_detection());
2514 assert_eq!(v.repeat_reset_after(), Some(3));
2515 assert!(v.silence_dispatch());
2516 assert!(v.pitch_silence_override());
2517 assert_eq!(v.ambe_plus2_synth(), AmbePlus2Synth::Baseline);
2518 }
2519
2520 #[test]
2526 fn ambe_plus2_synth_modes_both_decode_cleanly() {
2527 let mut tx = Vocoder::new(Rate::AmbePlus2_3600x2450);
2528 let pcm = periodic_pcm(40, 6000);
2529 let mut bits_buf: Vec<u8> = Vec::new();
2530 for _ in 0..5 {
2531 bits_buf.extend(tx.encode_pcm(&pcm).unwrap());
2532 }
2533
2534 for gen in [AmbePlus2Synth::AmbePlus, AmbePlus2Synth::Baseline] {
2535 let mut rx = Vocoder::builder(Rate::AmbePlus2_3600x2450)
2536 .ambe_plus2_synth(gen)
2537 .build();
2538 assert_eq!(rx.ambe_plus2_synth(), gen);
2539 let mut out_pcm: Vec<i16> = Vec::new();
2540 for chunk in bits_buf.chunks_exact(9) {
2541 out_pcm.extend(rx.decode_bits(chunk).unwrap());
2542 }
2543 let rms = (out_pcm.iter().map(|&s| (s as f64) * (s as f64)).sum::<f64>()
2544 / out_pcm.len() as f64).sqrt();
2545 assert!(rms > 50.0, "{gen:?} output too quiet: rms={rms}");
2546 }
2547 }
2548
2549 #[test]
2551 fn builder_defaults_match_vocoder_new() {
2552 let a = Vocoder::builder(Rate::Imbe7200x4400).build();
2553 let b = Vocoder::new(Rate::Imbe7200x4400);
2554 assert_eq!(a.rate(), b.rate());
2555 assert_eq!(a.tone_detection(), b.tone_detection());
2556 assert_eq!(a.repeat_reset_after(), b.repeat_reset_after());
2557 assert_eq!(a.silence_dispatch(), b.silence_dispatch());
2558 assert_eq!(a.pitch_silence_override(), b.pitch_silence_override());
2559 assert_eq!(a.ambe_plus2_synth(), b.ambe_plus2_synth());
2560 assert!(!a.tone_detection());
2561 assert!(a.repeat_reset_after().is_none());
2562 assert!(!a.silence_dispatch());
2563 assert!(!a.pitch_silence_override());
2564 assert_eq!(a.ambe_plus2_synth(), AmbePlus2Synth::AmbePlus);
2565 }
2566
2567 #[test]
2570 fn live_encoder_flush_emits_padded_residue() {
2571 let mut live = LiveEncoder::new(Rate::Imbe7200x4400);
2572 assert!(matches!(live.flush(), Ok(None)));
2574
2575 let pcm = periodic_pcm(40, 6000);
2577 let _ = live.push(&pcm[..80]);
2578 assert_eq!(live.pending_samples(), 80);
2579 let tail = live.flush().unwrap().expect("residue → frame");
2580 assert_eq!(tail.len(), 18);
2581 assert_eq!(live.pending_samples(), 0);
2582
2583 assert!(matches!(live.flush(), Ok(None)));
2585 }
2586
2587 #[test]
2589 fn live_encoder_reset_clears_everything() {
2590 let mut live = LiveEncoder::new(Rate::AmbePlus2_3600x2450);
2591 let pcm = periodic_pcm(40, 5000);
2592 let _ = live.push(&pcm[..120]);
2593 assert_eq!(live.pending_samples(), 120);
2594 let _ = live.vocoder().last_stats();
2595 live.reset();
2596 assert_eq!(live.pending_samples(), 0);
2597 assert!(live.vocoder().last_stats().analysis.is_none());
2598 assert_eq!(live.rate(), Rate::AmbePlus2_3600x2450);
2599 }
2600
2601 #[test]
2605 fn encode_stream_matches_per_frame_calls_byte_for_byte() {
2606 let mut a = Vocoder::new(Rate::Imbe7200x4400);
2607 let mut b = Vocoder::new(Rate::Imbe7200x4400);
2608 let mut pcm: Vec<i16> = Vec::with_capacity(4 * FRAME_SAMPLES);
2609 for _ in 0..4 {
2610 pcm.extend_from_slice(&periodic_pcm(40, 6000));
2611 }
2612 let by_stream: Vec<u8> = a
2613 .encode_stream(&pcm)
2614 .collect::<Result<Vec<_>, _>>()
2615 .unwrap()
2616 .into_iter()
2617 .flatten()
2618 .collect();
2619 let mut by_call: Vec<u8> = Vec::new();
2620 for chunk in pcm.chunks_exact(FRAME_SAMPLES) {
2621 by_call.extend(b.encode_pcm(chunk).unwrap());
2622 }
2623 assert_eq!(by_stream, by_call);
2624 }
2625}