use crate::range_decoder::RangeDecoder;
use crate::range_encoder::RangeEncoder;
use crate::silk_excitation::{Excitation, ExcitationConfig, ExcitationSymbols, SilkFrameSize};
use crate::silk_frame::{
FrameKind, QuantizationOffsetType, SignalType, SilkFrameHeader, SilkFrameHeaderConfig,
SilkHeaderSymbols,
};
use crate::silk_gains::{GainSymbol, SubframeGains, SubframeGainsConfig};
use crate::silk_lcg_seed::{decode_lcg_seed, encode_lcg_seed};
use crate::silk_lsf_interp::{LsfInterpContext, LsfInterpolated};
use crate::silk_lsf_recon::NlsfReconstructed;
use crate::silk_lsf_stabilize::NlsfStabilized;
use crate::silk_lsf_stage2::LsfStage2;
use crate::silk_lsf_to_lpc::LpcQ12;
use crate::silk_ltp::{LagCoding, LtpConfig, LtpParameters, LtpSymbols};
use crate::toc::Bandwidth;
use crate::Error;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SilkFrameConfig {
pub bandwidth: Bandwidth,
pub frame_size: SilkFrameSize,
pub voice_active: bool,
pub first_subframe_independent: bool,
pub previous_log_gain: Option<u8>,
pub previous_primary_lag: Option<i32>,
pub ltp_scaling_present: bool,
pub lsf_interp_after_reset: bool,
pub previous_nlsf_q15: Option<[i16; crate::silk_lsf_stage2::D_LPC_MAX]>,
pub previous_nlsf_len: usize,
pub stereo: Option<StereoHeaderContext>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct StereoHeaderContext {
pub has_mid_only_flag: bool,
}
#[derive(Debug, Clone, PartialEq)]
pub struct SilkFrameDecoded {
pub signal_type: SignalType,
pub qoff_type: QuantizationOffsetType,
pub gains: SubframeGains,
pub lsf_stage1: u8,
pub nlsf_q15: [i16; crate::silk_lsf_stage2::D_LPC_MAX],
pub d_lpc: usize,
pub lsf_interp_q2: Option<u8>,
pub lpc_second_half: LpcQ12,
pub lpc_first_half: Option<LpcQ12>,
pub ltp: LtpParameters,
pub lcg_seed: u8,
pub excitation: Excitation,
pub stereo_pred: Option<crate::silk_frame::StereoPredictionWeights>,
pub mid_only_flag: Option<bool>,
}
pub fn decode_silk_frame(
rd: &mut RangeDecoder<'_>,
cfg: SilkFrameConfig,
) -> Result<SilkFrameDecoded, Error> {
let num_subframes: u8 = match cfg.frame_size {
SilkFrameSize::TenMs => 2,
SilkFrameSize::TwentyMs => 4,
};
let header_cfg = SilkFrameHeaderConfig {
stereo_mid_channel: cfg.stereo.is_some(),
stereo: cfg.stereo.is_some(),
has_mid_only_flag: cfg.stereo.is_some_and(|s| s.has_mid_only_flag),
kind: if cfg.voice_active {
FrameKind::RegularActive
} else {
FrameKind::RegularInactive
},
bandwidth: cfg.bandwidth,
};
let pre = SilkFrameHeader::decode_pre_gains(rd, header_cfg)?;
let stereo_pred = pre.stereo_pred;
let mid_only_flag = pre.mid_only_flag;
let gains = SubframeGains::decode(
rd,
SubframeGainsConfig {
signal_type: pre.signal_type,
num_subframes,
first_subframe_is_independent: cfg.first_subframe_independent,
previous_log_gain: cfg.previous_log_gain,
},
)?;
let lsf_stage1 = SilkFrameHeader::decode_lsf_stage1(rd, cfg.bandwidth, pre.signal_type)?;
let stage2 = LsfStage2::decode(rd, cfg.bandwidth, lsf_stage1)?;
let recon = NlsfReconstructed::from_stage1_and_stage2(cfg.bandwidth, lsf_stage1, &stage2)?;
let stabilized = NlsfStabilized::from_reconstructed(cfg.bandwidth, &recon)?;
let d_lpc = stabilized.len();
let mut nlsf_q15 = [0i16; crate::silk_lsf_stage2::D_LPC_MAX];
nlsf_q15[..d_lpc].copy_from_slice(stabilized.nlsf_q15());
let interp_context = match cfg.frame_size {
SilkFrameSize::TenMs => LsfInterpContext::TenMs,
SilkFrameSize::TwentyMs => {
if cfg.lsf_interp_after_reset || cfg.previous_nlsf_q15.is_none() {
LsfInterpContext::TwentyMsAfterResetOrUncoded
} else {
LsfInterpContext::TwentyMs
}
}
};
let n0_slice: Option<&[i16]> = match (&cfg.previous_nlsf_q15, cfg.frame_size) {
(Some(prev), SilkFrameSize::TwentyMs) if cfg.previous_nlsf_len == d_lpc => {
Some(&prev[..d_lpc])
}
_ => None,
};
let interp = LsfInterpolated::decode(rd, &stabilized, n0_slice, interp_context)?;
let lsf_interp_q2 = interp.w_q2();
let lpc_second_half = nlsf_to_stable_lpc(cfg.bandwidth, &nlsf_q15[..d_lpc])?;
let lpc_first_half = match interp.n1_q15() {
Some(n1) => Some(nlsf_to_stable_lpc(cfg.bandwidth, n1)?),
None => None,
};
let lag_coding = match cfg.previous_primary_lag {
Some(previous_lag) => LagCoding::Relative { previous_lag },
None => LagCoding::Absolute,
};
let ltp = LtpParameters::decode(
rd,
LtpConfig {
bandwidth: cfg.bandwidth,
signal_type: pre.signal_type,
num_subframes,
lag_coding,
ltp_scaling_present: cfg.ltp_scaling_present,
},
)?;
let lcg_seed = decode_lcg_seed(rd);
let excitation = Excitation::decode(
rd,
ExcitationConfig {
bandwidth: cfg.bandwidth,
frame_size: cfg.frame_size,
signal_type: pre.signal_type,
qoff_type: pre.qoff_type,
lcg_seed,
},
)?;
if rd.has_error() {
return Err(Error::MalformedPacket);
}
Ok(SilkFrameDecoded {
signal_type: pre.signal_type,
qoff_type: pre.qoff_type,
gains,
lsf_stage1,
nlsf_q15,
d_lpc,
lsf_interp_q2,
lpc_second_half,
lpc_first_half,
ltp,
lcg_seed,
excitation,
stereo_pred,
mid_only_flag,
})
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SilkFrameSymbols<'a> {
pub header: SilkHeaderSymbols,
pub gains: &'a [GainSymbol],
pub lsf_stage1: u8,
pub lsf_stage2_i2: &'a [i8],
pub lsf_interp_w_q2: Option<u8>,
pub ltp: Option<LtpSymbols>,
pub lcg_seed: u8,
pub excitation: ExcitationSymbols<'a>,
}
pub fn encode_silk_frame(
re: &mut RangeEncoder,
cfg: SilkFrameConfig,
symbols: &SilkFrameSymbols<'_>,
) -> Result<SilkFrameDecoded, Error> {
let num_subframes: u8 = match cfg.frame_size {
SilkFrameSize::TenMs => 2,
SilkFrameSize::TwentyMs => 4,
};
let header_cfg = SilkFrameHeaderConfig {
stereo_mid_channel: cfg.stereo.is_some(),
stereo: cfg.stereo.is_some(),
has_mid_only_flag: cfg.stereo.is_some_and(|s| s.has_mid_only_flag),
kind: if cfg.voice_active {
FrameKind::RegularActive
} else {
FrameKind::RegularInactive
},
bandwidth: cfg.bandwidth,
};
let pre = SilkFrameHeader::encode_pre_gains(re, header_cfg, &symbols.header)?;
let gains = SubframeGains::encode(
re,
SubframeGainsConfig {
signal_type: pre.signal_type,
num_subframes,
first_subframe_is_independent: cfg.first_subframe_independent,
previous_log_gain: cfg.previous_log_gain,
},
symbols.gains,
)?;
SilkFrameHeader::encode_lsf_stage1(re, cfg.bandwidth, pre.signal_type, symbols.lsf_stage1)?;
let stage2 = LsfStage2::encode(re, cfg.bandwidth, symbols.lsf_stage1, symbols.lsf_stage2_i2)?;
let recon =
NlsfReconstructed::from_stage1_and_stage2(cfg.bandwidth, symbols.lsf_stage1, &stage2)?;
let stabilized = NlsfStabilized::from_reconstructed(cfg.bandwidth, &recon)?;
let d_lpc = stabilized.len();
let mut nlsf_q15 = [0i16; crate::silk_lsf_stage2::D_LPC_MAX];
nlsf_q15[..d_lpc].copy_from_slice(stabilized.nlsf_q15());
let interp_context = match cfg.frame_size {
SilkFrameSize::TenMs => LsfInterpContext::TenMs,
SilkFrameSize::TwentyMs => {
if cfg.lsf_interp_after_reset || cfg.previous_nlsf_q15.is_none() {
LsfInterpContext::TwentyMsAfterResetOrUncoded
} else {
LsfInterpContext::TwentyMs
}
}
};
LsfInterpolated::encode_index(re, interp_context, symbols.lsf_interp_w_q2)?;
let n0_slice: Option<&[i16]> = match (&cfg.previous_nlsf_q15, cfg.frame_size) {
(Some(prev), SilkFrameSize::TwentyMs) if cfg.previous_nlsf_len == d_lpc => {
Some(&prev[..d_lpc])
}
_ => None,
};
let interp = match (cfg.frame_size, symbols.lsf_interp_w_q2) {
(SilkFrameSize::TwentyMs, Some(w)) => {
LsfInterpolated::from_decoded_index(w, &stabilized, n0_slice, interp_context)
}
_ => LsfInterpolated::decode(
&mut RangeDecoder::new(&[]),
&stabilized,
n0_slice,
LsfInterpContext::TenMs,
)?,
};
let lsf_interp_q2 = interp.w_q2();
let lpc_second_half = nlsf_to_stable_lpc(cfg.bandwidth, &nlsf_q15[..d_lpc])?;
let lpc_first_half = match interp.n1_q15() {
Some(n1) => Some(nlsf_to_stable_lpc(cfg.bandwidth, n1)?),
None => None,
};
if (pre.signal_type == SignalType::Voiced) != symbols.ltp.is_some() {
return Err(Error::MalformedPacket);
}
let lag_coding = match cfg.previous_primary_lag {
Some(previous_lag) => LagCoding::Relative { previous_lag },
None => LagCoding::Absolute,
};
let ltp = LtpParameters::encode(
re,
LtpConfig {
bandwidth: cfg.bandwidth,
signal_type: pre.signal_type,
num_subframes,
lag_coding,
ltp_scaling_present: cfg.ltp_scaling_present,
},
symbols.ltp.as_ref(),
)?;
encode_lcg_seed(re, symbols.lcg_seed)?;
let excitation = Excitation::encode(
re,
ExcitationConfig {
bandwidth: cfg.bandwidth,
frame_size: cfg.frame_size,
signal_type: pre.signal_type,
qoff_type: pre.qoff_type,
lcg_seed: symbols.lcg_seed,
},
&symbols.excitation,
)?;
Ok(SilkFrameDecoded {
signal_type: pre.signal_type,
qoff_type: pre.qoff_type,
gains,
lsf_stage1: symbols.lsf_stage1,
nlsf_q15,
d_lpc,
lsf_interp_q2,
lpc_second_half,
lpc_first_half,
ltp,
lcg_seed: symbols.lcg_seed,
excitation,
stereo_pred: pre.stereo_pred,
mid_only_flag: pre.mid_only_flag,
})
}
fn nlsf_to_stable_lpc(bandwidth: Bandwidth, nlsf_q15: &[i16]) -> Result<LpcQ12, Error> {
let lpc_q17 = crate::silk_lsf_to_lpc::LpcQ17::from_nlsf(bandwidth, nlsf_q15)?;
let range_limited = lpc_q17.range_limited();
Ok(range_limited.prediction_gain_limited())
}
#[cfg(test)]
mod tests {
use super::*;
fn fresh_cfg(bandwidth: Bandwidth, frame_size: SilkFrameSize, voiced: bool) -> SilkFrameConfig {
SilkFrameConfig {
bandwidth,
frame_size,
voice_active: voiced,
first_subframe_independent: true,
previous_log_gain: None,
previous_primary_lag: None,
ltp_scaling_present: true,
lsf_interp_after_reset: true,
previous_nlsf_q15: None,
previous_nlsf_len: 0,
stereo: None,
}
}
#[test]
fn decode_from_zero_buffer_is_total() {
for &bw in &[Bandwidth::Nb, Bandwidth::Mb, Bandwidth::Wb] {
for &fs in &[SilkFrameSize::TenMs, SilkFrameSize::TwentyMs] {
for voiced in [false, true] {
let buf = [0u8; 64];
let mut rd = RangeDecoder::new(&buf);
let cfg = fresh_cfg(bw, fs, voiced);
let _ = decode_silk_frame(&mut rd, cfg);
}
}
}
}
#[test]
fn decode_consumes_bits_and_sets_d_lpc() {
let buf: Vec<u8> = (0..96u16)
.map(|i| (i.wrapping_mul(37) & 0xff) as u8)
.collect();
for (&bw, expected_d) in [Bandwidth::Nb, Bandwidth::Mb, Bandwidth::Wb]
.iter()
.zip([10usize, 10, 16])
{
let mut rd = RangeDecoder::new(&buf);
let start = rd.tell();
let cfg = fresh_cfg(bw, SilkFrameSize::TwentyMs, false);
if let Ok(decoded) = decode_silk_frame(&mut rd, cfg) {
assert!(rd.tell() > start, "bw={bw:?} must consume bits");
assert_eq!(decoded.d_lpc, expected_d, "bw={bw:?}");
assert!(decoded.lsf_interp_q2.is_some());
assert!(decoded.lpc_first_half.is_some());
assert_eq!(decoded.lpc_second_half.a_q12().len(), expected_d);
}
}
}
#[test]
fn ten_ms_frame_has_no_interpolation_split() {
let buf: Vec<u8> = (0..96u16)
.map(|i| (i.wrapping_mul(91) & 0xff) as u8)
.collect();
let mut rd = RangeDecoder::new(&buf);
let cfg = fresh_cfg(Bandwidth::Wb, SilkFrameSize::TenMs, false);
if let Ok(decoded) = decode_silk_frame(&mut rd, cfg) {
assert!(decoded.lsf_interp_q2.is_none());
assert!(decoded.lpc_first_half.is_none());
}
}
#[test]
fn swb_fb_rejected() {
let buf = [0x42u8; 32];
for &bw in &[Bandwidth::Swb, Bandwidth::Fb] {
let mut rd = RangeDecoder::new(&buf);
let cfg = fresh_cfg(bw, SilkFrameSize::TwentyMs, false);
assert!(matches!(
decode_silk_frame(&mut rd, cfg),
Err(Error::MalformedPacket)
));
}
}
#[test]
fn mono_frame_has_no_stereo_fields() {
let buf: Vec<u8> = (0..96u16)
.map(|i| (i.wrapping_mul(71).wrapping_add(3) & 0xff) as u8)
.collect();
let mut rd = RangeDecoder::new(&buf);
let cfg = fresh_cfg(Bandwidth::Nb, SilkFrameSize::TwentyMs, false);
if let Ok(decoded) = decode_silk_frame(&mut rd, cfg) {
assert!(decoded.stereo_pred.is_none());
assert!(decoded.mid_only_flag.is_none());
}
}
#[test]
fn stereo_mid_channel_reads_prediction_weights() {
let buf: Vec<u8> = (0..128u16)
.map(|i| (i.wrapping_mul(83).wrapping_add(17) & 0xff) as u8)
.collect();
let mut rd_stereo = RangeDecoder::new(&buf);
let mut cfg_stereo = fresh_cfg(Bandwidth::Wb, SilkFrameSize::TwentyMs, true);
cfg_stereo.stereo = Some(StereoHeaderContext {
has_mid_only_flag: false,
});
let start = rd_stereo.tell();
if let Ok(decoded) = decode_silk_frame(&mut rd_stereo, cfg_stereo) {
assert!(decoded.stereo_pred.is_some());
assert!(decoded.mid_only_flag.is_none()); assert!(rd_stereo.tell() > start);
}
}
#[test]
fn stereo_mid_only_flag_decoded_when_present() {
let buf: Vec<u8> = (0..128u16)
.map(|i| (i.wrapping_mul(59).wrapping_add(29) & 0xff) as u8)
.collect();
let mut rd = RangeDecoder::new(&buf);
let mut cfg = fresh_cfg(Bandwidth::Nb, SilkFrameSize::TwentyMs, false);
cfg.stereo = Some(StereoHeaderContext {
has_mid_only_flag: true,
});
if let Ok(decoded) = decode_silk_frame(&mut rd, cfg) {
assert!(decoded.stereo_pred.is_some());
assert!(matches!(decoded.mid_only_flag, Some(false) | Some(true)));
}
}
struct Lcg(u64);
impl Lcg {
fn next_u32(&mut self) -> u32 {
self.0 = self
.0
.wrapping_mul(6364136223846793005)
.wrapping_add(1442695040888963407);
(self.0 >> 32) as u32
}
fn below(&mut self, n: u32) -> u32 {
self.next_u32() % n
}
}
#[test]
fn whole_frame_encode_decode_roundtrip_random() {
use crate::range_encoder::RangeEncoder;
use crate::silk_excitation::{shell_block_count, SHELL_BLOCK_SAMPLES};
use crate::silk_frame::StereoWeightSymbols;
use crate::silk_gains::GainSymbol;
use crate::silk_ltp::{LagSymbols, LtpSymbols, LTP_MAX_SUBFRAMES};
let mut rng = Lcg(0xF8A3_0382);
let mut done = 0u32;
while done < 250 {
let bandwidth = match rng.below(3) {
0 => Bandwidth::Nb,
1 => Bandwidth::Mb,
_ => Bandwidth::Wb,
};
let frame_size = if rng.below(2) == 0 {
SilkFrameSize::TenMs
} else {
SilkFrameSize::TwentyMs
};
let num_subframes = if frame_size == SilkFrameSize::TenMs {
2u8
} else {
4
};
let voice_active = rng.below(2) == 1;
let stereo_mid = rng.below(3) == 0;
let has_mid_only = stereo_mid && rng.below(2) == 0;
let first_independent = rng.below(2) == 0;
let previous_log_gain = if first_independent && rng.below(2) == 0 {
None
} else {
Some(rng.below(64) as u8)
};
let previous_primary_lag = if rng.below(2) == 0 {
Some(20 + rng.below(200) as i32)
} else {
None
};
let cfg = SilkFrameConfig {
bandwidth,
frame_size,
voice_active,
first_subframe_independent: first_independent,
previous_log_gain,
previous_primary_lag,
ltp_scaling_present: rng.below(2) == 1,
lsf_interp_after_reset: rng.below(2) == 1,
previous_nlsf_q15: None,
previous_nlsf_len: 0,
stereo: stereo_mid.then_some(StereoHeaderContext {
has_mid_only_flag: has_mid_only,
}),
};
let frame_type = if voice_active {
2 + rng.below(4) as u8
} else {
rng.below(2) as u8
};
let voiced = frame_type >= 4;
let header = SilkHeaderSymbols {
stereo: stereo_mid.then(|| StereoWeightSymbols {
n: rng.below(25) as u8,
i0: rng.below(3) as u8,
i1: rng.below(5) as u8,
i2: rng.below(3) as u8,
i3: rng.below(5) as u8,
}),
mid_only_flag: has_mid_only.then(|| rng.below(2) == 1),
frame_type,
};
let gains: Vec<GainSymbol> = (0..num_subframes as usize)
.map(|k| {
if k == 0 && first_independent {
GainSymbol::Independent(rng.below(64) as u8)
} else {
GainSymbol::Delta(rng.below(41) as u8)
}
})
.collect();
let lsf_stage1 = rng.below(32) as u8;
let d_lpc = if bandwidth == Bandwidth::Wb { 16 } else { 10 };
let i2: Vec<i8> = (0..d_lpc).map(|_| rng.below(21) as i8 - 10).collect();
let lsf_interp_w_q2 =
(frame_size == SilkFrameSize::TwentyMs).then(|| rng.below(5) as u8);
let ltp = voiced.then(|| {
let lag_low_count = match bandwidth {
Bandwidth::Nb => 4u32,
Bandwidth::Mb => 6,
_ => 8,
};
let lag = if previous_primary_lag.is_some() {
if rng.below(2) == 0 {
LagSymbols::RelativeDelta {
delta_index: 1 + rng.below(20) as u8,
}
} else {
LagSymbols::RelativeFallback {
lag_high: rng.below(32) as u8,
lag_low: rng.below(lag_low_count) as u8,
}
}
} else {
LagSymbols::Absolute {
lag_high: rng.below(32) as u8,
lag_low: rng.below(lag_low_count) as u8,
}
};
let contour_cells = match (bandwidth, num_subframes) {
(Bandwidth::Nb, 2) => 3u32,
(Bandwidth::Nb, 4) => 11,
(_, 2) => 12,
_ => 34,
};
let periodicity_index = rng.below(3) as u8;
let filter_cells = [8u32, 16, 32][periodicity_index as usize];
let mut filter_indices = [0u8; LTP_MAX_SUBFRAMES];
for f in filter_indices.iter_mut().take(num_subframes as usize) {
*f = rng.below(filter_cells) as u8;
}
LtpSymbols {
lag,
contour_index: rng.below(contour_cells) as u8,
periodicity_index,
filter_indices,
ltp_scaling_index: cfg.ltp_scaling_present.then(|| rng.below(3) as u8),
}
});
let blocks = shell_block_count(bandwidth, frame_size).unwrap();
let total = blocks * SHELL_BLOCK_SAMPLES;
let mut lsb_counts = vec![0u8; blocks];
let mut e_raw = vec![0i32; total];
for (b, lc) in lsb_counts.iter_mut().enumerate() {
let lsbs = if rng.below(4) == 0 {
1 + rng.below(2)
} else {
0
};
*lc = lsbs as u8;
let budget = rng.below(17);
let base = b * SHELL_BLOCK_SAMPLES;
let mut spent = 0u32;
while spent < budget {
let i = base + rng.below(16) as usize;
let add = 1 + rng.below(budget - spent);
e_raw[i] += (add << lsbs) as i32;
spent += add;
}
for slot in e_raw[base..base + SHELL_BLOCK_SAMPLES].iter_mut() {
if lsbs > 0 {
*slot += (rng.next_u32() & ((1 << lsbs) - 1)) as i32;
}
if *slot != 0 && rng.below(2) == 0 {
*slot = -*slot;
}
}
}
let symbols = SilkFrameSymbols {
header,
gains: &gains,
lsf_stage1,
lsf_stage2_i2: &i2,
lsf_interp_w_q2,
ltp,
lcg_seed: rng.below(4) as u8,
excitation: crate::silk_excitation::ExcitationSymbols {
rate_level: rng.below(9) as u8,
lsb_counts: &lsb_counts,
e_raw: &e_raw,
},
};
let mut re = RangeEncoder::new();
let predicted = encode_silk_frame(&mut re, cfg, &symbols).expect("encode");
let bytes = re.finish();
let mut rd = RangeDecoder::new(&bytes);
let decoded = decode_silk_frame(&mut rd, cfg).expect("decode");
assert!(!rd.has_error());
assert_eq!(decoded, predicted, "cfg={cfg:?}");
done += 1;
}
}
#[test]
fn whole_frame_encode_rejects_ltp_mismatch() {
use crate::range_encoder::RangeEncoder;
let cfg = fresh_cfg(Bandwidth::Nb, SilkFrameSize::TenMs, true);
let gains = [
crate::silk_gains::GainSymbol::Independent(30),
crate::silk_gains::GainSymbol::Delta(4),
];
let i2 = [0i8; 10];
let lsb = [0u8; 5];
let e = [0i32; 80];
let symbols = SilkFrameSymbols {
header: SilkHeaderSymbols {
stereo: None,
mid_only_flag: None,
frame_type: 4, },
gains: &gains,
lsf_stage1: 0,
lsf_stage2_i2: &i2,
lsf_interp_w_q2: None, ltp: None, lcg_seed: 0,
excitation: crate::silk_excitation::ExcitationSymbols {
rate_level: 0,
lsb_counts: &lsb,
e_raw: &e,
},
};
let mut re = RangeEncoder::new();
assert!(encode_silk_frame(&mut re, cfg, &symbols).is_err());
}
}