use std::num::NonZeroUsize;
use non_empty_slice::NonEmptySlice;
use super::{Band, BandLayout};
#[inline]
#[must_use]
pub fn hz_to_bark(hz: f32) -> f32 {
if hz <= 0.0 {
return 0.0;
}
(26.81_f32 * hz / (1960.0_f32 + hz) - 0.53_f32).max(0.0)
}
#[inline]
#[must_use]
pub fn bark_to_hz(bark: f32) -> f32 {
let bark = bark.max(0.0);
(1960.0_f32 * (bark + 0.53_f32) / (26.28_f32 - bark)).max(0.0)
}
#[inline]
#[must_use]
pub fn hz_to_mel(hz: f32) -> f32 {
if hz <= 0.0 {
return 0.0;
}
2595.0_f32 * (1.0_f32 + hz / 700.0_f32).log10()
}
#[inline]
#[must_use]
pub fn mel_to_hz(mel: f32) -> f32 {
if mel <= 0.0 {
return 0.0;
}
(700.0_f32 * (10.0_f32.powf(mel / 2595.0_f32) - 1.0_f32)).max(0.0)
}
#[inline]
fn hz_to_bin(hz: f32, sample_rate_hz: f32, n_bins: usize) -> usize {
let nyquist = sample_rate_hz / 2.0;
let bin = (hz / nyquist * (n_bins - 1) as f32).round() as isize;
bin.clamp(0, (n_bins - 1) as isize) as usize
}
#[inline]
#[must_use]
pub fn hz_to_erb(hz: f32) -> f32 {
24.7_f32 * (4.37_f32 * hz.max(0.0) / 1000.0_f32 + 1.0_f32)
}
#[inline]
#[must_use]
pub fn erb_to_hz(erb: f32) -> f32 {
(1000.0_f32 * (erb / 24.7_f32 - 1.0_f32) / 4.37_f32).max(0.0)
}
#[must_use]
pub fn scale_band_layout(layout: &BandLayout, from_n_bins: NonZeroUsize, to_n_bins: NonZeroUsize) -> BandLayout {
let from = from_n_bins.get();
let to = to_n_bins.get();
let bands: Vec<super::Band> = layout.as_slice().iter().map(|band| {
let start = band.start_bin * to / from;
let end = (band.end_bin * to / from).max(start + 1).min(to);
unsafe { super::Band::new(start, end, band.centre_frequency, band.perceptual_position) }
}).collect();
let ne = unsafe { NonEmptySlice::new_unchecked(&bands) };
BandLayout::new(ne)
}
impl BandLayout {
#[must_use]
pub fn bark(n_bands: NonZeroUsize, sample_rate_hz: f32, n_bins: NonZeroUsize) -> Self {
let n = n_bands.get();
let bins = n_bins.get();
let nyquist = sample_rate_hz / 2.0;
let max_bark = hz_to_bark(nyquist);
let bands: Vec<Band> = (0..n)
.map(|i| {
let bark_start = i as f32 * max_bark / n as f32;
let bark_end = (i + 1) as f32 * max_bark / n as f32;
let bark_centre = (bark_start + bark_end) / 2.0;
let hz_start = bark_to_hz(bark_start);
let hz_end = bark_to_hz(bark_end);
let hz_centre = bark_to_hz(bark_centre);
let start_bin = hz_to_bin(hz_start, sample_rate_hz, bins);
let end_bin_raw = hz_to_bin(hz_end, sample_rate_hz, bins);
let end_bin = end_bin_raw.max(start_bin + 1).min(bins);
let perceptual_position = bark_centre / max_bark;
unsafe { Band::new(start_bin, end_bin, hz_centre, perceptual_position) }
})
.collect();
let bands_non_empty = unsafe { NonEmptySlice::new_unchecked(&bands) };
Self::new(bands_non_empty)
}
#[must_use]
pub fn mel(n_bands: NonZeroUsize, sample_rate_hz: f32, n_bins: NonZeroUsize) -> Self {
let n = n_bands.get();
let bins = n_bins.get();
let nyquist = sample_rate_hz / 2.0;
let max_mel = hz_to_mel(nyquist);
let bands: Vec<Band> = (0..n)
.map(|i| {
let mel_start = i as f32 * max_mel / n as f32;
let mel_end = (i + 1) as f32 * max_mel / n as f32;
let mel_centre = (mel_start + mel_end) / 2.0;
let hz_start = mel_to_hz(mel_start);
let hz_end = mel_to_hz(mel_end);
let hz_centre = mel_to_hz(mel_centre);
let start_bin = hz_to_bin(hz_start, sample_rate_hz, bins);
let end_bin_raw = hz_to_bin(hz_end, sample_rate_hz, bins);
let end_bin = end_bin_raw.max(start_bin + 1).min(bins);
let perceptual_position = mel_centre / max_mel;
unsafe { Band::new(start_bin, end_bin, hz_centre, perceptual_position) }
})
.collect();
let bands_non_empty = unsafe { NonEmptySlice::new_unchecked(&bands) };
Self::new(bands_non_empty)
}
#[must_use]
pub fn erb(n_bands: NonZeroUsize, sample_rate_hz: f32, n_bins: NonZeroUsize) -> Self {
let n = n_bands.get();
let bins = n_bins.get();
let nyquist = sample_rate_hz / 2.0;
let erb_min = hz_to_erb(0.0);
let erb_max = hz_to_erb(nyquist);
let erb_range = erb_max - erb_min;
let bands: Vec<Band> = (0..n)
.map(|i| {
let erb_start = erb_min + i as f32 * erb_range / n as f32;
let erb_end = erb_min + (i + 1) as f32 * erb_range / n as f32;
let erb_centre = (erb_start + erb_end) / 2.0;
let hz_start = erb_to_hz(erb_start);
let hz_end = erb_to_hz(erb_end);
let hz_centre = erb_to_hz(erb_centre);
let start_bin = hz_to_bin(hz_start, sample_rate_hz, bins);
let end_bin_raw = hz_to_bin(hz_end, sample_rate_hz, bins);
let end_bin = end_bin_raw.max(start_bin + 1).min(bins);
let perceptual_position = (erb_centre - erb_min) / erb_range;
unsafe { Band::new(start_bin, end_bin, hz_centre, perceptual_position) }
})
.collect();
let bands_non_empty = unsafe { NonEmptySlice::new_unchecked(&bands) };
Self::new(bands_non_empty)
}
}