use circular_buffer::CircularBuffer;
use ndarray::{s, stack, Array, Array1, Array2, Array4, Axis, Ix1, Ix2, IxDyn};
use ort::inputs;
use ort::session::builder::SessionBuilder;
use ort::session::{Session, SessionOutputs};
use ort::value::{Tensor, TensorRef};
use rust_embed::Embed;
use crate::CHUNK;
#[derive(Embed)]
#[folder = "models/"]
struct Models;
const FEATURE_BUFFER_SIZE: usize = 41;
const MELS_BUFFER_SIZE: usize = 397;
pub struct AudioFeatures {
mel_session: Session,
emb_session: Session,
pub feature_buffer: Box<CircularBuffer<FEATURE_BUFFER_SIZE, Array<f32, Ix1>>>,
pub mel_spectrogram_buffer: Box<CircularBuffer<397, Array1<f32>>>,
}
impl AudioFeatures {
pub fn new() -> Self {
let mel_session = SessionBuilder::new().unwrap()
.with_intra_threads(1).unwrap()
.with_inter_threads(1).unwrap()
.commit_from_memory(&Models::get("melspectrogram.onnx").unwrap().data)
.unwrap();
let emb_session = SessionBuilder::new().unwrap()
.with_intra_threads(1).unwrap()
.with_inter_threads(1).unwrap()
.commit_from_memory(&Models::get("embedding_model.onnx").unwrap().data).unwrap();
let mut feature_buffer = CircularBuffer::<FEATURE_BUFFER_SIZE, Array<f32, Ix1>>::boxed();
for _ in 0..FEATURE_BUFFER_SIZE {
feature_buffer.push_back(Array1::<f32>::zeros(96));
}
let mut mel_spectrogram_buffer: Box<CircularBuffer<MELS_BUFFER_SIZE, Array1<f32>>> =
CircularBuffer::<MELS_BUFFER_SIZE, Array1<f32>>::boxed();
for _ in 0..MELS_BUFFER_SIZE {
mel_spectrogram_buffer.push_back(Array1::<f32>::zeros(32));
}
AudioFeatures {
mel_session,
emb_session,
feature_buffer,
mel_spectrogram_buffer,
}
}
pub fn get_melspectrogram(
&mut self,
data: &Array2<f32>, ) -> Result<Array<f32, IxDyn>, String> {
let input_tensor = inputs![TensorRef::from_array_view(data).unwrap()];
let outputs = self.mel_session.run(input_tensor).unwrap();
let array1 = outputs["output"].try_extract_array::<f32>().unwrap();
let array2 = array1.squeeze().mapv(|v| (v / 10.0) + 2.0);
Ok(array2)
}
pub(crate) fn get_audio_features(&mut self, data: Vec<f32>) -> Result<Array2<f32>, String> {
if data.len() != CHUNK {
return Err(format!("Invalid raw size {}", data.len()));
}
let data_array = Array2::from_shape_vec((1, CHUNK), data).map_err(|e| e.to_string())?;
let mels_chunk = self.get_melspectrogram(&data_array)?;
let mels = self.mel_convert_update_buffer(mels_chunk)?;
let input = create_input_tensor(&mels);
let input_tensor = inputs! {"input_1"=> Tensor::from_array(input).unwrap()};
let outputs = self.emb_session.run(input_tensor).unwrap();
let d = features_convert_and_update_buffer(&mut self.feature_buffer, outputs);
d
}
fn mel_convert_update_buffer(&mut self, mels_chunk: Array<f32, IxDyn>) -> Result<Array<f32, Ix2>, String> {
for row in mels_chunk.axis_iter(Axis(0)) {
let x: Array<f32, Ix1> = row.into_dimensionality::<Ix1>().unwrap().to_owned();
self.mel_spectrogram_buffer.push_back(x);
}
let arrays_ref: Vec<_> = self.mel_spectrogram_buffer.iter().map(|arr| arr.view()).collect();
let mels = stack(Axis(0), &arrays_ref).map_err(|e| e.to_string())?;
Ok(mels)
}
}
fn features_convert_and_update_buffer(
feature_buffer: &mut Box<CircularBuffer<41, Array<f32, Ix1>>>,
outputs: SessionOutputs,
) -> Result<Array2<f32>, String> {
for value in outputs.values() {
let tt: ndarray::ArrayViewD<'_, f32> = value.try_extract_array::<f32>().unwrap();
let ttt: Array<f32, Ix1> = tt.flatten().into_owned();
feature_buffer.push_back(ttt);
let vectors: Vec<Array<f32, Ix1>> = feature_buffer.to_vec();
let a = ndarray::stack(Axis(0), &vectors.iter().map(|x| x.view()).collect::<Vec<_>>())
.map_err(|e| e.to_string())?;
return Ok(a);
}
Err("No values from model".to_string())
}
fn create_input_tensor(mels: &Array<f32, Ix2>) -> Array4<f32> {
let (mel_x, _) = mels.dim();
let window_size = 76;
let window = mels
.slice(s![mel_x - window_size.., ..])
.into_owned()
.insert_axis(Axis(0))
.insert_axis(Axis(3));
window
}