pub fn time_to_samples(times: &[f64], sr: u32) -> Vec<usize> {
let sr = sr as f64;
times.iter().map(|&t| (t * sr) as usize).collect()
}
pub fn samples_to_time(samples: &[usize], sr: u32) -> Vec<f64> {
let sr = sr as f64;
samples.iter().map(|&s| s as f64 / sr).collect()
}
pub fn frames_to_samples(frames: &[usize], hop_length: usize, n_fft: Option<usize>) -> Vec<usize> {
let offset = n_fft.map_or(0, |n| n / 2);
frames.iter().map(|&f| f * hop_length + offset).collect()
}
pub fn samples_to_frames(samples: &[usize], hop_length: usize, n_fft: Option<usize>) -> Vec<i64> {
let offset = n_fft.map_or(0, |n| n / 2) as i64;
let hop = hop_length as i64;
samples
.iter()
.map(|&s| {
let adjusted = s as i64 - offset;
adjusted.div_euclid(hop)
})
.collect()
}
pub fn frames_to_time(
frames: &[usize],
sr: u32,
hop_length: usize,
n_fft: Option<usize>,
) -> Vec<f64> {
let sample_indices = frames_to_samples(frames, hop_length, n_fft);
samples_to_time(&sample_indices, sr)
}
pub fn time_to_frames(times: &[f64], sr: u32, hop_length: usize, n_fft: Option<usize>) -> Vec<i64> {
let sample_indices = time_to_samples(times, sr);
samples_to_frames(&sample_indices, hop_length, n_fft)
}
#[cfg(test)]
mod tests {
use super::*;
use serde::Deserialize;
#[derive(Deserialize)]
struct ConvertTestCase {
function: String,
name: String,
#[serde(default)]
times: Option<Vec<f64>>,
#[serde(default)]
samples: Option<Vec<i64>>,
#[serde(default)]
frames: Option<Vec<i64>>,
#[serde(default)]
sr: Option<u32>,
#[serde(default)]
hop_length: Option<usize>,
#[serde(default)]
n_fft: Option<usize>,
expected: serde_json::Value,
}
fn load_references() -> Vec<ConvertTestCase> {
let data = include_str!("../../tests/references/convert.json");
serde_json::from_str(data).expect("Failed to parse convert.json")
}
#[test]
fn test_time_to_samples() {
for case in load_references()
.into_iter()
.filter(|c| c.function == "time_to_samples")
{
let times = case.times.unwrap();
let sr = case.sr.unwrap();
let result = time_to_samples(×, sr);
let expected: Vec<usize> = serde_json::from_value(case.expected).unwrap();
assert_eq!(result, expected, "FAIL: {}", case.name);
}
}
#[test]
fn test_samples_to_time() {
for case in load_references()
.into_iter()
.filter(|c| c.function == "samples_to_time")
{
let samples: Vec<usize> = case
.samples
.unwrap()
.into_iter()
.map(|s| s as usize)
.collect();
let sr = case.sr.unwrap();
let result = samples_to_time(&samples, sr);
let expected: Vec<f64> = serde_json::from_value(case.expected).unwrap();
assert_eq!(result, expected, "FAIL: {}", case.name);
}
}
#[test]
fn test_frames_to_samples() {
for case in load_references()
.into_iter()
.filter(|c| c.function == "frames_to_samples")
{
let frames: Vec<usize> = case
.frames
.unwrap()
.into_iter()
.map(|f| f as usize)
.collect();
let hop = case.hop_length.unwrap();
let n_fft = case.n_fft;
let result = frames_to_samples(&frames, hop, n_fft);
let expected: Vec<usize> = serde_json::from_value(case.expected).unwrap();
assert_eq!(result, expected, "FAIL: {}", case.name);
}
}
#[test]
fn test_samples_to_frames() {
for case in load_references()
.into_iter()
.filter(|c| c.function == "samples_to_frames")
{
let samples: Vec<usize> = case
.samples
.unwrap()
.into_iter()
.map(|s| s as usize)
.collect();
let hop = case.hop_length.unwrap();
let n_fft = case.n_fft;
let result = samples_to_frames(&samples, hop, n_fft);
let expected: Vec<i64> = serde_json::from_value(case.expected).unwrap();
assert_eq!(result, expected, "FAIL: {}", case.name);
}
}
#[test]
fn test_frames_to_time() {
for case in load_references()
.into_iter()
.filter(|c| c.function == "frames_to_time")
{
let frames: Vec<usize> = case
.frames
.unwrap()
.into_iter()
.map(|f| f as usize)
.collect();
let sr = case.sr.unwrap();
let hop = case.hop_length.unwrap();
let n_fft = case.n_fft;
let result = frames_to_time(&frames, sr, hop, n_fft);
let expected: Vec<f64> = serde_json::from_value(case.expected).unwrap();
for (i, (a, e)) in result.iter().zip(expected.iter()).enumerate() {
let diff = (a - e).abs();
assert!(
diff < 1e-12,
"FAIL: {}[{}]: got {}, expected {}, diff {}",
case.name,
i,
a,
e,
diff
);
}
}
}
#[test]
fn test_time_to_frames() {
for case in load_references()
.into_iter()
.filter(|c| c.function == "time_to_frames")
{
let times = case.times.unwrap();
let sr = case.sr.unwrap();
let hop = case.hop_length.unwrap();
let n_fft = case.n_fft;
let result = time_to_frames(×, sr, hop, n_fft);
let expected: Vec<i64> = serde_json::from_value(case.expected).unwrap();
assert_eq!(result, expected, "FAIL: {}", case.name);
}
}
}