#[cfg(feature = "osce-bwe")]
pub mod bbwenet;
pub mod features;
pub mod lace;
mod math;
pub mod nndsp;
pub mod nolace;
use self::features::{OSCE_FEATURE_DIM, OsceFeatureState, osce_calculate_features};
use self::lace::{LaceState, lace_process_20ms_frame};
use self::math::float2int;
use self::nolace::{NolaceState, nolace_process_20ms_frame};
use super::DnnArch;
use crate::in_place::write_fields;
use super::holder::DecoderDnn;
use super::models::lace::LaceLayers;
use super::models::nolace::NolaceLayers;
#[cfg(feature = "osce-bwe")]
use self::bbwenet::{BbwenetState, bbwenet_process_frames};
#[cfg(feature = "osce-bwe")]
use self::features::{OSCE_BWE_FEATURE_DIM, OsceBweFeatureState, osce_bwe_calculate_features};
#[cfg(feature = "osce-bwe")]
use super::models::bbwenet::BbwenetLayers;
pub use self::features::{osce_bwe_cross_fade_10ms, osce_cross_fade_10ms};
pub const OSCE_BWE_OUTPUT_DELAY: usize = 21;
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[repr(i32)]
pub enum OsceMethod {
None = 0,
Lace = 1,
Nolace = 2,
}
pub const OSCE_DEFAULT_METHOD: OsceMethod = OsceMethod::Nolace;
impl OsceMethod {
pub fn for_complexity(complexity: i32) -> OsceMethod {
if complexity >= 7 {
OsceMethod::Nolace
} else if complexity >= 6 {
OsceMethod::Lace
} else {
OsceMethod::None
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[repr(i32)]
pub enum OsceExtMode {
Unset = 0,
SilkOnly = 1000,
Hybrid = 1001,
CeltOnly = 1002,
SilkBbwe = 1003,
}
#[derive(Clone, Copy, Debug)]
pub struct OsceFrame<'a> {
pub fs_khz: i32,
pub nb_subfr: i32,
pub lpc_order: i32,
pub signal_type: i32,
pub pred_coef_q12: &'a [[i16; 16]; 2],
pub pitch_l: &'a [i32; 4],
pub ltp_coef_q14: &'a [i16; 20],
pub gains_q16: &'a [i32; 4],
pub num_bits: i32,
}
#[derive(Clone, Copy)]
pub struct OsceModels<'a> {
pub arch: DnnArch,
pub lace: Option<&'a LaceLayers<'a>>,
pub nolace: Option<&'a NolaceLayers<'a>>,
#[cfg(feature = "osce-bwe")]
pub bbwenet: Option<&'a BbwenetLayers<'a>>,
}
impl<'a> OsceModels<'a> {
pub fn from_decoder(dnn: &'a DecoderDnn) -> OsceModels<'a> {
OsceModels {
arch: dnn.arch(),
lace: dnn.lace(),
nolace: dnn.nolace(),
#[cfg(feature = "osce-bwe")]
bbwenet: dnn.bbwenet(),
}
}
pub fn builtin(arch: DnnArch) -> OsceModels<'static> {
let set = super::builtin::models();
OsceModels {
arch,
lace: set.lace.as_ref(),
nolace: set.nolace.as_ref(),
#[cfg(feature = "osce-bwe")]
bbwenet: set.bbwenet.as_ref(),
}
}
}
#[derive(Clone)]
#[allow(clippy::large_enum_variant)]
enum ModelState {
None,
Lace(LaceState),
Nolace(NolaceState),
}
#[derive(Clone)]
pub struct OsceChannel {
method: OsceMethod,
features: OsceFeatureState,
state: ModelState,
}
impl OsceChannel {
pub fn new() -> OsceChannel {
let mut ch = core::mem::MaybeUninit::<OsceChannel>::uninit();
unsafe {
OsceChannel::write_new(ch.as_mut_ptr());
ch.assume_init()
}
}
pub(crate) unsafe fn write_new(this: *mut OsceChannel) {
unsafe {
write_fields!(this, {
method: OsceMethod::None,
features: OsceFeatureState::default(),
state: ModelState::None,
});
osce_reset(&mut *this, OSCE_DEFAULT_METHOD);
}
}
pub fn method(&self) -> OsceMethod {
self.method
}
pub fn bypass_count(&self) -> i32 {
self.features.reset
}
}
impl Default for OsceChannel {
fn default() -> OsceChannel {
OsceChannel::new()
}
}
pub fn osce_reset(ch: &mut OsceChannel, method: OsceMethod) {
ch.features = OsceFeatureState::default();
ch.state = match method {
OsceMethod::None => ModelState::None,
OsceMethod::Lace => ModelState::Lace(LaceState::default()),
OsceMethod::Nolace => ModelState::Nolace(NolaceState::default()),
};
ch.method = method;
ch.features.reset = 2;
}
pub fn osce_enhance_frame(ch: &mut OsceChannel, models: &OsceModels<'_>, frame: &OsceFrame<'_>, xq: &mut [i16]) {
enhance_frame(ch, models, frame, xq, false);
}
#[cfg(any(test, feature = "__internals"))]
#[doc(hidden)]
pub fn osce_enhance_frame_with_features(ch: &mut OsceChannel, models: &OsceModels<'_>, frame: &OsceFrame<'_>, xq: &mut [i16]) {
enhance_frame(ch, models, frame, xq, true);
}
fn enhance_frame(ch: &mut OsceChannel, models: &OsceModels<'_>, frame: &OsceFrame<'_>, xq: &mut [i16], always_features: bool) {
if frame.fs_khz != 16 || frame.nb_subfr != 4 {
let method = ch.method;
osce_reset(ch, method);
return;
}
let xq = &mut xq[..320];
if ch.method == OsceMethod::None && !always_features {
step_bypass_counter(&mut ch.features);
for v in xq.iter_mut() {
*v = (*v).max(-32767);
}
return;
}
let mut features = [0.0f32; 4 * OSCE_FEATURE_DIM];
let mut numbits = [0.0f32; 2];
let mut periods = [0i32; 4];
osce_calculate_features(&mut ch.features, frame, xq, &mut features, &mut numbits, &mut periods);
let mut in_buffer = [0.0f32; 320];
for (b, &v) in in_buffer.iter_mut().zip(xq.iter()) {
*b = v as f32 * (1.0 / 32768.0);
}
let mut out_buffer = in_buffer;
match (&mut ch.state, ch.method) {
(ModelState::Lace(st), OsceMethod::Lace) => {
if let Some(layers) = models.lace {
lace_process_20ms_frame(layers, st, &mut out_buffer, &in_buffer, &features, &numbits, &periods, models.arch);
}
}
(ModelState::Nolace(st), OsceMethod::Nolace) => {
if let Some(layers) = models.nolace {
nolace_process_20ms_frame(layers, st, &mut out_buffer, &in_buffer, &features, &numbits, &periods, models.arch);
}
}
_ => {}
}
if ch.features.reset > 1 {
out_buffer = in_buffer;
ch.features.reset -= 1;
} else if ch.features.reset != 0 {
osce_cross_fade_10ms(&mut out_buffer, &in_buffer);
ch.features.reset = 0;
}
for (v, &y) in xq.iter_mut().zip(out_buffer.iter()) {
*v = float2int((32768.0 * y).clamp(-32767.0, 32767.0)) as i16;
}
}
fn step_bypass_counter(features: &mut OsceFeatureState) {
if features.reset > 1 {
features.reset -= 1;
} else {
features.reset = 0;
}
}
#[cfg(feature = "osce-bwe")]
#[derive(Clone, Default)]
pub struct OsceBweChannel {
features: OsceBweFeatureState,
state: BbwenetState,
}
#[cfg(feature = "osce-bwe")]
impl OsceBweChannel {
pub fn new() -> OsceBweChannel {
OsceBweChannel::default()
}
pub(crate) unsafe fn write_new(this: *mut OsceBweChannel) {
unsafe {
write_fields!(this, {
features: OsceBweFeatureState::default(),
state: BbwenetState::default(),
});
}
}
}
#[cfg(feature = "osce-bwe")]
pub fn osce_bwe_reset(ch: &mut OsceBweChannel) {
*ch = OsceBweChannel::default();
for k in 0..=features::OSCE_BWE_MAX_INSTAFREQ_BIN {
ch.features.last_spec[2 * k] = 1e-9;
}
}
#[cfg(feature = "osce-bwe")]
pub fn osce_bwe(ch: &mut OsceBweChannel, layers: &BbwenetLayers<'_>, arch: DnnArch, xq48: &mut [i16], xq16: &[i16]) {
let len = xq16.len();
assert!(len == 160 || len == 320, "osce_bwe takes 10 or 20 ms");
let xq48 = &mut xq48[..3 * len];
let num_frames = len / 160;
let mut in_buffer = [0.0f32; 320];
for (b, &v) in in_buffer.iter_mut().zip(xq16) {
*b = v as f32 * (1.0 / 32768.0);
}
let mut feat = [0.0f32; 2 * OSCE_BWE_FEATURE_DIM];
osce_bwe_calculate_features(&mut ch.features, &mut feat, xq16);
let mut out_buffer = [0.0f32; 3 * 320];
bbwenet_process_frames(layers, &mut ch.state, &mut out_buffer[..3 * len], &in_buffer[..len], &feat, num_frames, arch);
let to_i16 = |y: f32| float2int((32768.0 * y).clamp(-32767.0, 32767.0)) as i16;
let d = OSCE_BWE_OUTPUT_DELAY;
xq48[..d].copy_from_slice(&ch.state.output_buffer);
for (o, &y) in xq48[d..].iter_mut().zip(out_buffer.iter()) {
*o = to_i16(y);
}
for (o, &y) in ch.state.output_buffer.iter_mut().zip(out_buffer[3 * len - d..3 * len].iter()) {
*o = to_i16(y);
}
}
#[cfg(all(test, feature = "std"))]
mod tests {
use super::*;
struct TestFrame {
pred: [[i16; 16]; 2],
pitch: [i32; 4],
ltp: [i16; 20],
gains: [i32; 4],
xq: [i16; 320],
}
impl TestFrame {
fn new(n: usize, amplitude: f32) -> TestFrame {
let mut pred = [[0i16; 16]; 2];
pred[0][0] = 3000;
pred[0][1] = -1500;
pred[1] = pred[0];
TestFrame {
pred,
pitch: [100; 4],
ltp: [1000, 2000, 8000, 2000, 1000, 1000, 2000, 8000, 2000, 1000, 1000, 2000, 8000, 2000, 1000, 1000, 2000, 8000, 2000, 1000],
gains: [65536 * 300; 4],
xq: core::array::from_fn(|i| {
let t = (n * 320 + i) as f32 / 16000.0;
(amplitude * (2.0 * core::f32::consts::PI * 160.0 * t).sin()) as i16
}),
}
}
fn frame(&self, fs_khz: i32, nb_subfr: i32) -> OsceFrame<'_> {
OsceFrame {
fs_khz,
nb_subfr,
lpc_order: 16,
signal_type: 2,
pred_coef_q12: &self.pred,
pitch_l: &self.pitch,
ltp_coef_q14: &self.ltp,
gains_q16: &self.gains,
num_bits: 300,
}
}
}
#[test]
fn a_new_channel_uses_nolace_and_bypasses_two_frames() {
let models = OsceModels::builtin(crate::dnn::arch::detect());
let mut ch = OsceChannel::new();
assert_eq!(ch.method(), OsceMethod::Nolace);
let counts: Vec<i32> = (0..4)
.map(|n| {
let f = TestFrame::new(n, 8000.0);
let mut x = f.xq;
osce_enhance_frame(&mut ch, &models, &f.frame(16, 4), &mut x);
if n == 0 {
assert_eq!(x, f.xq);
}
ch.bypass_count()
})
.collect();
assert_eq!(counts, [1, 0, 0, 0]);
}
#[test]
fn an_ineligible_frame_resets_and_is_left_alone() {
let models = OsceModels::builtin(crate::dnn::arch::detect());
let mut ch = OsceChannel::new();
osce_reset(&mut ch, OsceMethod::Lace);
for n in 0..3 {
let f = TestFrame::new(n, 8000.0);
let mut x = f.xq;
osce_enhance_frame(&mut ch, &models, &f.frame(16, 4), &mut x);
}
assert_eq!(ch.bypass_count(), 0);
for (fs, subfr) in [(16, 2), (12, 4), (8, 4)] {
let f = TestFrame::new(9, 8000.0);
let mut x = f.xq;
osce_enhance_frame(&mut ch, &models, &f.frame(fs, subfr), &mut x);
assert_eq!(x, f.xq);
assert_eq!(ch.method(), OsceMethod::Lace);
assert_eq!(ch.bypass_count(), 2);
}
}
#[test]
fn method_none_clips_only_the_most_negative_sample() {
let models = OsceModels::builtin(crate::dnn::arch::detect());
let mut ch = OsceChannel::new();
osce_reset(&mut ch, OsceMethod::None);
let mut f = TestFrame::new(0, 32767.0);
f.xq[5] = -32768;
f.xq[6] = 32767;
for _ in 0..3 {
let mut x = f.xq;
osce_enhance_frame(&mut ch, &models, &f.frame(16, 4), &mut x);
let mut want = f.xq;
want[5] = -32767;
assert_eq!(x, want);
}
}
#[test]
fn skipping_the_features_of_method_none_changes_nothing() {
let models = OsceModels::builtin(crate::dnn::arch::detect());
let mut a = OsceChannel::new();
let mut b = OsceChannel::new();
let methods = [OsceMethod::None, OsceMethod::Lace, OsceMethod::None, OsceMethod::Nolace];
for n in 0..24 {
let m = methods[n / 6];
if a.method() != m {
osce_reset(&mut a, m);
osce_reset(&mut b, m);
}
let f = TestFrame::new(n, 12000.0);
let (mut x, mut y) = (f.xq, f.xq);
osce_enhance_frame(&mut a, &models, &f.frame(16, 4), &mut x);
osce_enhance_frame_with_features(&mut b, &models, &f.frame(16, 4), &mut y);
assert_eq!(x, y, "frame {n}");
}
}
#[test]
fn unloaded_layers_act_as_method_none_but_keep_the_features() {
let loaded = OsceModels::builtin(crate::dnn::arch::detect());
let unloaded = OsceModels { lace: None, nolace: None, ..loaded };
let mut ch = OsceChannel::new();
for n in 0..3 {
let f = TestFrame::new(n, 8000.0);
let mut x = f.xq;
osce_enhance_frame(&mut ch, &unloaded, &f.frame(16, 4), &mut x);
assert_eq!(x, f.xq);
}
assert_eq!(ch.method(), OsceMethod::Nolace);
assert_ne!(ch.features.numbits_smooth, 0.0);
}
#[cfg(feature = "osce-bwe")]
#[test]
fn the_bandwidth_extension_is_delayed_by_21_samples() {
let models = OsceModels::builtin(crate::dnn::arch::detect());
let layers = models.bbwenet.unwrap();
let mut ch = OsceBweChannel::new();
let mut out = [1i16; 960];
let silence = [0i16; 320];
osce_bwe(&mut ch, layers, models.arch, &mut out, &silence);
assert!(out.iter().all(|&v| v == 0));
let f = TestFrame::new(1, 8000.0);
osce_bwe(&mut ch, layers, models.arch, &mut out[..480], &f.xq[..160]);
assert!(out[..OSCE_BWE_OUTPUT_DELAY].iter().all(|&v| v == 0));
assert!(out[OSCE_BWE_OUTPUT_DELAY..480].iter().any(|&v| v != 0));
}
#[cfg(feature = "osce-bwe")]
#[test]
#[should_panic(expected = "10 or 20 ms")]
fn the_bandwidth_extension_takes_10_or_20_ms() {
let models = OsceModels::builtin(crate::dnn::arch::detect());
let mut ch = OsceBweChannel::new();
let mut out = [0i16; 960];
osce_bwe(&mut ch, models.bbwenet.unwrap(), models.arch, &mut out, &[0i16; 240]);
}
#[cfg(feature = "osce-bwe")]
#[test]
fn the_bwe_reset_sets_the_previous_spectrum_to_1e_9() {
let mut ch = OsceBweChannel::new();
assert!(ch.features.last_spec.iter().all(|&v| v == 0.0));
osce_bwe_reset(&mut ch);
for (k, &v) in ch.features.last_spec.iter().enumerate() {
assert_eq!(v, if k % 2 == 0 { 1e-9 } else { 0.0 });
}
}
#[cfg(all(feature = "osce-bwe", not(debug_assertions)))]
#[test]
fn the_osce_path_runs_on_32_kb_of_stack() {
let models = OsceModels::builtin(crate::dnn::arch::detect());
let mut ch = std::boxed::Box::new(OsceChannel::new());
let mut bwe = std::boxed::Box::new(OsceBweChannel::new());
let apple_arm = cfg!(all(target_arch = "aarch64", target_vendor = "apple"));
let usable = if apple_arm { 32 * 1024 } else { 64 * 1024 };
let windows_reserve = if cfg!(windows) { 32 * 1024 } else { 0 };
std::thread::Builder::new()
.stack_size(usable + windows_reserve)
.spawn(move || {
for method in [OsceMethod::Nolace, OsceMethod::Lace] {
osce_reset(&mut ch, method);
for n in 0..3 {
let f = TestFrame::new(n, 8000.0);
let mut x = f.xq;
osce_enhance_frame(&mut ch, &models, &f.frame(16, 4), &mut x);
let mut out = [0i16; 960];
osce_bwe(&mut bwe, models.bbwenet.unwrap(), models.arch, &mut out, &x);
}
}
})
.unwrap()
.join()
.unwrap();
}
}