use crate::Bandwidth;
pub const SUPPORTED_OUTPUT_RATES_HZ: &[u32] = &[8_000, 12_000, 16_000, 24_000, 48_000];
pub const REFERENCE_RATE_HZ: u32 = 48_000;
pub const SILK_RESAMPLER_DELAY_MS_NB: f64 = 0.538;
pub const SILK_RESAMPLER_DELAY_MS_MB: f64 = 0.692;
pub const SILK_RESAMPLER_DELAY_MS_WB: f64 = 0.706;
pub fn silk_resampler_delay_ms(bw: Bandwidth) -> Option<f64> {
match bw {
Bandwidth::Nb => Some(SILK_RESAMPLER_DELAY_MS_NB),
Bandwidth::Mb => Some(SILK_RESAMPLER_DELAY_MS_MB),
Bandwidth::Wb => Some(SILK_RESAMPLER_DELAY_MS_WB),
Bandwidth::Swb | Bandwidth::Fb => None,
}
}
pub fn silk_resampler_delay_samples_at(bw: Bandwidth, output_rate_hz: u32) -> Option<u32> {
if output_rate_hz == 0 {
return None;
}
let delay_ms = silk_resampler_delay_ms(bw)?;
let samples = (delay_ms * (output_rate_hz as f64) / 1000.0).round();
if !samples.is_finite() || samples < 0.0 || samples > u32::MAX as f64 {
return None;
}
Some(samples as u32)
}
pub fn is_supported_output_rate(rate_hz: u32) -> bool {
SUPPORTED_OUTPUT_RATES_HZ.contains(&rate_hz)
}
pub fn silk_internal_rate_hz(bw: Bandwidth) -> Option<u32> {
match bw {
Bandwidth::Nb => Some(8_000),
Bandwidth::Mb => Some(12_000),
Bandwidth::Wb => Some(16_000),
Bandwidth::Swb | Bandwidth::Fb => None,
}
}
pub fn silk_frame_samples_internal(
bw: Bandwidth,
silk_frame_duration_tenths_ms: u16,
) -> Option<u32> {
let rate = silk_internal_rate_hz(bw)?;
match silk_frame_duration_tenths_ms {
100 => Some(rate / 100), 200 => Some(rate / 50), _ => None,
}
}
pub fn silk_frame_samples_at_output(
bw: Bandwidth,
silk_frame_duration_tenths_ms: u16,
output_rate_hz: u32,
) -> Option<u32> {
if output_rate_hz == 0 {
return None;
}
silk_frame_samples_internal(bw, silk_frame_duration_tenths_ms)?;
let ms = match silk_frame_duration_tenths_ms {
100 => 10.0_f64,
200 => 20.0_f64,
_ => return None,
};
let s = (ms * (output_rate_hz as f64) / 1000.0).round();
if !s.is_finite() || s < 0.0 || s > u32::MAX as f64 {
return None;
}
Some(s as u32)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn table54_delay_ms_matches_rfc_for_nb_mb_wb() {
assert_eq!(silk_resampler_delay_ms(Bandwidth::Nb), Some(0.538));
assert_eq!(silk_resampler_delay_ms(Bandwidth::Mb), Some(0.692));
assert_eq!(silk_resampler_delay_ms(Bandwidth::Wb), Some(0.706));
}
#[test]
fn table54_excludes_swb_and_fb() {
assert_eq!(silk_resampler_delay_ms(Bandwidth::Swb), None);
assert_eq!(silk_resampler_delay_ms(Bandwidth::Fb), None);
}
#[test]
fn delay_table_is_strictly_increasing_nb_lt_mb_lt_wb() {
let nb = silk_resampler_delay_ms(Bandwidth::Nb).unwrap();
let mb = silk_resampler_delay_ms(Bandwidth::Mb).unwrap();
let wb = silk_resampler_delay_ms(Bandwidth::Wb).unwrap();
assert!(nb < mb, "NB ({nb}) should be < MB ({mb})");
assert!(mb < wb, "MB ({mb}) should be < WB ({wb})");
}
#[test]
fn delay_samples_at_48khz_matches_table54_reference() {
assert_eq!(
silk_resampler_delay_samples_at(Bandwidth::Nb, 48_000),
Some(26)
);
assert_eq!(
silk_resampler_delay_samples_at(Bandwidth::Mb, 48_000),
Some(33)
);
assert_eq!(
silk_resampler_delay_samples_at(Bandwidth::Wb, 48_000),
Some(34)
);
}
#[test]
fn delay_samples_at_internal_rate_makes_sense() {
assert_eq!(
silk_resampler_delay_samples_at(Bandwidth::Nb, 8_000),
Some(4)
);
assert_eq!(
silk_resampler_delay_samples_at(Bandwidth::Mb, 12_000),
Some(8)
);
assert_eq!(
silk_resampler_delay_samples_at(Bandwidth::Wb, 16_000),
Some(11)
);
}
#[test]
fn delay_samples_at_24khz_intermediate_rate() {
assert_eq!(
silk_resampler_delay_samples_at(Bandwidth::Nb, 24_000),
Some(13)
);
assert_eq!(
silk_resampler_delay_samples_at(Bandwidth::Mb, 24_000),
Some(17)
);
assert_eq!(
silk_resampler_delay_samples_at(Bandwidth::Wb, 24_000),
Some(17)
);
}
#[test]
fn delay_samples_rejects_swb_fb_and_zero_rate() {
assert_eq!(
silk_resampler_delay_samples_at(Bandwidth::Swb, 48_000),
None
);
assert_eq!(silk_resampler_delay_samples_at(Bandwidth::Fb, 48_000), None);
assert_eq!(silk_resampler_delay_samples_at(Bandwidth::Nb, 0), None);
}
#[test]
fn supported_output_rates_are_the_five_spec_rates() {
assert_eq!(
SUPPORTED_OUTPUT_RATES_HZ,
&[8_000, 12_000, 16_000, 24_000, 48_000][..]
);
for &r in SUPPORTED_OUTPUT_RATES_HZ {
assert!(is_supported_output_rate(r), "rate {r} should be supported");
}
for r in [0u32, 11_025, 22_050, 32_000, 44_100, 96_000] {
assert!(
!is_supported_output_rate(r),
"rate {r} should NOT be in §4.2.9 list"
);
}
}
#[test]
fn reference_rate_is_48khz() {
assert_eq!(REFERENCE_RATE_HZ, 48_000);
assert!(is_supported_output_rate(REFERENCE_RATE_HZ));
}
#[test]
fn internal_silk_rate_per_bandwidth() {
assert_eq!(silk_internal_rate_hz(Bandwidth::Nb), Some(8_000));
assert_eq!(silk_internal_rate_hz(Bandwidth::Mb), Some(12_000));
assert_eq!(silk_internal_rate_hz(Bandwidth::Wb), Some(16_000));
assert_eq!(silk_internal_rate_hz(Bandwidth::Swb), None);
assert_eq!(silk_internal_rate_hz(Bandwidth::Fb), None);
}
#[test]
fn internal_silk_rate_is_a_supported_output_rate_for_nb_and_wb() {
for bw in [Bandwidth::Nb, Bandwidth::Mb, Bandwidth::Wb] {
let r = silk_internal_rate_hz(bw).unwrap();
assert!(
is_supported_output_rate(r),
"internal SILK rate {r} for {bw:?} should be in §4.2.9 list"
);
}
}
#[test]
fn silk_frame_samples_internal_canonical_cases() {
assert_eq!(silk_frame_samples_internal(Bandwidth::Nb, 100), Some(80));
assert_eq!(silk_frame_samples_internal(Bandwidth::Nb, 200), Some(160));
assert_eq!(silk_frame_samples_internal(Bandwidth::Mb, 100), Some(120));
assert_eq!(silk_frame_samples_internal(Bandwidth::Mb, 200), Some(240));
assert_eq!(silk_frame_samples_internal(Bandwidth::Wb, 100), Some(160));
assert_eq!(silk_frame_samples_internal(Bandwidth::Wb, 200), Some(320));
}
#[test]
fn silk_frame_samples_internal_rejects_non_silk_durations() {
for dur in [0u16, 25, 50, 400, 600, 1234] {
assert_eq!(
silk_frame_samples_internal(Bandwidth::Nb, dur),
None,
"dur {dur} should be rejected"
);
assert_eq!(silk_frame_samples_internal(Bandwidth::Mb, dur), None);
assert_eq!(silk_frame_samples_internal(Bandwidth::Wb, dur), None);
}
}
#[test]
fn silk_frame_samples_internal_rejects_swb_and_fb() {
for bw in [Bandwidth::Swb, Bandwidth::Fb] {
for dur in [100u16, 200] {
assert_eq!(
silk_frame_samples_internal(bw, dur),
None,
"{bw:?} {dur} should be rejected"
);
}
}
}
#[test]
fn silk_frame_samples_at_output_48khz_matches_duration() {
for bw in [Bandwidth::Nb, Bandwidth::Mb, Bandwidth::Wb] {
assert_eq!(
silk_frame_samples_at_output(bw, 100, 48_000),
Some(480),
"{bw:?} 10 ms"
);
assert_eq!(
silk_frame_samples_at_output(bw, 200, 48_000),
Some(960),
"{bw:?} 20 ms"
);
}
}
#[test]
fn silk_frame_samples_at_output_matches_internal_when_rate_matches() {
for bw in [Bandwidth::Nb, Bandwidth::Mb, Bandwidth::Wb] {
let r = silk_internal_rate_hz(bw).unwrap();
for dur in [100u16, 200] {
assert_eq!(
silk_frame_samples_at_output(bw, dur, r),
silk_frame_samples_internal(bw, dur),
"{bw:?} {dur} at {r} Hz"
);
}
}
}
#[test]
fn silk_frame_samples_at_output_rejects_zero_rate_and_non_silk() {
assert_eq!(silk_frame_samples_at_output(Bandwidth::Nb, 100, 0), None);
assert_eq!(
silk_frame_samples_at_output(Bandwidth::Swb, 100, 48_000),
None
);
assert_eq!(
silk_frame_samples_at_output(Bandwidth::Nb, 25, 48_000),
None
);
assert_eq!(
silk_frame_samples_at_output(Bandwidth::Nb, 400, 48_000),
None
);
}
#[test]
fn delay_is_smaller_than_one_silk_frame_at_every_supported_rate() {
for &out_rate in SUPPORTED_OUTPUT_RATES_HZ {
for bw in [Bandwidth::Nb, Bandwidth::Mb, Bandwidth::Wb] {
let delay = silk_resampler_delay_samples_at(bw, out_rate).unwrap();
let one_frame = silk_frame_samples_at_output(bw, 100, out_rate).unwrap();
assert!(
(delay as u64) < (one_frame as u64),
"{bw:?} delay {delay} >= one 10ms SILK frame {one_frame} at {out_rate} Hz"
);
}
}
}
}