use crate::analysis;
use crate::celt::{self, CeltEncoder};
use crate::config::{Application, Bandwidth, OpusMode, RateControl, Signal};
use crate::hp_cutoff::hp_cutoff;
use crate::range_coder::RangeCoder;
use crate::silk::{
self, control_codec::silk_control_encoder, enc_api::silk_encode,
init_encoder::silk_init_encoder, macros::*, structs::SilkEncoder,
};
use crate::soft_clip::i16_to_float;
use crate::toc::{celt_endband_for_bandwidth, frame_rate_from_params, gen_toc};
use crate::{Error, Result};
pub struct OpusEncoder {
celt_enc: CeltEncoder,
silk_enc: Box<SilkEncoder>,
application: Application,
sampling_rate: i32,
channels: usize,
bandwidth: Bandwidth,
pub bitrate_bps: i32,
pub complexity: i32,
pub rate_control: RateControl,
pub use_inband_fec: bool,
pub use_dtx: bool,
nb_no_activity_ms_q1: i32,
range_final: u32,
pub packet_loss_perc: i32,
lbrr_coded: bool,
silk_initialized: bool,
prev_enc_mode: Option<OpusMode>,
variable_hp_smth2_q15: i32,
auto_bandwidth: i32,
first_frame: bool,
pub force_bandwidth: Option<Bandwidth>,
pub signal_type: Option<Signal>,
pub max_bandwidth: Bandwidth,
coded_lsb_depth: i32,
tonality: analysis::TonalityAnalysisState,
analysis_kfft: Option<celt::kiss_fft::KissFftState>,
pub lsb_depth: i32,
voice_ratio: i32,
detected_bandwidth: i32,
hp_mem: Vec<i32>,
buf_from_s16: Vec<f32>,
buf_filtered: Vec<i16>,
buf_silk_input: Vec<i16>,
buf_stereo_mid: Vec<i16>,
buf_stereo_side: Vec<i16>,
buf_celt_input: Vec<f32>,
down_fir_l: Option<silk::resampler::SilkEncoderResampler>,
down_fir_r: Option<silk::resampler::SilkEncoderResampler>,
silk_prefill_tail: Vec<i16>,
silk_prefill_pending: bool,
buf_left: Vec<i16>,
buf_right: Vec<i16>,
celt_prefill_tail: Vec<f32>,
celt_delay: Vec<f32>,
celt_delay_next: Vec<f32>,
rc: RangeCoder<'static>,
}
const DEFAULT_LSB_DEPTH: i32 = 24;
pub(crate) const MAX_ENCODING_DEPTH: i32 = 24;
fn coerce_mode_for_packet_rate(mode: OpusMode, packet_rate: i32) -> OpusMode {
match packet_rate {
400 | 200 => OpusMode::CeltOnly,
_ => mode,
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
struct PacketDuration(i32);
impl PacketDuration {
fn classify(sampling_rate: i32, frame_size: usize) -> Option<Self> {
let fs = i32::try_from(frame_size).ok()?;
if fs <= 0 {
return None;
}
let tenths_ms = if 400 * fs == sampling_rate {
25
} else if 200 * fs == sampling_rate {
50
} else if 100 * fs == sampling_rate {
100
} else if 50 * fs == sampling_rate {
200
} else if 25 * fs == sampling_rate {
400
} else if 50 * fs == 3 * sampling_rate {
600
} else if 25 * fs == 2 * sampling_rate {
800
} else if 10 * fs == sampling_rate {
1000
} else if 25 * fs == 3 * sampling_rate {
1200
} else {
return None;
};
Some(PacketDuration(tenths_ms))
}
fn layout(self, sampling_rate: i32, mode: OpusMode) -> PacketLayout {
let ms20 = (sampling_rate / 50) as usize;
let ms40 = (sampling_rate / 25) as usize;
let ms60 = (3 * sampling_rate / 50) as usize;
let frame_size = (sampling_rate as i64 * self.0 as i64 / 10_000) as usize;
let silk = mode == OpusMode::SilkOnly;
let enc_frame_size = match self.0 {
d if d <= 200 => frame_size,
400 | 800 if silk => ms40,
600 | 1200 if silk => ms60,
_ => ms20,
};
PacketLayout {
enc_frame_size,
nb_frames: frame_size / enc_frame_size,
frame_rate: frame_rate_from_params(sampling_rate, enc_frame_size)
.expect("every frame size `layout` produces is a coded frame duration"),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
struct PacketLayout {
enc_frame_size: usize,
nb_frames: usize,
frame_rate: i32,
}
fn max_header_bytes(nb_frames: usize) -> usize {
if nb_frames == 2 {
3
} else {
2 + (nb_frames - 1) * 2
}
}
fn bandwidth_from_i32(v: i32) -> Bandwidth {
match v {
x if x == Bandwidth::Narrowband as i32 => Bandwidth::Narrowband,
x if x == Bandwidth::Mediumband as i32 => Bandwidth::Mediumband,
x if x == Bandwidth::Superwideband as i32 => Bandwidth::Superwideband,
x if x == Bandwidth::Fullband as i32 => Bandwidth::Fullband,
_ => Bandwidth::Wideband,
}
}
fn clamp_bandwidth_to_rate(bw: Bandwidth, sampling_rate: i32) -> Bandwidth {
let max = match sampling_rate {
r if r <= 8000 => Bandwidth::Narrowband,
r if r <= 12000 => Bandwidth::Mediumband,
r if r <= 16000 => Bandwidth::Wideband,
r if r <= 24000 => Bandwidth::Superwideband,
_ => Bandwidth::Fullband,
};
if (bw as i32) > (max as i32) { max } else { bw }
}
fn celt_can_code_frame(frame_size_48k: usize) -> bool {
let blocks = frame_size_48k / celt::modes::SHORT_MDCT_SIZE;
frame_size_48k.is_multiple_of(celt::modes::SHORT_MDCT_SIZE)
&& blocks.is_power_of_two()
&& blocks <= 1 << celt::modes::MAX_LM
}
fn celt_upsample(sampling_rate: i32) -> usize {
(48_000 / sampling_rate) as usize
}
fn celt_prefill_samples(sampling_rate: i32) -> usize {
celt::modes::SHORT_MDCT_SIZE / celt_upsample(sampling_rate)
}
fn celt_delay_samples(sampling_rate: i32, application: Application) -> usize {
match application {
Application::RestrictedLowDelay => 0,
_ => (sampling_rate / 250) as usize,
}
}
pub const MAX_PACKET_BYTES: usize = 6 * 1275 + 2 + 5 * 2;
const MIN_BITRATE_BPS: i32 = 500;
const MAX_BITRATE_BPS_PER_CHANNEL: i32 = 300_000;
const MAX_ONE_FRAME_PACKET: usize = 1276;
fn emit_one_frame_packet(output: &mut [u8], toc: u8, frame: &[u8], target_total: usize) -> usize {
let target_total = target_total.min(output.len());
if frame.len() + 1 >= target_total {
output[0] = toc;
let copy_len = frame.len().min(target_total - 1);
output[1..1 + copy_len].copy_from_slice(&frame[..copy_len]);
return copy_len + 1;
}
output[0] = toc | 0x03;
if frame.len() + 2 >= target_total {
output[1] = 0x01;
output[2..2 + frame.len()].copy_from_slice(frame);
return target_total;
}
output[1] = 0x41;
let pad_amount = target_total - frame.len() - 2;
let nb_255s = (pad_amount - 1) / 255;
let mut ptr = 2;
for _ in 0..nb_255s {
output[ptr] = 255;
ptr += 1;
}
output[ptr] = (pad_amount - 255 * nb_255s - 1) as u8;
ptr += 1;
output[ptr..ptr + frame.len()].copy_from_slice(frame);
ptr += frame.len();
output[ptr..target_total].fill(0);
target_total
}
const MONO_VOICE_BANDWIDTH_THRESHOLDS: [i32; 8] = [9000, 700, 9000, 700, 13500, 1000, 14000, 2000];
const MONO_MUSIC_BANDWIDTH_THRESHOLDS: [i32; 8] = [9000, 700, 9000, 700, 11000, 1000, 12000, 2000];
const STEREO_VOICE_BANDWIDTH_THRESHOLDS: [i32; 8] =
[9000, 700, 9000, 700, 13500, 1000, 14000, 2000];
const STEREO_MUSIC_BANDWIDTH_THRESHOLDS: [i32; 8] =
[9000, 700, 9000, 700, 11000, 1000, 12000, 2000];
fn decide_fec(
use_inband_fec: bool,
packet_loss_perc: i32,
last_fec: bool,
mode: OpusMode,
bandwidth: &mut i32,
rate: i32,
) -> bool {
const FEC_THRESHOLDS: [(i32, i32); 5] = [
(12000, 1000), (14000, 1000), (16000, 1000), (20000, 1000), (22000, 1000), ];
if !use_inband_fec || packet_loss_perc == 0 || mode == OpusMode::CeltOnly {
return false;
}
let nb = Bandwidth::Narrowband as i32;
let orig_bandwidth = *bandwidth;
loop {
let idx = ((*bandwidth - nb) as usize).min(FEC_THRESHOLDS.len() - 1);
let (thres, hysteresis) = FEC_THRESHOLDS[idx];
let mut lbrr_rate_thres_bps = if last_fec {
thres - hysteresis
} else {
thres + hysteresis
};
lbrr_rate_thres_bps =
silk_smulwb(lbrr_rate_thres_bps * (125 - packet_loss_perc.min(25)), 655);
if rate > lbrr_rate_thres_bps {
return true;
} else if packet_loss_perc <= 5 {
return false;
} else if *bandwidth > nb {
*bandwidth -= 1;
} else {
break;
}
}
*bandwidth = orig_bandwidth;
false
}
fn compute_equiv_rate(
bitrate: i32,
channels: usize,
frame_rate: i32,
vbr: bool,
complexity: i32,
loss: i32,
) -> i32 {
let mut equiv = bitrate;
if frame_rate > 50 {
equiv -= (40 * channels as i32 + 20) * (frame_rate - 50);
}
if !vbr {
equiv -= equiv / 12;
}
equiv = equiv * (90 + complexity) / 100;
if loss > 0 {
equiv -= equiv * loss / (12 * loss + 20);
}
equiv
}
fn compute_mode_threshold(
application: Application,
channels: usize,
prev_was_celt: bool,
has_prev_mode: bool,
voice_est: i32,
) -> i32 {
let mode_voice = if channels == 1 { 64000 } else { 44000 };
let mode_music = 10000;
let diff = mode_voice - mode_music;
let offset = (voice_est * voice_est * diff) >> 14;
let mut threshold = mode_music + offset;
if application == Application::Voip {
threshold += 8000;
}
if has_prev_mode {
if prev_was_celt {
threshold -= 4000;
} else {
threshold += 4000;
}
}
if application == Application::RestrictedLowDelay {
threshold = 0;
}
threshold
}
fn bits_to_bitrate(bits: i32, fs: i32, frame_size: i32) -> i32 {
((bits as i64 * (6 * fs / frame_size) as i64) / 6) as i32
}
fn bitrate_to_bits(bitrate: i32, fs: i32, frame_size: i32) -> i32 {
((bitrate as i64 * 6) / (6 * fs / frame_size) as i64) as i32
}
fn compute_silk_rate_for_hybrid(
rate_bps: i32,
bandwidth: Bandwidth,
frame20ms: bool,
vbr: bool,
fec: bool,
channels: usize,
) -> i32 {
#[rustfmt::skip]
const RATE_TABLE: &[(i32, i32, i32, i32, i32)] = &[
( 0, 0, 0, 0, 0),
(12000, 10000, 10000, 11000, 11000),
(16000, 13500, 13500, 15000, 15000),
(20000, 16000, 16000, 18000, 18000),
(24000, 18000, 18000, 21000, 21000),
(32000, 22000, 22000, 28000, 28000),
(64000, 38000, 38000, 50000, 50000),
];
let share = |row: &(i32, i32, i32, i32, i32)| match (fec, frame20ms) {
(false, false) => row.1,
(false, true) => row.2,
(true, false) => row.3,
(true, true) => row.4,
};
let rate_bps = rate_bps / channels as i32;
let n = RATE_TABLE.len();
let mut i = 1;
while i < n && RATE_TABLE[i].0 <= rate_bps {
i += 1;
}
let mut silk_rate = if i == n {
let last = &RATE_TABLE[n - 1];
share(last) + (rate_bps - last.0) / 2
} else {
let (lo_row, hi_row) = (&RATE_TABLE[i - 1], &RATE_TABLE[i]);
let (x0, x1) = (lo_row.0, hi_row.0);
(share(lo_row) * (x1 - rate_bps) + share(hi_row) * (rate_bps - x0)) / (x1 - x0)
};
if !vbr {
silk_rate += 100;
}
if bandwidth == Bandwidth::Superwideband {
silk_rate += 300;
}
silk_rate *= channels as i32;
if channels == 2 && rate_bps >= 12000 {
silk_rate -= 1000;
}
silk_rate
}
impl std::fmt::Debug for OpusEncoder {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("OpusEncoder")
.field("sampling_rate", &self.sampling_rate)
.field("channels", &self.channels)
.field("application", &self.application)
.field("bitrate_bps", &self.bitrate_bps)
.field("complexity", &self.complexity)
.field("rate_control", &self.rate_control)
.field("use_inband_fec", &self.use_inband_fec)
.field("use_dtx", &self.use_dtx)
.field("packet_loss_perc", &self.packet_loss_perc)
.field("force_bandwidth", &self.force_bandwidth)
.field("max_bandwidth", &self.max_bandwidth)
.field("signal_type", &self.signal_type)
.field("lsb_depth", &self.lsb_depth)
.finish_non_exhaustive()
}
}
impl OpusEncoder {
pub fn new(sampling_rate: i32, channels: usize, application: Application) -> Result<Self> {
if ![8000, 12000, 16000, 24000, 48000].contains(&sampling_rate) {
return Err(Error::InvalidArgument("Invalid sampling rate"));
}
if ![1, 2].contains(&channels) {
return Err(Error::InvalidArgument("Invalid number of channels"));
}
let mode = celt::modes::default_mode();
let mut celt_enc = CeltEncoder::new(mode, channels);
celt_enc.set_upsample(celt_upsample(sampling_rate));
let mut silk_enc = Box::new(SilkEncoder::default());
if silk_init_encoder(&mut silk_enc.state[0], 0) != 0 {
return Err(Error::Internal("SILK encoder initialization failed"));
}
let bw = match application {
Application::Voip => match sampling_rate {
8000 => Bandwidth::Narrowband,
12000 => Bandwidth::Mediumband,
16000 => Bandwidth::Wideband,
24000 => Bandwidth::Superwideband,
48000 => Bandwidth::Fullband,
_ => Bandwidth::Narrowband,
},
Application::RestrictedLowDelay => match sampling_rate {
8000 => Bandwidth::Narrowband,
12000 => Bandwidth::Mediumband,
16000 => Bandwidth::Wideband,
24000 => Bandwidth::Superwideband,
_ => Bandwidth::Fullband,
},
Application::Audio => {
if sampling_rate <= 16000 {
match sampling_rate {
8000 => Bandwidth::Narrowband,
12000 => Bandwidth::Mediumband,
_ => Bandwidth::Wideband,
}
} else {
match sampling_rate {
24000 => Bandwidth::Superwideband,
_ => Bandwidth::Fullband,
}
}
}
};
let variable_hp_smth2_q15 = silk_lin2log(60) << 8;
Ok(Self {
celt_enc,
silk_enc,
application,
sampling_rate,
channels,
bandwidth: bw,
bitrate_bps: 64000,
complexity: 9,
rate_control: RateControl::ConstrainedVbr,
use_inband_fec: false,
use_dtx: false,
nb_no_activity_ms_q1: 0,
range_final: 0,
packet_loss_perc: 0,
lbrr_coded: false,
silk_initialized: false,
prev_enc_mode: None,
variable_hp_smth2_q15,
auto_bandwidth: 0,
first_frame: true,
force_bandwidth: None,
signal_type: None,
max_bandwidth: Bandwidth::Fullband,
tonality: analysis::TonalityAnalysisState::new(sampling_rate),
analysis_kfft: celt::kiss_fft::KissFftState::new(480),
lsb_depth: DEFAULT_LSB_DEPTH,
coded_lsb_depth: DEFAULT_LSB_DEPTH,
voice_ratio: -1,
detected_bandwidth: 0,
hp_mem: vec![0; channels * 2],
buf_from_s16: Vec::new(),
buf_filtered: Vec::new(),
buf_silk_input: Vec::new(),
buf_stereo_mid: Vec::new(),
buf_stereo_side: Vec::new(),
buf_celt_input: Vec::new(),
down_fir_l: None,
down_fir_r: None,
silk_prefill_tail: Vec::new(),
silk_prefill_pending: false,
buf_left: Vec::new(),
buf_right: Vec::new(),
celt_prefill_tail: Vec::new(),
celt_delay: vec![0.0; celt_delay_samples(sampling_rate, application) * channels],
celt_delay_next: Vec::new(),
rc: RangeCoder::new_encoder(1),
})
}
pub fn final_range(&self) -> u32 {
self.range_final
}
pub fn sample_rate(&self) -> i32 {
self.sampling_rate
}
pub fn channels(&self) -> usize {
self.channels
}
pub fn application(&self) -> Application {
self.application
}
pub fn lookahead(&self) -> usize {
celt_prefill_samples(self.sampling_rate)
+ celt_delay_samples(self.sampling_rate, self.application)
}
pub fn reset_state(&mut self) -> Result<()> {
let mut fresh = Self::new(self.sampling_rate, self.channels, self.application)?;
fresh.bitrate_bps = self.bitrate_bps;
fresh.complexity = self.complexity;
fresh.rate_control = self.rate_control;
fresh.use_inband_fec = self.use_inband_fec;
fresh.use_dtx = self.use_dtx;
fresh.packet_loss_perc = self.packet_loss_perc;
fresh.force_bandwidth = self.force_bandwidth;
fresh.signal_type = self.signal_type;
fresh.max_bandwidth = self.max_bandwidth;
fresh.lsb_depth = self.lsb_depth;
*self = fresh;
Ok(())
}
fn compute_voice_est(&self) -> i32 {
match self.signal_type {
Some(Signal::Voice) => return 127,
Some(Signal::Music) => return 0,
None => {}
}
if self.voice_ratio >= 0 {
let mut v = (self.voice_ratio * 327) >> 8;
if self.application == Application::Audio {
v = v.min(115);
}
v
} else {
match self.application {
Application::Voip => 115,
Application::Audio => 48,
Application::RestrictedLowDelay => 0,
}
}
}
pub fn encode(&mut self, input: &[f32], frame_size: usize, output: &mut [u8]) -> Result<usize> {
self.encode_native(input, frame_size, output, MAX_ENCODING_DEPTH)
}
pub fn encode_s16(
&mut self,
input: &[i16],
frame_size: usize,
output: &mut [u8],
) -> Result<usize> {
let wanted = frame_size * self.channels;
if input.len() < wanted {
return Err(Error::InvalidArgument(
"input is shorter than frame_size * channels",
));
}
let mut converted = std::mem::take(&mut self.buf_from_s16);
converted.clear();
converted.extend(input[..wanted].iter().copied().map(i16_to_float));
let result = self.encode_native(&converted, frame_size, output, 16);
self.buf_from_s16 = converted;
result
}
fn normalize_settings(&mut self) -> Result<()> {
if self.bitrate_bps <= 0 {
return Err(Error::InvalidArgument(
"bitrate_bps must be a positive rate; this crate has no \
OPUS_AUTO/OPUS_BITRATE_MAX sentinel",
));
}
self.bitrate_bps = self.bitrate_bps.clamp(
MIN_BITRATE_BPS,
MAX_BITRATE_BPS_PER_CHANNEL * self.channels as i32,
);
self.complexity = self.complexity.clamp(0, 10);
self.packet_loss_perc = self.packet_loss_perc.clamp(0, 100);
self.lsb_depth = self.lsb_depth.clamp(8, MAX_ENCODING_DEPTH);
Ok(())
}
pub(crate) fn encode_native(
&mut self,
input: &[f32],
frame_size: usize,
output: &mut [u8],
api_lsb_depth: i32,
) -> Result<usize> {
self.normalize_settings()?;
self.coded_lsb_depth = api_lsb_depth.min(self.lsb_depth);
if output.len() < 2 {
return Err(Error::buffer_too_small(2, output.len()));
}
let duration = PacketDuration::classify(self.sampling_rate, frame_size).ok_or(
Error::InvalidArgument("Invalid frame size for sampling rate"),
)?;
if input.len() < frame_size * self.channels {
return Err(Error::InvalidArgument(
"input is shorter than frame_size * channels",
));
}
let packet_rate = self.sampling_rate / frame_size as i32;
let mut analysis_at = None;
let mut analysis_info = analysis::AnalysisInfo::default();
if self.complexity >= 7 && self.sampling_rate >= 16000 {
if let Some(kfft) = &self.analysis_kfft {
analysis_at = Some(self.tonality.read_position());
analysis_info = analysis::run_analysis(
&mut self.tonality,
kfft,
input,
frame_size,
frame_size,
self.channels,
self.sampling_rate,
self.coded_lsb_depth,
);
}
} else if self.tonality.initialized() {
self.tonality.reset();
}
let is_silence = self.is_digital_silence(&input[..frame_size * self.channels]);
if !is_silence {
self.voice_ratio = -1;
}
self.detected_bandwidth = 0;
if analysis_info.valid {
let prob = if self.prev_enc_mode.is_none() {
analysis_info.music_prob
} else if self.prev_enc_mode == Some(OpusMode::CeltOnly) {
analysis_info.music_prob_max
} else {
analysis_info.music_prob_min
};
self.voice_ratio = (0.5 + 100.0 * (1.0 - prob)).floor() as i32;
let ab = analysis_info.bandwidth;
self.detected_bandwidth = if ab <= 12 {
Bandwidth::Narrowband as i32
} else if ab <= 14 {
Bandwidth::Mediumband as i32
} else if ab <= 16 {
Bandwidth::Wideband as i32
} else if ab <= 18 {
Bandwidth::Superwideband as i32
} else {
Bandwidth::Fullband as i32
};
}
let mut mode = if self.application == Application::RestrictedLowDelay {
OpusMode::CeltOnly
} else {
let equiv = compute_equiv_rate(
self.bitrate_bps,
self.channels,
packet_rate,
!self.rate_control.is_cbr(),
self.complexity,
self.packet_loss_perc,
);
let prev_was_celt = self.prev_enc_mode == Some(OpusMode::CeltOnly);
let has_prev_mode = self.prev_enc_mode.is_some();
let voice_est = self.compute_voice_est();
let threshold = compute_mode_threshold(
self.application,
self.channels,
prev_was_celt,
has_prev_mode,
voice_est,
);
if equiv >= threshold {
OpusMode::CeltOnly
} else {
OpusMode::SilkOnly
}
};
{
let equiv = compute_equiv_rate(
self.bitrate_bps,
self.channels,
packet_rate,
!self.rate_control.is_cbr(),
self.complexity,
self.packet_loss_perc,
);
let voice_est: i32 = self.compute_voice_est();
let (vt, mt) = if self.channels == 2 {
(
&STEREO_VOICE_BANDWIDTH_THRESHOLDS,
&STEREO_MUSIC_BANDWIDTH_THRESHOLDS,
)
} else {
(
&MONO_VOICE_BANDWIDTH_THRESHOLDS,
&MONO_MUSIC_BANDWIDTH_THRESHOLDS,
)
};
let mut th = [0i32; 8];
for i in 0..8 {
th[i] = mt[i] + ((voice_est * voice_est * (vt[i] - mt[i])) >> 14);
}
const NB: i32 = Bandwidth::Narrowband as i32; const MB: i32 = Bandwidth::Mediumband as i32; const FB: i32 = Bandwidth::Fullband as i32; let mut bw = FB;
while bw > NB {
let idx = (2 * (bw - MB)) as usize;
let mut threshold = th[idx];
let hysteresis = th[idx + 1];
if !self.first_frame {
if self.auto_bandwidth >= bw {
threshold -= hysteresis;
} else {
threshold += hysteresis;
}
}
if equiv >= threshold {
break;
}
bw -= 1;
}
if bw == MB {
bw = Bandwidth::Wideband as i32;
}
self.auto_bandwidth = bw;
if mode != OpusMode::CeltOnly && self.rate_control.is_cbr() && self.bitrate_bps < 15000
{
bw = bw.min(Bandwidth::Wideband as i32);
}
if mode != OpusMode::CeltOnly && self.sampling_rate > 16000 {
bw = bw.max(Bandwidth::Wideband as i32);
}
if self.sampling_rate <= 24000 {
bw = bw.min(Bandwidth::Superwideband as i32);
}
if self.sampling_rate <= 16000 {
bw = bw.min(Bandwidth::Wideband as i32);
}
if self.sampling_rate <= 12000 {
bw = bw.min(Bandwidth::Mediumband as i32);
}
if self.sampling_rate <= 8000 {
bw = bw.min(Bandwidth::Narrowband as i32);
}
if bw == Bandwidth::Mediumband as i32 && self.sampling_rate > 12000 {
bw = Bandwidth::Wideband as i32;
}
if self.detected_bandwidth != 0
&& self.force_bandwidth.is_none()
&& mode != OpusMode::CeltOnly
{
let ch = self.channels as i32;
let equiv2 = equiv; let min_det = if equiv2 <= 18000 * ch && mode == OpusMode::CeltOnly {
NB
} else if equiv2 <= 24000 * ch && mode == OpusMode::CeltOnly {
MB
} else if equiv2 <= 30000 * ch {
Bandwidth::Wideband as i32
} else if equiv2 <= 44000 * ch {
Bandwidth::Superwideband as i32
} else {
FB
};
bw = bw.min(self.detected_bandwidth.max(min_det));
}
let mut max_bw = self.max_bandwidth as i32;
if mode != OpusMode::CeltOnly && self.sampling_rate > 16000 {
max_bw = max_bw.max(Bandwidth::Wideband as i32);
}
bw = bw.min(max_bw);
if mode == OpusMode::CeltOnly && bw == MB {
bw = NB;
}
self.bandwidth = match self.force_bandwidth {
Some(f) => f,
None => match bw {
x if x == NB => Bandwidth::Narrowband,
x if x == MB => Bandwidth::Mediumband,
x if x == Bandwidth::Wideband as i32 => Bandwidth::Wideband,
x if x == Bandwidth::Superwideband as i32 => Bandwidth::Superwideband,
x if x == FB => Bandwidth::Fullband,
_ => Bandwidth::Wideband,
},
};
self.first_frame = false;
}
self.bandwidth = clamp_bandwidth_to_rate(self.bandwidth, self.sampling_rate);
{
let equiv = compute_equiv_rate(
self.bitrate_bps,
self.channels,
packet_rate,
!self.rate_control.is_cbr(),
self.complexity,
self.packet_loss_perc,
);
let mut bw = self.bandwidth as i32;
self.lbrr_coded = decide_fec(
self.use_inband_fec,
self.packet_loss_perc.clamp(0, 100),
self.lbrr_coded,
mode,
&mut bw,
equiv,
);
if bw != self.bandwidth as i32 {
self.bandwidth = bandwidth_from_i32(bw);
}
}
if mode == OpusMode::SilkOnly
&& matches!(
self.bandwidth,
Bandwidth::Superwideband | Bandwidth::Fullband
)
{
mode = OpusMode::Hybrid;
}
if mode == OpusMode::Hybrid
&& matches!(
self.bandwidth,
Bandwidth::Narrowband | Bandwidth::Mediumband | Bandwidth::Wideband
)
{
mode = OpusMode::SilkOnly;
}
if self.channels == 2 && mode == OpusMode::Hybrid && self.bitrate_bps > 28000 {
mode = OpusMode::CeltOnly;
self.bandwidth = Bandwidth::Fullband;
}
mode = coerce_mode_for_packet_rate(mode, packet_rate);
if mode == OpusMode::CeltOnly && self.bandwidth == Bandwidth::Mediumband {
self.bandwidth = Bandwidth::Wideband;
}
let layout = duration.layout(self.sampling_rate, mode);
if layout.nb_frames == 1 {
return self.encode_frame(
input,
frame_size,
output,
mode,
layout.frame_rate,
&analysis_info,
is_silence,
);
}
self.encode_split_packet(input, output, mode, layout, analysis_at)
}
fn encode_split_packet(
&mut self,
input: &[f32],
output: &mut [u8],
mode: OpusMode,
layout: PacketLayout,
analysis_at: Option<analysis::AnalysisReadPos>,
) -> Result<usize> {
let PacketLayout {
enc_frame_size,
nb_frames,
frame_rate,
} = layout;
let frame_size = enc_frame_size * nb_frames;
let target_bits =
(self.bitrate_bps as i64 * frame_size as i64 / self.sampling_rate as i64) as i32;
let repacketize_len = if self.rate_control.is_cbr() {
(((target_bits + 4) / 8) as usize).min(output.len())
} else {
output.len()
};
let header = max_header_bytes(nb_frames);
if repacketize_len + nb_frames <= header {
return Err(Error::buffer_too_small(header + 1, repacketize_len));
}
let max_len_sum = nb_frames + repacketize_len - header;
let per_frame_bitrate_bytes = ((self.bitrate_bps as i64 * enc_frame_size as i64
/ self.sampling_rate as i64)
/ 8) as usize;
let mut rp = crate::repacketizer::Repacketizer::new();
if max_len_sum < 3 * nb_frames {
let toc = gen_toc(mode, frame_rate, self.bandwidth, self.channels);
for _ in 0..nb_frames {
rp.cat(&[toc])?;
}
self.range_final = 0;
self.prev_enc_mode = Some(mode);
let pad_to = self.rate_control.is_cbr().then_some(repacketize_len);
return Self::emit(rp, nb_frames, pad_to, output);
}
let mut scratch = vec![0u8; max_len_sum];
let mut tot_size = 0usize;
let mut dtx_count = 0usize;
if let Some(at) = analysis_at {
self.tonality.set_read_position(at);
}
for i in 0..nb_frames {
let curr_max = per_frame_bitrate_bytes
.min(max_len_sum / nb_frames)
.max(3)
.min(max_len_sum - tot_size);
let base = i * enc_frame_size * self.channels;
let frame_input = &input[base..base + enc_frame_size * self.channels];
let analysis_info = match analysis_at {
Some(_) => analysis::tonality_get_info(&mut self.tonality, enc_frame_size),
None => analysis::AnalysisInfo::default(),
};
let is_silence = self.is_digital_silence(frame_input);
let len = self.encode_frame(
frame_input,
enc_frame_size,
&mut scratch[tot_size..tot_size + curr_max],
mode,
frame_rate,
&analysis_info,
is_silence,
)?;
if len == 1 {
dtx_count += 1;
}
rp.cat(&scratch[tot_size..tot_size + len])?;
tot_size += len;
}
let pad_to =
(self.rate_control.is_cbr() && dtx_count != nb_frames).then_some(repacketize_len);
Self::emit(rp, nb_frames, pad_to, output)
}
fn emit(
rp: crate::repacketizer::Repacketizer,
nb_frames: usize,
pad_to: Option<usize>,
output: &mut [u8],
) -> Result<usize> {
let packet = rp.out_range_impl(0, nb_frames, pad_to)?;
if packet.len() > output.len() {
return Err(Error::buffer_too_small(packet.len(), output.len()));
}
output[..packet.len()].copy_from_slice(&packet);
Ok(packet.len())
}
fn is_digital_silence(&self, input: &[f32]) -> bool {
let thresh = 1.0f32 / (1i64 << self.coded_lsb_depth) as f32;
input.iter().fold(0.0f32, |m, &v| m.max(v.abs())) <= thresh
}
#[allow(clippy::too_many_arguments)]
fn encode_frame(
&mut self,
input: &[f32],
frame_size: usize,
output: &mut [u8],
mode: OpusMode,
frame_rate: i32,
analysis_info: &analysis::AnalysisInfo,
is_silence: bool,
) -> Result<usize> {
if output.len() < 2 {
return Err(Error::buffer_too_small(2, output.len()));
}
let curr_bw = self.bandwidth;
let codable = match mode {
OpusMode::CeltOnly => matches!(frame_rate, 400 | 200 | 100 | 50),
OpusMode::Hybrid => matches!(frame_rate, 100 | 50),
OpusMode::SilkOnly => matches!(frame_rate, 100 | 50 | 25 | 16),
};
if !codable {
return Err(Error::Internal("frame size is not codable in this mode"));
}
if mode != OpusMode::SilkOnly
&& !celt_can_code_frame(frame_size * celt_upsample(self.sampling_rate))
{
return Err(Error::Internal(
"frame size is not codable by the CELT layer at this sampling rate",
));
}
let activity = if is_silence {
false
} else if analysis_info.valid {
analysis_info.activity_probability >= 0.1
} else {
true
};
if let Some(prev) = self.prev_enc_mode
&& prev != mode
{
if mode != OpusMode::SilkOnly {
let ch = self.channels;
self.celt_enc = CeltEncoder::new(celt::modes::default_mode(), ch);
self.celt_enc
.set_upsample(celt_upsample(self.sampling_rate));
let prefill = celt_prefill_samples(self.sampling_rate);
if self.celt_prefill_tail.len() == prefill * ch {
let mut dummy = RangeCoder::new_encoder(2);
let tail = std::mem::take(&mut self.celt_prefill_tail);
self.celt_enc
.encode_with_budget(&tail, prefill, &mut dummy, 0, 21, 16);
self.celt_prefill_tail = tail;
}
}
if mode != OpusMode::CeltOnly && prev == OpusMode::CeltOnly {
self.silk_initialized = false;
self.silk_prefill_pending = true;
}
}
if self.channels == 1 {
let n10 = (self.sampling_rate / 100) as usize;
if frame_size >= n10 {
self.silk_prefill_tail.resize(n10, 0);
for i in 0..n10 {
self.silk_prefill_tail[i] =
(input[frame_size - n10 + i] * 32768.0).clamp(-32768.0, 32767.0) as i16;
}
}
}
{
let ch = self.channels;
let delay = celt_delay_samples(self.sampling_rate, self.application);
debug_assert_eq!(self.celt_delay.len(), delay * ch);
let timeline = |celt_delay: &[f32], c: usize, i: usize| -> f32 {
if i < delay {
celt_delay[c * delay + i]
} else {
input[(i - delay) * ch + c]
}
};
if mode != OpusMode::SilkOnly {
self.buf_celt_input.resize(frame_size * ch, 0.0);
for c in 0..ch {
for i in 0..frame_size {
self.buf_celt_input[c * frame_size + i] = timeline(&self.celt_delay, c, i);
}
}
}
let prefill = celt_prefill_samples(self.sampling_rate);
if frame_size >= prefill {
self.celt_prefill_tail.resize(prefill * ch, 0.0);
for c in 0..ch {
for i in 0..prefill {
self.celt_prefill_tail[c * prefill + i] =
timeline(&self.celt_delay, c, frame_size - prefill + i);
}
}
} else {
self.celt_prefill_tail.clear();
}
if delay > 0 {
let mut next = std::mem::take(&mut self.celt_delay_next);
next.clear();
next.resize(delay * ch, 0.0);
for c in 0..ch {
for i in 0..delay {
next[c * delay + i] = timeline(&self.celt_delay, c, frame_size + i);
}
}
self.celt_delay_next = std::mem::replace(&mut self.celt_delay, next);
}
}
let toc = gen_toc(mode, frame_rate, self.bandwidth, self.channels);
output[0] = toc;
if self.use_dtx && (analysis_info.valid || is_silence) {
let frame_ms_q1 = 2 * 1000 * frame_size as i32 / self.sampling_rate;
let dtx = if !activity {
self.nb_no_activity_ms_q1 += frame_ms_q1;
const LO: i32 = silk::define::NB_SPEECH_FRAMES_BEFORE_DTX * 20 * 2; const HI: i32 = (silk::define::NB_SPEECH_FRAMES_BEFORE_DTX
+ silk::define::MAX_CONSECUTIVE_DTX)
* 20
* 2; if self.nb_no_activity_ms_q1 > LO {
if self.nb_no_activity_ms_q1 <= HI {
true
} else {
self.nb_no_activity_ms_q1 = LO;
false
}
} else {
false
}
} else {
self.nb_no_activity_ms_q1 = 0;
false
};
if dtx {
self.prev_enc_mode = Some(mode);
self.range_final = 0;
return Ok(1);
}
} else {
self.nb_no_activity_ms_q1 = 0;
}
let target_bits =
(self.bitrate_bps as i64 * frame_size as i64 / self.sampling_rate as i64) as i32;
let cbr_bytes = ((target_bits + 4) / 8) as usize;
let max_data_bytes = output.len();
let n_bytes = if self.rate_control.is_cbr() {
cbr_bytes.min(max_data_bytes).max(1)
} else {
max_data_bytes.max(3)
};
let frame_bytes = n_bytes.min(MAX_ONE_FRAME_PACKET);
self.rc.reset_for_encode((frame_bytes - 1) as u32);
let mut hybrid_silk_rate = 0i32;
if mode == OpusMode::SilkOnly || mode == OpusMode::Hybrid {
let silk_fs_khz = if mode == OpusMode::Hybrid {
16
} else {
let by_bandwidth = match self.bandwidth {
Bandwidth::Narrowband => 8,
Bandwidth::Mediumband => 12,
_ => 16,
};
by_bandwidth.min(self.sampling_rate / 1000)
};
let silk_fs_hz = silk_fs_khz * 1000;
let frame_ms = (frame_size as i32 * 1000) / self.sampling_rate;
let n_channels_internal = self.channels as i32;
let channels_changed = n_channels_internal != self.silk_enc.n_channels_internal;
if n_channels_internal > self.silk_enc.n_channels_internal {
silk_init_encoder(&mut self.silk_enc.state[1], 0);
self.silk_enc.stereo.reset_for_stereo();
}
self.silk_enc.n_channels_internal = n_channels_internal;
let resampler_stale = !self.silk_initialized
|| self.silk_enc.state[0].s_cmn.fs_khz != silk_fs_khz
|| channels_changed;
for ch in 0..n_channels_internal as usize {
silk_control_encoder(
&mut self.silk_enc.state[ch],
silk_fs_khz,
frame_ms,
self.complexity,
);
self.silk_enc.state[ch].s_cmn.use_cbr =
if self.rate_control.is_cbr() { 1 } else { 0 };
}
if resampler_stale {
self.silk_initialized = true;
self.down_fir_l =
silk::resampler::SilkEncoderResampler::new(self.sampling_rate, silk_fs_hz);
self.down_fir_r =
silk::resampler::SilkEncoderResampler::new(self.sampling_rate, silk_fs_hz);
}
if self.silk_prefill_pending {
self.silk_prefill_pending = false;
let n10 = (self.sampling_rate / 100) as usize;
if self.channels == 1 && self.silk_prefill_tail.len() == n10 {
let need = silk_fs_khz as usize * 10;
let mut resampled = vec![0i16; need];
if let Some(r) = &mut self.down_fir_l {
r.process(&mut resampled, &self.silk_prefill_tail);
} else {
resampled.copy_from_slice(&self.silk_prefill_tail[..need]);
}
let (state, stereo) = (&mut self.silk_enc.state[0], &mut self.silk_enc.stereo);
silk::enc_api::silk_encode_prefill(state, stereo, &resampled, 0);
}
}
for ch in 0..n_channels_internal as usize {
let cmn = &mut self.silk_enc.state[ch].s_cmn;
cmn.packet_loss_perc = self.packet_loss_perc.clamp(0, 100);
let lbrr_in_previous_packet = cmn.lbrr_enabled != 0;
cmn.lbrr_enabled = if self.lbrr_coded { 1 } else { 0 };
if cmn.lbrr_enabled != 0 {
cmn.lbrr_gain_increases = if !lbrr_in_previous_packet {
7
} else {
(7 - silk_smulwb(cmn.packet_loss_perc, 13107)).max(3)
};
}
}
let hp_freq_smth1 = if mode == OpusMode::CeltOnly {
silk_lin2log(60) << 8
} else {
self.silk_enc.state[0].s_cmn.variable_hp_smth1_q15
};
const VARIABLE_HP_SMTH_COEF2_Q16: i32 = 984;
self.variable_hp_smth2_q15 = silk_smlawb(
self.variable_hp_smth2_q15,
hp_freq_smth1 - self.variable_hp_smth2_q15,
VARIABLE_HP_SMTH_COEF2_Q16,
);
let cutoff_hz = silk_log2lin(silk_rshift(self.variable_hp_smth2_q15, 8));
let required_size = frame_size * self.channels;
self.buf_filtered.resize(required_size, 0);
if self.application == Application::Voip {
hp_cutoff(
input,
cutoff_hz,
&mut self.buf_filtered,
&mut self.hp_mem,
frame_size,
self.channels,
self.sampling_rate,
);
} else {
for (i, &x) in input.iter().enumerate() {
self.buf_filtered[i] = (x * 32768.0).clamp(-32768.0, 32767.0) as i16;
}
}
let input_i16 = &self.buf_filtered;
let (silk_left, silk_right): (&[i16], &[i16]) = if self.channels == 2 {
let frame_length = input_i16.len() / 2;
self.buf_left.resize(frame_length, 0);
self.buf_right.resize(frame_length, 0);
for i in 0..frame_length {
self.buf_left[i] = input_i16[2 * i];
self.buf_right[i] = input_i16[2 * i + 1];
}
let ds_len =
frame_length * silk_fs_khz as usize / (self.sampling_rate as usize / 1000);
self.buf_stereo_mid.resize(ds_len, 0);
self.buf_stereo_side.resize(ds_len, 0);
if let (Some(rl), Some(rr)) = (&mut self.down_fir_l, &mut self.down_fir_r) {
rl.process(&mut self.buf_stereo_mid, &self.buf_left);
rr.process(&mut self.buf_stereo_side, &self.buf_right);
}
self.buf_left.resize(ds_len, 0);
self.buf_right.resize(ds_len, 0);
self.buf_left
.copy_from_slice(&self.buf_stereo_mid[..ds_len]);
self.buf_right
.copy_from_slice(&self.buf_stereo_side[..ds_len]);
(&self.buf_left[..ds_len], &self.buf_right[..ds_len])
} else {
let silk_frame_size =
frame_size * silk_fs_khz as usize / (self.sampling_rate as usize / 1000);
self.buf_silk_input.resize(silk_frame_size, 0);
if let Some(r) = &mut self.down_fir_l {
r.process(&mut self.buf_silk_input, input_i16);
} else {
self.buf_silk_input
.copy_from_slice(&input_i16[..silk_frame_size]);
}
(&self.buf_silk_input[..], &[][..])
};
let mut pn_bytes = 0;
let silk_rate_for_calc = silk_fs_hz;
let silk_frame_len = silk_left.len();
let silk_bitrate = if mode == OpusMode::Hybrid {
let frame_duration_ms = frame_size as i32 * 1000 / self.sampling_rate;
let frame20ms = frame_duration_ms >= 20;
let fs = self.sampling_rate;
let bits_target = (8 * frame_bytes as i32).min(bitrate_to_bits(
self.bitrate_bps,
fs,
frame_size as i32,
)) - 8;
let r = compute_silk_rate_for_hybrid(
bits_to_bitrate(bits_target, fs, frame_size as i32),
curr_bw,
frame20ms,
!self.rate_control.is_cbr(),
self.lbrr_coded,
self.channels,
);
hybrid_silk_rate = r;
r
} else if self.rate_control.is_cbr() {
(8i64 * (n_bytes - 1) as i64 * silk_rate_for_calc as i64 / silk_frame_len as i64)
as i32
} else {
self.bitrate_bps
};
let silk_max_bits = if mode == OpusMode::Hybrid {
let total_max_bits = ((frame_bytes - 1) * 8) as i32;
if self.rate_control.is_cbr() {
let silk_bits = (silk_bitrate as i64 * silk_frame_len as i64
/ silk_rate_for_calc as i64) as i32;
let other_bits = 0i32.max(total_max_bits - silk_bits);
0i32.max(total_max_bits - other_bits * 3 / 4)
} else {
let frame_duration_ms = frame_size as i32 * 1000 / self.sampling_rate;
let frame20ms = frame_duration_ms >= 20;
let max_bit_rate = compute_silk_rate_for_hybrid(
bits_to_bitrate(total_max_bits, self.sampling_rate, frame_size as i32),
curr_bw,
frame20ms,
!self.rate_control.is_cbr(),
self.lbrr_coded,
self.channels,
);
max_bit_rate * frame_size as i32 / self.sampling_rate
}
} else {
((frame_bytes - 1) * 8) as i32
};
let silk_use_cbr = if mode == OpusMode::Hybrid && self.rate_control.is_cbr() {
0
} else if self.rate_control.is_cbr() {
1
} else {
0
};
let ret = silk_encode(
&mut self.silk_enc,
silk_left,
silk_right,
&mut self.rc,
&mut pn_bytes,
silk_bitrate,
silk_max_bits,
silk_use_cbr,
1,
);
if ret != 0 {
return Err(Error::Internal("SILK encoding failed"));
}
let idx = &self.silk_enc.state[0].s_cmn.indices;
self.celt_enc.silk_signal_type = idx.signal_type as i32;
self.celt_enc.silk_offset = crate::silk::tables::SILK_QUANTIZATION_OFFSETS_Q10
[(idx.signal_type >> 1) as usize][idx.quant_offset_type as usize]
as i32;
}
if mode == OpusMode::Hybrid && self.rc.tell() + 37 <= ((frame_bytes - 1) * 8) as i32 {
self.rc.encode_bit_logp(false, 12); }
if mode == OpusMode::Hybrid {
let nb_compr_bytes = (frame_bytes - 1) as u32;
self.rc.shrink(nb_compr_bytes);
}
let silk_ret_bytes = if mode == OpusMode::SilkOnly {
((self.rc.tell() + 7) >> 3) as usize
} else {
0
};
if mode == OpusMode::CeltOnly || mode == OpusMode::Hybrid {
self.celt_enc.analysis = celt::AnalysisInfo {
valid: analysis_info.valid,
tonality: analysis_info.tonality,
tonality_slope: analysis_info.tonality_slope,
noisiness: analysis_info.noisiness,
activity: analysis_info.activity,
music_prob: analysis_info.music_prob,
music_prob_min: analysis_info.music_prob_min,
music_prob_max: analysis_info.music_prob_max,
bandwidth: analysis_info.bandwidth,
activity_probability: analysis_info.activity_probability,
max_pitch_ratio: analysis_info.max_pitch_ratio,
leak_boost: analysis_info.leak_boost,
};
self.celt_enc.complexity = self.complexity;
self.celt_enc.lsb_depth = self.coded_lsb_depth;
self.celt_enc.loss_rate = self.packet_loss_perc;
let start_band = if mode == OpusMode::Hybrid { 17 } else { 0 };
let end_band = celt_endband_for_bandwidth(self.bandwidth);
let total_packet_bits = ((frame_bytes - 1) * 8) as i32;
self.celt_enc.constrained_vbr =
mode != OpusMode::Hybrid && self.rate_control != RateControl::Vbr;
self.celt_enc.vbr_rate = if self.rate_control.is_cbr() {
0
} else {
let den = self.sampling_rate >> 3; let rate = self.bitrate_bps - hybrid_silk_rate;
((rate as i64 * frame_size as i64 + (den >> 1) as i64) / den as i64) as i32
};
let celt_input: &[f32] = &self.buf_celt_input;
if self.rc.tell() <= total_packet_bits {
self.celt_enc
.set_upsample(celt_upsample(self.sampling_rate));
self.celt_enc.encode_with_budget(
celt_input,
frame_size,
&mut self.rc,
start_band,
end_band,
total_packet_bits,
);
}
}
self.rc.done();
self.range_final = self.rc.rng;
let payload_len = if mode == OpusMode::SilkOnly {
let mut len = silk_ret_bytes.min(self.rc.storage as usize);
while len > 2 && self.rc.buf[len - 1] == 0 {
len -= 1;
}
len
} else if self.rate_control.is_cbr() {
frame_bytes - 1
} else {
(self.rc.storage as usize).min(frame_bytes - 1)
};
let target_total = if self.rate_control.is_cbr() {
n_bytes
} else {
payload_len + 1
};
self.prev_enc_mode = Some(mode);
Ok(emit_one_frame_packet(
output,
toc,
&self.rc.buf[..payload_len],
target_total,
))
}
}
#[cfg(test)]
mod silk_rate_tests {
use super::compute_silk_rate_for_hybrid;
use crate::Bandwidth;
fn mono(rate: i32) -> i32 {
compute_silk_rate_for_hybrid(rate, Bandwidth::Fullband, true, true, false, 1)
}
#[test]
fn test_reference_table_exact_entries() {
assert_eq!(mono(12000), 10000);
assert_eq!(mono(16000), 13500);
assert_eq!(mono(20000), 16000);
assert_eq!(mono(24000), 18000);
assert_eq!(mono(32000), 22000);
assert_eq!(mono(64000), 38000);
}
#[test]
fn test_32kbps_gives_22kbps_silk() {
assert_eq!(mono(32000), 22000);
}
#[test]
fn test_interpolation_between_table_entries() {
assert_eq!(mono(18000), 14750);
}
#[test]
fn test_above_table_max_gives_half_extra() {
assert_eq!(mono(72000), 38000 + (72000 - 64000) / 2);
}
#[test]
fn fec_widens_the_silk_share() {
for rate in [12000, 16000, 20000, 24000, 32000, 64000] {
let no_fec =
compute_silk_rate_for_hybrid(rate, Bandwidth::Fullband, true, false, false, 1);
let fec = compute_silk_rate_for_hybrid(rate, Bandwidth::Fullband, true, false, true, 1);
assert!(
fec > no_fec,
"{rate}: FEC should widen SILK's share, got {fec} against {no_fec}"
);
}
assert_eq!(
compute_silk_rate_for_hybrid(20000, Bandwidth::Fullband, true, true, true, 1),
18000
);
assert_eq!(
compute_silk_rate_for_hybrid(64000, Bandwidth::Fullband, true, true, true, 1),
50000
);
}
#[test]
fn stereo_allocates_per_channel() {
assert_eq!(
compute_silk_rate_for_hybrid(32000, Bandwidth::Fullband, true, true, false, 2),
13500 * 2 - 1000
);
let per_channel =
compute_silk_rate_for_hybrid(10000, Bandwidth::Fullband, true, true, false, 1);
assert_eq!(
compute_silk_rate_for_hybrid(20000, Bandwidth::Fullband, true, true, false, 2),
per_channel * 2
);
assert_ne!(
compute_silk_rate_for_hybrid(32000, Bandwidth::Fullband, true, true, false, 2),
compute_silk_rate_for_hybrid(32000, Bandwidth::Fullband, true, true, false, 1)
);
}
#[test]
fn superwideband_adds_to_the_silk_share() {
assert_eq!(
compute_silk_rate_for_hybrid(20000, Bandwidth::Superwideband, true, true, false, 1),
16000 + 300
);
assert_eq!(
compute_silk_rate_for_hybrid(40000, Bandwidth::Superwideband, true, true, false, 2),
(16000 + 300) * 2 - 1000
);
}
#[test]
fn cbr_boosts_the_silk_share() {
assert_eq!(
compute_silk_rate_for_hybrid(20000, Bandwidth::Fullband, true, false, false, 1),
16000 + 100
);
}
}