dasp_rs/utils/
time.rs

1use std::path::Path;
2use std::io::Cursor;
3
4use crate::{core::AudioData, AudioError};
5use hound::WavReader;
6use ndarray::Array2;
7
8/// Calculates the duration of an audio signal in seconds.
9///
10/// # Arguments
11/// * `audio` - Reference to an `AudioData` struct containing samples and sample rate
12///
13/// # Returns
14/// Returns a `f32` representing the duration in seconds.
15///
16/// # Examples
17/// ```
18/// let audio = AudioData { samples: vec![0.0; 44100], sample_rate: 44100 };
19/// let duration = get_duration(&audio);
20/// assert_eq!(duration, 1.0); // 1 second
21/// ```
22pub fn get_duration(audio: &AudioData) -> f32 {
23    audio.samples.len() as f32 / audio.sample_rate as f32
24}
25
26/// Calculates the duration of an audio file from its path.
27///
28/// # Arguments
29/// * `path` - Path to the audio file, implementing `AsRef<Path>`
30///
31/// # Returns
32/// Returns a `Result<f32, AudioError>` containing the duration in seconds or an error if loading fails.
33///
34/// # Errors
35/// Returns `AudioError` if the audio file cannot be loaded.
36///
37/// # Examples
38/// ```
39/// let duration = get_duration_from_path("test.wav");
40/// // Assuming test.wav is 2 seconds long at 44100 Hz
41/// assert!(duration.is_ok_and(|d| d == 2.0));
42/// ```
43pub fn get_duration_from_path<P: AsRef<std::path::Path>>(path: P) -> Result<f32, crate::core::AudioError> {
44    let audio = crate::core::load(path, None, None, None, None)?;
45    Ok(get_duration(&audio))
46}
47
48/// Converts frame indices to sample indices.
49///
50/// # Arguments
51/// * `frames` - Array of frame indices
52/// * `hop_length` - Optional hop length in samples (defaults to 512)
53/// * `_n_fft` - Optional FFT size (unused, defaults to None)
54///
55/// # Returns
56/// Returns a `Vec<usize>` containing corresponding sample indices.
57///
58/// # Examples
59/// ```
60/// let frames = vec![0, 1, 2];
61/// let samples = frames_to_samples(&frames, None, None);
62/// assert_eq!(samples, vec![0, 512, 1024]);
63/// ```
64pub fn frames_to_samples(frames: &[usize], hop_length: Option<usize>, _n_fft: Option<usize>) -> Vec<usize> {
65    let hop = hop_length.unwrap_or(512);
66    frames.iter().map(|&f| f * hop).collect()
67}
68
69/// Converts frame indices to time values in seconds.
70///
71/// # Arguments
72/// * `frames` - Array of frame indices
73/// * `sr` - Optional sample rate in Hz (defaults to 44100)
74/// * `hop_length` - Optional hop length in samples (defaults to 512)
75///
76/// # Returns
77/// Returns a `Vec<f32>` containing corresponding time values in seconds.
78///
79/// # Examples
80/// ```
81/// let frames = vec![0, 1, 2];
82/// let times = frames_to_time(&frames, None, None);
83/// assert_eq!(times, vec![0.0, 0.011609977, 0.023219954]); // Approx at 44100 Hz, hop 512
84/// ```
85pub fn frames_to_time(frames: &[usize], sr: Option<u32>, hop_length: Option<usize>) -> Vec<f32> {
86    let sample_rate = sr.unwrap_or(44100);
87    let hop = hop_length.unwrap_or(512);
88    frames.iter().map(|&f| f as f32 * hop as f32 / sample_rate as f32).collect()
89}
90
91/// Converts sample indices to frame indices.
92///
93/// # Arguments
94/// * `samples` - Array of sample indices
95/// * `hop_length` - Optional hop length in samples (defaults to 512)
96///
97/// # Returns
98/// Returns a `Vec<usize>` containing corresponding frame indices (integer division).
99///
100/// # Examples
101/// ```
102/// let samples = vec![0, 512, 1024];
103/// let frames = samples_to_frames(&samples, None);
104/// assert_eq!(frames, vec![0, 1, 2]);
105/// ```
106pub fn samples_to_frames(samples: &[usize], hop_length: Option<usize>) -> Vec<usize> {
107    let hop = hop_length.unwrap_or(512);
108    samples.iter().map(|&s| s / hop).collect()
109}
110
111/// Converts sample indices to time values in seconds.
112///
113/// # Arguments
114/// * `samples` - Array of sample indices
115/// * `sr` - Optional sample rate in Hz (defaults to 44100)
116///
117/// # Returns
118/// Returns a `Vec<f32>` containing corresponding time values in seconds.
119///
120/// # Examples
121/// ```
122/// let samples = vec![0, 44100];
123/// let times = samples_to_time(&samples, None);
124/// assert_eq!(times, vec![0.0, 1.0]);
125/// ```
126pub fn samples_to_time(samples: &[usize], sr: Option<u32>) -> Vec<f32> {
127    let sample_rate = sr.unwrap_or(44100);
128    samples.iter().map(|&s| s as f32 / sample_rate as f32).collect()
129}
130
131/// Converts time values in seconds to frame indices.
132///
133/// # Arguments
134/// * `times` - Array of time values in seconds
135/// * `sr` - Optional sample rate in Hz (defaults to 44100)
136/// * `hop_length` - Optional hop length in samples (defaults to 512)
137/// * `_n_fft` - Optional FFT size (unused, defaults to None)
138///
139/// # Returns
140/// Returns a `Vec<usize>` containing corresponding frame indices.
141///
142/// # Examples
143/// ```
144/// let times = vec![0.0, 0.011609977];
145/// let frames = time_to_frames(&times, None, None, None);
146/// assert_eq!(frames, vec![0, 1]);
147/// ```
148pub fn time_to_frames(times: &[f32], sr: Option<u32>, hop_length: Option<usize>, _n_fft: Option<usize>) -> Vec<usize> {
149    let sample_rate = sr.unwrap_or(44100);
150    let hop = hop_length.unwrap_or(512);
151    times.iter().map(|&t| (t * sample_rate as f32 / hop as f32) as usize).collect()
152}
153
154/// Converts time values in seconds to sample indices.
155///
156/// # Arguments
157/// * `times` - Array of time values in seconds
158/// * `sr` - Optional sample rate in Hz (defaults to 44100)
159///
160/// # Returns
161/// Returns a `Vec<usize>` containing corresponding sample indices.
162///
163/// # Examples
164/// ```
165/// let times = vec![0.0, 1.0];
166/// let samples = time_to_samples(&times, None);
167/// assert_eq!(samples, vec![0, 44100]);
168/// ```
169pub fn time_to_samples(times: &[f32], sr: Option<u32>) -> Vec<usize> {
170    let sample_rate = sr.unwrap_or(44100);
171    times.iter().map(|&t| (t * sample_rate as f32) as usize).collect()
172}
173
174/// Converts block indices to frame indices.
175///
176/// # Arguments
177/// * `blocks` - Array of block indices
178/// * `block_length` - Number of frames per block
179///
180/// # Returns
181/// Returns a `Vec<usize>` containing corresponding frame indices.
182///
183/// # Examples
184/// ```
185/// let blocks = vec![0, 1, 2];
186/// let frames = blocks_to_frames(&blocks, 10);
187/// assert_eq!(frames, vec![0, 10, 20]);
188/// ```
189pub fn blocks_to_frames(blocks: &[usize], block_length: usize) -> Vec<usize> {
190    blocks.iter().map(|&b| b * block_length).collect()
191}
192
193/// Converts block indices to sample indices.
194///
195/// # Arguments
196/// * `blocks` - Array of block indices
197/// * `block_length` - Number of frames per block
198/// * `hop_length` - Optional hop length in samples (defaults to 512)
199///
200/// # Returns
201/// Returns a `Vec<usize>` containing corresponding sample indices.
202///
203/// # Examples
204/// ```
205/// let blocks = vec![0, 1];
206/// let samples = blocks_to_samples(&blocks, 2, None);
207/// assert_eq!(samples, vec![0, 1024]); // 2 frames * 512 hop
208/// ```
209pub fn blocks_to_samples(blocks: &[usize], block_length: usize, hop_length: Option<usize>) -> Vec<usize> {
210    let hop = hop_length.unwrap_or(512);
211    blocks.iter().map(|&b| b * block_length * hop).collect()
212}
213
214/// Converts block indices to time values in seconds.
215///
216/// # Arguments
217/// * `blocks` - Array of block indices
218/// * `block_length` - Number of frames per block
219/// * `hop_length` - Optional hop length in samples (defaults to 512)
220/// * `sr` - Optional sample rate in Hz (defaults to 44100)
221///
222/// # Returns
223/// Returns a `Vec<f32>` containing corresponding time values in seconds.
224///
225/// # Examples
226/// ```
227/// let blocks = vec![0, 1];
228/// let times = blocks_to_time(&blocks, 2, None, None);
229/// assert_eq!(times, vec![0.0, 0.023219954]); // 2 frames * 512 hop / 44100 Hz
230/// ```
231pub fn blocks_to_time(blocks: &[usize], block_length: usize, hop_length: Option<usize>, sr: Option<u32>) -> Vec<f32> {
232    let hop = hop_length.unwrap_or(512);
233    let sample_rate = sr.unwrap_or(44100);
234    blocks.iter().map(|&b| b as f32 * block_length as f32 * hop as f32 / sample_rate as f32).collect()
235}
236
237/// Generates sample indices corresponding to the columns of a 2D array.
238///
239/// # Arguments
240/// * `X` - 2D array (typically a spectrogram)
241/// * `hop_length` - Optional hop length in samples (defaults to 512)
242/// * `_n_fft` - Optional FFT size (unused, defaults to None)
243/// * `_axis` - Optional axis (unused, defaults to None)
244///
245/// # Returns
246/// Returns a `Vec<usize>` containing sample indices for each column of `X`.
247///
248/// # Examples
249/// ```
250/// use ndarray::arr2;
251/// let X = arr2(&[[1.0, 2.0], [3.0, 4.0]]);
252/// let samples = samples_like(&X, None, None, None);
253/// assert_eq!(samples, vec![0, 512]);
254/// ```
255pub fn samples_like(x: &Array2<f32>, hop_length: Option<usize>, _n_fft: Option<usize>, _axis: Option<isize>) -> Vec<usize> {
256    let hop = hop_length.unwrap_or(512);
257    (0..x.shape()[1]).map(|i| i * hop).collect()
258}
259
260/// Generates time values corresponding to the columns of a 2D array.
261///
262/// # Arguments
263/// * `X` - 2D array (typically a spectrogram)
264/// * `sr` - Optional sample rate in Hz (defaults to 44100)
265/// * `hop_length` - Optional hop length in samples (defaults to 512)
266/// * `_n_fft` - Optional FFT size (unused, defaults to None)
267/// * `_axis` - Optional axis (unused, defaults to None)
268///
269/// # Returns
270/// Returns a `Vec<f32>` containing time values in seconds for each column of `X`.
271///
272/// # Examples
273/// ```
274/// use ndarray::arr2;
275/// let X = arr2(&[[1.0, 2.0], [3.0, 4.0]]);
276/// let times = times_like(&X, None, None, None, None);
277/// assert_eq!(times, vec![0.0, 0.011609977]); // 512 hop / 44100 Hz
278/// ```
279pub fn times_like(x: &Array2<f32>, sr: Option<u32>, hop_length: Option<usize>, _n_fft: Option<usize>, _axis: Option<isize>) -> Vec<f32> {
280    let sample_rate = sr.unwrap_or(44100);
281    let hop = hop_length.unwrap_or(512);
282    (0..x.shape()[1]).map(|i| i as f32 * hop as f32 / sample_rate as f32).collect()
283}
284
285/// Extracts sample rate from WAV file header.
286///
287/// Lightweight metadata query without full sample loading.
288///
289/// # Parameters
290/// - `path`: WAV file path (`AsRef<Path>`).
291///
292/// # Returns
293/// - `Ok(u32)`: Sample rate in Hz.
294/// - `Err(AudioError)`: I/O or format error.
295/// 
296/// # Example
297/// ```
298/// let rate = get_samplerate("audio.wav")?;
299/// assert_eq!(rate, 44100);
300/// ```
301pub fn get_samplerate<P: AsRef<Path>>(path: P) -> Result<u32, AudioError> {
302    let wav_data = std::fs::read(&path)?;
303    let reader = WavReader::new(Cursor::new(wav_data))?;
304    Ok(reader.spec().sample_rate)
305}