use super::layer::LstmLayer;
macro_rules! define_lstm1_process {
(
$fn_name:ident,
$target_meta:meta,
$layer_proc:ident,
) => {
#[$target_meta]
unsafe fn $fn_name(&mut self, input: &[f32], output: &mut [f32]) {
unsafe {
for (i, &val) in input.iter().enumerate() {
self.layer.$layer_proc(&[val]);
let h_f32 = self.layer.get_hidden_state();
output[i] = $crate::math::common::scalar_ref::dot_product_f32_native_kahan(
h_f32,
&self.head_weights_f32,
) + self.head_bias;
}
}
}
};
}
pub struct LstmModel1<const H: usize, const H1_IH: usize, const H_H4: usize> {
pub layer: LstmLayer<1, H, H1_IH, H_H4>,
pub head_weights: [f32; H],
pub head_weights_f32: [f32; H],
pub head_bias: f32,
pub prewarm_on_reset: bool,
pub expected_sample_rate: f64,
}
impl<const H: usize, const H1_IH: usize, const H_H4: usize> LstmModel1<H, H1_IH, H_H4> {
pub fn new() -> Self {
Self {
layer: LstmLayer::new(),
head_weights: [0.0f32; H],
head_weights_f32: [0.0f32; H],
head_bias: 0.0,
prewarm_on_reset: true,
expected_sample_rate: 48000.0,
}
}
define_lstm1_process!(
process_avx2,
target_feature(enable = "avx2,fma,f16c"),
process_sample_avx2,
);
define_lstm1_process!(
process_avx512,
target_feature(enable = "avx512f,avx512vl"),
process_sample_avx512,
);
pub fn process(&mut self, input: &[f32], output: &mut [f32]) {
unsafe {
crate::math::common::dispatch_simd!(
@self,
process_avx512,
process_avx512,
process_avx2,
input,
output
);
}
}
pub fn process_scalar(&mut self, input: &[f32], output: &mut [f32]) {
for i in 0..input.len() {
self.layer.process_sample_scalar(&[input[i]]);
let hidden = self.layer.get_hidden_state();
let dot = crate::math::common::scalar_ref::dot_product_f32_native_kahan(
hidden,
&self.head_weights_f32,
);
output[i] = dot + self.head_bias;
}
}
pub fn reset_states(&mut self) {
self.layer.reset_states();
}
}
impl<const H: usize, const H1_IH: usize, const H_H4: usize> Default for LstmModel1<H, H1_IH, H_H4> {
fn default() -> Self {
Self::new()
}
}