#[cfg(not(feature = "std"))]
use crate::compat::Math;
use crate::dnn::Activation;
use crate::dnn::arch::DnnArch;
use crate::dnn::freq::fmaf;
use crate::dnn::models::pitchdnn::{
DENSE_DOWNSAMPLER_OUT_SIZE, DENSE_FINAL_UPSAMPLER_OUT_SIZE, DENSE_IF_UPSAMPLER_1_OUT_SIZE,
DENSE_IF_UPSAMPLER_2_OUT_SIZE, GRU_1_STATE_SIZE, PitchDnn,
};
use crate::dnn::nnet::{compute_conv2d, compute_generic_dense, compute_generic_gru};
use super::{NB_XCORR_FEATURES, PITCH_IF_FEATURES};
const PADDED: usize = NB_XCORR_FEATURES + 2;
const CONV1_CHANNELS: usize = 4;
const PITCH_BINS: usize = 180;
#[derive(Clone)]
pub struct PitchDnnState {
gru_state: [f32; GRU_1_STATE_SIZE],
xcorr_mem1: [f32; PADDED * 2],
xcorr_mem2: [f32; PADDED * 2 * CONV1_CHANNELS],
}
impl Default for PitchDnnState {
fn default() -> Self {
Self::new()
}
}
impl PitchDnnState {
pub const fn new() -> Self {
PitchDnnState {
gru_state: [0.0; GRU_1_STATE_SIZE],
xcorr_mem1: [0.0; PADDED * 2],
xcorr_mem2: [0.0; PADDED * 2 * CONV1_CHANNELS],
}
}
pub fn reset(&mut self) {
self.gru_state.fill(0.0);
self.xcorr_mem1.fill(0.0);
self.xcorr_mem2.fill(0.0);
}
pub fn gru_state(&self) -> &[f32; GRU_1_STATE_SIZE] {
&self.gru_state
}
pub fn xcorr_mem1(&self) -> &[f32] {
&self.xcorr_mem1
}
pub fn xcorr_mem2(&self) -> &[f32] {
&self.xcorr_mem2
}
}
pub fn compute_pitchdnn(
st: &mut PitchDnnState,
if_features: &[f32; PITCH_IF_FEATURES],
xcorr_features: &[f32; NB_XCORR_FEATURES],
model: &PitchDnn,
arch: DnnArch,
) -> f32 {
let mut if1_out = [0.0f32; DENSE_IF_UPSAMPLER_1_OUT_SIZE];
let mut downsampler_in = [0.0f32; NB_XCORR_FEATURES + DENSE_IF_UPSAMPLER_2_OUT_SIZE];
let mut downsampler_out = [0.0f32; DENSE_DOWNSAMPLER_OUT_SIZE];
let mut conv1_tmp1 = [0.0f32; PADDED];
let mut conv1_tmp2 = [0.0f32; PADDED * CONV1_CHANNELS];
let mut output = [0.0f32; DENSE_FINAL_UPSAMPLER_OUT_SIZE];
compute_generic_dense(
&model.dense_if_upsampler_1,
&mut if1_out,
if_features,
Activation::Tanh,
arch,
);
compute_generic_dense(
&model.dense_if_upsampler_2,
&mut downsampler_in[NB_XCORR_FEATURES..],
&if1_out,
Activation::Tanh,
arch,
);
conv1_tmp1[1..=NB_XCORR_FEATURES].copy_from_slice(xcorr_features);
compute_conv2d(
&model.conv2d_1,
&mut conv1_tmp2[1..],
&mut st.xcorr_mem1,
&conv1_tmp1,
NB_XCORR_FEATURES,
PADDED,
Activation::Tanh,
arch,
);
compute_conv2d(
&model.conv2d_2,
&mut downsampler_in[..NB_XCORR_FEATURES],
&mut st.xcorr_mem2,
&conv1_tmp2,
NB_XCORR_FEATURES,
NB_XCORR_FEATURES,
Activation::Tanh,
arch,
);
compute_generic_dense(
&model.dense_downsampler,
&mut downsampler_out,
&downsampler_in,
Activation::Tanh,
arch,
);
compute_generic_gru(
&model.gru_1_input,
&model.gru_1_recurrent,
&mut st.gru_state,
&downsampler_out,
arch,
);
compute_generic_dense(
&model.dense_final_upsampler,
&mut output,
&st.gru_state,
Activation::Linear,
arch,
);
let mut pos = 0usize;
let mut maxval = -1.0f32;
for (i, &v) in output[..PITCH_BINS].iter().enumerate() {
if v > maxval {
pos = i;
maxval = v;
}
}
let mut sum = 0.0f32;
let mut count = 0.0f32;
for i in pos.saturating_sub(2)..=(pos + 2).min(PITCH_BINS - 1) {
let p = (output[i] as f64).exp() as f32;
sum = fmaf(p, i as f32, sum);
count += p;
}
(((1.0f32 / 60.0) * (sum / count)) as f64 - 1.5) as f32
}