mod raw;
use {
crate::OperationError,
std::{ffi::CString, slice::from_raw_parts},
};
fn create_string(text: &str) -> Result<raw::std::string, OperationError> {
Ok(unsafe { raw::knf::create_string(CString::new(text)?.into_raw()) })
}
pub type OnlineMfccFeatureExtractor = OnlineFbankFeatureExtractor<raw::knf::MfccOptions>;
pub type OnlineWhisperFbankFeatureExtractor =
OnlineFbankFeatureExtractor<raw::knf::WhisperFeatureOptions>;
pub struct FrameExtractionOptions<'a> {
pub samp_freq: f32,
pub frame_shift_ms: f32,
pub frame_length_ms: f32,
pub dither: f32,
pub preemph_coeff: f32,
pub remove_dc_offset: bool,
pub window_type: &'a str,
pub round_to_power_of_two: bool,
pub blackman_coeff: f32,
pub snip_edges: bool,
}
impl<'a> Default for FrameExtractionOptions<'a> {
fn default() -> Self {
Self {
samp_freq: 16000f32,
frame_shift_ms: 10f32,
frame_length_ms: 25f32,
dither: 0.00003,
preemph_coeff: 0.97,
remove_dc_offset: true,
window_type: "povey",
round_to_power_of_two: true,
blackman_coeff: 0.42,
snip_edges: true,
}
}
}
impl<'a> FrameExtractionOptions<'a> {
fn into_raw(self) -> Result<raw::knf::FrameExtractionOptions, OperationError> {
Ok(raw::knf::FrameExtractionOptions {
blackman_coeff: self.blackman_coeff,
dither: self.dither,
frame_length_ms: self.frame_length_ms,
frame_shift_ms: self.frame_shift_ms,
preemph_coeff: self.preemph_coeff,
remove_dc_offset: self.remove_dc_offset,
round_to_power_of_two: self.round_to_power_of_two,
samp_freq: self.samp_freq,
snip_edges: self.snip_edges,
window_type: create_string(self.window_type)?,
})
}
}
pub struct MelBanksOptions<'a> {
pub num_bins: i32,
pub low_freq: f32,
pub high_freq: f32,
pub vtln_low: f32,
pub vtln_high: f32,
pub debug_mel: bool,
pub htk_mode: bool,
pub is_librosa: bool,
pub norm: &'a str,
pub use_slaney_mel_scale: bool,
pub floor_to_int_bin: bool,
}
impl<'a> Default for MelBanksOptions<'a> {
fn default() -> Self {
Self {
num_bins: 25,
low_freq: 20f32,
high_freq: 0f32,
vtln_low: 100f32,
vtln_high: -500f32,
debug_mel: false,
htk_mode: false,
is_librosa: false,
norm: "slaney",
use_slaney_mel_scale: true,
floor_to_int_bin: false,
}
}
}
impl<'a> MelBanksOptions<'a> {
fn into_raw(self) -> Result<raw::knf::MelBanksOptions, OperationError> {
Ok(raw::knf::MelBanksOptions {
debug_mel: self.debug_mel,
floor_to_int_bin: self.floor_to_int_bin,
high_freq: self.high_freq,
htk_mode: self.htk_mode,
is_librosa: self.is_librosa,
low_freq: self.low_freq,
norm: create_string(self.norm)?,
num_bins: self.num_bins,
use_slaney_mel_scale: self.use_slaney_mel_scale,
vtln_high: self.vtln_high,
vtln_low: self.vtln_low,
})
}
}
pub struct OnlineFbankFeatureExtractorBuilder {
opts: raw::knf::FbankOptions,
}
impl OnlineFbankFeatureExtractorBuilder {
fn new() -> Result<Self, OperationError> {
let frame_opts = FrameExtractionOptions::default();
let mel_opts = MelBanksOptions::default();
let opts = raw::knf::FbankComputer_Options {
frame_opts: frame_opts.into_raw()?,
mel_opts: mel_opts.into_raw()?,
energy_floor: 0f32, htk_compat: false, raw_energy: true, use_energy: false,
use_log_fbank: true,
use_power: true,
};
Ok(Self { opts })
}
pub fn with_frame_opts(
&mut self,
frame_opts: FrameExtractionOptions,
) -> Result<&mut Self, OperationError> {
self.opts.frame_opts = frame_opts.into_raw()?;
Ok(self)
}
pub fn with_mel_opts(
&mut self,
mel_opts: MelBanksOptions,
) -> Result<&mut Self, OperationError> {
self.opts.mel_opts = mel_opts.into_raw()?;
Ok(self)
}
pub fn with_energy_floor(&mut self, energy_floor: f32) -> &mut Self {
self.opts.energy_floor = energy_floor;
self
}
pub fn with_htk_compat(&mut self, htk_compat: bool) -> &mut Self {
self.opts.htk_compat = htk_compat;
self
}
pub fn with_raw_energy(&mut self, raw_energy: bool) -> &mut Self {
self.opts.raw_energy = raw_energy;
self
}
pub fn with_use_energy(&mut self, use_energy: bool) -> &mut Self {
self.opts.use_energy = use_energy;
self
}
pub fn with_use_log_fbank(&mut self, use_log_fbank: bool) -> &mut Self {
self.opts.use_log_fbank = use_log_fbank;
self
}
pub fn with_use_power(&mut self, use_power: bool) -> &mut Self {
self.opts.use_power = use_power;
self
}
pub fn build(&self) -> Result<OnlineFbankFeatureExtractor, OperationError> {
Ok(OnlineFbankFeatureExtractor::from(self.opts))
}
}
pub struct OnlineMfccFeatureExtractorBuilder {
opts: raw::knf::MfccOptions,
}
impl OnlineMfccFeatureExtractorBuilder {
fn new() -> Result<Self, OperationError> {
let frame_opts = FrameExtractionOptions::default();
let mel_opts = MelBanksOptions::default();
let opts = raw::knf::MfccOptions {
frame_opts: frame_opts.into_raw()?,
mel_opts: mel_opts.into_raw()?,
cepstral_lifter: 22.,
energy_floor: 0.,
htk_compat: false,
num_ceps: 13,
raw_energy: true,
use_energy: true,
};
Ok(Self { opts })
}
pub fn with_frame_opts(
&mut self,
frame_opts: FrameExtractionOptions,
) -> Result<&mut Self, OperationError> {
self.opts.frame_opts = frame_opts.into_raw()?;
Ok(self)
}
pub fn with_mel_opts(
&mut self,
mel_opts: MelBanksOptions,
) -> Result<&mut Self, OperationError> {
self.opts.mel_opts = mel_opts.into_raw()?;
Ok(self)
}
pub fn with_cepstral_lifter(&mut self, cepstral_lifter: f32) -> &mut Self {
self.opts.cepstral_lifter = cepstral_lifter;
self
}
pub fn with_energy_floor(&mut self, energy_floor: f32) -> &mut Self {
self.opts.energy_floor = energy_floor;
self
}
pub fn with_htk_compat(&mut self, htk_compat: bool) -> &mut Self {
self.opts.htk_compat = htk_compat;
self
}
pub fn with_num_ceps(&mut self, num_ceps: i32) -> &mut Self {
self.opts.num_ceps = num_ceps;
self
}
pub fn with_raw_energy(&mut self, raw_energy: bool) -> &mut Self {
self.opts.raw_energy = raw_energy;
self
}
pub fn with_use_energy(&mut self, use_energy: bool) -> &mut Self {
self.opts.use_energy = use_energy;
self
}
pub fn build(&self) -> Result<OnlineMfccFeatureExtractor, OperationError> {
Ok(OnlineFbankFeatureExtractor::from(self.opts))
}
}
pub struct OnlineWhisperFbankFeatureExtractorBuilder {
opts: raw::knf::WhisperFeatureOptions,
}
impl OnlineWhisperFbankFeatureExtractorBuilder {
fn new() -> Result<Self, OperationError> {
let frame_opts = FrameExtractionOptions::default();
let opts = raw::knf::WhisperFeatureOptions {
frame_opts: frame_opts.into_raw()?,
dim: 80,
};
Ok(Self { opts })
}
pub fn with_frame_opts(
&mut self,
frame_opts: FrameExtractionOptions,
) -> Result<&mut Self, OperationError> {
self.opts.frame_opts = frame_opts.into_raw()?;
Ok(self)
}
pub fn with_dim(&mut self, dim: i32) -> &mut Self {
self.opts.dim = dim;
self
}
pub fn build(&self) -> Result<OnlineWhisperFbankFeatureExtractor, OperationError> {
Ok(OnlineFbankFeatureExtractor::from(self.opts))
}
}
pub trait FbankOptions {
fn run<const SR: usize>(&self, input: &[f32], len: &mut i32) -> *mut f32;
}
impl FbankOptions for raw::knf::FbankOptions {
fn run<const SR: usize>(&self, input: &[f32], len: &mut i32) -> *mut f32 {
unsafe {
raw::knf::fbank_extract(
self as *const _,
SR as _,
input.as_ptr() as _,
input.len() as _,
len,
)
}
}
}
impl FbankOptions for raw::knf::MfccOptions {
fn run<const SR: usize>(&self, input: &[f32], len: &mut i32) -> *mut f32 {
unsafe {
raw::knf::mfcc_extract(
self as *const _,
SR as _,
input.as_ptr() as _,
input.len() as _,
len,
)
}
}
}
impl FbankOptions for raw::knf::WhisperFeatureOptions {
fn run<const SR: usize>(&self, input: &[f32], len: &mut i32) -> *mut f32 {
unsafe {
raw::knf::whisper_fbank_extract(
self as *const _,
SR as _,
input.as_ptr() as _,
input.len() as _,
len,
)
}
}
}
pub struct OnlineFbankFeatureExtractor<O = raw::knf::FbankOptions> {
opts: O,
}
impl<O> From<O> for OnlineFbankFeatureExtractor<O>
where
O: FbankOptions,
{
fn from(value: O) -> Self {
Self { opts: value }
}
}
impl OnlineFbankFeatureExtractor {
pub fn fbank() -> Result<OnlineFbankFeatureExtractorBuilder, OperationError> {
OnlineFbankFeatureExtractorBuilder::new()
}
pub fn mfcc() -> Result<OnlineMfccFeatureExtractorBuilder, OperationError> {
OnlineMfccFeatureExtractorBuilder::new()
}
pub fn whisper_fbank() -> Result<OnlineWhisperFbankFeatureExtractorBuilder, OperationError> {
OnlineWhisperFbankFeatureExtractorBuilder::new()
}
pub fn new() -> Result<OnlineFbankFeatureExtractor, OperationError> {
Self::fbank()?.build()
}
}
impl<O> OnlineFbankFeatureExtractor<O> {
pub fn extract<const SR: usize>(&self, audio: &[f32]) -> Vec<f32>
where
O: FbankOptions,
{
let mut ret = 0;
let ptr = self.opts.run::<SR>(audio, &mut ret);
unsafe {
let res = from_raw_parts(ptr, ret as _).to_owned();
raw::knf::free_result(ptr);
res
}
}
}