use crate::*;
const fdmdv_os_filter: [f32; 48] = [
-0.0008215855034550382,
-0.0007833023901802921,
0.001075563790768233,
0.001199092367787555,
-0.001765309502928316,
-0.002055372115328064,
0.002986877604154257,
0.003462567920638414,
-0.004856570111126334,
-0.005563143845031497,
0.007533613299748122,
0.008563932468880897,
-0.01126857129039911,
-0.01280782411693687,
0.01651443896361847,
0.01894875110322284,
-0.02421604439474981,
-0.02845107338464062,
0.03672973563400258,
0.04542046150312214,
-0.06189165826716491,
-0.08721876380763803,
0.1496157094199961,
0.4497962274137046,
0.4497962274137046,
0.1496157094199961,
-0.08721876380763803,
-0.0618916582671649,
0.04542046150312216,
0.03672973563400257,
-0.02845107338464062,
-0.02421604439474984,
0.01894875110322284,
0.01651443896361848,
-0.01280782411693687,
-0.0112685712903991,
0.008563932468880899,
0.007533613299748123,
-0.005563143845031501,
-0.004856570111126346,
0.003462567920638419,
0.002986877604154259,
-0.002055372115328063,
-0.001765309502928318,
0.001199092367787557,
0.001075563790768233,
-0.0007833023901802925,
-0.0008215855034550383,
];
const nlp_fir: [f32; 48] = [
-1.0818124e-03,
-1.1008344e-03,
-9.2768838e-04,
-4.2289438e-04,
5.5034190e-04,
2.0029849e-03,
3.7058509e-03,
5.1449415e-03,
5.5924666e-03,
4.3036754e-03,
8.0284511e-04,
-4.8204610e-03,
-1.1705810e-02,
-1.8199275e-02,
-2.2065282e-02,
-2.0920610e-02,
-1.2808831e-02,
3.2204775e-03,
2.6683811e-02,
5.5520624e-02,
8.6305944e-02,
1.1480192e-01,
1.3674206e-01,
1.4867556e-01,
1.4867556e-01,
1.3674206e-01,
1.1480192e-01,
8.6305944e-02,
5.5520624e-02,
2.6683811e-02,
3.2204775e-03,
-1.2808831e-02,
-2.0920610e-02,
-2.2065282e-02,
-1.8199275e-02,
-1.1705810e-02,
-4.8204610e-03,
8.0284511e-04,
4.3036754e-03,
5.5924666e-03,
5.1449415e-03,
3.7058509e-03,
2.0029849e-03,
5.5034190e-04,
-4.2289438e-04,
-9.2768838e-04,
-1.1008344e-03,
-1.0818124e-03,
];
const COEFF: f32 = 0.95;
const PE_FFT_SIZE: i32 = 512;
const DEC: i32 = 5;
const SAMPLE_RATE: i32 = 8000;
const CNLP: f32 = 0.3;
const NLP_NTAP: i32 = 48;
const FDMDV_OS: i32 = 2;
pub const FDMDV_OS_TAPS_16K: i32 = 48;
fn post_process_sub_multiples(
Fw: &[COMP],
_pmin: i32,
pmax: i32,
gmax: f32,
gmax_bin: i32,
prev_f0: &f32,
) -> f32 {
let mut mult = 2;
let min_bin = PE_FFT_SIZE * DEC / pmax;
let mut cmax_bin = gmax_bin;
let prev_f0_bin = (*prev_f0) * ((PE_FFT_SIZE * DEC) as f32) / (SAMPLE_RATE as f32);
while gmax_bin / mult >= min_bin {
let b = gmax_bin / mult;
let mut bmin = (0.8 * (b as f32)) as usize;
let bmax = (1.2 * (b as f32)) as usize;
if bmin < min_bin as usize {
bmin = min_bin as usize;
}
let thresh = if (prev_f0_bin > bmin as f32) && (prev_f0_bin < bmax as f32) {
CNLP * 0.5 * gmax
} else {
CNLP * gmax
};
let mut lmax = 0.0;
let mut lmax_bin = bmin;
for b in bmin..bmax {
if Fw[b].r > lmax {
lmax = Fw[b].r;
lmax_bin = b;
}
}
if lmax > thresh {
if (lmax > Fw[lmax_bin - 1].r) && (lmax > Fw[lmax_bin + 1].r) {
cmax_bin = lmax_bin as i32;
}
}
mult += 1;
}
cmax_bin as f32 * SAMPLE_RATE as f32 / (PE_FFT_SIZE as f32 * DEC as f32)
}
fn fdmdv_16_to_8(out8k: &mut [f32], in16k: &mut [f32], in16koff: usize, n: i32) {
let mut i = 0;
for k in 0..n as usize {
let mut acc = 0.0;
for j in 0..FDMDV_OS_TAPS_16K as usize {
acc += fdmdv_os_filter[j] * in16k[in16koff + i - j];
}
out8k[k] = acc;
i += FDMDV_OS as usize;
}
for i in -FDMDV_OS_TAPS_16K..0 {
in16k[(in16koff as i32 + i) as usize] =
in16k[(in16koff as isize + i as isize + n as isize * FDMDV_OS as isize) as usize];
}
}
pub fn nlp(
nlp: &mut NLP,
Sn: &[f32],
mut n: usize,
pitch: &mut f32,
_Sw: &[COMP],
_W: &[f32],
prev_f0: &mut f32,
) -> f32 {
let mut m = nlp.m;
if nlp.Fs == 8000 {
for i in m - n..m {
nlp.sq[i] = Sn[i] * Sn[i];
}
} else {
for i in 0..n {
nlp.Sn16k[FDMDV_OS_TAPS_16K as usize + i] = Sn[m + i - n];
}
m /= 2;
n /= 2;
let mut Sn8k = vec![0.0; n];
fdmdv_16_to_8(
&mut Sn8k,
&mut nlp.Sn16k,
FDMDV_OS_TAPS_16K as usize,
n as i32,
);
let mut j = 0;
for i in m - n..m {
nlp.sq[i] = Sn8k[j] * Sn8k[j];
j += 1;
}
}
for i in m - n..m {
let mut notch = nlp.sq[i] - nlp.mem_x;
notch += COEFF * nlp.mem_y;
nlp.mem_x = nlp.sq[i];
nlp.mem_y = notch;
nlp.sq[i] = notch + 1.0;
}
for i in m - n..m {
for j in 0..NLP_NTAP as usize - 1 {
nlp.mem_fir[j] = nlp.mem_fir[j + 1];
}
nlp.mem_fir[NLP_NTAP as usize - 1] = nlp.sq[i];
nlp.sq[i] = 0.0;
for j in 0..NLP_NTAP as usize {
nlp.sq[i] += nlp.mem_fir[j] * nlp_fir[j];
}
}
let mut Fw = [COMP::new(); PE_FFT_SIZE as usize];
for i in 0..m / DEC as usize {
Fw[i].r = nlp.sq[i * DEC as usize] * nlp.w[i];
}
crate::codec2_fft_inplace(&nlp.fft_cfg, &mut Fw);
for i in 0..PE_FFT_SIZE as usize {
Fw[i].r = Fw[i].r * Fw[i].r + Fw[i].i * Fw[i].i;
}
let pmin = (SAMPLE_RATE as f32 * P_MIN_S).floor() as i32;
let pmax = (SAMPLE_RATE as f32 * P_MAX_S).floor() as i32;
let mut gmax = 0.0;
let mut gmax_bin = PE_FFT_SIZE * DEC / pmax;
for i in (PE_FFT_SIZE * DEC / pmax) as usize..(PE_FFT_SIZE * DEC / pmin) as usize + 1 {
if Fw[i].r > gmax {
gmax = Fw[i].r;
gmax_bin = i as i32;
}
}
let best_f0 = post_process_sub_multiples(&Fw, pmin, pmax, gmax, gmax_bin, prev_f0);
for i in 0..m - n {
nlp.sq[i] = nlp.sq[i + n];
}
*pitch = nlp.Fs as f32 / best_f0;
*prev_f0 = best_f0;
best_f0
}