use crate::Error;
use crate::in_place::{write_fields, write_zero};
#[cfg(not(feature = "std"))]
use crate::compat::Math;
use super::coding::compute_quantizer;
use super::rdovae_enc::RdovaeEncState;
use super::{
DRED_DFRAME_SIZE, DRED_EXPERIMENTAL_BYTES, DRED_EXPERIMENTAL_VERSION, DRED_FRAME_SIZE, DRED_MAX_DATA_SIZE,
DRED_MAX_FRAMES, DRED_MIN_BYTES, DRED_NUM_REDUNDANCY_FRAMES, DRED_SILK_ENCODER_DELAY,
};
use crate::dnn::Activation;
use crate::dnn::DnnBlob;
use crate::dnn::arch::DnnArch;
use crate::dnn::holder::DredEncoderDnn;
use crate::dnn::models::DredEncModels;
use crate::dnn::models::dred_rdovae_stats::{
DRED_LATENT_DEAD_ZONE_Q8, DRED_LATENT_DIM, DRED_LATENT_P0_Q8, DRED_LATENT_QUANT_SCALES_Q8, DRED_LATENT_R_Q8,
DRED_NUM_FEATURES, DRED_STATE_DEAD_ZONE_Q8, DRED_STATE_DIM, DRED_STATE_P0_Q8, DRED_STATE_QUANT_SCALES_Q8,
DRED_STATE_R_Q8,
};
use crate::dnn::nnet::compute_activation_in_place;
use crate::dnn::plc::NB_TOTAL_FEATURES;
use crate::dnn::plc::features::{LpcnetEncState, compute_single_frame_features};
use crate::range_coder::RangeCoder;
use crate::{bitrate_to_bits, bits_to_bitrate, float2int16};
const RESAMPLING_ORDER: usize = 8;
const MAX_DOWNMIX_BUFFER: usize = 960 * 2;
const VERY_SMALL: f32 = 1e-30;
const DRED_BITS_TABLE: [f32; 16] =
[73.2, 68.1, 62.5, 57.0, 51.5, 45.7, 39.9, 32.4, 26.4, 20.4, 16.3, 13., 9.3, 8.2, 7.2, 6.4];
#[derive(Clone)]
struct DredEnc {
lpcnet: LpcnetEncState,
rdovae: RdovaeEncState,
fs: i32,
channels: usize,
input_buffer: [f32; 2 * DRED_DFRAME_SIZE],
input_buffer_fill: usize,
dred_offset: i32,
latent_offset: i32,
last_extra_dred_offset: i32,
latents_buffer: [f32; DRED_MAX_FRAMES * DRED_LATENT_DIM],
latents_buffer_fill: usize,
state_buffer: [f32; DRED_MAX_FRAMES * DRED_STATE_DIM],
resample_mem: [f32; RESAMPLING_ORDER + 1],
}
pub(crate) struct DredEncoder {
dnn: DredEncoderDnn,
duration: i32,
q0: i32,
dq: i32,
qmax: i32,
target_chunks: i32,
bitrate_bps: i32,
activity_mem: [u8; 4 * DRED_MAX_FRAMES],
enc: DredEnc,
}
impl DredEncoder {
pub(crate) unsafe fn write_new(this: *mut Self, fs: i32, channels: usize) {
unsafe {
write_fields!(this, {
dnn: DredEncoderDnn::new(),
duration: 0,
q0: 0,
dq: 0,
qmax: 0,
target_chunks: 0,
bitrate_bps: 0,
activity_mem = |p| write_zero(p),
enc = |p| DredEnc::write_new(p, fs, channels),
});
}
}
pub(crate) fn take_models(&mut self) -> DredEncoderDnn {
core::mem::replace(&mut self.dnn, DredEncoderDnn::new())
}
pub(crate) fn set_models(&mut self, dnn: DredEncoderDnn) {
self.dnn = dnn;
}
pub(crate) fn set_duration(&mut self, frames: i32) -> Result<(), Error> {
if !(0..=DRED_MAX_FRAMES as i32).contains(&frames) {
return Err(Error::bad_arg("invalid DRED duration"));
}
self.duration = frames;
Ok(())
}
pub(crate) fn duration(&self) -> i32 {
self.duration
}
pub(crate) fn bitrate_bps(&self) -> i32 {
self.bitrate_bps
}
pub(crate) fn load(&mut self, blob: &DnnBlob) -> Result<crate::DnnModels, Error> {
self.dnn.load(blob)
}
pub(crate) fn is_loaded(&self) -> bool {
self.dnn.model().is_some()
}
pub(crate) fn compute_bitrate(&mut self, bitrate_bps: i32, frame_size: usize, fec: bool, loss: i32) -> i32 {
let fs = self.enc.fs;
let min16 = |a: f32, b: f32| if a < b { a } else { b };
let (mut dred_frac, bitrate_offset) = if fec {
(min16(0.7, 3.0 * loss as f32 / 100.0), 20000)
} else if loss > 5 {
(min16(0.8, 0.55 + loss as f32 / 100.0), 12000)
} else {
((12 * loss) as f32 / 100.0, 12000)
};
dred_frac = dred_frac / (dred_frac + (1.0 - dred_frac) * (frame_size as f32 * 50.0) / fs as f32);
let rate = bitrate_bps - bitrate_offset;
let q0 = (51 - 3 * ec_ilog(rate.max(1))).clamp(4, 15);
let dq = if rate > 36000 { 3 } else { 5 };
let qmax = 15;
let target_dred_bitrate = ((dred_frac * rate as f32) as i32).max(0);
let (max_dred_bits, target_chunks) = if self.duration > 0 {
let target_bits = bitrate_to_bits(target_dred_bitrate, fs, frame_size);
estimate_bitrate(q0, dq, qmax, self.duration, target_bits)
} else {
(0, 0)
};
let mut dred_bitrate = target_dred_bitrate.min(bits_to_bitrate(max_dred_bits, fs, frame_size));
if target_chunks < 2 {
dred_bitrate = 0;
}
self.q0 = q0;
self.dq = dq;
self.qmax = qmax;
self.target_chunks = target_chunks;
self.bitrate_bps = dred_bitrate;
dred_bitrate
}
pub(crate) fn analyze_frame(&mut self, pcm: &[f32], frame_size: usize, extra_delay: usize, activity: i32) {
if self.duration > 0
&& let Some(model) = self.dnn.model()
{
self.enc.compute_latents(model, pcm, frame_size, extra_delay, self.dnn.arch());
let n = frame_size * 400 / self.enc.fs as usize;
self.activity_mem.copy_within(..4 * DRED_MAX_FRAMES - n, n);
self.activity_mem[..n].fill(activity as u8);
} else {
self.enc.latents_buffer_fill = 0;
self.activity_mem.fill(0);
}
}
pub(crate) fn set_activity(&mut self, frame_size: usize, activity: i32) {
let n = frame_size * 400 / self.enc.fs as usize;
self.activity_mem[..n].fill(activity as u8);
}
pub(crate) fn write_extension(
&mut self,
rc: &mut RangeCoder,
buf: &mut [u8; DRED_MAX_DATA_SIZE],
room: i32,
use_vbr: bool,
) -> usize {
if self.duration <= 0 || !self.is_loaded() {
return 0;
}
let mut dred_chunks = ((self.duration + 5) / 4).min(DRED_NUM_REDUNDANCY_FRAMES as i32 / 2);
if use_vbr {
dred_chunks = dred_chunks.min(self.target_chunks);
}
let mut bytes_left = (DRED_MAX_DATA_SIZE as i32).min(room);
bytes_left -= (bytes_left + 1 + DRED_EXPERIMENTAL_BYTES as i32) / 255;
if dred_chunks < 1 || bytes_left < (DRED_MIN_BYTES + DRED_EXPERIMENTAL_BYTES) as i32 {
return 0;
}
buf[0] = b'D';
buf[1] = DRED_EXPERIMENTAL_VERSION;
let max_bytes = bytes_left as usize - DRED_EXPERIMENTAL_BYTES;
let n = self.encode_silk_frame(rc, &mut buf[DRED_EXPERIMENTAL_BYTES..], dred_chunks, max_bytes);
if n == 0 { 0 } else { n + DRED_EXPERIMENTAL_BYTES }
}
fn encode_silk_frame(&mut self, rc: &mut RangeCoder, out: &mut [u8], max_chunks: i32, max_bytes: usize) -> usize {
let (q0, dq, qmax) = (self.q0, self.dq, self.qmax);
let arch = self.dnn.arch();
let enc = &mut self.enc;
let activity_mem = &self.activity_mem;
let fill = enc.latents_buffer_fill as i32;
let mut latent_offset = enc.latent_offset;
let mut extra_dred_offset = 0i32;
let mut delayed_dred = false;
if activity_mem[0] != 0 && enc.last_extra_dred_offset > 0 {
latent_offset = enc.last_extra_dred_offset;
delayed_dred = true;
enc.last_extra_dred_offset = 0;
}
while latent_offset < fill && !voice_active(activity_mem, latent_offset) {
latent_offset += 1;
extra_dred_offset += 1;
}
if !delayed_dred {
enc.last_extra_dred_offset = extra_dred_offset;
}
rc.reset_for_encode(max_bytes as u32);
rc.enc_uint(q0 as u32, 16);
rc.enc_uint(dq as u32, 8);
let total_offset = 16 - (enc.dred_offset - extra_dred_offset * 8);
debug_assert!(total_offset >= 0);
if total_offset > 31 {
rc.enc_uint(1, 2);
rc.enc_uint((total_offset >> 5) as u32, 256);
rc.enc_uint((total_offset & 31) as u32, 32);
} else {
rc.enc_uint(0, 2);
rc.enc_uint(total_offset as u32, 32);
}
debug_assert!(qmax >= q0);
if q0 < 14 && dq > 0 {
let nvals = 15 - (q0 + 1);
let (fl, fh) = if qmax >= 15 { (0, nvals) } else { (nvals + qmax - (q0 + 1), nvals + qmax - q0) };
rc.encode(fl as u32, fh as u32, 2 * nvals as u32);
}
let state_q = q0 as usize * DRED_STATE_DIM;
let state_at = latent_offset as usize * DRED_STATE_DIM;
encode_latents(
rc,
&enc.state_buffer[state_at..state_at + DRED_STATE_DIM],
&DRED_STATE_QUANT_SCALES_Q8[state_q..],
&DRED_STATE_DEAD_ZONE_Q8[state_q..],
&DRED_STATE_R_Q8[state_q..],
&DRED_STATE_P0_Q8[state_q..],
arch,
);
if rc.tell() > 8 * max_bytes as i32 {
return 0;
}
let mut bak = rc.save_state();
let mut prev_active = false;
let mut dred_encoded = 0;
let end = (2 * max_chunks).min(fill - latent_offset - 1);
let mut i = 0;
while i < end {
let q_level = compute_quantizer(q0, dq as usize, qmax, i / 2) as usize;
let offset = q_level * DRED_LATENT_DIM;
let at = (i + latent_offset) as usize * DRED_LATENT_DIM;
encode_latents(
rc,
&enc.latents_buffer[at..at + DRED_LATENT_DIM],
&DRED_LATENT_QUANT_SCALES_Q8[offset..],
&DRED_LATENT_DEAD_ZONE_Q8[offset..],
&DRED_LATENT_R_Q8[offset..],
&DRED_LATENT_P0_Q8[offset..],
arch,
);
if rc.tell() > 8 * max_bytes as i32 {
if i == 0 {
return 0;
}
break;
}
let active = voice_active(activity_mem, i + latent_offset);
if active || prev_active {
bak = rc.save_state();
dred_encoded = i + 2;
}
prev_active = active;
i += 2;
}
if dred_encoded == 0 || (dred_encoded <= 2 && extra_dred_offset != 0) {
return 0;
}
rc.restore_state(&bak);
let n = ((rc.tell() + 7) / 8) as usize;
rc.shrink(n as u32);
rc.done();
out[..n].copy_from_slice(&rc.buf[..n]);
n
}
}
impl DredEnc {
unsafe fn write_new(this: *mut Self, fs: i32, channels: usize) {
unsafe {
write_fields!(this, {
lpcnet = |p| write_zero(p),
rdovae = |p| write_zero(p),
fs: fs,
channels: channels,
input_buffer = |p| write_zero(p),
input_buffer_fill: DRED_SILK_ENCODER_DELAY,
dred_offset: 0,
latent_offset: 0,
last_extra_dred_offset: 0,
latents_buffer = |p| write_zero(p),
latents_buffer_fill: 0,
state_buffer = |p| write_zero(p),
resample_mem: [0.0; RESAMPLING_ORDER + 1],
});
}
}
fn compute_latents(
&mut self,
model: &DredEncModels,
pcm: &[f32],
frame_size: usize,
extra_delay: usize,
arch: DnnArch,
) {
let fs = self.fs as usize;
let mut frame_size16k = frame_size * 16000 / fs;
let curr_offset16k = 40 + (extra_delay * 16000 / fs) as i32 - self.input_buffer_fill as i32;
self.dred_offset = ((curr_offset16k as f32 + 20.0) / 40.0).floor() as i32;
self.latent_offset = 0;
let mut pos = 0;
while frame_size16k > 0 {
let process_size16k = (2 * DRED_FRAME_SIZE).min(frame_size16k);
let process_size = process_size16k * fs / 16000;
let fill = self.input_buffer_fill;
self.convert_to_16k(&pcm[pos..], process_size, fill, process_size16k);
self.input_buffer_fill += process_size16k;
if self.input_buffer_fill >= 2 * DRED_FRAME_SIZE {
self.process_frame(model, arch);
self.input_buffer_fill -= 2 * DRED_FRAME_SIZE;
self.input_buffer
.copy_within(2 * DRED_FRAME_SIZE..2 * DRED_FRAME_SIZE + self.input_buffer_fill, 0);
if self.dred_offset < 6 {
self.dred_offset += 8;
} else {
self.latent_offset += 1;
}
}
pos += process_size;
frame_size16k -= process_size16k;
}
}
fn process_frame(&mut self, model: &DredEncModels, arch: DnnArch) {
self.latents_buffer.copy_within(..(DRED_MAX_FRAMES - 1) * DRED_LATENT_DIM, DRED_LATENT_DIM);
self.state_buffer.copy_within(..(DRED_MAX_FRAMES - 1) * DRED_STATE_DIM, DRED_STATE_DIM);
let mut features = [[0.0f32; NB_TOTAL_FEATURES]; 2];
for (k, f) in features.iter_mut().enumerate() {
let frame = self.input_buffer[k * DRED_FRAME_SIZE..(k + 1) * DRED_FRAME_SIZE].try_into().unwrap();
compute_single_frame_features(&mut self.lpcnet, frame, f, &model.pitchdnn, arch);
}
let mut input = [0.0f32; 2 * DRED_NUM_FEATURES];
input[..DRED_NUM_FEATURES].copy_from_slice(&features[0][..DRED_NUM_FEATURES]);
input[DRED_NUM_FEATURES..].copy_from_slice(&features[1][..DRED_NUM_FEATURES]);
self.rdovae.encode_dframe(
&model.rdovae,
&mut self.latents_buffer[..DRED_LATENT_DIM],
&mut self.state_buffer[..DRED_STATE_DIM],
&input,
arch,
);
self.latents_buffer_fill = (self.latents_buffer_fill + 1).min(DRED_NUM_REDUNDANCY_FRAMES);
}
#[allow(clippy::excessive_precision)]
fn convert_to_16k(&mut self, pcm: &[f32], in_len: usize, at: usize, out_len: usize) {
let up = match self.fs {
8000 => 2,
12000 => 4,
16000 => 1,
24000 => 2,
48000 => 1,
_ => unreachable!("the Opus encoder takes no other rate"),
};
debug_assert!(self.channels * in_len <= MAX_DOWNMIX_BUFFER && up * in_len <= MAX_DOWNMIX_BUFFER);
let mut downmix = [0.0f32; MAX_DOWNMIX_BUFFER];
if self.channels == 1 {
for i in 0..in_len {
downmix[up * i] = float2int16(up as f32 * pcm[i]) as f32 + VERY_SMALL;
}
} else {
for i in 0..in_len {
let x = (0.5 * up as f64 * (pcm[2 * i] + pcm[2 * i + 1]) as f64) as f32;
downmix[up * i] = float2int16(x) as f32 + VERY_SMALL;
}
}
let out = &mut self.input_buffer[at..at + out_len];
match self.fs {
16000 => out.copy_from_slice(&downmix[..out_len]),
48000 | 24000 => {
const B: [f32; 8] = [
0.005873358047, 0.012980854831, 0.014531340042, 0.014531340042, 0.012980854831,
0.005873358047, 0.004523418224, 0.,
];
const A: [f32; 8] = [
-3.878718597768, 7.748834257468, -9.653651699533, 8.007342726666, -4.379450178552,
1.463182111810, -0.231720677804, 0.,
];
let b0 = 0.004523418224f32;
filter_df2t(&mut downmix[..up * in_len], b0, &B, &A, &mut self.resample_mem);
for (i, o) in out.iter_mut().enumerate() {
*o = downmix[3 * i];
}
}
12000 => {
const B: [f32; 8] = [
-0.001017101081, 0.003673127243, 0.001009165267, 0.001009165267, 0.003673127243,
-0.001017101081, 0.002033596776, 0.,
];
const A: [f32; 8] = [
-4.930414411612, 11.291643096504, -15.322037343815, 13.216403930898, -7.220409219553,
2.310550142771, -0.334338618782, 0.,
];
let b0 = 0.002033596776f32;
filter_df2t(&mut downmix[..up * in_len], b0, &B, &A, &mut self.resample_mem);
for (i, o) in out.iter_mut().enumerate() {
*o = downmix[3 * i];
}
}
_ => {
const B: [f32; 8] = [
0.081670120929, 0.180401598565, 0.259391051971, 0.259391051971, 0.180401598565,
0.081670120929, 0.020109185709, 0.,
];
const A: [f32; 8] = [
-1.393651933659, 2.609789872676, -2.403541968806, 2.056814957331, -1.148908574570,
0.473001413788, -0.110359852412, 0.,
];
let b0 = 0.020109185709f32;
out.copy_from_slice(&downmix[..out_len]);
filter_df2t(out, b0, &B, &A, &mut self.resample_mem);
}
}
}
}
fn filter_df2t(x: &mut [f32], b0: f32, b: &[f32; 8], a: &[f32; 8], mem: &mut [f32; RESAMPLING_ORDER + 1]) {
for v in x.iter_mut() {
let xi = *v;
let yi = xi.mul_add(b0, mem[0]);
let nyi = -yi;
for j in 0..RESAMPLING_ORDER {
mem[j] = a[j].mul_add(nyi, b[j].mul_add(xi, mem[j + 1]));
}
*v = yi;
}
}
fn encode_latents(rc: &mut RangeCoder, x: &[f32], scale: &[u8], dzone: &[u8], r: &[u8], p0: &[u8], arch: DnnArch) {
const MAX_DIM: usize = if DRED_LATENT_DIM > DRED_STATE_DIM { DRED_LATENT_DIM } else { DRED_STATE_DIM };
let dim = x.len();
let eps = 0.1f32;
let mut xq = [0.0f32; MAX_DIM];
let mut delta = [0.0f32; MAX_DIM];
let mut deadzone = [0.0f32; MAX_DIM];
for i in 0..dim {
delta[i] = dzone[i] as f32 * (1.0 / 256.0);
xq[i] = x[i] * scale[i] as f32 * (1.0 / 256.0);
deadzone[i] = xq[i] / (delta[i] + eps);
}
compute_activation_in_place(&mut deadzone[..dim], Activation::Tanh, arch);
for i in 0..dim {
xq[i] = (-delta[i]).mul_add(deadzone[i], xq[i]);
let q = (0.5 + xq[i]).floor() as i32;
if r[i] != 0 && p0[i] != 255 {
rc.laplace_encode_p0(q, (p0[i] as u16) << 7, (r[i] as u16) << 7);
}
}
}
fn voice_active(activity_mem: &[u8; 4 * DRED_MAX_FRAMES], offset: i32) -> bool {
let start = 8 * offset as usize;
(start..start + 16).any(|i| activity_mem.get(i) == Some(&1))
}
fn estimate_bitrate(q0: i32, dq: i32, qmax: i32, duration: i32, target_bits: i32) -> (i32, i32) {
let mut bits = (8 * (3 + DRED_EXPERIMENTAL_BYTES)) as f32;
bits += 50.0 + DRED_BITS_TABLE[q0 as usize];
let dred_chunks = ((duration + 5) / 4).min(DRED_NUM_REDUNDANCY_FRAMES as i32 / 2);
let mut target_chunks = 0;
for i in 0..dred_chunks {
let q = compute_quantizer(q0, dq as usize, qmax, i);
bits += DRED_BITS_TABLE[q as usize];
if bits < target_bits as f32 {
target_chunks = i + 1;
}
}
((0.5 + bits).floor() as i32, target_chunks)
}
fn ec_ilog(x: i32) -> i32 {
32 - (x as u32).leading_zeros() as i32
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn bits_table_and_ilog() {
assert_eq!(ec_ilog(1), 1);
assert_eq!(ec_ilog(36000), 16);
let (bits, chunks) = estimate_bitrate(4, 5, 15, 100, 1000);
assert!(bits > 0 && chunks > 0);
}
}