#![allow(clippy::needless_range_loop)]
use super::params::{
SmplGainParams, SmplInternalParams, SmplLsfParams, SmplPitchParams, SmplPulseParams, SmplRawSym,
};
use super::smpl_celp::{CelpEncoder, smpl_distribute_fcb_surv};
use super::smpl_decode::{SmplLsfState, smpl_advance_lsf_state};
use super::smpl_harmcomb::{smpl_filt_arma2, smpl_get_hp_coefs};
use super::smpl_lpc::{
SMPL_F_LEN, SMPL_LPC_BUF_LEN, smpl_a2nlsf_16, smpl_lpc_analyze_with_f2, smpl_window_lpc20,
};
use super::smpl_lsf_quant::{lsf_quant, lsf_quant_cond};
use super::smpl_mem::{SmplMem, load_smpl_mem};
use super::smpl_perc::{
BitrateController, BitrateControllerInputs, PercModelState, SMPL_PERC_EMPH_UV,
SMPL_PERC_EMPH_V, SMPL_PERC_REG, smpl_perc_ac2a, smpl_perc_model,
};
use super::smpl_signal_mode::{VuvMode, smpl_get_signal_mode};
use super::smpl_synth::{
SMPL_INTF_LEN, SMPL_ORDER, SMPL_SUBFR_COUNT, SMPL_SUBFR_LEN, SMPL_VOICED_NORM_GAIN,
SmplFrameSynth, SmplPitchSynth, SmplSynthTables, load_smpl_synth_tables, smpl_gain_lin,
smpl_nlsf2a, smpl_reconstruct_nlsf, synth_internal_frame,
};
const SMPL_LPC_HIST_LEN: usize = 144;
const SMPL_LPC_PRE: usize = 96;
const SMPL_LSF_SURV: usize = 6;
const SMPL_WINNEXT_WB_LEN: usize = 32;
const SMPL_LSF_RDW_ADJ: f32 = 1.1952286;
#[derive(Default)]
pub(crate) struct SmplEncoderState {
hist: Vec<f64>,
vad_pcm: Vec<i16>,
hp: Vec<f32>,
x: Vec<f64>,
xn: Vec<f32>,
hp_full: Vec<f32>,
hp_coefs: Option<([f32; 3], [f32; 3])>,
hp_state: [f32; 4],
celp: Option<CelpEncoder>,
perc: Option<PercModelState>,
perc_prev: Vec<f32>,
bitrate: Option<BitrateController>,
lpc_hist: Vec<f32>,
prev_lsfq: Vec<f32>,
prev_voiced: bool,
vad: Option<super::smpl_vad::SmplVadState>,
vuv: VuvMode,
hp_pitch_hist: Vec<f32>,
ltp_buf: Vec<f32>,
pitch_est: super::smpl_pitch_enc::PitchEstState,
lpc_fft: Option<super::smpl_perc::FftScratch>,
}
const SMPL_MAIN_BIT_RATE: i32 = 20000;
const SMPL_COMPLEXITY: i32 = 8;
const SMPL_CELP_LOW_RATE: bool = false;
const SMPL_CELP_PERC_RESP_LEN: usize = 32;
const SMPL_CELP_FCB_SUBFRLEN: usize = 80;
const SMPL_CELP_SUBFR_PER_PACKET: usize = 12;
const SMPL_PERC_R_LEN: usize = SMPL_CELP_PERC_RESP_LEN + 1;
const SMPL_FCB_TOT_SURV_20MS_MAX: i32 = 100;
const SMPL_ENC_HP_FCORNER_HZ: f32 = 35.0;
fn unvoiced_pitch() -> SmplPitchSynth {
SmplPitchSynth {
voiced: false,
lag_subfr: [0.0; 4],
norm_gain: 0.0,
}
}
struct Candidate {
ip: SmplInternalParams,
stage1: i32,
grid: i32,
qsym: [i32; 16],
pulse_vec: Vec<i32>,
gain_q: [i32; 4],
pitch: SmplPitchSynth,
silent: bool,
}
struct CelpFrameCtx<'a> {
celp: &'a mut CelpEncoder,
perc: &'a mut PercModelState,
perc_prev: &'a mut Vec<f32>,
bitrate: &'a mut BitrateController,
hp_n: &'a [f32],
intf: usize,
sp_act_prob: f32,
coded_as_active_voice: bool,
f2: [f32; SMPL_F_LEN],
voicing_strength: f32,
vuv: &'a mut VuvMode,
hp_pitch_hist: &'a [f32],
ltp_buf: &'a mut Vec<f32>,
pitch_est: &'a mut super::smpl_pitch_enc::PitchEstState,
perc_corrs: Vec<Vec<f32>>,
block_lags: [[f32; 2]; SMPL_SUBFR_COUNT],
}
pub(crate) fn smpl_analyze_frame_st(
es: &mut SmplEncoderState,
pcm: &[f32],
) -> super::params::SmplFrameParams {
let need = SMPL_INTF_LEN * 3;
let mut owned;
let pcm: &[f32] = if pcm.len() < need {
owned = vec![0f32; need];
owned[..pcm.len()].copy_from_slice(pcm);
&owned
} else {
pcm
};
let synth_t = load_smpl_synth_tables();
es.vad_pcm.clear();
es.vad_pcm.extend(
pcm[..need]
.iter()
.map(|&s| (s * 32768.0).round().clamp(-32768.0, 32767.0) as i16),
);
let vad = es
.vad
.get_or_insert_with(super::smpl_vad::SmplVadState::new)
.process_packet(&es.vad_pcm, SMPL_INTF_LEN);
let sp_act_prob = vad.vad_results;
let coded_as_active_voice = vad.coded_as_active_voice;
let (hp_ma, hp_ar) = *es
.hp_coefs
.get_or_insert_with(|| smpl_get_hp_coefs(SMPL_ENC_HP_FCORNER_HZ));
es.hp.resize(need, 0.0);
es.hp.fill(0.0);
smpl_filt_arma2(
&pcm[..need],
need,
hp_ma,
hp_ar,
&mut es.hp_state,
&mut es.hp,
);
es.x.resize(SMPL_ORDER + need, 0.0);
es.x.fill(0.0);
if es.hist.len() >= SMPL_ORDER {
es.x[..SMPL_ORDER].copy_from_slice(&es.hist[es.hist.len() - SMPL_ORDER..]);
}
for i in 0..need {
es.x[SMPL_ORDER + i] = es.hp[i] as f64 * 32768.0;
}
let mut shadow = SmplFrameSynth::default();
let mut prev_nlsf: Vec<f32> = Vec::new();
let mut lstate = SmplLsfState::default();
es.celp.get_or_insert_with(|| {
CelpEncoder::new(
SMPL_CELP_LOW_RATE,
SMPL_CELP_PERC_RESP_LEN,
SMPL_CELP_FCB_SUBFRLEN,
SMPL_CELP_SUBFR_PER_PACKET,
)
});
es.perc.get_or_insert_with(PercModelState::new);
es.bitrate.get_or_insert_with(BitrateController::new);
if es.perc_prev.len() != SMPL_PERC_R_LEN {
es.perc_prev = vec![0.0; SMPL_PERC_R_LEN];
}
let res_lead: usize = SMPL_ORDER + SMPL_WINNEXT_WB_LEN;
es.xn.resize(res_lead + need, 0.0);
es.xn.fill(0.0);
if es.hist.len() >= res_lead {
for i in 0..res_lead {
es.xn[i] = (es.hist[es.hist.len() - res_lead + i] / 32768.0) as f32;
}
}
es.xn[res_lead..res_lead + need].copy_from_slice(&es.hp[..need]);
es.hp_full
.resize(SMPL_LPC_HIST_LEN + need + SMPL_WINNEXT_WB_LEN, 0.0);
es.hp_full.fill(0.0);
if es.lpc_hist.len() == SMPL_LPC_HIST_LEN {
es.hp_full[..SMPL_LPC_HIST_LEN].copy_from_slice(&es.lpc_hist);
}
es.hp_full[SMPL_LPC_HIST_LEN..SMPL_LPC_HIST_LEN + need].copy_from_slice(&es.hp[..need]);
let hp = es.hp.as_slice();
let x = es.x.as_slice();
let xn = es.xn.as_slice();
let hp_full = es.hp_full.as_slice();
let mut hp_pitch_hist = std::mem::take(&mut es.hp_pitch_hist);
if hp_pitch_hist.len() != SMPL_PITCH_LAG_MAX {
hp_pitch_hist.resize(SMPL_PITCH_LAG_MAX, 0.0);
}
if es.ltp_buf.len() != super::smpl_pitch_enc::MAX_LTP_BUF_LEN {
es.ltp_buf = vec![0.0f32; super::smpl_pitch_enc::MAX_LTP_BUF_LEN];
}
let celp = es.celp.as_mut().expect("celp built above");
let perc = es.perc.as_mut().expect("perc built above");
let bitrate = es.bitrate.as_mut().expect("bitrate built above");
let ltp_buf = &mut es.ltp_buf;
let pitch_est = &mut es.pitch_est;
let mut prev_lsfq = std::mem::take(&mut es.prev_lsfq);
let mut prev_voiced = es.prev_voiced;
let mut internal: [SmplInternalParams; 3] = Default::default();
for f in 0..3 {
let base = SMPL_ORDER + f * SMPL_INTF_LEN;
let win = &x[base - SMPL_ORDER..base + SMPL_INTF_LEN];
let nbase = res_lead + f * SMPL_INTF_LEN;
let win_n = &xn[nbase - res_lead..nbase + SMPL_INTF_LEN];
let lpc_start = SMPL_LPC_HIST_LEN - SMPL_LPC_PRE + f * SMPL_INTF_LEN;
let mut lpcbuf = [0f32; SMPL_LPC_BUF_LEN];
lpcbuf.copy_from_slice(&hp_full[lpc_start..lpc_start + SMPL_LPC_BUF_LEN]);
let windowed = smpl_window_lpc20(&lpcbuf, f < 2);
let lpc_fft = es
.lpc_fft
.get_or_insert_with(super::smpl_lpc::new_lpc_fft_scratch);
let (a, f2) = smpl_lpc_analyze_with_f2(&windowed, lpc_fft);
let nlsf = smpl_a2nlsf_16(&a);
let mut cs = CelpFrameCtx {
celp,
perc,
perc_prev: &mut es.perc_prev,
bitrate,
hp_n: hp,
intf: f,
sp_act_prob: sp_act_prob[f],
coded_as_active_voice,
f2,
voicing_strength: 0.0,
vuv: &mut es.vuv,
hp_pitch_hist: &hp_pitch_hist,
ltp_buf: &mut *ltp_buf,
pitch_est: &mut *pitch_est,
perc_corrs: Vec::new(),
block_lags: [[0.0; 2]; SMPL_SUBFR_COUNT],
};
let fe = FrontEndLsf {
a,
nlsf,
prev_lsfq: &prev_lsfq,
prev_voiced,
intf: f,
};
let (ip, nlsf_out, voiced_out) = smpl_analyze_internal(
synth_t,
&mut shadow,
&mut lstate,
f,
win,
win_n,
&prev_nlsf,
&fe,
&mut cs,
);
prev_nlsf = nlsf_out.clone();
prev_lsfq = nlsf_out;
prev_voiced = voiced_out;
internal[f] = ip;
if f == 2 {
pitch_est.reset_cond();
}
}
es.hist.clear();
es.hist
.extend_from_slice(&x[x.len() - (SMPL_ORDER + SMPL_WINNEXT_WB_LEN)..]);
es.lpc_hist.clear();
es.lpc_hist
.extend_from_slice(&hp[need - SMPL_LPC_HIST_LEN..need]);
hp_pitch_hist.copy_from_slice(&hp[need - SMPL_PITCH_LAG_MAX..need]);
es.hp_pitch_hist = hp_pitch_hist;
es.prev_lsfq = prev_lsfq;
es.prev_voiced = prev_voiced;
super::params::SmplFrameParams {
toc: 0x50,
config: 0,
internal,
}
}
struct FrontEndLsf<'a> {
a: [f32; SMPL_LPC_ORDER + 1],
nlsf: [f32; SMPL_LPC_ORDER],
prev_lsfq: &'a [f32],
prev_voiced: bool,
intf: usize,
}
const SMPL_LPC_ORDER: usize = 16;
impl FrontEndLsf<'_> {
fn quantize(
&self,
synth_t: &SmplSynthTables,
voiced: usize,
prev_nlsf: &[f32],
) -> (i32, [i32; 16], Vec<f32>, [f32; 17]) {
let cond = (self.prev_voiced == (voiced != 0)) && self.intf > 0;
let res = if cond && self.prev_lsfq.len() == SMPL_LPC_ORDER {
lsf_quant_cond(
&self.a,
&self.nlsf,
self.prev_lsfq,
voiced,
0,
SMPL_LSF_RDW_ADJ,
SMPL_LSF_SURV,
)
} else {
lsf_quant(
&self.a,
&self.nlsf,
voiced,
0,
SMPL_LSF_RDW_ADJ,
SMPL_LSF_SURV,
)
};
let grid = res.qi[0];
let mut stage2 = [0i32; 16];
stage2.copy_from_slice(&res.qi[1..=SMPL_LPC_ORDER]);
let committed =
smpl_reconstruct_nlsf(synth_t, voiced, 0, grid as usize, &stage2, prev_nlsf);
let a_vq = smpl_nlsf2a(&committed);
let mut predcoef = [0.0f32; 17];
for (i, &c) in a_vq.iter().enumerate().take(17) {
predcoef[i] = c;
}
predcoef[0] = 1.0;
(grid, stage2, committed, predcoef)
}
}
fn commit_candidate(
synth_t: &SmplSynthTables,
st: &mut SmplFrameSynth,
cand: &Candidate,
prev_nlsf: &[f32],
) -> Vec<f32> {
if cand.silent {
let nlsf = smpl_reconstruct_nlsf(
synth_t,
0,
0,
cand.ip.lsf.grid as usize,
&cand.ip.lsf.stage2,
prev_nlsf,
);
let pulse_vec = vec![0i32; SMPL_INTF_LEN];
synth_internal_frame(
synth_t,
st,
0,
0,
cand.ip.lsf.grid as usize,
&cand.ip.lsf.stage2,
prev_nlsf,
&pulse_vec,
&cand.gain_q,
&cand.pitch,
);
return nlsf;
}
let (_, nlsf) = synth_internal_frame(
synth_t,
st,
cand.stage1 as usize,
0,
cand.grid as usize,
&cand.qsym,
prev_nlsf,
&cand.pulse_vec,
&cand.gain_q,
&cand.pitch,
);
nlsf
}
fn smpl_unvoiced_candidate(
synth_t: &SmplSynthTables,
_st: &SmplFrameSynth,
win: &[f64],
win_n: &[f32],
prev_nlsf: &[f32],
fe: &FrontEndLsf,
cs: &mut CelpFrameCtx,
) -> Candidate {
let frame = &win[SMPL_ORDER..];
let r0 = smpl_autocorr(frame, 0)[0];
if r0 <= 0.0 {
let mut flat = [[0.0f32; 17]; SMPL_SUBFR_COUNT];
for p in &mut flat {
p[0] = 1.0;
}
let perc_corrs = std::mem::take(&mut cs.perc_corrs);
run_celp_subframes(
cs,
&flat,
&[0.0f32; SMPL_INTF_LEN],
&[[0.0; 2]; SMPL_SUBFR_COUNT],
&perc_corrs,
SMPL_PERC_EMPH_UV,
0,
);
cs.perc_corrs = perc_corrs;
return smpl_silent_internal(synth_t);
}
let (bgrid, bsym, brec, _predcoef) = fe.quantize(synth_t, 0, prev_nlsf);
let (predcoefs, res_lpc, interpol_idx) = smpl_lsf_interpol_search(&brec, fe.prev_lsfq, win_n);
let perc_corrs = std::mem::take(&mut cs.perc_corrs);
let celp_out = run_celp_subframes(
cs,
&predcoefs,
&res_lpc,
&[[0.0; 2]; SMPL_SUBFR_COUNT],
&perc_corrs,
SMPL_PERC_EMPH_UV,
0,
);
cs.perc_corrs = perc_corrs;
let mut pulse_vec = vec![0i32; SMPL_INTF_LEN];
let mut fcbg_idx = [0i32; 4];
const MAIN: usize = 1;
for sf in 0..SMPL_SUBFR_COUNT {
let out = &celp_out[sf];
for &v in &out.pulses[MAIN] {
let sign = 1 + 2 * ((v as i32) >> 15);
let pos = (v as i32 * sign) - 1;
if (0..SMPL_SUBFR_LEN as i32).contains(&pos) {
pulse_vec[sf * SMPL_SUBFR_LEN + pos as usize] += sign;
}
}
fcbg_idx[sf] = out.gain_idx[MAIN] as i32;
}
let mut nrgres = [0f32; 4];
for (sf, n) in nrgres.iter_mut().enumerate() {
let res = &res_lpc[sf * SMPL_SUBFR_LEN..(sf + 1) * SMPL_SUBFR_LEN];
let e: f32 = res.iter().map(|&v| v * v).sum();
*n = e / SMPL_SUBFR_LEN as f32;
}
let nq = super::smpl_nrgres::quant_nrg_res_4(&nrgres);
let gm = nq.frame_qi;
let gd = nq.shape_qi;
let gain_q = nq.dbq_q14;
let pp = smpl_build_pulse_params(&pulse_vec);
let mut gains = SmplGainParams {
gain_main: gm,
gain_delta: gd,
nrg_res: [-1; 4],
};
for sf in 0..4 {
gains.nrg_res[sf] = if pp.subfr[sf] > 0 { fcbg_idx[sf] } else { -1 };
}
Candidate {
ip: SmplInternalParams {
lsf: SmplLsfParams {
stage1: 0,
grid: bgrid,
stage2: bsym,
extra: interpol_idx,
},
pulses: pp,
pitch: Default::default(),
gains,
},
stage1: 0,
grid: bgrid,
qsym: bsym,
pulse_vec,
gain_q,
pitch: unvoiced_pitch(),
silent: false,
}
}
fn run_celp_subframes(
cs: &mut CelpFrameCtx,
predcoefs: &[[f32; 17]; SMPL_SUBFR_COUNT],
res_lpc: &[f32],
block_lags: &[[f32; 2]; SMPL_SUBFR_COUNT],
perc_corrs: &[Vec<f32>],
emph: [f32; 2],
voiced: i32,
) -> Vec<super::smpl_celp::CelpSubframeOut> {
let perc_wght = perc_corrs_to_wght(perc_corrs, emph, SMPL_CELP_PERC_RESP_LEN);
let mut outs = Vec::with_capacity(SMPL_SUBFR_COUNT);
let wnrgs: Vec<f32> = (0..SMPL_SUBFR_COUNT)
.map(|sf| {
let res = &res_lpc[sf * SMPL_SUBFR_LEN..(sf + 1) * SMPL_SUBFR_LEN];
let scale = 32768.0f32;
res.iter().map(|&v| (v * scale) * (v * scale)).sum::<f32>()
})
.collect();
let enc = BitrateControllerInputs {
internal_sample_rate: 16000,
payload_size_ms: 60,
fec_bit_rate: 0,
main_bit_rate: SMPL_MAIN_BIT_RATE,
complexity: SMPL_COMPLEXITY,
use_fec_rate_compensation: 0,
use_dtx: 0,
sub_frame_importance_factor: 1.0,
};
for sf in 0..SMPL_SUBFR_COUNT {
let wnrg = wnrgs[sf];
let wnrg_next = if sf + 1 < SMPL_SUBFR_COUNT {
wnrgs[sf + 1]
} else {
wnrgs[sf]
};
let nonflatness = if voiced != 0 { 0.0 } else { 2.0 };
let voicing_strength = cs.voicing_strength;
let (max_pulses, importance) = cs.bitrate.control(
&enc,
0,
cs.coded_as_active_voice as i32,
cs.sp_act_prob,
nonflatness,
voicing_strength,
voiced,
wnrg,
wnrg_next,
0,
320,
80,
);
let mut numsurv = [1i16; SMPL_MAX_PULSES_PER_SF as usize];
let tot_surv =
1000 * (SMPL_FCB_TOT_SURV_20MS_MAX * SMPL_CELP_FCB_SUBFRLEN as i32) / (20 * 16000);
smpl_distribute_fcb_surv(&mut numsurv, max_pulses[1] as i32, tot_surv);
let lags = [block_lags[sf][0], block_lags[sf][1], block_lags[sf][1]];
let res = &res_lpc[sf * SMPL_SUBFR_LEN..(sf + 1) * SMPL_SUBFR_LEN];
let out = cs.celp.encode_subframe(
res,
&predcoefs[sf],
&perc_wght[sf],
&lags,
importance,
max_pulses,
&numsurv,
);
outs.push(out);
}
outs
}
const SMPL_MAX_PULSES_PER_SF: i32 = 40;
fn compute_perc_corrs(cs: &mut CelpFrameCtx) -> [Vec<f32>; SMPL_SUBFR_COUNT] {
let frame_ms = 20i32;
let shorter = 32usize; let mut corrs: [Vec<f32>; SMPL_SUBFR_COUNT] = Default::default();
let mut sf = 1;
while sf < SMPL_SUBFR_COUNT {
let start = cs.intf * SMPL_INTF_LEN + (sf - 1) * SMPL_SUBFR_LEN;
let xlen = 2 * SMPL_SUBFR_LEN + shorter;
let mut xsubfr = vec![0.0f32; xlen];
for i in 0..xlen {
let idx = start + i;
xsubfr[i] = if idx < cs.hp_n.len() {
cs.hp_n[idx]
} else {
0.0
};
}
let is_last = (cs.intf == 2 && sf == SMPL_SUBFR_COUNT - 1) as i32;
let r = smpl_perc_model(cs.perc, &xsubfr, xlen, frame_ms, is_last, SMPL_PERC_R_LEN);
let mut even = vec![0.0f32; SMPL_PERC_R_LEN];
for i in 0..SMPL_PERC_R_LEN {
let prev = cs.perc_prev.get(i).copied().unwrap_or(0.0);
even[i] = 0.5 * (r[i] + prev);
}
corrs[sf - 1] = even;
cs.perc_prev.clear();
cs.perc_prev.extend_from_slice(&r);
corrs[sf] = r;
sf += 2;
}
corrs
}
fn perc_corrs_to_wght(corrs: &[Vec<f32>], emph: [f32; 2], resp_len: usize) -> Vec<Vec<f32>> {
corrs
.iter()
.map(|c| {
smpl_perc_ac2a(
c,
SMPL_PERC_R_LEN,
emph[if SMPL_CELP_LOW_RATE { 1 } else { 0 }],
resp_len,
SMPL_PERC_REG,
)
})
.collect()
}
fn smpl_lsf_interpol_search(
brec: &[f32],
prev_lsfq: &[f32],
win_n: &[f32],
) -> ([[f32; 17]; SMPL_SUBFR_COUNT], Vec<f32>, i32) {
let residual_for = |idx: usize| -> ([[f32; 17]; SMPL_SUBFR_COUNT], Vec<f32>, f32) {
let (predcoefs, _ilsf) =
super::smpl_lpc::smpl_lpc_interpol_idx(brec, prev_lsfq, idx, smpl_nlsf2a);
let mut res = vec![0f32; SMPL_INTF_LEN];
let mut sum_rms = 0.0f32;
for sf in 0..SMPL_SUBFR_COUNT {
let r = smpl_analysis_residual_subfr(&predcoefs[sf], win_n, sf);
let nrg: f32 = r.iter().map(|&v| v * v).sum();
sum_rms += (nrg + 1e-30).sqrt();
res[sf * SMPL_SUBFR_LEN..(sf + 1) * SMPL_SUBFR_LEN].copy_from_slice(&r);
}
(predcoefs, res, sum_rms)
};
let (pc0, res0, rms0) = residual_for(0);
let (pc1, res1, rms1) = residual_for(1);
if rms1 < rms0 * 0.998 {
(pc1, res1, 1)
} else {
(pc0, res0, 0)
}
}
fn smpl_analysis_residual_subfr(
a_syn: &[f32; 17],
win_n: &[f32],
sf: usize,
) -> [f32; SMPL_SUBFR_LEN] {
let mut res = [0f32; SMPL_SUBFR_LEN];
for (n, rn) in res.iter_mut().enumerate() {
let idx = SMPL_ORDER + sf * SMPL_SUBFR_LEN + n;
let mut acc = win_n[idx];
for j in 1..=SMPL_ORDER {
acc += a_syn[j] * win_n[idx - j];
}
*rn = acc;
}
res
}
fn smpl_silent_internal(synth_t: &SmplSynthTables) -> Candidate {
let mut sym = [0i32; 16];
for (k, s) in sym.iter_mut().enumerate() {
*s = (synth_t.valtables[0][0][0][k].len() / 2) as i32;
}
let (gm, gd, _) = smpl_rate_control_gains(0.0);
Candidate {
ip: SmplInternalParams {
lsf: SmplLsfParams {
stage1: 0,
grid: 0,
stage2: sym,
extra: 0,
},
pulses: SmplPulseParams::default(),
pitch: Default::default(),
gains: SmplGainParams {
gain_main: gm,
gain_delta: gd,
nrg_res: [-1; 4],
},
},
stage1: 0,
grid: 0,
qsym: sym,
pulse_vec: vec![0i32; SMPL_INTF_LEN],
gain_q: [0; 4],
pitch: unvoiced_pitch(),
silent: true,
}
}
fn smpl_autocorr(x: &[f64], order: usize) -> Vec<f64> {
let n = x.len();
let mut r = vec![0f64; order + 1];
for (lag, rl) in r.iter_mut().enumerate() {
let mut s = 0f64;
for i in lag..n {
s += x[i] * x[i - lag];
}
*rl = s;
}
r
}
fn smpl_build_pulse_params(pulse: &[i32]) -> SmplPulseParams {
const P3: usize = 4;
let pos_per = SMPL_INTF_LEN / P3; let mut pp = SmplPulseParams::default();
for sf in 0..P3 {
let mut s = 0i32;
for n in sf * pos_per..(sf + 1) * pos_per {
s += pulse[n].abs();
}
pp.subfr[sf] = s;
}
pp.total = pp.subfr.iter().sum();
let mut mag_runs: Vec<i32> = Vec::new();
let mut signs: Vec<i32> = Vec::new();
for sf in 0..P3 {
if pp.subfr[sf] <= 0 {
continue;
}
let base_pos = pos_per * sf;
let mut positions: Vec<(usize, i32)> = Vec::new();
for n in base_pos..base_pos + pos_per {
if pulse[n] != 0 {
positions.push((n, pulse[n]));
}
}
let mut run_pos = base_pos as i32;
let mut first = true;
for &(p, magv) in &positions {
let mag = magv.abs();
let m = if first {
p as i32 - base_pos as i32
} else {
p as i32 - run_pos
};
mag_runs.push(m);
run_pos = p as i32;
if mag > 1 {
mag_runs.resize(mag_runs.len() + (mag - 1) as usize, 0);
}
signs.push(if magv < 0 { -1 } else { 1 });
first = false;
}
}
pp.mag_runs = mag_runs;
let num_pos = signs.len();
let mut sign_syms: Vec<SmplRawSym> = Vec::new();
let mut p = 0;
while p < num_pos {
let nbits = (num_pos - p).min(15);
let mut sym = 0u32;
for q in 0..nbits {
let bit = if signs[p + q] > 0 { 1u32 } else { 0 };
sym |= bit << (nbits - 1 - q) as u32;
}
sign_syms.push(SmplRawSym {
sym,
nbits: nbits as u32,
});
p += nbits;
}
pp.sign_syms = sign_syms;
pp
}
fn smpl_rate_control_gains(target_linear: f64) -> (i32, i32, i32) {
let cc = super::smpl_cc_tables::load_cc_tables();
let cfg_sel = 2i32;
let cb1 = cc.nrg_step(cfg_sel);
let mut best_d = f64::INFINITY;
let (mut bgm, mut bgd, mut bgq) = (0i32, 0i32, 0i32);
for gm in 0..84 {
let base7 = gm * cb1 - 0x154000;
for gd in 0..98 {
let cbv = cc.gain_recon(true, 4 * gd);
let gq = base7 + (cbv << 4);
let d = (smpl_gain_lin(gq) - target_linear).abs();
if d < best_d {
best_d = d;
bgm = gm;
bgd = gd;
bgq = gq;
}
}
}
(bgm, bgd, bgq)
}
const SMPL_PERC_EMPH_PITCH: f32 = -0.82;
const SMPL_PITCH_PERC_RESP_LEN: usize = 17;
const SMPL_PITCH_LAG_MAX: usize = 320;
const SMPL_PITCH_LOOKAHEAD_LEN: usize = 7;
fn build_ltp_buf(cs: &mut CelpFrameCtx, perc_corrs: &[Vec<f32>]) {
let resp_pitch = perc_corrs_to_wght(
perc_corrs,
[SMPL_PERC_EMPH_PITCH, SMPL_PERC_EMPH_PITCH],
SMPL_PITCH_PERC_RESP_LEN,
);
let max_len = super::smpl_pitch_enc::MAX_LTP_BUF_LEN; let look = SMPL_PITCH_LOOKAHEAD_LEN; let framelen = SMPL_INTF_LEN; let keep = max_len - framelen - look;
cs.ltp_buf.copy_within(framelen..framelen + keep, 0);
let frame_start = cs.intf as isize * SMPL_INTF_LEN as isize - SMPL_WINNEXT_WB_LEN as isize;
let hist = SMPL_PITCH_LAG_MAX as isize;
let sample = |rel: isize| -> f32 {
let idx = frame_start + rel;
if idx >= 0 {
let u = idx as usize;
if u < cs.hp_n.len() { cs.hp_n[u] } else { 0.0 }
} else if cs.hp_pitch_hist.len() == hist as usize {
let k = idx + hist;
if k >= 0 {
cs.hp_pitch_hist[k as usize]
} else {
0.0
}
} else {
0.0
}
};
let w_origin = max_len - SMPL_SUBFR_COUNT * SMPL_SUBFR_LEN - look; for i in 0..SMPL_SUBFR_COUNT {
let coef = &resp_pitch[i];
for n in 0..SMPL_SUBFR_LEN {
let pos = (i * SMPL_SUBFR_LEN + n) as isize;
let mut res = sample(pos); for (j, &c) in coef
.iter()
.enumerate()
.take(SMPL_PITCH_PERC_RESP_LEN)
.skip(1)
{
res += c * sample(pos - j as isize);
}
cs.ltp_buf[w_origin + i * SMPL_SUBFR_LEN + n] = res;
}
}
let coef = &resp_pitch[SMPL_SUBFR_COUNT - 1];
for n in 0..look {
let pos = (framelen + n) as isize;
let mut res = sample(pos);
for (j, &c) in coef
.iter()
.enumerate()
.take(SMPL_PITCH_PERC_RESP_LEN)
.skip(1)
{
res += c * sample(pos - j as isize);
}
cs.ltp_buf[max_len - look + n] = res;
}
}
#[allow(clippy::too_many_arguments)]
fn smpl_analyze_internal(
synth_t: &SmplSynthTables,
st: &mut SmplFrameSynth,
lstate: &mut SmplLsfState,
intf: usize,
win: &[f64],
win_n: &[f32],
prev_nlsf: &[f32],
fe: &FrontEndLsf,
cs: &mut CelpFrameCtx,
) -> (SmplInternalParams, Vec<f32>, bool) {
let mem = load_smpl_mem();
cs.perc_corrs = compute_perc_corrs(cs).into();
let perc_corrs = std::mem::take(&mut cs.perc_corrs);
build_ltp_buf(cs, &perc_corrs);
cs.perc_corrs = perc_corrs;
let f2 = cs.f2;
let pr =
super::smpl_pitch_enc::smpl_pitch(cs.pitch_est, cs.ltp_buf, &f2, cs.coded_as_active_voice);
let pitchcorr = pr.pitchcorr;
let avg_lag = pr.avg_lag;
let harm = pr.harm_strength;
let mut lags8 = pr.lags;
let lag_samples = pr.lags[0];
let sp = cs.sp_act_prob;
let vstr = smpl_get_signal_mode(pitchcorr, &lags8, avg_lag, harm, &f2, sp, cs.vuv);
cs.voicing_strength = vstr;
let is_voiced_decision = vstr > 0.0 && cs.coded_as_active_voice;
lstate.prev_lag_samples = if is_voiced_decision { lag_samples } else { 0.0 };
if !is_voiced_decision {
cs.pitch_est.reset_cond();
lags8 = [0.0; 8];
}
let mut voiced_lstate = lstate.clone();
smpl_advance_lsf_state(&mut voiced_lstate, intf, 1);
let voiced = if is_voiced_decision {
smpl_voiced_decision_for_lag(pr.blockseg_idx, &pr.laginds, cs, &mut lags8)
} else {
None
};
let (chosen, chosen_lstate, is_voiced) = match voiced {
Some(vd) => {
let cand = smpl_voiced_candidate(synth_t, win_n, prev_nlsf, fe, cs, &vd);
(cand, Some(voiced_lstate), true)
}
None => (
smpl_unvoiced_candidate(synth_t, st, win, win_n, prev_nlsf, fe, cs),
None,
false,
),
};
let committed_nlsf = commit_candidate(synth_t, st, &chosen, prev_nlsf);
if chosen.stage1 == 1 {
*lstate = chosen_lstate.expect("voiced candidate set its lstate");
let subfr = chosen.ip.pulses.subfr;
smpl_replay_pitch_state(mem, lstate, 4, subfr, &chosen.ip.pitch);
} else {
smpl_advance_lsf_state(lstate, intf, chosen.stage1);
}
(chosen.ip, committed_nlsf, is_voiced)
}
fn smpl_replay_pitch_state(
_mem: &SmplMem,
st: &mut SmplLsfState,
p3: i32,
subfr_counts: [i32; 4],
pp: &SmplPitchParams,
) {
for sf in 0..(p3 as usize).min(4) {
st.prev_gain_idx = pp.gain_idx[sf];
if subfr_counts[sf] > 0 {
st.prev_filt_idx = pp.filt_idx[sf];
}
}
let tab = super::smpl_pitch_enc::load_pitch_tables();
let (nblk, nidx) =
super::smpl_pitch_enc::smpl_lags_predictor_after(tab, pp.blockseg_idx, &pp.laginds);
st.prev_lagblk = nblk;
st.prev_lagidx = nidx;
}
struct VoicedDecision {
pp: SmplPitchParams,
pitch: SmplPitchSynth,
}
fn smpl_voiced_decision_for_lag(
blockseg_idx: usize,
laginds: &[i32; 8],
cs: &mut CelpFrameCtx,
lags8: &mut [f32; 8],
) -> Option<VoicedDecision> {
let mut block_lags8 = [0.0f32; 8];
for b in 0..8 {
block_lags8[b] = (laginds[b] as f32 * 0.5 + 32.0).min(320.0);
}
*lags8 = block_lags8;
for sf in 0..SMPL_SUBFR_COUNT {
cs.block_lags[sf] = [block_lags8[2 * sf], block_lags8[2 * sf + 1]];
}
let mean_lag = block_lags8.iter().sum::<f32>() / 8.0;
let pp = SmplPitchParams {
gain_idx: [5i32; 4],
filt_idx: [0i32; 4],
blockseg_idx,
laginds: *laginds,
};
let pitch = SmplPitchSynth {
voiced: true,
lag_subfr: [mean_lag as f64; 4],
norm_gain: SMPL_VOICED_NORM_GAIN,
};
Some(VoicedDecision { pp, pitch })
}
fn smpl_voiced_candidate(
synth_t: &SmplSynthTables,
win_n: &[f32],
prev_nlsf: &[f32],
fe: &FrontEndLsf,
cs: &mut CelpFrameCtx,
vd: &VoicedDecision,
) -> Candidate {
let gain_q = [0i32; 4];
let (bgrid, bsym, brec, _predcoef) = fe.quantize(synth_t, 1, prev_nlsf);
let (predcoefs, _ilsf) = super::smpl_lpc::smpl_lpc_interpol(&brec, fe.prev_lsfq, smpl_nlsf2a);
let mut res_lpc = vec![0f32; SMPL_INTF_LEN];
for sf in 0..SMPL_SUBFR_COUNT {
let r = smpl_analysis_residual_subfr(&predcoefs[sf], win_n, sf);
res_lpc[sf * SMPL_SUBFR_LEN..(sf + 1) * SMPL_SUBFR_LEN].copy_from_slice(&r);
}
let block_lags = cs.block_lags;
let perc_corrs = std::mem::take(&mut cs.perc_corrs);
let celp_out = run_celp_subframes(
cs,
&predcoefs,
&res_lpc,
&block_lags,
&perc_corrs,
SMPL_PERC_EMPH_V,
1,
);
cs.perc_corrs = perc_corrs;
const MAIN: usize = 1;
let mut pulse_vec = vec![0i32; SMPL_INTF_LEN];
let mut acbg = [0i32; 4];
let mut fcbg = [0i32; 4];
for sf in 0..SMPL_SUBFR_COUNT {
let out = &celp_out[sf];
for &v in &out.pulses[MAIN] {
let sign = 1 + 2 * ((v as i32) >> 15);
let pos = (v as i32 * sign) - 1;
if (0..SMPL_SUBFR_LEN as i32).contains(&pos) {
pulse_vec[sf * SMPL_SUBFR_LEN + pos as usize] += sign;
}
}
acbg[sf] = (out.acb_idx[MAIN] as i32).clamp(0, 15);
fcbg[sf] = (out.gain_idx[MAIN] as i32).max(0);
}
let pp_pulses = smpl_build_pulse_params(&pulse_vec);
let subfr = pp_pulses.subfr;
let mut pp = vd.pp.clone();
pp.gain_idx = acbg;
for sf in 0..4 {
pp.filt_idx[sf] = if subfr[sf] > 0 { fcbg[sf] } else { -1 };
}
Candidate {
ip: SmplInternalParams {
lsf: SmplLsfParams {
stage1: 1,
grid: bgrid,
stage2: bsym,
extra: 0,
},
pulses: pp_pulses,
pitch: pp,
gains: SmplGainParams::default(),
},
stage1: 1,
grid: bgrid,
qsym: bsym,
pulse_vec,
gain_q,
pitch: vd.pitch.clone(),
silent: false,
}
}