audio.rs (1603B)
1 use anyhow::{Context, Result}; 2 use async_walkdir::DirEntry; 3 use fon::{chan::Ch32, Audio}; 4 use itertools::Itertools; 5 use knf_rs::compute_fbank; 6 use rodio::{Decoder, Source}; 7 use std::io::Cursor; 8 9 pub const SAMPLE_RATE: u32 = 16000; 10 pub const NUM_FRAMES: usize = 1024; 11 pub const NUM_MEL_BINS: usize = 128; 12 13 pub async fn is_audio_file(entry: DirEntry) -> bool { 14 if let Some(mime) = mime_guess::from_path(entry.path()).first() { 15 mime.type_() == "audio" 16 } else { 17 false 18 } 19 } 20 21 fn extract_samples(decoder: Decoder<Cursor<Vec<u8>>>) -> Result<(Box<[f32]>, u32)> { 22 let channels = decoder.channels() as usize; 23 let sample_rate = decoder.sample_rate(); 24 let samples = decoder 25 .map(|sample| sample as f32 / i16::MAX as f32) 26 .chunks(channels) 27 .into_iter() 28 .map(|chunk| chunk.into_iter().sum::<f32>() / channels as f32) 29 .collect_vec() 30 .into_boxed_slice(); 31 Ok((samples, sample_rate)) 32 } 33 34 pub fn extract_audio(buffer: Vec<u8>) -> Result<Audio<Ch32, 1>> { 35 let decoder = Decoder::new(Cursor::new(buffer)).context("Failed to decode audio")?; 36 let (samples, sample_rate) = extract_samples(decoder)?; 37 Ok(Audio::with_f32_buffer(sample_rate, samples)) 38 } 39 40 pub fn resample(audio: &mut Audio<Ch32, 1>, target_sample_rate: u32) { 41 *audio = Audio::with_audio(target_sample_rate, audio); 42 } 43 44 pub fn create_fbank(audio: &mut Audio<Ch32, 1>) -> Result<Box<[[f32; NUM_MEL_BINS]]>> { 45 let samples = audio.as_f32_slice(); 46 let mut fbank = compute_fbank(samples)?; 47 fbank.truncate(NUM_FRAMES); 48 Ok(fbank.into_boxed_slice()) 49 }