use crate::decoder::ChannelDecodeState;
use crate::range_encoder::RangeEncoder;
use crate::silk_decode::{
encode_silk_frame, SilkFrameConfig, SilkFrameDecoded, SilkFrameSymbols, StereoHeaderContext,
};
use crate::silk_excitation::SilkFrameSize;
use crate::silk_header::{silk_frame_count, PerFrameLbrr, SilkChannelHeader, SilkHeaderBits};
use crate::toc::{Bandwidth, FrameCountCode, Mode, OpusTocByte};
use crate::Error;
pub fn encode_silk_only_packet_mono(
bandwidth: Bandwidth,
frame_size_tenths_ms: u16,
frames: &[SilkFrameSymbols<'_>],
) -> Result<(Vec<u8>, Vec<SilkFrameDecoded>), Error> {
let n = silk_frame_count(frame_size_tenths_ms).ok_or(Error::MalformedPacket)? as usize;
let no_lbrr = vec![None; n];
let (packet, regular, _) =
encode_silk_only_packet_mono_with_lbrr(bandwidth, frame_size_tenths_ms, frames, &no_lbrr)?;
Ok((packet, regular))
}
#[allow(clippy::type_complexity)]
pub fn encode_silk_only_packet_mono_with_lbrr(
bandwidth: Bandwidth,
frame_size_tenths_ms: u16,
frames: &[SilkFrameSymbols<'_>],
lbrr: &[Option<SilkFrameSymbols<'_>>],
) -> Result<
(
Vec<u8>,
Vec<SilkFrameDecoded>,
Vec<Option<SilkFrameDecoded>>,
),
Error,
> {
let num_silk_frames = silk_frame_count(frame_size_tenths_ms).ok_or(Error::MalformedPacket)?;
if frames.len() != num_silk_frames as usize || lbrr.len() != num_silk_frames as usize {
return Err(Error::MalformedPacket);
}
let frame_size = if frame_size_tenths_ms == 100 {
SilkFrameSize::TenMs
} else {
SilkFrameSize::TwentyMs
};
let toc = OpusTocByte::compose_byte(
Mode::SilkOnly,
bandwidth,
frame_size_tenths_ms,
false,
FrameCountCode::One,
)?;
let mut re = RangeEncoder::new();
let mut vad_flags = 0u8;
for (idx, f) in frames.iter().enumerate() {
if f.header.frame_type >= 2 {
vad_flags |= 1 << idx;
}
}
let mut lbrr_bits = 0u8;
for (idx, l) in lbrr.iter().enumerate() {
if l.is_some() {
lbrr_bits |= 1 << idx;
}
}
let header = SilkHeaderBits {
num_silk_frames,
mid: SilkChannelHeader {
vad_flags,
lbrr_flag: lbrr_bits != 0,
},
side: None,
per_frame_lbrr: PerFrameLbrr {
mid: lbrr_bits,
side: 0,
},
};
header.encode(&mut re)?;
let mut lbrr_prev_gain: Option<u8> = None;
let mut lbrr_prev_lag: Option<i32> = None;
let mut lbrr_first = true;
let mut lbrr_predictions: Vec<Option<SilkFrameDecoded>> = Vec::with_capacity(lbrr.len());
for entry in lbrr.iter() {
let Some(symbols) = entry else {
lbrr_predictions.push(None);
continue;
};
if symbols.header.frame_type < 2 {
return Err(Error::MalformedPacket);
}
let cfg = SilkFrameConfig {
bandwidth,
frame_size,
voice_active: true,
first_subframe_independent: lbrr_first || lbrr_prev_gain.is_none(),
previous_log_gain: lbrr_prev_gain,
previous_primary_lag: lbrr_prev_lag,
ltp_scaling_present: lbrr_first,
lsf_interp_after_reset: lbrr_first,
previous_nlsf_q15: None,
previous_nlsf_len: 0,
stereo: None,
};
let decoded = encode_silk_frame(&mut re, cfg, symbols)?;
lbrr_prev_gain = Some(decoded.gains.last_log_gain());
lbrr_prev_lag = Some(decoded.ltp.primary_lag());
lbrr_first = false;
lbrr_predictions.push(Some(decoded));
}
let mut prev_gain: Option<u8> = None;
let mut prev_lag: Option<i32> = None;
let mut prev_nlsf: Option<[i16; crate::silk_lsf_stage2::D_LPC_MAX]> = None;
let mut prev_nlsf_len = 0usize;
let mut first = true;
let mut predictions = Vec::with_capacity(frames.len());
for (idx, symbols) in frames.iter().enumerate() {
let cfg = SilkFrameConfig {
bandwidth,
frame_size,
voice_active: (vad_flags >> idx) & 1 == 1,
first_subframe_independent: first || prev_gain.is_none(),
previous_log_gain: prev_gain,
previous_primary_lag: prev_lag,
ltp_scaling_present: first,
lsf_interp_after_reset: first || prev_nlsf.is_none(),
previous_nlsf_q15: prev_nlsf,
previous_nlsf_len: prev_nlsf_len,
stereo: None,
};
let decoded = encode_silk_frame(&mut re, cfg, symbols)?;
prev_gain = Some(decoded.gains.last_log_gain());
prev_lag = Some(decoded.ltp.primary_lag());
prev_nlsf = Some(decoded.nlsf_q15);
prev_nlsf_len = decoded.d_lpc;
first = false;
predictions.push(decoded);
}
let body = re.finish();
let mut packet = Vec::with_capacity(1 + body.len());
packet.push(toc);
packet.extend_from_slice(&body);
Ok((packet, predictions, lbrr_predictions))
}
#[derive(Debug, Clone, Copy)]
pub struct StereoIntervalScripts<'a> {
pub mid: SilkFrameSymbols<'a>,
pub side: Option<SilkFrameSymbols<'a>>,
}
#[derive(Debug, Clone)]
pub struct StereoPacketPredictions {
pub mid: Vec<SilkFrameDecoded>,
pub side: Vec<Option<SilkFrameDecoded>>,
}
pub fn encode_silk_only_packet_stereo(
bandwidth: Bandwidth,
frame_size_tenths_ms: u16,
intervals: &[StereoIntervalScripts<'_>],
) -> Result<(Vec<u8>, StereoPacketPredictions), Error> {
let n = silk_frame_count(frame_size_tenths_ms).ok_or(Error::MalformedPacket)? as usize;
let no_lbrr = vec![StereoIntervalLbrr::default(); n];
let (packet, regular, _) = encode_silk_only_packet_stereo_with_lbrr(
bandwidth,
frame_size_tenths_ms,
intervals,
&no_lbrr,
)?;
Ok((packet, regular))
}
#[derive(Debug, Clone, Copy, Default)]
pub struct StereoIntervalLbrr<'a> {
pub mid: Option<SilkFrameSymbols<'a>>,
pub side: Option<SilkFrameSymbols<'a>>,
}
pub fn encode_silk_only_packet_stereo_with_lbrr(
bandwidth: Bandwidth,
frame_size_tenths_ms: u16,
intervals: &[StereoIntervalScripts<'_>],
lbrr: &[StereoIntervalLbrr<'_>],
) -> Result<(Vec<u8>, StereoPacketPredictions, StereoLbrrPredictions), Error> {
let num_silk_frames = silk_frame_count(frame_size_tenths_ms).ok_or(Error::MalformedPacket)?;
if intervals.len() != num_silk_frames as usize || lbrr.len() != num_silk_frames as usize {
return Err(Error::MalformedPacket);
}
let frame_size = if frame_size_tenths_ms == 100 {
SilkFrameSize::TenMs
} else {
SilkFrameSize::TwentyMs
};
let toc = OpusTocByte::compose_byte(
Mode::SilkOnly,
bandwidth,
frame_size_tenths_ms,
true,
FrameCountCode::One,
)?;
let mut re = RangeEncoder::new();
let mut mid_vad_flags = 0u8;
let mut side_vad_flags = 0u8;
for (idx, iv) in intervals.iter().enumerate() {
if iv.mid.header.frame_type >= 2 {
mid_vad_flags |= 1 << idx;
}
if let Some(side) = &iv.side {
if side.header.frame_type >= 2 {
side_vad_flags |= 1 << idx;
}
}
}
let mut mid_lbrr_bits = 0u8;
let mut side_lbrr_bits = 0u8;
for (idx, l) in lbrr.iter().enumerate() {
if l.mid.is_some() {
mid_lbrr_bits |= 1 << idx;
}
if l.side.is_some() {
side_lbrr_bits |= 1 << idx;
}
}
let header = SilkHeaderBits {
num_silk_frames,
mid: SilkChannelHeader {
vad_flags: mid_vad_flags,
lbrr_flag: mid_lbrr_bits != 0,
},
side: Some(SilkChannelHeader {
vad_flags: side_vad_flags,
lbrr_flag: side_lbrr_bits != 0,
}),
per_frame_lbrr: PerFrameLbrr {
mid: mid_lbrr_bits,
side: side_lbrr_bits,
},
};
header.encode(&mut re)?;
let mut lbrr_mid_state = ChannelDecodeState::new();
let mut lbrr_side_state = ChannelDecodeState::new();
let mut lbrr_mid_pred: Vec<Option<SilkFrameDecoded>> = Vec::with_capacity(lbrr.len());
let mut lbrr_side_pred: Vec<Option<SilkFrameDecoded>> = Vec::with_capacity(lbrr.len());
for entry in lbrr.iter() {
let side_lbrr = entry.side.is_some();
if let Some(mid_sym) = &entry.mid {
if mid_sym.header.frame_type < 2 {
return Err(Error::MalformedPacket);
}
if !side_lbrr && mid_sym.header.mid_only_flag != Some(true) {
return Err(Error::MalformedPacket);
}
let stereo_ctx = StereoHeaderContext {
has_mid_only_flag: !side_lbrr,
};
let decoded = encode_silk_frame(
&mut re,
lbrr_mid_state.config(bandwidth, frame_size, true, Some(stereo_ctx)),
mid_sym,
)?;
lbrr_mid_state.advance(&decoded);
lbrr_mid_pred.push(Some(decoded));
} else {
lbrr_mid_pred.push(None);
}
if let Some(side_sym) = &entry.side {
if side_sym.header.frame_type < 2 {
return Err(Error::MalformedPacket);
}
let decoded = encode_silk_frame(
&mut re,
lbrr_side_state.config(bandwidth, frame_size, true, None),
side_sym,
)?;
lbrr_side_state.advance(&decoded);
lbrr_side_pred.push(Some(decoded));
} else {
lbrr_side_pred.push(None);
}
}
let mut mid_state = ChannelDecodeState::new();
let mut side_state = ChannelDecodeState::new();
let mut mid_pred: Vec<SilkFrameDecoded> = Vec::with_capacity(intervals.len());
let mut side_pred: Vec<Option<SilkFrameDecoded>> = Vec::with_capacity(intervals.len());
for (idx, iv) in intervals.iter().enumerate() {
let side_active = (side_vad_flags >> idx) & 1 == 1;
match iv.mid.header.mid_only_flag {
Some(flag) => {
if side_active || flag != iv.side.is_none() {
return Err(Error::MalformedPacket);
}
}
None => {
if !side_active || iv.side.is_none() {
return Err(Error::MalformedPacket);
}
}
}
let stereo_ctx = StereoHeaderContext {
has_mid_only_flag: !side_active,
};
let mid_decoded = encode_silk_frame(
&mut re,
mid_state.config(
bandwidth,
frame_size,
(mid_vad_flags >> idx) & 1 == 1,
Some(stereo_ctx),
),
&iv.mid,
)?;
mid_state.advance(&mid_decoded);
mid_pred.push(mid_decoded);
if let Some(side_sym) = &iv.side {
let side_decoded = encode_silk_frame(
&mut re,
side_state.config(bandwidth, frame_size, side_active, None),
side_sym,
)?;
side_state.advance(&side_decoded);
side_pred.push(Some(side_decoded));
} else {
side_pred.push(None);
}
}
let body = re.finish();
let mut packet = Vec::with_capacity(1 + body.len());
packet.push(toc);
packet.extend_from_slice(&body);
Ok((
packet,
StereoPacketPredictions {
mid: mid_pred,
side: side_pred,
},
StereoLbrrPredictions {
mid: lbrr_mid_pred,
side: lbrr_side_pred,
},
))
}
#[derive(Debug, Clone)]
pub struct StereoLbrrPredictions {
pub mid: Vec<Option<SilkFrameDecoded>>,
pub side: Vec<Option<SilkFrameDecoded>>,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::decoder::{FrameDecodeStatus, OpusDecoder};
use crate::range_decoder::RangeDecoder;
use crate::silk_decode::decode_silk_frame;
use crate::silk_excitation::{shell_block_count, ExcitationSymbols, SHELL_BLOCK_SAMPLES};
use crate::silk_frame::SilkHeaderSymbols;
use crate::silk_gains::GainSymbol;
use crate::silk_ltp::{LagSymbols, LtpSymbols, LTP_MAX_SUBFRAMES};
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
}
}
struct ScriptBufs {
gains: Vec<GainSymbol>,
i2: Vec<i8>,
lsb_counts: Vec<u8>,
e_raw: Vec<i32>,
header: SilkHeaderSymbols,
lsf_stage1: u8,
lsf_interp_w_q2: Option<u8>,
ltp: Option<LtpSymbols>,
lcg_seed: u8,
rate_level: u8,
}
fn random_frame_script(
rng: &mut Lcg,
bandwidth: Bandwidth,
frame_size: SilkFrameSize,
first: bool,
has_prev_lag: bool,
) -> ScriptBufs {
let num_subframes = if frame_size == SilkFrameSize::TenMs {
2usize
} else {
4
};
let frame_type = rng.below(6) as u8;
let voiced = frame_type >= 4;
let gains: Vec<GainSymbol> = (0..num_subframes)
.map(|k| {
if k == 0 && first {
GainSymbol::Independent(rng.below(64) as u8)
} else {
GainSymbol::Delta(rng.below(41) as u8)
}
})
.collect();
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 ltp = voiced.then(|| {
let lag_low_count = match bandwidth {
Bandwidth::Nb => 4u32,
Bandwidth::Mb => 6,
_ => 8,
};
let lag = if has_prev_lag {
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) {
*f = rng.below(filter_cells) as u8;
}
LtpSymbols {
lag,
contour_index: rng.below(contour_cells) as u8,
periodicity_index,
filter_indices,
ltp_scaling_index: first.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 } 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) as i32;
}
if *slot != 0 && rng.below(2) == 0 {
*slot = -*slot;
}
}
}
ScriptBufs {
gains,
i2,
lsb_counts,
e_raw,
header: SilkHeaderSymbols {
stereo: None,
mid_only_flag: None,
frame_type,
},
lsf_stage1: rng.below(32) as u8,
lsf_interp_w_q2: (frame_size == SilkFrameSize::TwentyMs).then(|| rng.below(5) as u8),
ltp,
lcg_seed: rng.below(4) as u8,
rate_level: rng.below(9) as u8,
}
}
fn symbols_of(bufs: &ScriptBufs) -> SilkFrameSymbols<'_> {
SilkFrameSymbols {
header: bufs.header,
gains: &bufs.gains,
lsf_stage1: bufs.lsf_stage1,
lsf_stage2_i2: &bufs.i2,
lsf_interp_w_q2: bufs.lsf_interp_w_q2,
ltp: bufs.ltp,
lcg_seed: bufs.lcg_seed,
excitation: ExcitationSymbols {
rate_level: bufs.rate_level,
lsb_counts: &bufs.lsb_counts,
e_raw: &bufs.e_raw,
},
}
}
#[test]
fn packet_encode_decodes_end_to_end() {
let mut rng = Lcg(0x0AC4_E701);
for round in 0..120 {
let bandwidth = match rng.below(3) {
0 => Bandwidth::Nb,
1 => Bandwidth::Mb,
_ => Bandwidth::Wb,
};
let fs_tenths: u16 = [100u16, 200, 400, 600][rng.below(4) as usize];
let frame_size = if fs_tenths == 100 {
SilkFrameSize::TenMs
} else {
SilkFrameSize::TwentyMs
};
let n = silk_frame_count(fs_tenths).unwrap() as usize;
let bufs: Vec<ScriptBufs> = (0..n)
.map(|idx| random_frame_script(&mut rng, bandwidth, frame_size, idx == 0, idx > 0))
.collect();
let scripts: Vec<SilkFrameSymbols<'_>> = bufs.iter().map(symbols_of).collect();
let (packet, predictions) =
encode_silk_only_packet_mono(bandwidth, fs_tenths, &scripts)
.expect("packet encode");
assert_eq!(predictions.len(), n);
let mut dec = OpusDecoder::new();
let out = dec.decode_packet(&packet).expect("packet decode");
assert_eq!(out.channels, 1, "round {round}");
assert_eq!(out.frame_outcomes.len(), 1, "round {round}");
assert_eq!(
out.frame_outcomes[0].status,
FrameDecodeStatus::SilkParamsDecoded,
"round {round} bw={bandwidth:?} fs={fs_tenths}"
);
assert_eq!(
out.samples_per_channel() as u32,
48_000 * fs_tenths as u32 / 10_000,
"round {round}"
);
let mut rd = RangeDecoder::new(&packet[1..]);
let header = SilkHeaderBits::decode(&mut rd, n as u8, false).expect("header bits");
assert!(!header.mid.lbrr_flag);
let mut prev_gain: Option<u8> = None;
let mut prev_lag: Option<i32> = None;
let mut prev_nlsf: Option<[i16; crate::silk_lsf_stage2::D_LPC_MAX]> = None;
let mut prev_nlsf_len = 0usize;
let mut first = true;
for (idx, expected) in predictions.iter().enumerate() {
let cfg = SilkFrameConfig {
bandwidth,
frame_size,
voice_active: header.mid_vad(idx as u8),
first_subframe_independent: first || prev_gain.is_none(),
previous_log_gain: prev_gain,
previous_primary_lag: prev_lag,
ltp_scaling_present: first,
lsf_interp_after_reset: first || prev_nlsf.is_none(),
previous_nlsf_q15: prev_nlsf,
previous_nlsf_len: prev_nlsf_len,
stereo: None,
};
let decoded = decode_silk_frame(&mut rd, cfg).expect("frame decode");
assert_eq!(&decoded, expected, "round {round} frame {idx}");
prev_gain = Some(decoded.gains.last_log_gain());
prev_lag = Some(decoded.ltp.primary_lag());
prev_nlsf = Some(decoded.nlsf_q15);
prev_nlsf_len = decoded.d_lpc;
first = false;
}
assert!(!rd.has_error());
}
}
#[test]
fn packet_encode_with_lbrr_fec_roundtrip() {
use crate::decoder::FecDecodeStatus;
let mut rng = Lcg(0xFEC0_0382);
for round in 0..60 {
let bandwidth = match rng.below(3) {
0 => Bandwidth::Nb,
1 => Bandwidth::Mb,
_ => Bandwidth::Wb,
};
let fs_tenths: u16 = [100u16, 200, 400, 600][rng.below(4) as usize];
let frame_size = if fs_tenths == 100 {
SilkFrameSize::TenMs
} else {
SilkFrameSize::TwentyMs
};
let n = silk_frame_count(fs_tenths).unwrap() as usize;
let bufs: Vec<ScriptBufs> = (0..n)
.map(|idx| random_frame_script(&mut rng, bandwidth, frame_size, idx == 0, idx > 0))
.collect();
let scripts: Vec<SilkFrameSymbols<'_>> = bufs.iter().map(symbols_of).collect();
let mut which = vec![false; n];
which[rng.below(n as u32) as usize] = true;
for w in which.iter_mut() {
if rng.below(2) == 0 {
*w = true;
}
}
let mut lbrr_bufs: Vec<Option<ScriptBufs>> = Vec::with_capacity(n);
let mut coded_first = true;
for &w in &which {
if !w {
lbrr_bufs.push(None);
continue;
}
let mut b =
random_frame_script(&mut rng, bandwidth, frame_size, coded_first, !coded_first);
if b.header.frame_type < 2 {
b.header.frame_type += 2; }
lbrr_bufs.push(Some(b));
coded_first = false;
}
let lbrr_scripts: Vec<Option<SilkFrameSymbols<'_>>> = lbrr_bufs
.iter()
.map(|b| b.as_ref().map(symbols_of))
.collect();
let (packet, regular, lbrr_pred) = encode_silk_only_packet_mono_with_lbrr(
bandwidth,
fs_tenths,
&scripts,
&lbrr_scripts,
)
.expect("packet encode with lbrr");
assert_eq!(regular.len(), n);
assert_eq!(
lbrr_pred.iter().filter(|p| p.is_some()).count(),
which.iter().filter(|&&w| w).count()
);
let mut dec = OpusDecoder::new();
let out = dec.decode_packet(&packet).expect("packet decode");
assert_eq!(
out.frame_outcomes[0].status,
FrameDecodeStatus::SilkParamsDecoded,
"round {round} bw={bandwidth:?} fs={fs_tenths}"
);
let mut fec_dec = OpusDecoder::new();
let rec = fec_dec.decode_packet_fec(&packet).expect("fec decode");
assert_eq!(
rec.status,
FecDecodeStatus::Recovered,
"round {round} bw={bandwidth:?} fs={fs_tenths}"
);
assert_eq!(
rec.pcm.len() as u32,
48_000 * fs_tenths as u32 / 10_000,
"round {round}"
);
}
let bufs = random_frame_script(
&mut rng,
Bandwidth::Nb,
SilkFrameSize::TwentyMs,
true,
false,
);
let script = symbols_of(&bufs);
let (packet, _) =
encode_silk_only_packet_mono(Bandwidth::Nb, 200, &[script]).expect("encode");
let mut dec = OpusDecoder::new();
let rec = dec.decode_packet_fec(&packet).expect("fec decode");
assert_eq!(rec.status, FecDecodeStatus::NoLbrr);
}
use crate::silk_frame::StereoWeightSymbols;
fn random_weights(rng: &mut Lcg) -> StereoWeightSymbols {
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,
}
}
#[derive(Clone, Copy, PartialEq)]
enum SidePattern {
Active,
InactiveCoded,
MidOnly,
}
#[allow(clippy::too_many_arguments)]
fn random_stereo_interval(
rng: &mut Lcg,
bandwidth: Bandwidth,
frame_size: SilkFrameSize,
pattern: SidePattern,
mid_first: bool,
mid_has_prev_lag: bool,
side_first: bool,
side_has_prev_lag: bool,
) -> (ScriptBufs, Option<ScriptBufs>) {
let mut mid = random_frame_script(rng, bandwidth, frame_size, mid_first, mid_has_prev_lag);
mid.header.stereo = Some(random_weights(rng));
mid.header.mid_only_flag = match pattern {
SidePattern::Active => None,
SidePattern::InactiveCoded => Some(false),
SidePattern::MidOnly => Some(true),
};
let side = match pattern {
SidePattern::MidOnly => None,
SidePattern::Active => {
let mut s =
random_frame_script(rng, bandwidth, frame_size, side_first, side_has_prev_lag);
if s.header.frame_type < 2 {
s.header.frame_type += 2;
}
Some(s)
}
SidePattern::InactiveCoded => {
let mut s =
random_frame_script(rng, bandwidth, frame_size, side_first, side_has_prev_lag);
if s.header.frame_type >= 2 {
s.header.frame_type %= 2;
s.ltp = None;
}
Some(s)
}
};
(mid, side)
}
#[test]
fn stereo_packet_encode_decodes_end_to_end() {
let mut rng = Lcg(0x57E2_E001);
for round in 0..120 {
let bandwidth = match rng.below(3) {
0 => Bandwidth::Nb,
1 => Bandwidth::Mb,
_ => Bandwidth::Wb,
};
let fs_tenths: u16 = [100u16, 200, 400, 600][rng.below(4) as usize];
let frame_size = if fs_tenths == 100 {
SilkFrameSize::TenMs
} else {
SilkFrameSize::TwentyMs
};
let n = silk_frame_count(fs_tenths).unwrap() as usize;
let mut interval_bufs: Vec<(ScriptBufs, Option<ScriptBufs>)> = Vec::with_capacity(n);
let mut patterns: Vec<SidePattern> = Vec::with_capacity(n);
let mut side_first = true;
let mut side_has_prev = false;
for idx in 0..n {
let pattern = match rng.below(3) {
0 => SidePattern::Active,
1 => SidePattern::InactiveCoded,
_ => SidePattern::MidOnly,
};
let iv = random_stereo_interval(
&mut rng,
bandwidth,
frame_size,
pattern,
idx == 0,
idx > 0,
side_first,
side_has_prev,
);
if pattern != SidePattern::MidOnly {
side_first = false;
side_has_prev = true;
}
patterns.push(pattern);
interval_bufs.push(iv);
}
let intervals: Vec<StereoIntervalScripts<'_>> = interval_bufs
.iter()
.map(|(m, s)| StereoIntervalScripts {
mid: symbols_of(m),
side: s.as_ref().map(symbols_of),
})
.collect();
let (packet, predictions) =
encode_silk_only_packet_stereo(bandwidth, fs_tenths, &intervals)
.expect("stereo packet encode");
assert_eq!(predictions.mid.len(), n);
assert_eq!(predictions.side.len(), n);
let mut dec = OpusDecoder::new();
let out = dec.decode_packet(&packet).expect("packet decode");
assert_eq!(out.channels, 2, "round {round}");
assert_eq!(
out.frame_outcomes[0].status,
FrameDecodeStatus::SilkStereoDecoded,
"round {round} bw={bandwidth:?} fs={fs_tenths}"
);
assert_eq!(
out.samples_per_channel() as u32,
48_000 * fs_tenths as u32 / 10_000,
"round {round}"
);
assert_eq!(
out.pcm.len() as u32,
2 * 48_000 * fs_tenths as u32 / 10_000,
"round {round}"
);
let mut rd = RangeDecoder::new(&packet[1..]);
let header = SilkHeaderBits::decode(&mut rd, n as u8, true).expect("header bits");
assert!(!header.mid.lbrr_flag);
assert!(header.side.is_some_and(|s| !s.lbrr_flag));
let mut mid_state = crate::decoder::ChannelDecodeState::new();
let mut side_state = crate::decoder::ChannelDecodeState::new();
for (idx, pattern) in patterns.iter().enumerate() {
let side_active = header.side_vad(idx as u8);
assert_eq!(
side_active,
*pattern == SidePattern::Active,
"round {round} interval {idx}"
);
let stereo_ctx = crate::silk_decode::StereoHeaderContext {
has_mid_only_flag: !side_active,
};
let mid_decoded = decode_silk_frame(
&mut rd,
mid_state.config(
bandwidth,
frame_size,
header.mid_vad(idx as u8),
Some(stereo_ctx),
),
)
.expect("mid decode");
assert_eq!(
&mid_decoded, &predictions.mid[idx],
"round {round} mid {idx}"
);
let side_coded = side_active || mid_decoded.mid_only_flag == Some(false);
mid_state.advance(&mid_decoded);
if side_coded {
let side_decoded = decode_silk_frame(
&mut rd,
side_state.config(bandwidth, frame_size, side_active, None),
)
.expect("side decode");
assert_eq!(
Some(&side_decoded),
predictions.side[idx].as_ref(),
"round {round} side {idx}"
);
side_state.advance(&side_decoded);
} else {
assert!(predictions.side[idx].is_none(), "round {round} side {idx}");
}
}
assert!(!rd.has_error());
}
}
#[test]
fn stereo_packet_encode_with_lbrr_fec_roundtrip() {
use crate::decoder::FecDecodeStatus;
let mut rng = Lcg(0xFEC0_57E2);
for round in 0..60 {
let bandwidth = match rng.below(3) {
0 => Bandwidth::Nb,
1 => Bandwidth::Mb,
_ => Bandwidth::Wb,
};
let fs_tenths: u16 = [100u16, 200, 400, 600][rng.below(4) as usize];
let frame_size = if fs_tenths == 100 {
SilkFrameSize::TenMs
} else {
SilkFrameSize::TwentyMs
};
let n = silk_frame_count(fs_tenths).unwrap() as usize;
let mut interval_bufs: Vec<(ScriptBufs, Option<ScriptBufs>)> = Vec::with_capacity(n);
for idx in 0..n {
interval_bufs.push(random_stereo_interval(
&mut rng,
bandwidth,
frame_size,
SidePattern::Active,
idx == 0,
idx > 0,
idx == 0,
idx > 0,
));
}
let intervals: Vec<StereoIntervalScripts<'_>> = interval_bufs
.iter()
.map(|(m, s)| StereoIntervalScripts {
mid: symbols_of(m),
side: s.as_ref().map(symbols_of),
})
.collect();
let mut kinds = vec![0u32; n];
kinds[rng.below(n as u32) as usize] = 1 + rng.below(3);
for k in kinds.iter_mut() {
if *k == 0 && rng.below(2) == 0 {
*k = 1 + rng.below(3);
}
}
let mut lbrr_bufs: Vec<(Option<ScriptBufs>, Option<ScriptBufs>)> =
Vec::with_capacity(n);
let mut mid_first = true;
let mut mid_prev = false;
let mut side_first = true;
let mut side_prev = false;
for &kind in &kinds {
let want_mid = kind == 1 || kind == 2;
let want_side = kind == 2 || kind == 3;
let mid = want_mid.then(|| {
let mut b =
random_frame_script(&mut rng, bandwidth, frame_size, mid_first, mid_prev);
if b.header.frame_type < 2 {
b.header.frame_type += 2; }
b.header.stereo = Some(random_weights(&mut rng));
b.header.mid_only_flag = (!want_side).then_some(true);
mid_first = false;
mid_prev = true;
b
});
let side = want_side.then(|| {
let mut b =
random_frame_script(&mut rng, bandwidth, frame_size, side_first, side_prev);
if b.header.frame_type < 2 {
b.header.frame_type += 2;
}
side_first = false;
side_prev = true;
b
});
lbrr_bufs.push((mid, side));
}
let lbrr_scripts: Vec<StereoIntervalLbrr<'_>> = lbrr_bufs
.iter()
.map(|(m, s)| StereoIntervalLbrr {
mid: m.as_ref().map(symbols_of),
side: s.as_ref().map(symbols_of),
})
.collect();
let (packet, regular, lbrr_pred) = encode_silk_only_packet_stereo_with_lbrr(
bandwidth,
fs_tenths,
&intervals,
&lbrr_scripts,
)
.expect("stereo packet encode with lbrr");
assert_eq!(regular.mid.len(), n);
assert_eq!(
lbrr_pred.mid.iter().filter(|p| p.is_some()).count(),
kinds.iter().filter(|&&k| k == 1 || k == 2).count()
);
assert_eq!(
lbrr_pred.side.iter().filter(|p| p.is_some()).count(),
kinds.iter().filter(|&&k| k == 2 || k == 3).count()
);
let mut dec = OpusDecoder::new();
let out = dec.decode_packet(&packet).expect("packet decode");
assert_eq!(
out.frame_outcomes[0].status,
FrameDecodeStatus::SilkStereoDecoded,
"round {round} bw={bandwidth:?} fs={fs_tenths} kinds={kinds:?}"
);
let any_mid_lbrr = kinds.iter().any(|&k| k == 1 || k == 2);
let mut fec_dec = OpusDecoder::new();
let rec = fec_dec.decode_packet_fec(&packet).expect("fec decode");
assert_eq!(
rec.status,
if any_mid_lbrr {
FecDecodeStatus::Recovered
} else {
FecDecodeStatus::NoLbrr
},
"round {round} bw={bandwidth:?} fs={fs_tenths} kinds={kinds:?}"
);
assert_eq!(rec.channels, 2, "round {round}");
assert_eq!(
rec.pcm.len() as u32,
2 * 48_000 * fs_tenths as u32 / 10_000,
"round {round}"
);
}
}
#[test]
fn stereo_packet_encode_rejects_inconsistent_scripts() {
let mut rng = Lcg(0xBAD5_7E2E);
let (mid, side) = random_stereo_interval(
&mut rng,
Bandwidth::Wb,
SilkFrameSize::TwentyMs,
SidePattern::Active,
true,
false,
true,
false,
);
let side = side.unwrap();
let iv = StereoIntervalScripts {
mid: symbols_of(&mid),
side: Some(symbols_of(&side)),
};
assert!(encode_silk_only_packet_stereo(Bandwidth::Wb, 400, &[iv]).is_err());
let mut mid_no_w = symbols_of(&mid);
mid_no_w.header.stereo = None;
let iv = StereoIntervalScripts {
mid: mid_no_w,
side: Some(symbols_of(&side)),
};
assert!(encode_silk_only_packet_stereo(Bandwidth::Wb, 200, &[iv]).is_err());
let mut mid_bad_flag = symbols_of(&mid);
mid_bad_flag.header.mid_only_flag = Some(false);
let iv = StereoIntervalScripts {
mid: mid_bad_flag,
side: Some(symbols_of(&side)),
};
assert!(encode_silk_only_packet_stereo(Bandwidth::Wb, 200, &[iv]).is_err());
let (mid_mo, _) = random_stereo_interval(
&mut rng,
Bandwidth::Wb,
SilkFrameSize::TwentyMs,
SidePattern::MidOnly,
true,
false,
true,
false,
);
let mut side_inactive = random_frame_script(
&mut rng,
Bandwidth::Wb,
SilkFrameSize::TwentyMs,
true,
false,
);
if side_inactive.header.frame_type >= 2 {
side_inactive.header.frame_type %= 2;
side_inactive.ltp = None;
}
let iv = StereoIntervalScripts {
mid: symbols_of(&mid_mo),
side: Some(symbols_of(&side_inactive)),
};
assert!(encode_silk_only_packet_stereo(Bandwidth::Wb, 200, &[iv]).is_err());
let mut side_with_w = symbols_of(&side);
side_with_w.header.stereo = Some(random_weights(&mut rng));
let iv = StereoIntervalScripts {
mid: symbols_of(&mid),
side: Some(side_with_w),
};
assert!(encode_silk_only_packet_stereo(Bandwidth::Wb, 200, &[iv]).is_err());
let (mid_ok, side_ok) = random_stereo_interval(
&mut rng,
Bandwidth::Wb,
SilkFrameSize::TwentyMs,
SidePattern::Active,
true,
false,
true,
false,
);
let side_ok = side_ok.unwrap();
let mut lbrr_mid = random_frame_script(
&mut rng,
Bandwidth::Wb,
SilkFrameSize::TwentyMs,
true,
false,
);
if lbrr_mid.header.frame_type >= 2 {
lbrr_mid.header.frame_type %= 2;
lbrr_mid.ltp = None;
}
lbrr_mid.header.stereo = Some(random_weights(&mut rng));
lbrr_mid.header.mid_only_flag = Some(true);
let iv = StereoIntervalScripts {
mid: symbols_of(&mid_ok),
side: Some(symbols_of(&side_ok)),
};
let lbrr = StereoIntervalLbrr {
mid: Some(symbols_of(&lbrr_mid)),
side: None,
};
assert!(
encode_silk_only_packet_stereo_with_lbrr(Bandwidth::Wb, 200, &[iv], &[lbrr]).is_err()
);
let mut lbrr_mid_bad = random_frame_script(
&mut rng,
Bandwidth::Wb,
SilkFrameSize::TwentyMs,
true,
false,
);
if lbrr_mid_bad.header.frame_type < 2 {
lbrr_mid_bad.header.frame_type += 2;
}
lbrr_mid_bad.header.stereo = Some(random_weights(&mut rng));
lbrr_mid_bad.header.mid_only_flag = Some(false);
let iv = StereoIntervalScripts {
mid: symbols_of(&mid_ok),
side: Some(symbols_of(&side_ok)),
};
let lbrr = StereoIntervalLbrr {
mid: Some(symbols_of(&lbrr_mid_bad)),
side: None,
};
assert!(
encode_silk_only_packet_stereo_with_lbrr(Bandwidth::Wb, 200, &[iv], &[lbrr]).is_err()
);
}
#[test]
fn packet_encode_rejects_bad_shape() {
let mut rng = Lcg(7);
let bufs = random_frame_script(
&mut rng,
Bandwidth::Nb,
SilkFrameSize::TwentyMs,
true,
false,
);
let script = symbols_of(&bufs);
assert!(encode_silk_only_packet_mono(Bandwidth::Nb, 400, &[script]).is_err());
let script = symbols_of(&bufs);
assert!(encode_silk_only_packet_mono(Bandwidth::Nb, 25, &[script]).is_err());
let script = symbols_of(&bufs);
assert!(encode_silk_only_packet_mono(Bandwidth::Swb, 200, &[script]).is_err());
}
}