diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e23425..2ba6e06 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,12 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -# [0.3.0] - 2024-08-XX +# [0.3.0] - 2024-09-19 ### Added -Forward and inverse STFT. +- Added forward STFT and real STFT. ### Fixed -`HArray`'s `is_shared` is now `is_unique` with a new implementation. +- `HArray`'s `is_shared` is now `is_unique` with a new implementation. # [0.2.0] - 2024-07-14 ### Added diff --git a/Cargo.toml b/Cargo.toml index 23d854d..3e0394f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,11 +6,9 @@ members = [ "harmonium-fft", "harmonium-resample", "harmonium-window", - "harmonium-stft", ] [workspace.dependencies] -num-traits = { version = "0.2", default-features = false } rustfft = { version = "6.2", default-features = false, features = ["avx", "sse", "neon"] } realfft = { version = "3.3", default-features = false } rubato = { version = "0.15.0", default-features = false, features = ["fft_resampler"] } @@ -18,13 +16,13 @@ ndarray = { version = "0.16", default-features = false } symphonia = { version = "0.5.4", default-features = false } rodio = { version = "0.19.0", default-features = false } num-complex = { version = "0.4", default-features = false } +num-traits = { version = "0.2", default-features = false } harmonium-core = { path = "harmonium-core", default-features = false } harmonium-io = { path = "harmonium-io", default-features = false } harmonium-fft = { path = "harmonium-fft", default-features = false } harmonium-resample = { path = "harmonium-resample", default-features = false } harmonium-window = { path = "harmonium-window", default-features = false } -harmonium-stft = { path = "harmonium-stft", default-features = false } [profile.release] opt-level = 3 diff --git a/README.md b/README.md index 4e3b47d..078c1f2 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Harmonium is an audio interface inspired by Python's [librosa](https://github.co - Decode audio - Retrieve audio metadata - Asynchronous and Synchronous resampling -- FFT +- FFT and STFT To learn more, read the [documentation](https://daniellga.github.io/harmonium/). diff --git a/harmonium-fft/src/lib.rs b/harmonium-fft/src/lib.rs index 14b7cbe..2361f84 100644 --- a/harmonium-fft/src/lib.rs +++ b/harmonium-fft/src/lib.rs @@ -1 +1,2 @@ pub mod fft; +pub mod stft; diff --git a/harmonium-stft/src/stft.rs b/harmonium-fft/src/stft.rs similarity index 72% rename from harmonium-stft/src/stft.rs rename to harmonium-fft/src/stft.rs index 8796f7e..29f002f 100644 --- a/harmonium-stft/src/stft.rs +++ b/harmonium-fft/src/stft.rs @@ -1,8 +1,8 @@ +use crate::fft::{make_mut_slice, Fft, ProcessFft, RealFftForward}; use harmonium_core::{ array::HArray, errors::{HError, HResult}, }; -use harmonium_fft::fft::{make_mut_slice, Fft, ProcessFft, RealFftForward}; use ndarray::{s, ArcArray, ArcArray2, Axis, Dimension, Ix1, Ix2, Ix3, IxDyn}; use rustfft::{ num_complex::{Complex, ComplexFloat}, @@ -11,8 +11,10 @@ use rustfft::{ }; use std::{num::NonZero, sync::Arc}; +#[derive(Clone)] pub struct Stft(Fft); +#[derive(Clone)] pub struct RealStftForward { inner: RealFftForward, scratch_real_buffer: Arc<[T]>, @@ -24,7 +26,7 @@ impl Stft where T: FftNum + Float + FloatConst + ConstZero, { - pub fn new_stft_forward(length: usize) -> Self { + pub fn new_forward(length: usize) -> Self { Stft(Fft::new_forward(length)) } @@ -35,12 +37,12 @@ where #[allow(clippy::len_without_is_empty)] // A `RealStftForward` is used to process real stft. It caches results internally, so when making more than one stft it is advisable to reuse the same -// `RealdStftForward` instance. +// `RealStftForward` instance. impl RealStftForward where T: FftNum + Float + FloatConst + ConstZero, { - pub fn new_real_stft_forward(length: usize) -> Self { + pub fn new(length: usize) -> Self { let scratch_real_buffer = vec![T::ZERO; length]; let scratch_real_buffer: Arc<[T]> = Arc::from(scratch_real_buffer); @@ -55,9 +57,8 @@ where } } -pub trait ProcessStft +pub trait ProcessStft where - T: FftNum + Float + FloatConst, D: Dimension, { type U: ComplexFloat; @@ -72,48 +73,38 @@ where /// `harray` - A 1D or 2D HArray to be used as input. /// `hop_length` - The distance between neighboring sliding window frames. /// `window_length` - Size of window frame. Must be greater than the fft length. - /// `window` - An optional window function. `window.len()` must be equal to `window_length`. + /// `window` - An optional window. `window.len()` must be equal to `window_length`. fn process( &mut self, harray: &HArray, hop_length: NonZero, window_length: NonZero, - window: Option<&[T]>, + window: Option<&[::Real]>, ) -> HResult; } -impl ProcessStft for Stft +impl ProcessStft for Stft where T: FftNum + Float + FloatConst + ConstZero, { type U = Complex; - type Output = HArray, Ix2>; + type Output = HArray; fn process( &mut self, - harray: &HArray, Ix1>, + harray: &HArray, hop_length: NonZero, window_length: NonZero, window: Option<&[T]>, - ) -> HResult, Ix2>> { + ) -> HResult { // Since fft_length is checked to be >= window_length and window_length is NonZero, we can be sure fft_length > 0. let fft_length = self.len(); let window_length = window_length.get(); let hop_length = hop_length.get(); let length = harray.len(); - if fft_length < window_length || fft_length > length { - return Err(HError::OutOfSpecError( - "Expected harray.len() >= fft_length >= window_length.".to_string(), - )); - } - if let Some(slice) = window { - if slice.len() != window_length { - return Err(HError::OutOfSpecError( - "Expected window.len() == window_length.".to_string(), - )); - } - } + validate_fft_length(fft_length, window_length, length)?; + validate_window_length(window, window_length)?; let n_fft = 1 + (length - fft_length) / hop_length; let mut stft_ndarray = ArcArray2::>::zeros((n_fft, fft_length)); @@ -143,20 +134,20 @@ where } } -impl ProcessStft for Stft +impl ProcessStft for Stft where T: FftNum + Float + FloatConst + ConstZero, { type U = Complex; - type Output = HArray, Ix3>; + type Output = HArray; fn process( &mut self, - harray: &HArray, Ix2>, + harray: &HArray, hop_length: NonZero, window_length: NonZero, window: Option<&[T]>, - ) -> HResult, Ix3>> { + ) -> HResult { // Since fft_length is checked to be >= window_length and window_length is NonZero, we can be sure fft_length > 0. let fft_length = self.len(); let window_length = window_length.get(); @@ -164,18 +155,8 @@ where let nrows = harray.0.len_of(Axis(0)); let ncols = harray.0.len_of(Axis(1)); - if fft_length < window_length || fft_length > ncols { - return Err(HError::OutOfSpecError( - "Expected ncols >= fft_length >= window_length.".to_string(), - )); - } - if let Some(slice) = window { - if slice.len() != window_length { - return Err(HError::OutOfSpecError( - "Expected window.len() == window_length.".to_string(), - )); - } - } + validate_fft_length(fft_length, window_length, ncols)?; + validate_window_length(window, window_length)?; let n_fft = 1 + (ncols - fft_length) / hop_length; let mut stft_ndarray = ArcArray::, Ix3>::zeros((nrows, n_fft, fft_length)); @@ -209,20 +190,20 @@ where } } -impl ProcessStft for Stft +impl ProcessStft for Stft where T: FftNum + Float + FloatConst + ConstZero, { type U = Complex; - type Output = HArray, IxDyn>; + type Output = HArray; fn process( &mut self, - harray: &HArray, IxDyn>, + harray: &HArray, hop_length: NonZero, window_length: NonZero, window: Option<&[T]>, - ) -> HResult, IxDyn>> { + ) -> HResult { // Since fft_length is checked to be >= window_length and window_length is NonZero, we can be sure fft_length > 0. let fft_length = self.len(); let window_length = window_length.get(); @@ -236,18 +217,8 @@ where 1 => { let length = harray.len(); - if fft_length < window_length || fft_length > length { - return Err(HError::OutOfSpecError( - "Expected harray.len() >= fft_length >= window_length.".to_string(), - )); - } - if let Some(slice) = window { - if slice.len() != window_length { - return Err(HError::OutOfSpecError( - "Expected window.len() == window_length.".to_string(), - )); - } - } + validate_fft_length(fft_length, window_length, length)?; + validate_window_length(window, window_length)?; let n_fft = 1 + (length - fft_length) / hop_length; let mut stft_ndarray = ArcArray2::>::zeros((n_fft, fft_length)); @@ -282,18 +253,8 @@ where let nrows = harray.0.len_of(Axis(0)); let ncols = harray.0.len_of(Axis(1)); - if fft_length < window_length || fft_length > ncols { - return Err(HError::OutOfSpecError( - "Expected ncols >= fft_length >= window_length.".to_string(), - )); - } - if let Some(slice) = window { - if slice.len() != window_length { - return Err(HError::OutOfSpecError( - "Expected window.len() == window_length.".to_string(), - )); - } - } + validate_fft_length(fft_length, window_length, ncols)?; + validate_window_length(window, window_length)?; let n_fft = 1 + (ncols - fft_length) / hop_length; let mut stft_ndarray = @@ -336,20 +297,20 @@ where } } -impl ProcessStft for RealStftForward +impl ProcessStft for RealStftForward where T: FftNum + Float + FloatConst + ConstZero, { type U = T; - type Output = HArray, Ix2>; + type Output = HArray, Ix2>; fn process( &mut self, - harray: &HArray, + harray: &HArray, hop_length: NonZero, window_length: NonZero, window: Option<&[T]>, - ) -> HResult, Ix2>> { + ) -> HResult { // Since fft_length is checked to be >= window_length and window_length is NonZero, we can be sure fft_length > 0. let fft_length = self.len(); let real_fft_length = fft_length / 2 + 1; @@ -359,18 +320,8 @@ where let scratch_real_buffer = make_mut_slice(&mut self.scratch_real_buffer); let scratch_buffer = make_mut_slice(&mut self.inner.scratch_buffer); - if fft_length < window_length || fft_length > length { - return Err(HError::OutOfSpecError( - "Expected harray.len() >= fft_length >= window_length.".to_string(), - )); - } - if let Some(slice) = window { - if slice.len() != window_length { - return Err(HError::OutOfSpecError( - "Expected window.len() == window_length.".to_string(), - )); - } - } + validate_fft_length(fft_length, window_length, length)?; + validate_window_length(window, window_length)?; let n_fft = 1 + (length - fft_length) / hop_length; let mut stft_ndarray = ArcArray2::>::zeros((n_fft, real_fft_length)); @@ -405,20 +356,20 @@ where } } -impl ProcessStft for RealStftForward +impl ProcessStft for RealStftForward where T: FftNum + Float + FloatConst + ConstZero, { type U = T; - type Output = HArray, Ix3>; + type Output = HArray, Ix3>; fn process( &mut self, - harray: &HArray, + harray: &HArray, hop_length: NonZero, window_length: NonZero, window: Option<&[T]>, - ) -> HResult, Ix3>> { + ) -> HResult { // Since fft_length is checked to be >= window_length and window_length is NonZero, we can be sure fft_length > 0. let fft_length = self.len(); let real_fft_length = fft_length / 2 + 1; @@ -429,18 +380,8 @@ where let scratch_real_buffer = make_mut_slice(&mut self.scratch_real_buffer); let scratch_buffer = make_mut_slice(&mut self.inner.scratch_buffer); - if fft_length < window_length || fft_length > ncols { - return Err(HError::OutOfSpecError( - "Expected harray.len() >= fft_length >= window_length.".to_string(), - )); - } - if let Some(slice) = window { - if slice.len() != window_length { - return Err(HError::OutOfSpecError( - "Expected window.len() == window_length.".to_string(), - )); - } - } + validate_fft_length(fft_length, window_length, ncols)?; + validate_window_length(window, window_length)?; let n_fft = 1 + (ncols - fft_length) / hop_length; let mut stft_ndarray = ArcArray::, Ix3>::zeros((nrows, n_fft, real_fft_length)); @@ -478,128 +419,115 @@ where } } -//impl ProcessRealStftForward for RealStftForward -//where -// T: FftNum + Float + FloatConst + ConstZero, -//{ -// fn process( -// &mut self, -// harray: &HArray, -// hop_length: NonZero, -// window_length: NonZero, -// window: Option<&[T]>, -// ) -> HResult, IxDyn>> { -// let fft_length = self.len(); // Since fft_length is checked to be >= window_length and window_length is NonZero, we can be sure fft_length > 0. -// let window_length = window_length.get(); -// let hop_length = hop_length.get(); -// -// // Center PAD the window if fft_length > window_length. -// let left = (fft_length - window_length) / 2; -// let right = left + window_length; -// -// match harray.ndim() { -// 1 => { -// let length = harray.len(); -// -// if fft_length < window_length || fft_length > length { -// return Err(HError::OutOfSpecError( -// "Expected harray.len() >= fft_length >= window_length.".to_string(), -// )); -// } -// if let Some(slice) = window { -// if slice.len() != window_length { -// return Err(HError::OutOfSpecError( -// "Expected window.len() == window_length.".to_string(), -// )); -// } -// } -// -// let n_fft = 1 + (length - fft_length) / hop_length; -// let mut stft_ndarray = ArcArray2::>::zeros((n_fft, fft_length)); -// -// let slice_info = s![.., left..right]; -// let slice_info_1d = s![left..right]; -// -// for (mut row, win) in stft_ndarray -// .slice_mut(slice_info) -// .lanes_mut(Axis(1)) -// .into_iter() -// .zip( -// harray -// .0 -// .windows(IxDyn(&[fft_length])) -// .into_iter() -// .step_by(hop_length), -// ) -// { -// row.assign(&win.slice(slice_info_1d)); -// if let Some(w) = window { -// row.as_slice_mut().unwrap().apply_window(w); -// } -// } -// -// let mut output = HArray(stft_ndarray.into_dyn()); -// self.0.process(&mut output)?; -// -// Ok(output) -// } -// 2 => { -// let nrows = harray.0.len_of(Axis(0)); -// let ncols = harray.0.len_of(Axis(1)); -// -// if fft_length < window_length || fft_length > ncols { -// return Err(HError::OutOfSpecError( -// "Expected ncols >= fft_length >= window_length.".to_string(), -// )); -// } -// if let Some(slice) = window { -// if slice.len() != window_length { -// return Err(HError::OutOfSpecError( -// "Expected window.len() == window_length.".to_string(), -// )); -// } -// } -// -// let n_fft = 1 + (ncols - fft_length) / hop_length; -// let mut stft_ndarray = -// ArcArray::, Ix3>::zeros((nrows, n_fft, fft_length)); -// -// let slice_info = s![.., left..right]; -// let slice_info_1d = s![left..right]; -// let scratch_buffer = make_mut_slice(&mut self.0.scratch_buffer); -// -// for (mut matrix, win) in stft_ndarray.axis_iter_mut(Axis(1)).zip( -// harray -// .0 -// .windows(IxDyn(&[nrows, fft_length])) -// .into_iter() -// .step_by(hop_length), -// ) { -// matrix.slice_mut(slice_info).assign(&win.slice(slice_info)); -// -// for mut col in matrix.lanes_mut(Axis(1)) { -// if let Some(w) = window { -// col.slice_mut(slice_info_1d) -// .as_slice_mut() -// .unwrap() -// .apply_window(w); -// } -// self.0 -// .fft -// .process_with_scratch(col.as_slice_mut().unwrap(), scratch_buffer); -// } -// } -// -// let output = HArray(stft_ndarray.into_dyn()); -// -// Ok(output) -// } -// _ => Err(HError::OutOfSpecError( -// "The HArray's ndim should be 1 or 2.".into(), -// )), -// } -// } -//} +impl ProcessStft for RealStftForward +where + T: FftNum + Float + FloatConst + ConstZero, +{ + type U = T; + type Output = HArray, IxDyn>; + + fn process( + &mut self, + harray: &HArray, + hop_length: NonZero, + window_length: NonZero, + window: Option<&[T]>, + ) -> HResult { + // Since fft_length is checked to be >= window_length and window_length is NonZero, we can be sure fft_length > 0. + let fft_length = self.len(); + let real_fft_length = fft_length / 2 + 1; + let window_length = window_length.get(); + let hop_length = hop_length.get(); + + // Center PAD the window if fft_length > window_length. + let left = (fft_length - window_length) / 2; + let right = left + window_length; + + let scratch_real_buffer = make_mut_slice(&mut self.scratch_real_buffer); + let scratch_buffer = make_mut_slice(&mut self.inner.scratch_buffer); + + match harray.ndim() { + 1 => { + let length = harray.len(); + + validate_fft_length(fft_length, window_length, length)?; + validate_window_length(window, window_length)?; + + let n_fft = 1 + (length - fft_length) / hop_length; + let mut stft_ndarray = ArcArray2::>::zeros((n_fft, real_fft_length)); + + for (mut row, win) in stft_ndarray.lanes_mut(Axis(1)).into_iter().zip( + harray + .0 + .windows(IxDyn(&[fft_length])) + .into_iter() + .step_by(hop_length), + ) { + let scratch_real_buffer_slice = &mut scratch_real_buffer[left..right]; + scratch_real_buffer_slice + .copy_from_slice(&win.as_slice().unwrap()[left..right]); + if let Some(w) = window { + scratch_real_buffer_slice.apply_window(w); + } + self.inner + .fft + .process_with_scratch( + scratch_real_buffer, + row.as_slice_mut().unwrap(), + scratch_buffer, + ) + .unwrap(); + } + + let output = HArray(stft_ndarray.into_dyn()); + + Ok(output) + } + 2 => { + let nrows = harray.0.len_of(Axis(0)); + let ncols = harray.0.len_of(Axis(1)); + + validate_fft_length(fft_length, window_length, ncols)?; + validate_window_length(window, window_length)?; + + let n_fft = 1 + (ncols - fft_length) / hop_length; + let mut stft_ndarray = + ArcArray::, Ix3>::zeros((nrows, n_fft, real_fft_length)); + + for (mut col, win_col) in stft_ndarray.lanes_mut(Axis(2)).into_iter().zip( + harray + .0 + .windows(IxDyn(&[1, fft_length])) + .into_iter() + .step_by(hop_length), + ) { + let scratch_real_buffer_slice = &mut scratch_real_buffer[left..right]; + scratch_real_buffer_slice + .copy_from_slice(&win_col.as_slice().unwrap()[left..right]); + + if let Some(w) = window { + scratch_real_buffer_slice.apply_window(w); + } + self.inner + .fft + .process_with_scratch( + scratch_real_buffer, + col.as_slice_mut().unwrap(), + scratch_buffer, + ) + .unwrap(); + } + + let output = HArray(stft_ndarray.into_dyn()); + + Ok(output) + } + _ => Err(HError::OutOfSpecError( + "The HArray's ndim should be 1 or 2.".into(), + )), + } + } +} trait ApplyWindow { fn apply_window(&mut self, window: &[T]); @@ -618,7 +546,7 @@ where impl ApplyWindow for [T] where - T: ComplexFloat, + T: Float, { fn apply_window(&mut self, window: &[T]) { for (i, w) in self.iter_mut().zip(window.iter()) { @@ -627,6 +555,31 @@ where } } +fn validate_fft_length(fft_length: usize, lower: usize, upper: usize) -> HResult<()> { + if fft_length < lower || fft_length > upper { + Err(HError::OutOfSpecError(format!( + "Expected {upper} >= fft_length >= {lower}. Got {fft_length}." + ))) + } else { + Ok(()) + } +} + +fn validate_window_length(window: Option<&[T]>, window_length: usize) -> HResult<()> { + if let Some(slice) = window { + if slice.len() != window_length { + let got_length = slice.len(); + Err(HError::OutOfSpecError(format!( + "Expected window length == {window_length}. Got {got_length}." + ))) + } else { + Ok(()) + } + } else { + Ok(()) + } +} + #[cfg(test)] mod tests { use super::*; @@ -712,7 +665,7 @@ mod tests { { // Ix1 test. let harray = HArray::new_from_shape_vec(length, input.clone()).unwrap(); - let mut stft = Stft::::new_stft_forward(fft_length); + let mut stft = Stft::::new_forward(fft_length); let stft_harray = stft .process(&harray, hop_length, window_length, window) .unwrap(); @@ -724,7 +677,7 @@ mod tests { let harray = HArray::new_from_shape_vec(length, input.clone()) .unwrap() .into_dynamic(); - let mut stft = Stft::::new_stft_forward(fft_length); + let mut stft = Stft::::new_forward(fft_length); let stft_harray = stft .process(&harray, hop_length, window_length, window) .unwrap(); @@ -854,7 +807,7 @@ mod tests { { // Ix2 test. let harray = HArray::new_from_shape_vec((2, length / 2), input.clone()).unwrap(); - let mut stft = Stft::::new_stft_forward(fft_length); + let mut stft = Stft::::new_forward(fft_length); let stft_harray = stft .process(&harray, hop_length, window_length, window) .unwrap(); @@ -867,7 +820,7 @@ mod tests { let harray = HArray::new_from_shape_vec((2, length / 2), input.clone()) .unwrap() .into_dynamic(); - let mut stft = Stft::::new_stft_forward(fft_length); + let mut stft = Stft::::new_forward(fft_length); let stft_harray = stft .process(&harray, hop_length, window_length, window) .unwrap(); @@ -941,7 +894,7 @@ mod tests { { // Ix1 test. let harray = HArray::new_from_shape_vec(length, input.clone()).unwrap(); - let mut stft = RealStftForward::::new_real_stft_forward(fft_length); + let mut stft = RealStftForward::::new(fft_length); let stft_harray = stft .process(&harray, hop_length, window_length, window) .unwrap(); @@ -950,19 +903,19 @@ mod tests { HArray::new_from_shape_vec((n_fft, fft_length / 2 + 1), result.clone()).unwrap(); assert!(compare_harray_complex(&stft_harray, &rhs)); - //// IxDyn test. - //let harray = HArray::new_from_shape_vec(length, input.clone()) - // .unwrap() - // .into_dynamic(); - //let mut stft = RealStftForward::::new_real_stft_forward(fft_length); - //let stft_harray = stft - // .process(&harray, hop_length, window_length, window) - // .unwrap(); - //let n_fft = 1 + (harray.len() - fft_length) / hop_length; - //let rhs = HArray::new_from_shape_vec((n_fft, fft_length / 2 + 1), result.clone()) - // .unwrap() - // .into_dynamic(); - //assert!(compare_harray_complex(&stft_harray, &rhs)); + // IxDyn test. + let harray = HArray::new_from_shape_vec(length, input.clone()) + .unwrap() + .into_dynamic(); + let mut stft = RealStftForward::::new(fft_length); + let stft_harray = stft + .process(&harray, hop_length, window_length, window) + .unwrap(); + let n_fft = 1 + (harray.len() - fft_length) / hop_length; + let rhs = HArray::new_from_shape_vec((n_fft, fft_length / 2 + 1), result.clone()) + .unwrap() + .into_dynamic(); + assert!(compare_harray_complex(&stft_harray, &rhs)); } } @@ -1047,7 +1000,7 @@ mod tests { { // Ix2 test. let harray = HArray::new_from_shape_vec((2, length / 2), input.clone()).unwrap(); - let mut stft = RealStftForward::::new_real_stft_forward(fft_length); + let mut stft = RealStftForward::::new(fft_length); let stft_harray = stft .process(&harray, hop_length, window_length, window) .unwrap(); @@ -1057,19 +1010,20 @@ mod tests { HArray::new_from_shape_vec((2, n_fft, fft_length / 2 + 1), result.clone()).unwrap(); assert!(compare_harray_complex(&stft_harray, &rhs)); - //// IxDyn test. - //let harray = HArray::new_from_shape_vec(length, input.clone()) - // .unwrap() - // .into_dynamic(); - //let mut stft = RealStftForward::::new_real_stft_forward(fft_length); - //let stft_harray = stft - // .process(&harray, hop_length, window_length, window) - // .unwrap(); - //let n_fft = 1 + (harray.len() - fft_length) / hop_length; - //let rhs = HArray::new_from_shape_vec((n_fft, fft_length / 2 + 1), result.clone()) - // .unwrap() - // .into_dynamic(); - //assert!(compare_harray_complex(&stft_harray, &rhs)); + // IxDyn test. + let harray = HArray::new_from_shape_vec((2, length / 2), input.clone()) + .unwrap() + .into_dynamic(); + let mut stft = RealStftForward::::new(fft_length); + let stft_harray = stft + .process(&harray, hop_length, window_length, window) + .unwrap(); + let ncols = harray.0.len_of(Axis(1)); + let n_fft = 1 + (ncols - fft_length) / hop_length; + let rhs = HArray::new_from_shape_vec((2, n_fft, fft_length / 2 + 1), result.clone()) + .unwrap() + .into_dynamic(); + assert!(compare_harray_complex(&stft_harray, &rhs)); } } } diff --git a/harmonium-stft/Cargo.toml b/harmonium-stft/Cargo.toml deleted file mode 100644 index f6f5d67..0000000 --- a/harmonium-stft/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "harmonium-stft" -version = "0.1.0" -edition = "2021" - -[dependencies] -harmonium-core = { workspace = true } -harmonium-fft = { workspace = true } -rustfft = { workspace = true } -realfft = { workspace = true } -ndarray = { workspace = true } - diff --git a/harmonium-stft/src/lib.rs b/harmonium-stft/src/lib.rs deleted file mode 100644 index fc8194a..0000000 --- a/harmonium-stft/src/lib.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod stft; diff --git a/r-harmonium/.fft_bench.R b/r-harmonium/.fft_bench.R index 80378bb..20bb451 100644 --- a/r-harmonium/.fft_bench.R +++ b/r-harmonium/.fft_bench.R @@ -3,15 +3,16 @@ library(torch) library(ggplot2) devtools::load_all(".", export_all = FALSE) -# fft_matrix with complexes +# fft results <- bench::press( n = seq.int(30, 400000, 30000L), { r = as.double(sample(100, n, replace = TRUE)) i = as.double(sample(100, n, replace = TRUE)) + ncol = 10 x = complex(real=r, imaginary=i) - x = matrix(x, ncol = 10) - hfft = HFft$new_fft_forward(as.integer(n/10), HDataType$Complex64) + x = matrix(x, ncol = ncol) + hfft = HFft$new_forward(as.integer(n/ncol), HDataType$Complex64) mark( torch = as_array(torch::torch_fft_fft(torch_tensor(x, dtype = torch_cfloat64()), dim = 1)), harmonium_fft = { @@ -27,13 +28,14 @@ results <- bench::press( ) ggplot(results) + geom_point(aes(x = n, y = median, color = as.character(expression))) -# fft_real_matrix with floats +# real fft results <- bench::press( n = seq(30, 400000, 30000), { x = as.double(sample(100, n, replace = TRUE)) - x = matrix(x, ncol = 10) - hfft = HRealFft$new_real_fft(as.integer(n/10), HDataType$Float64) + x = matrix(x, ncol = ncol) + ncol = 10 + hfft = HFft$new_real_forward(as.integer(n/ncol), HDataType$Float64) mark( torch = as_array(torch::torch_fft_rfft(torch_tensor(x, dtype = torch_float64()), dim = 1)), harmonium_fft = { @@ -47,3 +49,38 @@ results <- bench::press( } ) ggplot(results) + geom_point(aes(x = n, y = median, color = as.character(expression))) + +# stft 1d with complexes +results <- bench::press( + n = seq.int(3000, 150000, 5000), + { + r = as.double(sample(100, n, replace = TRUE)) + i = as.double(sample(100, n, replace = TRUE)) + window = as.numeric(1:80) + x = as.array(complex(real=r, imaginary=i)) + n_fft = 200L + hop_length = 100L + win_length = 80L + hstft = HStft$new_forward(n_fft, HDataType$Complex64) + mark( + torch = as_array(torch::torch_stft( + input = torch_tensor(x, dtype = torch_cfloat64()), + n_fft = n_fft, + hop_length = hop_length, + win_length = win_length, + window = window, + center = FALSE, + return_complex = TRUE + )), + harmonium_stft = { + harray = HArray$new_from_values(x, HDataType$Complex64) + harray_window = HArray$new_from_values(as.array(window), HDataType$Float64) + hstft$process(harray, hop_length, win_length, harray_window) + harray$collect() + }, + iterations = 50, + check = function(a, b) { all.equal(a, b, tolerance = 1.0e-06) } + ) + } +) +ggplot(results) + geom_point(aes(x = n, y = median, color = as.character(expression))) diff --git a/r-harmonium/.testtest.R b/r-harmonium/.testtest.R index 83f3672..28aa87b 100644 --- a/r-harmonium/.testtest.R +++ b/r-harmonium/.testtest.R @@ -2,19 +2,3 @@ devtools::load_all(".", export_all = FALSE) devtools::test() devtools::check(document = FALSE, cran = FALSE, args = c("--no-manual", "--no-build-vignettes", "--no-codoc", "--no-examples", "--no-tests")) - -library(torch) -v <- c(1,2,3,4,5,6) - -complex_tensor = torch_tensor(v) -a = torch_stft( - input = complex_tensor, - n_fft = 5, - hop_length = 2, - win_length = 3, - window = c(1.,2.,3.), - center = FALSE, - onesided = TRUE, - return_complex = TRUE -) -t(as_array(a)) diff --git a/r-harmonium/DESCRIPTION b/r-harmonium/DESCRIPTION index 2474cc1..26973f4 100644 --- a/r-harmonium/DESCRIPTION +++ b/r-harmonium/DESCRIPTION @@ -1,6 +1,6 @@ Package: harmonium Title: Audio analysis and I/O -Version: 0.2.0 +Version: 0.3.0 Authors@R: person("Daniel", "Gurgel", email = "daniellga@gmail.com", role = c("aut", "cre")) Description: Audio analysis and I/O. diff --git a/r-harmonium/NAMESPACE b/r-harmonium/NAMESPACE index 239034d..6d9aa4d 100644 --- a/r-harmonium/NAMESPACE +++ b/r-harmonium/NAMESPACE @@ -12,6 +12,7 @@ export(HWindow) export(HMetadataType) export(HFile) export(HFft) +export(HStft) export(HArrayAudio) export(hdocs) export(HDecoderStream) @@ -43,5 +44,6 @@ S3method(print,HWindowType) S3method(print,HInterpolationType) S3method(print,HSincInterpolationParameters) S3method(print,HFft) +S3method(print,HStft) useDynLib(harmonium, .registration = TRUE) diff --git a/r-harmonium/R/000-wrappers.R b/r-harmonium/R/000-wrappers.R index 464589d..e71a8d9 100644 --- a/r-harmonium/R/000-wrappers.R +++ b/r-harmonium/R/000-wrappers.R @@ -750,12 +750,12 @@ class(`HDecoderStream`) <- "HDecoderStream__bundle" `HFft`$`new_real_forward` <- function(`length`, `dtype`) { `dtype` <- .savvy_extract_ptr(`dtype`, "HDataType") - .savvy_wrap_HRealFft(.Call(savvy_HFft_new_real_forward__impl, `length`, `dtype`)) + .savvy_wrap_HFft(.Call(savvy_HFft_new_real_forward__impl, `length`, `dtype`)) } `HFft`$`new_real_inverse` <- function(`length`, `dtype`) { `dtype` <- .savvy_extract_ptr(`dtype`, "HDataType") - .savvy_wrap_HRealFft(.Call(savvy_HFft_new_real_inverse__impl, `length`, `dtype`)) + .savvy_wrap_HFft(.Call(savvy_HFft_new_real_inverse__impl, `length`, `dtype`)) } @@ -1188,96 +1188,6 @@ class(`HPolynomialDegree`) <- "HPolynomialDegree__bundle" #' @export `[[<-.HPolynomialDegree__bundle` <- function(x, i, value) stop("HPolynomialDegree cannot be modified", call. = FALSE) -### wrapper functions for HRealFft - -`HRealFft_process` <- function(self) { - function(`harray`) { - `harray` <- .savvy_extract_ptr(`harray`, "HArray") - invisible(.Call(savvy_HRealFft_process__impl, `self`, `harray`)) - } -} - -`HRealFft_dtype` <- function(self) { - function() { - .savvy_wrap_HDataType(.Call(savvy_HRealFft_dtype__impl, `self`)) - } -} - -`HRealFft_print` <- function(self) { - function() { - invisible(.Call(savvy_HRealFft_print__impl, `self`)) - } -} - -`HRealFft_clone` <- function(self) { - function() { - .savvy_wrap_HRealFft(.Call(savvy_HRealFft_clone__impl, `self`)) - } -} - -`HRealFft_is_unique` <- function(self) { - function() { - .Call(savvy_HRealFft_is_unique__impl, `self`) - } -} - -`HRealFft_invalidate` <- function(self) { - function() { - invisible(.Call(savvy_HRealFft_invalidate__impl, `self`)) - } -} - -`.savvy_wrap_HRealFft` <- function(ptr) { - e <- new.env(parent = emptyenv()) - e$.ptr <- ptr - e$`process` <- `HRealFft_process`(ptr) - e$`dtype` <- `HRealFft_dtype`(ptr) - e$`print` <- `HRealFft_print`(ptr) - e$`clone` <- `HRealFft_clone`(ptr) - e$`is_unique` <- `HRealFft_is_unique`(ptr) - e$`invalidate` <- `HRealFft_invalidate`(ptr) - - class(e) <- "HRealFft" - e -} - -#' @export -`$<-.HRealFft` <- function(x, name, value) stop("HRealFft cannot be modified", call. = FALSE) - -#' @export -`[[<-.HRealFft` <- function(x, i, value) stop("HRealFft cannot be modified", call. = FALSE) - - - -`HRealFft` <- new.env(parent = emptyenv()) - -#' @export -`$<-.HRealFft` <- function(x, name, value) stop("HRealFft cannot be modified", call. = FALSE) - -#' @export -`[[<-.HRealFft` <- function(x, i, value) stop("HRealFft cannot be modified", call. = FALSE) - -### associated functions for HRealFft - -`HRealFft`$`new` <- function(`length`, `dtype`) { - `dtype` <- .savvy_extract_ptr(`dtype`, "HDataType") - .savvy_wrap_HRealFft(.Call(savvy_HRealFft_new__impl, `length`, `dtype`)) -} - - -class(`HRealFft`) <- "HRealFft__bundle" - -#' @export -`print.HRealFft__bundle` <- function(x, ...) { - cat('HRealFft') -} - -#' @export -`$<-.HRealFft__bundle` <- function(x, name, value) stop("HRealFft cannot be modified", call. = FALSE) - -#' @export -`[[<-.HRealFft__bundle` <- function(x, i, value) stop("HRealFft cannot be modified", call. = FALSE) - ### wrapper functions for HResampler `HResampler_process` <- function(self) { @@ -1593,6 +1503,106 @@ class(`HSincInterpolationParameters`) <- "HSincInterpolationParameters__bundle" #' @export `[[<-.HSincInterpolationParameters__bundle` <- function(x, i, value) stop("HSincInterpolationParameters cannot be modified", call. = FALSE) +### wrapper functions for HStft + +`HStft_process` <- function(self) { + function(`harray`, `hop_length`, `window_length`, `window` = NULL) { + `harray` <- .savvy_extract_ptr(`harray`, "HArray") + `window` <- .savvy_extract_ptr(`window`, "HArray") + invisible(.Call(savvy_HStft_process__impl, `self`, `harray`, `hop_length`, `window_length`, `window`)) + } +} + +`HStft_dtype` <- function(self) { + function() { + .savvy_wrap_HDataType(.Call(savvy_HStft_dtype__impl, `self`)) + } +} + +`HStft_print` <- function(self) { + function() { + invisible(.Call(savvy_HStft_print__impl, `self`)) + } +} + +`HStft_clone` <- function(self) { + function() { + .savvy_wrap_HStft(.Call(savvy_HStft_clone__impl, `self`)) + } +} + +`HStft_is_unique` <- function(self) { + function() { + .Call(savvy_HStft_is_unique__impl, `self`) + } +} + +`HStft_invalidate` <- function(self) { + function() { + invisible(.Call(savvy_HStft_invalidate__impl, `self`)) + } +} + +`.savvy_wrap_HStft` <- function(ptr) { + e <- new.env(parent = emptyenv()) + e$.ptr <- ptr + e$`process` <- `HStft_process`(ptr) + e$`dtype` <- `HStft_dtype`(ptr) + e$`print` <- `HStft_print`(ptr) + e$`clone` <- `HStft_clone`(ptr) + e$`is_unique` <- `HStft_is_unique`(ptr) + e$`invalidate` <- `HStft_invalidate`(ptr) + + class(e) <- "HStft" + e +} + +#' @export +`$<-.HStft` <- function(x, name, value) stop("HStft cannot be modified", call. = FALSE) + +#' @export +`[[<-.HStft` <- function(x, i, value) stop("HStft cannot be modified", call. = FALSE) + + +#' HStft +#' An `HStft` is used to create STFTs. It caches results internally, so when making more than one Stft it is advisable to reuse the same `HStft` instance. +#' +#' # Methods +#' +`HStft` <- new.env(parent = emptyenv()) + +#' @export +`$<-.HStft` <- function(x, name, value) stop("HStft cannot be modified", call. = FALSE) + +#' @export +`[[<-.HStft` <- function(x, i, value) stop("HStft cannot be modified", call. = FALSE) + +### associated functions for HStft + +`HStft`$`new_forward` <- function(`length`, `dtype`) { + `dtype` <- .savvy_extract_ptr(`dtype`, "HDataType") + .savvy_wrap_HStft(.Call(savvy_HStft_new_forward__impl, `length`, `dtype`)) +} + +`HStft`$`new_real_forward` <- function(`length`, `dtype`) { + `dtype` <- .savvy_extract_ptr(`dtype`, "HDataType") + .savvy_wrap_HStft(.Call(savvy_HStft_new_real_forward__impl, `length`, `dtype`)) +} + + +class(`HStft`) <- "HStft__bundle" + +#' @export +`print.HStft__bundle` <- function(x, ...) { + cat('HStft') +} + +#' @export +`$<-.HStft__bundle` <- function(x, name, value) stop("HStft cannot be modified", call. = FALSE) + +#' @export +`[[<-.HStft__bundle` <- function(x, i, value) stop("HStft cannot be modified", call. = FALSE) + ### wrapper functions for HWindow diff --git a/r-harmonium/R/structs.R b/r-harmonium/R/structs.R index 74063f2..bcf4f19 100644 --- a/r-harmonium/R/structs.R +++ b/r-harmonium/R/structs.R @@ -52,4 +52,8 @@ print.HInterpolationType = function(x, ...) { print.HFft = function(x, ...) { x$print() +} + +print.HStft = function(x, ...) { + x$print() } \ No newline at end of file diff --git a/r-harmonium/R/zzz.R b/r-harmonium/R/zzz.R index 8bc62df..bba6e39 100644 --- a/r-harmonium/R/zzz.R +++ b/r-harmonium/R/zzz.R @@ -12,10 +12,10 @@ lockEnvironment(HFile, bindings = TRUE) lockEnvironment(HResampler, bindings = TRUE) lockEnvironment(HFft, bindings = TRUE) - lockEnvironment(HRealFft, bindings = TRUE) + lockEnvironment(HStft, bindings = TRUE) lockEnvironment(HDecoderStream, bindings = TRUE) } .onAttach <- function(libname, pkgname) { - packageStartupMessage(paste0("harmonium ",packageVersion("harmonium"),", see harmonium::hdocs() for documentation.")) + packageStartupMessage(paste0("harmonium ",packageVersion("harmonium"),". Use harmonium::hdocs() for documentation.")) } diff --git a/r-harmonium/src/init.c b/r-harmonium/src/init.c index 6b9fa91..24d09de 100644 --- a/r-harmonium/src/init.c +++ b/r-harmonium/src/init.c @@ -35,8 +35,8 @@ SEXP handle_result(SEXP res_) { } -SEXP savvy_HArray_new_from_values__impl(SEXP arr, SEXP dtype) { - SEXP res = savvy_HArray_new_from_values__ffi(arr, dtype); +SEXP savvy_HArray_new_from_values__impl(SEXP c_arg__arr, SEXP c_arg__dtype) { + SEXP res = savvy_HArray_new_from_values__ffi(c_arg__arr, c_arg__dtype); return handle_result(res); } @@ -55,8 +55,8 @@ SEXP savvy_HArray_ndim__impl(SEXP self__) { return handle_result(res); } -SEXP savvy_HArray_slice__impl(SEXP self__, SEXP range) { - SEXP res = savvy_HArray_slice__ffi(self__, range); +SEXP savvy_HArray_slice__impl(SEXP self__, SEXP c_arg__range) { + SEXP res = savvy_HArray_slice__ffi(self__, c_arg__range); return handle_result(res); } @@ -65,13 +65,13 @@ SEXP savvy_HArray_print__impl(SEXP self__) { return handle_result(res); } -SEXP savvy_HArray_eq__impl(SEXP self__, SEXP other) { - SEXP res = savvy_HArray_eq__ffi(self__, other); +SEXP savvy_HArray_eq__impl(SEXP self__, SEXP c_arg__other) { + SEXP res = savvy_HArray_eq__ffi(self__, c_arg__other); return handle_result(res); } -SEXP savvy_HArray_ne__impl(SEXP self__, SEXP other) { - SEXP res = savvy_HArray_ne__ffi(self__, other); +SEXP savvy_HArray_ne__impl(SEXP self__, SEXP c_arg__other) { + SEXP res = savvy_HArray_ne__ffi(self__, c_arg__other); return handle_result(res); } @@ -110,23 +110,23 @@ SEXP savvy_HArray_invalidate__impl(SEXP self__) { return handle_result(res); } -SEXP savvy_HArrayAudio_nchannels__impl(SEXP harray) { - SEXP res = savvy_HArrayAudio_nchannels__ffi(harray); +SEXP savvy_HArrayAudio_nchannels__impl(SEXP c_arg__harray) { + SEXP res = savvy_HArrayAudio_nchannels__ffi(c_arg__harray); return handle_result(res); } -SEXP savvy_HArrayAudio_nframes__impl(SEXP harray) { - SEXP res = savvy_HArrayAudio_nframes__ffi(harray); +SEXP savvy_HArrayAudio_nframes__impl(SEXP c_arg__harray) { + SEXP res = savvy_HArrayAudio_nframes__ffi(c_arg__harray); return handle_result(res); } -SEXP savvy_HArrayAudio_db_to_amplitude__impl(SEXP harray, SEXP reference, SEXP power) { - SEXP res = savvy_HArrayAudio_db_to_amplitude__ffi(harray, reference, power); +SEXP savvy_HArrayAudio_db_to_amplitude__impl(SEXP c_arg__harray, SEXP c_arg__reference, SEXP c_arg__power) { + SEXP res = savvy_HArrayAudio_db_to_amplitude__ffi(c_arg__harray, c_arg__reference, c_arg__power); return handle_result(res); } -SEXP savvy_HArrayAudio_to_mono__impl(SEXP harray) { - SEXP res = savvy_HArrayAudio_to_mono__ffi(harray); +SEXP savvy_HArrayAudio_to_mono__impl(SEXP c_arg__harray) { + SEXP res = savvy_HArrayAudio_to_mono__ffi(c_arg__harray); return handle_result(res); } @@ -135,13 +135,13 @@ SEXP savvy_HAudioSink_new__impl(void) { return handle_result(res); } -SEXP savvy_HAudioSink_append_from_harray__impl(SEXP self__, SEXP harray, SEXP sr) { - SEXP res = savvy_HAudioSink_append_from_harray__ffi(self__, harray, sr); +SEXP savvy_HAudioSink_append_from_harray__impl(SEXP self__, SEXP c_arg__harray, SEXP c_arg__sr) { + SEXP res = savvy_HAudioSink_append_from_harray__ffi(self__, c_arg__harray, c_arg__sr); return handle_result(res); } -SEXP savvy_HAudioSink_append_from_file__impl(SEXP self__, SEXP fpath) { - SEXP res = savvy_HAudioSink_append_from_file__ffi(self__, fpath); +SEXP savvy_HAudioSink_append_from_file__impl(SEXP self__, SEXP c_arg__fpath) { + SEXP res = savvy_HAudioSink_append_from_file__ffi(self__, c_arg__fpath); return handle_result(res); } @@ -195,13 +195,13 @@ SEXP savvy_HAudioSink_play__impl(SEXP self__) { return handle_result(res); } -SEXP savvy_HAudioSink_set_speed__impl(SEXP self__, SEXP value) { - SEXP res = savvy_HAudioSink_set_speed__ffi(self__, value); +SEXP savvy_HAudioSink_set_speed__impl(SEXP self__, SEXP c_arg__value) { + SEXP res = savvy_HAudioSink_set_speed__ffi(self__, c_arg__value); return handle_result(res); } -SEXP savvy_HAudioSink_set_volume__impl(SEXP self__, SEXP value) { - SEXP res = savvy_HAudioSink_set_volume__ffi(self__, value); +SEXP savvy_HAudioSink_set_volume__impl(SEXP self__, SEXP c_arg__value) { + SEXP res = savvy_HAudioSink_set_volume__ffi(self__, c_arg__value); return handle_result(res); } @@ -225,8 +225,8 @@ SEXP savvy_HAudioSink_stop__impl(SEXP self__) { return handle_result(res); } -SEXP savvy_HAudioSink_try_seek__impl(SEXP self__, SEXP pos) { - SEXP res = savvy_HAudioSink_try_seek__ffi(self__, pos); +SEXP savvy_HAudioSink_try_seek__impl(SEXP self__, SEXP c_arg__pos) { + SEXP res = savvy_HAudioSink_try_seek__ffi(self__, c_arg__pos); return handle_result(res); } @@ -245,13 +245,13 @@ SEXP savvy_HDataType_print__impl(SEXP self__) { return handle_result(res); } -SEXP savvy_HDataType_eq__impl(SEXP self__, SEXP other) { - SEXP res = savvy_HDataType_eq__ffi(self__, other); +SEXP savvy_HDataType_eq__impl(SEXP self__, SEXP c_arg__other) { + SEXP res = savvy_HDataType_eq__ffi(self__, c_arg__other); return handle_result(res); } -SEXP savvy_HDataType_ne__impl(SEXP self__, SEXP other) { - SEXP res = savvy_HDataType_ne__ffi(self__, other); +SEXP savvy_HDataType_ne__impl(SEXP self__, SEXP c_arg__other) { + SEXP res = savvy_HDataType_ne__ffi(self__, c_arg__other); return handle_result(res); } @@ -275,28 +275,28 @@ SEXP savvy_HDecoderStream_stream__impl(SEXP self__) { return handle_result(res); } -SEXP savvy_HFft_new_forward__impl(SEXP length, SEXP dtype) { - SEXP res = savvy_HFft_new_forward__ffi(length, dtype); +SEXP savvy_HFft_new_forward__impl(SEXP c_arg__length, SEXP c_arg__dtype) { + SEXP res = savvy_HFft_new_forward__ffi(c_arg__length, c_arg__dtype); return handle_result(res); } -SEXP savvy_HFft_new_inverse__impl(SEXP length, SEXP dtype) { - SEXP res = savvy_HFft_new_inverse__ffi(length, dtype); +SEXP savvy_HFft_new_inverse__impl(SEXP c_arg__length, SEXP c_arg__dtype) { + SEXP res = savvy_HFft_new_inverse__ffi(c_arg__length, c_arg__dtype); return handle_result(res); } -SEXP savvy_HFft_new_real_forward__impl(SEXP length, SEXP dtype) { - SEXP res = savvy_HFft_new_real_forward__ffi(length, dtype); +SEXP savvy_HFft_new_real_forward__impl(SEXP c_arg__length, SEXP c_arg__dtype) { + SEXP res = savvy_HFft_new_real_forward__ffi(c_arg__length, c_arg__dtype); return handle_result(res); } -SEXP savvy_HFft_new_real_inverse__impl(SEXP length, SEXP dtype) { - SEXP res = savvy_HFft_new_real_inverse__ffi(length, dtype); +SEXP savvy_HFft_new_real_inverse__impl(SEXP c_arg__length, SEXP c_arg__dtype) { + SEXP res = savvy_HFft_new_real_inverse__ffi(c_arg__length, c_arg__dtype); return handle_result(res); } -SEXP savvy_HFft_process__impl(SEXP self__, SEXP harray) { - SEXP res = savvy_HFft_process__ffi(self__, harray); +SEXP savvy_HFft_process__impl(SEXP self__, SEXP c_arg__harray) { + SEXP res = savvy_HFft_process__ffi(self__, c_arg__harray); return handle_result(res); } @@ -325,28 +325,28 @@ SEXP savvy_HFft_invalidate__impl(SEXP self__) { return handle_result(res); } -SEXP savvy_HFile_decode__impl(SEXP fpath, SEXP dtype) { - SEXP res = savvy_HFile_decode__ffi(fpath, dtype); +SEXP savvy_HFile_decode__impl(SEXP c_arg__fpath, SEXP c_arg__dtype) { + SEXP res = savvy_HFile_decode__ffi(c_arg__fpath, c_arg__dtype); return handle_result(res); } -SEXP savvy_HFile_decode_stream__impl(SEXP fpath, SEXP frames, SEXP dtype) { - SEXP res = savvy_HFile_decode_stream__ffi(fpath, frames, dtype); +SEXP savvy_HFile_decode_stream__impl(SEXP c_arg__fpath, SEXP c_arg__frames, SEXP c_arg__dtype) { + SEXP res = savvy_HFile_decode_stream__ffi(c_arg__fpath, c_arg__frames, c_arg__dtype); return handle_result(res); } -SEXP savvy_HFile_metadata__impl(SEXP fpath, SEXP metadata_type) { - SEXP res = savvy_HFile_metadata__ffi(fpath, metadata_type); +SEXP savvy_HFile_metadata__impl(SEXP c_arg__fpath, SEXP c_arg__metadata_type) { + SEXP res = savvy_HFile_metadata__ffi(c_arg__fpath, c_arg__metadata_type); return handle_result(res); } -SEXP savvy_HFile_params__impl(SEXP fpath) { - SEXP res = savvy_HFile_params__ffi(fpath); +SEXP savvy_HFile_params__impl(SEXP c_arg__fpath) { + SEXP res = savvy_HFile_params__ffi(c_arg__fpath); return handle_result(res); } -SEXP savvy_HFile_verify__impl(SEXP fpath) { - SEXP res = savvy_HFile_verify__ffi(fpath); +SEXP savvy_HFile_verify__impl(SEXP c_arg__fpath) { + SEXP res = savvy_HFile_verify__ffi(c_arg__fpath); return handle_result(res); } @@ -355,13 +355,13 @@ SEXP savvy_HInterpolationType_print__impl(SEXP self__) { return handle_result(res); } -SEXP savvy_HInterpolationType_eq__impl(SEXP self__, SEXP other) { - SEXP res = savvy_HInterpolationType_eq__ffi(self__, other); +SEXP savvy_HInterpolationType_eq__impl(SEXP self__, SEXP c_arg__other) { + SEXP res = savvy_HInterpolationType_eq__ffi(self__, c_arg__other); return handle_result(res); } -SEXP savvy_HInterpolationType_ne__impl(SEXP self__, SEXP other) { - SEXP res = savvy_HInterpolationType_ne__ffi(self__, other); +SEXP savvy_HInterpolationType_ne__impl(SEXP self__, SEXP c_arg__other) { + SEXP res = savvy_HInterpolationType_ne__ffi(self__, c_arg__other); return handle_result(res); } @@ -370,13 +370,13 @@ SEXP savvy_HMetadataType_print__impl(SEXP self__) { return handle_result(res); } -SEXP savvy_HMetadataType_eq__impl(SEXP self__, SEXP other) { - SEXP res = savvy_HMetadataType_eq__ffi(self__, other); +SEXP savvy_HMetadataType_eq__impl(SEXP self__, SEXP c_arg__other) { + SEXP res = savvy_HMetadataType_eq__ffi(self__, c_arg__other); return handle_result(res); } -SEXP savvy_HMetadataType_ne__impl(SEXP self__, SEXP other) { - SEXP res = savvy_HMetadataType_ne__ffi(self__, other); +SEXP savvy_HMetadataType_ne__impl(SEXP self__, SEXP c_arg__other) { + SEXP res = savvy_HMetadataType_ne__ffi(self__, c_arg__other); return handle_result(res); } @@ -385,163 +385,168 @@ SEXP savvy_HPolynomialDegree_print__impl(SEXP self__) { return handle_result(res); } -SEXP savvy_HPolynomialDegree_eq__impl(SEXP self__, SEXP other) { - SEXP res = savvy_HPolynomialDegree_eq__ffi(self__, other); +SEXP savvy_HPolynomialDegree_eq__impl(SEXP self__, SEXP c_arg__other) { + SEXP res = savvy_HPolynomialDegree_eq__ffi(self__, c_arg__other); return handle_result(res); } -SEXP savvy_HPolynomialDegree_ne__impl(SEXP self__, SEXP other) { - SEXP res = savvy_HPolynomialDegree_ne__ffi(self__, other); +SEXP savvy_HPolynomialDegree_ne__impl(SEXP self__, SEXP c_arg__other) { + SEXP res = savvy_HPolynomialDegree_ne__ffi(self__, c_arg__other); return handle_result(res); } -SEXP savvy_HRealFft_new__impl(SEXP length, SEXP dtype) { - SEXP res = savvy_HRealFft_new__ffi(length, dtype); +SEXP savvy_HResampler_new_fft__impl(SEXP c_arg__sr_in, SEXP c_arg__sr_out, SEXP c_arg__chunk_size, SEXP c_arg__sub_chunks, SEXP c_arg__nchannels, SEXP c_arg__res_type, SEXP c_arg__dtype) { + SEXP res = savvy_HResampler_new_fft__ffi(c_arg__sr_in, c_arg__sr_out, c_arg__chunk_size, c_arg__sub_chunks, c_arg__nchannels, c_arg__res_type, c_arg__dtype); return handle_result(res); } -SEXP savvy_HRealFft_process__impl(SEXP self__, SEXP harray) { - SEXP res = savvy_HRealFft_process__ffi(self__, harray); +SEXP savvy_HResampler_new_sinc__impl(SEXP c_arg__resample_ratio, SEXP c_arg__max_resample_ratio_relative, SEXP c_arg__parameters, SEXP c_arg__chunk_size, SEXP c_arg__nchannels, SEXP c_arg__res_type, SEXP c_arg__dtype) { + SEXP res = savvy_HResampler_new_sinc__ffi(c_arg__resample_ratio, c_arg__max_resample_ratio_relative, c_arg__parameters, c_arg__chunk_size, c_arg__nchannels, c_arg__res_type, c_arg__dtype); return handle_result(res); } -SEXP savvy_HRealFft_dtype__impl(SEXP self__) { - SEXP res = savvy_HRealFft_dtype__ffi(self__); +SEXP savvy_HResampler_new_fast__impl(SEXP c_arg__resample_ratio, SEXP c_arg__max_resample_ratio_relative, SEXP c_arg__pol_deg, SEXP c_arg__chunk_size, SEXP c_arg__nchannels, SEXP c_arg__res_type, SEXP c_arg__dtype) { + SEXP res = savvy_HResampler_new_fast__ffi(c_arg__resample_ratio, c_arg__max_resample_ratio_relative, c_arg__pol_deg, c_arg__chunk_size, c_arg__nchannels, c_arg__res_type, c_arg__dtype); return handle_result(res); } -SEXP savvy_HRealFft_print__impl(SEXP self__) { - SEXP res = savvy_HRealFft_print__ffi(self__); +SEXP savvy_HResampler_process__impl(SEXP self__, SEXP c_arg__harray) { + SEXP res = savvy_HResampler_process__ffi(self__, c_arg__harray); return handle_result(res); } -SEXP savvy_HRealFft_clone__impl(SEXP self__) { - SEXP res = savvy_HRealFft_clone__ffi(self__); +SEXP savvy_HResampler_set_resample_ratio__impl(SEXP self__, SEXP c_arg__new_ratio, SEXP c_arg__ramp) { + SEXP res = savvy_HResampler_set_resample_ratio__ffi(self__, c_arg__new_ratio, c_arg__ramp); return handle_result(res); } -SEXP savvy_HRealFft_is_unique__impl(SEXP self__) { - SEXP res = savvy_HRealFft_is_unique__ffi(self__); +SEXP savvy_HResampler_set_resample_ratio_relative__impl(SEXP self__, SEXP c_arg__rel_ratio, SEXP c_arg__ramp) { + SEXP res = savvy_HResampler_set_resample_ratio_relative__ffi(self__, c_arg__rel_ratio, c_arg__ramp); return handle_result(res); } -SEXP savvy_HRealFft_invalidate__impl(SEXP self__) { - SEXP res = savvy_HRealFft_invalidate__ffi(self__); +SEXP savvy_HResampler_reset__impl(SEXP self__) { + SEXP res = savvy_HResampler_reset__ffi(self__); return handle_result(res); } -SEXP savvy_HResampler_new_fft__impl(SEXP sr_in, SEXP sr_out, SEXP chunk_size, SEXP sub_chunks, SEXP nchannels, SEXP res_type, SEXP dtype) { - SEXP res = savvy_HResampler_new_fft__ffi(sr_in, sr_out, chunk_size, sub_chunks, nchannels, res_type, dtype); +SEXP savvy_HResampler_res_type__impl(SEXP self__) { + SEXP res = savvy_HResampler_res_type__ffi(self__); return handle_result(res); } -SEXP savvy_HResampler_new_sinc__impl(SEXP resample_ratio, SEXP max_resample_ratio_relative, SEXP parameters, SEXP chunk_size, SEXP nchannels, SEXP res_type, SEXP dtype) { - SEXP res = savvy_HResampler_new_sinc__ffi(resample_ratio, max_resample_ratio_relative, parameters, chunk_size, nchannels, res_type, dtype); +SEXP savvy_HResampler_dtype__impl(SEXP self__) { + SEXP res = savvy_HResampler_dtype__ffi(self__); return handle_result(res); } -SEXP savvy_HResampler_new_fast__impl(SEXP resample_ratio, SEXP max_resample_ratio_relative, SEXP pol_deg, SEXP chunk_size, SEXP nchannels, SEXP res_type, SEXP dtype) { - SEXP res = savvy_HResampler_new_fast__ffi(resample_ratio, max_resample_ratio_relative, pol_deg, chunk_size, nchannels, res_type, dtype); +SEXP savvy_HResampler_print__impl(SEXP self__) { + SEXP res = savvy_HResampler_print__ffi(self__); return handle_result(res); } -SEXP savvy_HResampler_process__impl(SEXP self__, SEXP harray) { - SEXP res = savvy_HResampler_process__ffi(self__, harray); +SEXP savvy_HResamplerType_print__impl(SEXP self__) { + SEXP res = savvy_HResamplerType_print__ffi(self__); return handle_result(res); } -SEXP savvy_HResampler_set_resample_ratio__impl(SEXP self__, SEXP new_ratio, SEXP ramp) { - SEXP res = savvy_HResampler_set_resample_ratio__ffi(self__, new_ratio, ramp); +SEXP savvy_HResamplerType_eq__impl(SEXP self__, SEXP c_arg__other) { + SEXP res = savvy_HResamplerType_eq__ffi(self__, c_arg__other); return handle_result(res); } -SEXP savvy_HResampler_set_resample_ratio_relative__impl(SEXP self__, SEXP rel_ratio, SEXP ramp) { - SEXP res = savvy_HResampler_set_resample_ratio_relative__ffi(self__, rel_ratio, ramp); +SEXP savvy_HResamplerType_ne__impl(SEXP self__, SEXP c_arg__other) { + SEXP res = savvy_HResamplerType_ne__ffi(self__, c_arg__other); return handle_result(res); } -SEXP savvy_HResampler_reset__impl(SEXP self__) { - SEXP res = savvy_HResampler_reset__ffi(self__); +SEXP savvy_HSincInterpolationParameters_new__impl(SEXP c_arg__sinc_len, SEXP c_arg__f_cutoff, SEXP c_arg__oversampling_factor, SEXP c_arg__interpolation, SEXP c_arg__window) { + SEXP res = savvy_HSincInterpolationParameters_new__ffi(c_arg__sinc_len, c_arg__f_cutoff, c_arg__oversampling_factor, c_arg__interpolation, c_arg__window); return handle_result(res); } -SEXP savvy_HResampler_res_type__impl(SEXP self__) { - SEXP res = savvy_HResampler_res_type__ffi(self__); +SEXP savvy_HSincInterpolationParameters_print__impl(SEXP self__) { + SEXP res = savvy_HSincInterpolationParameters_print__ffi(self__); return handle_result(res); } -SEXP savvy_HResampler_dtype__impl(SEXP self__) { - SEXP res = savvy_HResampler_dtype__ffi(self__); +SEXP savvy_HStft_new_forward__impl(SEXP c_arg__length, SEXP c_arg__dtype) { + SEXP res = savvy_HStft_new_forward__ffi(c_arg__length, c_arg__dtype); return handle_result(res); } -SEXP savvy_HResampler_print__impl(SEXP self__) { - SEXP res = savvy_HResampler_print__ffi(self__); +SEXP savvy_HStft_new_real_forward__impl(SEXP c_arg__length, SEXP c_arg__dtype) { + SEXP res = savvy_HStft_new_real_forward__ffi(c_arg__length, c_arg__dtype); return handle_result(res); } -SEXP savvy_HResamplerType_print__impl(SEXP self__) { - SEXP res = savvy_HResamplerType_print__ffi(self__); +SEXP savvy_HStft_process__impl(SEXP self__, SEXP c_arg__harray, SEXP c_arg__hop_length, SEXP c_arg__window_length, SEXP c_arg__window) { + SEXP res = savvy_HStft_process__ffi(self__, c_arg__harray, c_arg__hop_length, c_arg__window_length, c_arg__window); return handle_result(res); } -SEXP savvy_HResamplerType_eq__impl(SEXP self__, SEXP other) { - SEXP res = savvy_HResamplerType_eq__ffi(self__, other); +SEXP savvy_HStft_dtype__impl(SEXP self__) { + SEXP res = savvy_HStft_dtype__ffi(self__); return handle_result(res); } -SEXP savvy_HResamplerType_ne__impl(SEXP self__, SEXP other) { - SEXP res = savvy_HResamplerType_ne__ffi(self__, other); +SEXP savvy_HStft_print__impl(SEXP self__) { + SEXP res = savvy_HStft_print__ffi(self__); return handle_result(res); } -SEXP savvy_HSincInterpolationParameters_new__impl(SEXP sinc_len, SEXP f_cutoff, SEXP oversampling_factor, SEXP interpolation, SEXP window) { - SEXP res = savvy_HSincInterpolationParameters_new__ffi(sinc_len, f_cutoff, oversampling_factor, interpolation, window); +SEXP savvy_HStft_clone__impl(SEXP self__) { + SEXP res = savvy_HStft_clone__ffi(self__); return handle_result(res); } -SEXP savvy_HSincInterpolationParameters_print__impl(SEXP self__) { - SEXP res = savvy_HSincInterpolationParameters_print__ffi(self__); +SEXP savvy_HStft_is_unique__impl(SEXP self__) { + SEXP res = savvy_HStft_is_unique__ffi(self__); + return handle_result(res); +} + +SEXP savvy_HStft_invalidate__impl(SEXP self__) { + SEXP res = savvy_HStft_invalidate__ffi(self__); return handle_result(res); } -SEXP savvy_HWindow_barthann__impl(SEXP npoints, SEXP sym, SEXP dtype) { - SEXP res = savvy_HWindow_barthann__ffi(npoints, sym, dtype); +SEXP savvy_HWindow_barthann__impl(SEXP c_arg__npoints, SEXP c_arg__sym, SEXP c_arg__dtype) { + SEXP res = savvy_HWindow_barthann__ffi(c_arg__npoints, c_arg__sym, c_arg__dtype); return handle_result(res); } -SEXP savvy_HWindow_bartlett__impl(SEXP npoints, SEXP sym, SEXP dtype) { - SEXP res = savvy_HWindow_bartlett__ffi(npoints, sym, dtype); +SEXP savvy_HWindow_bartlett__impl(SEXP c_arg__npoints, SEXP c_arg__sym, SEXP c_arg__dtype) { + SEXP res = savvy_HWindow_bartlett__ffi(c_arg__npoints, c_arg__sym, c_arg__dtype); return handle_result(res); } -SEXP savvy_HWindow_blackman__impl(SEXP npoints, SEXP sym, SEXP dtype) { - SEXP res = savvy_HWindow_blackman__ffi(npoints, sym, dtype); +SEXP savvy_HWindow_blackman__impl(SEXP c_arg__npoints, SEXP c_arg__sym, SEXP c_arg__dtype) { + SEXP res = savvy_HWindow_blackman__ffi(c_arg__npoints, c_arg__sym, c_arg__dtype); return handle_result(res); } -SEXP savvy_HWindow_blackmanharris__impl(SEXP npoints, SEXP sym, SEXP dtype) { - SEXP res = savvy_HWindow_blackmanharris__ffi(npoints, sym, dtype); +SEXP savvy_HWindow_blackmanharris__impl(SEXP c_arg__npoints, SEXP c_arg__sym, SEXP c_arg__dtype) { + SEXP res = savvy_HWindow_blackmanharris__ffi(c_arg__npoints, c_arg__sym, c_arg__dtype); return handle_result(res); } -SEXP savvy_HWindow_bohman__impl(SEXP npoints, SEXP sym, SEXP dtype) { - SEXP res = savvy_HWindow_bohman__ffi(npoints, sym, dtype); +SEXP savvy_HWindow_bohman__impl(SEXP c_arg__npoints, SEXP c_arg__sym, SEXP c_arg__dtype) { + SEXP res = savvy_HWindow_bohman__ffi(c_arg__npoints, c_arg__sym, c_arg__dtype); return handle_result(res); } -SEXP savvy_HWindow_boxcar__impl(SEXP npoints, SEXP dtype) { - SEXP res = savvy_HWindow_boxcar__ffi(npoints, dtype); +SEXP savvy_HWindow_boxcar__impl(SEXP c_arg__npoints, SEXP c_arg__dtype) { + SEXP res = savvy_HWindow_boxcar__ffi(c_arg__npoints, c_arg__dtype); return handle_result(res); } -SEXP savvy_HWindow_cosine__impl(SEXP npoints, SEXP sym, SEXP dtype) { - SEXP res = savvy_HWindow_cosine__ffi(npoints, sym, dtype); +SEXP savvy_HWindow_cosine__impl(SEXP c_arg__npoints, SEXP c_arg__sym, SEXP c_arg__dtype) { + SEXP res = savvy_HWindow_cosine__ffi(c_arg__npoints, c_arg__sym, c_arg__dtype); return handle_result(res); } -SEXP savvy_HWindow_hann__impl(SEXP npoints, SEXP sym, SEXP dtype) { - SEXP res = savvy_HWindow_hann__ffi(npoints, sym, dtype); +SEXP savvy_HWindow_hann__impl(SEXP c_arg__npoints, SEXP c_arg__sym, SEXP c_arg__dtype) { + SEXP res = savvy_HWindow_hann__ffi(c_arg__npoints, c_arg__sym, c_arg__dtype); return handle_result(res); } @@ -550,13 +555,13 @@ SEXP savvy_HWindowType_print__impl(SEXP self__) { return handle_result(res); } -SEXP savvy_HWindowType_eq__impl(SEXP self__, SEXP other) { - SEXP res = savvy_HWindowType_eq__ffi(self__, other); +SEXP savvy_HWindowType_eq__impl(SEXP self__, SEXP c_arg__other) { + SEXP res = savvy_HWindowType_eq__ffi(self__, c_arg__other); return handle_result(res); } -SEXP savvy_HWindowType_ne__impl(SEXP self__, SEXP other) { - SEXP res = savvy_HWindowType_ne__ffi(self__, other); +SEXP savvy_HWindowType_ne__impl(SEXP self__, SEXP c_arg__other) { + SEXP res = savvy_HWindowType_ne__ffi(self__, c_arg__other); return handle_result(res); } @@ -635,13 +640,6 @@ static const R_CallMethodDef CallEntries[] = { {"savvy_HPolynomialDegree_print__impl", (DL_FUNC) &savvy_HPolynomialDegree_print__impl, 1}, {"savvy_HPolynomialDegree_eq__impl", (DL_FUNC) &savvy_HPolynomialDegree_eq__impl, 2}, {"savvy_HPolynomialDegree_ne__impl", (DL_FUNC) &savvy_HPolynomialDegree_ne__impl, 2}, - {"savvy_HRealFft_new__impl", (DL_FUNC) &savvy_HRealFft_new__impl, 2}, - {"savvy_HRealFft_process__impl", (DL_FUNC) &savvy_HRealFft_process__impl, 2}, - {"savvy_HRealFft_dtype__impl", (DL_FUNC) &savvy_HRealFft_dtype__impl, 1}, - {"savvy_HRealFft_print__impl", (DL_FUNC) &savvy_HRealFft_print__impl, 1}, - {"savvy_HRealFft_clone__impl", (DL_FUNC) &savvy_HRealFft_clone__impl, 1}, - {"savvy_HRealFft_is_unique__impl", (DL_FUNC) &savvy_HRealFft_is_unique__impl, 1}, - {"savvy_HRealFft_invalidate__impl", (DL_FUNC) &savvy_HRealFft_invalidate__impl, 1}, {"savvy_HResampler_new_fft__impl", (DL_FUNC) &savvy_HResampler_new_fft__impl, 7}, {"savvy_HResampler_new_sinc__impl", (DL_FUNC) &savvy_HResampler_new_sinc__impl, 7}, {"savvy_HResampler_new_fast__impl", (DL_FUNC) &savvy_HResampler_new_fast__impl, 7}, @@ -657,6 +655,14 @@ static const R_CallMethodDef CallEntries[] = { {"savvy_HResamplerType_ne__impl", (DL_FUNC) &savvy_HResamplerType_ne__impl, 2}, {"savvy_HSincInterpolationParameters_new__impl", (DL_FUNC) &savvy_HSincInterpolationParameters_new__impl, 5}, {"savvy_HSincInterpolationParameters_print__impl", (DL_FUNC) &savvy_HSincInterpolationParameters_print__impl, 1}, + {"savvy_HStft_new_forward__impl", (DL_FUNC) &savvy_HStft_new_forward__impl, 2}, + {"savvy_HStft_new_real_forward__impl", (DL_FUNC) &savvy_HStft_new_real_forward__impl, 2}, + {"savvy_HStft_process__impl", (DL_FUNC) &savvy_HStft_process__impl, 5}, + {"savvy_HStft_dtype__impl", (DL_FUNC) &savvy_HStft_dtype__impl, 1}, + {"savvy_HStft_print__impl", (DL_FUNC) &savvy_HStft_print__impl, 1}, + {"savvy_HStft_clone__impl", (DL_FUNC) &savvy_HStft_clone__impl, 1}, + {"savvy_HStft_is_unique__impl", (DL_FUNC) &savvy_HStft_is_unique__impl, 1}, + {"savvy_HStft_invalidate__impl", (DL_FUNC) &savvy_HStft_invalidate__impl, 1}, {"savvy_HWindow_barthann__impl", (DL_FUNC) &savvy_HWindow_barthann__impl, 3}, {"savvy_HWindow_bartlett__impl", (DL_FUNC) &savvy_HWindow_bartlett__impl, 3}, {"savvy_HWindow_blackman__impl", (DL_FUNC) &savvy_HWindow_blackman__impl, 3}, diff --git a/r-harmonium/src/rust/Cargo.toml b/r-harmonium/src/rust/Cargo.toml index 4dde9c0..5fbc4fe 100644 --- a/r-harmonium/src/rust/Cargo.toml +++ b/r-harmonium/src/rust/Cargo.toml @@ -32,7 +32,7 @@ rustfft = { version = "6.2", default-features = false, features = ["avx", "sse", realfft = { version = "3.3", default-features = false } ndarray = { version = "0.16", default-features = false } num-complex = { version = "0.4", default-features = false } -savvy = { version = "0.6.5", default-features = false, features = ["complex"] } +savvy = { version = "0.6.8", default-features = false, features = ["complex"] } [profile.dev] debug = 1 # less precise locations. Reduce size of target dir. diff --git a/r-harmonium/src/rust/api.h b/r-harmonium/src/rust/api.h index bf79218..f09b575 100644 --- a/r-harmonium/src/rust/api.h +++ b/r-harmonium/src/rust/api.h @@ -1,14 +1,14 @@ // methods and associated functions for HArray -SEXP savvy_HArray_new_from_values__ffi(SEXP arr, SEXP dtype); +SEXP savvy_HArray_new_from_values__ffi(SEXP c_arg__arr, SEXP c_arg__dtype); SEXP savvy_HArray_len__ffi(SEXP self__); SEXP savvy_HArray_shape__ffi(SEXP self__); SEXP savvy_HArray_ndim__ffi(SEXP self__); -SEXP savvy_HArray_slice__ffi(SEXP self__, SEXP range); +SEXP savvy_HArray_slice__ffi(SEXP self__, SEXP c_arg__range); SEXP savvy_HArray_print__ffi(SEXP self__); -SEXP savvy_HArray_eq__ffi(SEXP self__, SEXP other); -SEXP savvy_HArray_ne__ffi(SEXP self__, SEXP other); +SEXP savvy_HArray_eq__ffi(SEXP self__, SEXP c_arg__other); +SEXP savvy_HArray_ne__ffi(SEXP self__, SEXP c_arg__other); SEXP savvy_HArray_clone__ffi(SEXP self__); SEXP savvy_HArray_collect__ffi(SEXP self__); SEXP savvy_HArray_dtype__ffi(SEXP self__); @@ -18,15 +18,15 @@ SEXP savvy_HArray_is_unique__ffi(SEXP self__); SEXP savvy_HArray_invalidate__ffi(SEXP self__); // methods and associated functions for HArrayAudio -SEXP savvy_HArrayAudio_nchannels__ffi(SEXP harray); -SEXP savvy_HArrayAudio_nframes__ffi(SEXP harray); -SEXP savvy_HArrayAudio_db_to_amplitude__ffi(SEXP harray, SEXP reference, SEXP power); -SEXP savvy_HArrayAudio_to_mono__ffi(SEXP harray); +SEXP savvy_HArrayAudio_nchannels__ffi(SEXP c_arg__harray); +SEXP savvy_HArrayAudio_nframes__ffi(SEXP c_arg__harray); +SEXP savvy_HArrayAudio_db_to_amplitude__ffi(SEXP c_arg__harray, SEXP c_arg__reference, SEXP c_arg__power); +SEXP savvy_HArrayAudio_to_mono__ffi(SEXP c_arg__harray); // methods and associated functions for HAudioSink SEXP savvy_HAudioSink_new__ffi(void); -SEXP savvy_HAudioSink_append_from_harray__ffi(SEXP self__, SEXP harray, SEXP sr); -SEXP savvy_HAudioSink_append_from_file__ffi(SEXP self__, SEXP fpath); +SEXP savvy_HAudioSink_append_from_harray__ffi(SEXP self__, SEXP c_arg__harray, SEXP c_arg__sr); +SEXP savvy_HAudioSink_append_from_file__ffi(SEXP self__, SEXP c_arg__fpath); SEXP savvy_HAudioSink_audio_default_device__ffi(void); SEXP savvy_HAudioSink_audio_output_devices__ffi(void); SEXP savvy_HAudioSink_audio_supported_configs__ffi(void); @@ -37,20 +37,20 @@ SEXP savvy_HAudioSink_is_paused__ffi(SEXP self__); SEXP savvy_HAudioSink_len__ffi(SEXP self__); SEXP savvy_HAudioSink_pause__ffi(SEXP self__); SEXP savvy_HAudioSink_play__ffi(SEXP self__); -SEXP savvy_HAudioSink_set_speed__ffi(SEXP self__, SEXP value); -SEXP savvy_HAudioSink_set_volume__ffi(SEXP self__, SEXP value); +SEXP savvy_HAudioSink_set_speed__ffi(SEXP self__, SEXP c_arg__value); +SEXP savvy_HAudioSink_set_volume__ffi(SEXP self__, SEXP c_arg__value); SEXP savvy_HAudioSink_skip_one__ffi(SEXP self__); SEXP savvy_HAudioSink_sleep_until_end__ffi(SEXP self__); SEXP savvy_HAudioSink_speed__ffi(SEXP self__); SEXP savvy_HAudioSink_stop__ffi(SEXP self__); -SEXP savvy_HAudioSink_try_seek__ffi(SEXP self__, SEXP pos); +SEXP savvy_HAudioSink_try_seek__ffi(SEXP self__, SEXP c_arg__pos); SEXP savvy_HAudioSink_volume__ffi(SEXP self__); SEXP savvy_HAudioSink_invalidate__ffi(SEXP self__); // methods and associated functions for HDataType SEXP savvy_HDataType_print__ffi(SEXP self__); -SEXP savvy_HDataType_eq__ffi(SEXP self__, SEXP other); -SEXP savvy_HDataType_ne__ffi(SEXP self__, SEXP other); +SEXP savvy_HDataType_eq__ffi(SEXP self__, SEXP c_arg__other); +SEXP savvy_HDataType_ne__ffi(SEXP self__, SEXP c_arg__other); // methods and associated functions for HDecodedAudio SEXP savvy_HDecodedAudio_harray__ffi(SEXP self__); @@ -61,11 +61,11 @@ SEXP savvy_HDecodedAudio_invalidate__ffi(SEXP self__); SEXP savvy_HDecoderStream_stream__ffi(SEXP self__); // methods and associated functions for HFft -SEXP savvy_HFft_new_forward__ffi(SEXP length, SEXP dtype); -SEXP savvy_HFft_new_inverse__ffi(SEXP length, SEXP dtype); -SEXP savvy_HFft_new_real_forward__ffi(SEXP length, SEXP dtype); -SEXP savvy_HFft_new_real_inverse__ffi(SEXP length, SEXP dtype); -SEXP savvy_HFft_process__ffi(SEXP self__, SEXP harray); +SEXP savvy_HFft_new_forward__ffi(SEXP c_arg__length, SEXP c_arg__dtype); +SEXP savvy_HFft_new_inverse__ffi(SEXP c_arg__length, SEXP c_arg__dtype); +SEXP savvy_HFft_new_real_forward__ffi(SEXP c_arg__length, SEXP c_arg__dtype); +SEXP savvy_HFft_new_real_inverse__ffi(SEXP c_arg__length, SEXP c_arg__dtype); +SEXP savvy_HFft_process__ffi(SEXP self__, SEXP c_arg__harray); SEXP savvy_HFft_dtype__ffi(SEXP self__); SEXP savvy_HFft_print__ffi(SEXP self__); SEXP savvy_HFft_clone__ffi(SEXP self__); @@ -73,43 +73,34 @@ SEXP savvy_HFft_is_unique__ffi(SEXP self__); SEXP savvy_HFft_invalidate__ffi(SEXP self__); // methods and associated functions for HFile -SEXP savvy_HFile_decode__ffi(SEXP fpath, SEXP dtype); -SEXP savvy_HFile_decode_stream__ffi(SEXP fpath, SEXP frames, SEXP dtype); -SEXP savvy_HFile_metadata__ffi(SEXP fpath, SEXP metadata_type); -SEXP savvy_HFile_params__ffi(SEXP fpath); -SEXP savvy_HFile_verify__ffi(SEXP fpath); +SEXP savvy_HFile_decode__ffi(SEXP c_arg__fpath, SEXP c_arg__dtype); +SEXP savvy_HFile_decode_stream__ffi(SEXP c_arg__fpath, SEXP c_arg__frames, SEXP c_arg__dtype); +SEXP savvy_HFile_metadata__ffi(SEXP c_arg__fpath, SEXP c_arg__metadata_type); +SEXP savvy_HFile_params__ffi(SEXP c_arg__fpath); +SEXP savvy_HFile_verify__ffi(SEXP c_arg__fpath); // methods and associated functions for HInterpolationType SEXP savvy_HInterpolationType_print__ffi(SEXP self__); -SEXP savvy_HInterpolationType_eq__ffi(SEXP self__, SEXP other); -SEXP savvy_HInterpolationType_ne__ffi(SEXP self__, SEXP other); +SEXP savvy_HInterpolationType_eq__ffi(SEXP self__, SEXP c_arg__other); +SEXP savvy_HInterpolationType_ne__ffi(SEXP self__, SEXP c_arg__other); // methods and associated functions for HMetadataType SEXP savvy_HMetadataType_print__ffi(SEXP self__); -SEXP savvy_HMetadataType_eq__ffi(SEXP self__, SEXP other); -SEXP savvy_HMetadataType_ne__ffi(SEXP self__, SEXP other); +SEXP savvy_HMetadataType_eq__ffi(SEXP self__, SEXP c_arg__other); +SEXP savvy_HMetadataType_ne__ffi(SEXP self__, SEXP c_arg__other); // methods and associated functions for HPolynomialDegree SEXP savvy_HPolynomialDegree_print__ffi(SEXP self__); -SEXP savvy_HPolynomialDegree_eq__ffi(SEXP self__, SEXP other); -SEXP savvy_HPolynomialDegree_ne__ffi(SEXP self__, SEXP other); - -// methods and associated functions for HRealFft -SEXP savvy_HRealFft_new__ffi(SEXP length, SEXP dtype); -SEXP savvy_HRealFft_process__ffi(SEXP self__, SEXP harray); -SEXP savvy_HRealFft_dtype__ffi(SEXP self__); -SEXP savvy_HRealFft_print__ffi(SEXP self__); -SEXP savvy_HRealFft_clone__ffi(SEXP self__); -SEXP savvy_HRealFft_is_unique__ffi(SEXP self__); -SEXP savvy_HRealFft_invalidate__ffi(SEXP self__); +SEXP savvy_HPolynomialDegree_eq__ffi(SEXP self__, SEXP c_arg__other); +SEXP savvy_HPolynomialDegree_ne__ffi(SEXP self__, SEXP c_arg__other); // methods and associated functions for HResampler -SEXP savvy_HResampler_new_fft__ffi(SEXP sr_in, SEXP sr_out, SEXP chunk_size, SEXP sub_chunks, SEXP nchannels, SEXP res_type, SEXP dtype); -SEXP savvy_HResampler_new_sinc__ffi(SEXP resample_ratio, SEXP max_resample_ratio_relative, SEXP parameters, SEXP chunk_size, SEXP nchannels, SEXP res_type, SEXP dtype); -SEXP savvy_HResampler_new_fast__ffi(SEXP resample_ratio, SEXP max_resample_ratio_relative, SEXP pol_deg, SEXP chunk_size, SEXP nchannels, SEXP res_type, SEXP dtype); -SEXP savvy_HResampler_process__ffi(SEXP self__, SEXP harray); -SEXP savvy_HResampler_set_resample_ratio__ffi(SEXP self__, SEXP new_ratio, SEXP ramp); -SEXP savvy_HResampler_set_resample_ratio_relative__ffi(SEXP self__, SEXP rel_ratio, SEXP ramp); +SEXP savvy_HResampler_new_fft__ffi(SEXP c_arg__sr_in, SEXP c_arg__sr_out, SEXP c_arg__chunk_size, SEXP c_arg__sub_chunks, SEXP c_arg__nchannels, SEXP c_arg__res_type, SEXP c_arg__dtype); +SEXP savvy_HResampler_new_sinc__ffi(SEXP c_arg__resample_ratio, SEXP c_arg__max_resample_ratio_relative, SEXP c_arg__parameters, SEXP c_arg__chunk_size, SEXP c_arg__nchannels, SEXP c_arg__res_type, SEXP c_arg__dtype); +SEXP savvy_HResampler_new_fast__ffi(SEXP c_arg__resample_ratio, SEXP c_arg__max_resample_ratio_relative, SEXP c_arg__pol_deg, SEXP c_arg__chunk_size, SEXP c_arg__nchannels, SEXP c_arg__res_type, SEXP c_arg__dtype); +SEXP savvy_HResampler_process__ffi(SEXP self__, SEXP c_arg__harray); +SEXP savvy_HResampler_set_resample_ratio__ffi(SEXP self__, SEXP c_arg__new_ratio, SEXP c_arg__ramp); +SEXP savvy_HResampler_set_resample_ratio_relative__ffi(SEXP self__, SEXP c_arg__rel_ratio, SEXP c_arg__ramp); SEXP savvy_HResampler_reset__ffi(SEXP self__); SEXP savvy_HResampler_res_type__ffi(SEXP self__); SEXP savvy_HResampler_dtype__ffi(SEXP self__); @@ -117,24 +108,34 @@ SEXP savvy_HResampler_print__ffi(SEXP self__); // methods and associated functions for HResamplerType SEXP savvy_HResamplerType_print__ffi(SEXP self__); -SEXP savvy_HResamplerType_eq__ffi(SEXP self__, SEXP other); -SEXP savvy_HResamplerType_ne__ffi(SEXP self__, SEXP other); +SEXP savvy_HResamplerType_eq__ffi(SEXP self__, SEXP c_arg__other); +SEXP savvy_HResamplerType_ne__ffi(SEXP self__, SEXP c_arg__other); // methods and associated functions for HSincInterpolationParameters -SEXP savvy_HSincInterpolationParameters_new__ffi(SEXP sinc_len, SEXP f_cutoff, SEXP oversampling_factor, SEXP interpolation, SEXP window); +SEXP savvy_HSincInterpolationParameters_new__ffi(SEXP c_arg__sinc_len, SEXP c_arg__f_cutoff, SEXP c_arg__oversampling_factor, SEXP c_arg__interpolation, SEXP c_arg__window); SEXP savvy_HSincInterpolationParameters_print__ffi(SEXP self__); +// methods and associated functions for HStft +SEXP savvy_HStft_new_forward__ffi(SEXP c_arg__length, SEXP c_arg__dtype); +SEXP savvy_HStft_new_real_forward__ffi(SEXP c_arg__length, SEXP c_arg__dtype); +SEXP savvy_HStft_process__ffi(SEXP self__, SEXP c_arg__harray, SEXP c_arg__hop_length, SEXP c_arg__window_length, SEXP c_arg__window); +SEXP savvy_HStft_dtype__ffi(SEXP self__); +SEXP savvy_HStft_print__ffi(SEXP self__); +SEXP savvy_HStft_clone__ffi(SEXP self__); +SEXP savvy_HStft_is_unique__ffi(SEXP self__); +SEXP savvy_HStft_invalidate__ffi(SEXP self__); + // methods and associated functions for HWindow -SEXP savvy_HWindow_barthann__ffi(SEXP npoints, SEXP sym, SEXP dtype); -SEXP savvy_HWindow_bartlett__ffi(SEXP npoints, SEXP sym, SEXP dtype); -SEXP savvy_HWindow_blackman__ffi(SEXP npoints, SEXP sym, SEXP dtype); -SEXP savvy_HWindow_blackmanharris__ffi(SEXP npoints, SEXP sym, SEXP dtype); -SEXP savvy_HWindow_bohman__ffi(SEXP npoints, SEXP sym, SEXP dtype); -SEXP savvy_HWindow_boxcar__ffi(SEXP npoints, SEXP dtype); -SEXP savvy_HWindow_cosine__ffi(SEXP npoints, SEXP sym, SEXP dtype); -SEXP savvy_HWindow_hann__ffi(SEXP npoints, SEXP sym, SEXP dtype); +SEXP savvy_HWindow_barthann__ffi(SEXP c_arg__npoints, SEXP c_arg__sym, SEXP c_arg__dtype); +SEXP savvy_HWindow_bartlett__ffi(SEXP c_arg__npoints, SEXP c_arg__sym, SEXP c_arg__dtype); +SEXP savvy_HWindow_blackman__ffi(SEXP c_arg__npoints, SEXP c_arg__sym, SEXP c_arg__dtype); +SEXP savvy_HWindow_blackmanharris__ffi(SEXP c_arg__npoints, SEXP c_arg__sym, SEXP c_arg__dtype); +SEXP savvy_HWindow_bohman__ffi(SEXP c_arg__npoints, SEXP c_arg__sym, SEXP c_arg__dtype); +SEXP savvy_HWindow_boxcar__ffi(SEXP c_arg__npoints, SEXP c_arg__dtype); +SEXP savvy_HWindow_cosine__ffi(SEXP c_arg__npoints, SEXP c_arg__sym, SEXP c_arg__dtype); +SEXP savvy_HWindow_hann__ffi(SEXP c_arg__npoints, SEXP c_arg__sym, SEXP c_arg__dtype); // methods and associated functions for HWindowType SEXP savvy_HWindowType_print__ffi(SEXP self__); -SEXP savvy_HWindowType_eq__ffi(SEXP self__, SEXP other); -SEXP savvy_HWindowType_ne__ffi(SEXP self__, SEXP other); \ No newline at end of file +SEXP savvy_HWindowType_eq__ffi(SEXP self__, SEXP c_arg__other); +SEXP savvy_HWindowType_ne__ffi(SEXP self__, SEXP c_arg__other); \ No newline at end of file diff --git a/r-harmonium/src/rust/src/hfft.rs b/r-harmonium/src/rust/src/hfft.rs index c7d9efe..758862e 100644 --- a/r-harmonium/src/rust/src/hfft.rs +++ b/r-harmonium/src/rust/src/hfft.rs @@ -20,17 +20,6 @@ pub trait HFftR: Send + Sync { #[derive(Clone)] pub struct HFft(pub Arc); -///// HRealFft -///// An HRealFft is used to create real FFTs. It caches results internally, so when making more than one FFT it is advisable to reuse the same planner. -///// -///// This planner is used to calculate FFTs of real valued inputs and its inverse operation. -///// -///// # Methods -///// -#[savvy] -#[derive(Clone)] -pub struct HRealFft(pub Arc); - #[savvy] impl HFft { /// HFft @@ -68,10 +57,8 @@ impl HFft { /// /// ```r /// library(harmonium) - /// arr = array(c(1+1i,2+2i,3+3i,4+4i,5+5i,6+6i), c(3,2)) /// dtype = HDataType$Complex32 - /// harray = HArray$new_from_values(arr, dtype) - /// hfft = HFft$new_forward(3L, harray$dtype()) + /// hfft = HFft$new_forward(3L, dtype) /// ``` /// /// _________ @@ -124,10 +111,8 @@ impl HFft { /// /// ```r /// library(harmonium) - /// arr = array(c(1+1i,2+2i,3+3i,4+4i,5+5i,6+6i), c(3,2)) /// dtype = HDataType$Complex32 - /// harray = HArray$new_from_values(arr, dtype) - /// hfft = HFft$new_inverse(3L, harray$dtype()) + /// hfft = HFft$new_inverse(3L, dtype) /// ``` /// /// _________ @@ -163,8 +148,7 @@ impl HFft { /// /// - `length` /// - /// An integer denoting the length of the input for forward FFTs and the length of the output for inverse FFTs. For 2D `HArray`'s, nrows must - /// be provided. + /// An integer denoting the length of the input. For 2D `HArray`'s, nrows must be provided. /// /// - `dtype` /// @@ -180,22 +164,20 @@ impl HFft { /// /// ```r /// library(harmonium) - /// arr = array(c(1,2,3,4,5,6,7,8,9,10,11,12), c(3,4)) /// dtype = HDataType$Float32 - /// harray = HArray$new_from_values(arr, dtype) - /// hfft = HFft$new_real_forward(3L, harray$dtype()) + /// hfft = HFft$new_real_forward(3L, dtype) /// ``` /// /// _________ /// - fn new_real_forward(length: Sexp, dtype: &HDataType) -> savvy::Result { + fn new_real_forward(length: Sexp, dtype: &HDataType) -> savvy::Result { let length: i32 = length.to_scalar()?; let length: usize = length .try_into() .map_err(|_| savvy::Error::new("Cannot convert i32 to usize."))?; match dtype { - HDataType::Float32 => Ok(HRealFft(Arc::new(RealFftForward::::new(length)))), - HDataType::Float64 => Ok(HRealFft(Arc::new(RealFftForward::::new(length)))), + HDataType::Float32 => Ok(HFft(Arc::new(RealFftForward::::new(length)))), + HDataType::Float64 => Ok(HFft(Arc::new(RealFftForward::::new(length)))), HDataType::Complex32 => Err("This HFft is for float dtypes.".into()), HDataType::Complex64 => Err("This HFft is for float dtypes.".into()), } @@ -219,8 +201,7 @@ impl HFft { /// /// - `length` /// - /// An integer denoting the length of the input for forward FFTs and the length of the output for inverse FFTs. For 2D `HArray`'s, nrows must - /// be provided. + /// An integer denoting the length of the output. For 2D `HArray`'s, nrows of the output must be provided. /// /// - `dtype` /// @@ -236,24 +217,22 @@ impl HFft { /// /// ```r /// library(harmonium) - /// arr = array(c(1,2,3,4,5,6,7,8,9,10,11,12), c(3,4)) /// dtype = HDataType$Float32 - /// harray = HArray$new_from_values(arr, dtype) - /// hfft = HFft$new_real_inverse(3L, harray$dtype()) + /// hfft = HFft$new_real_inverse(3L, dtype) /// ``` /// /// _________ /// - fn new_real_inverse(length: Sexp, dtype: &HDataType) -> savvy::Result { + fn new_real_inverse(length: Sexp, dtype: &HDataType) -> savvy::Result { let length: i32 = length.to_scalar()?; let length: usize = length .try_into() .map_err(|_| savvy::Error::new("Cannot convert i32 to usize."))?; match dtype { - HDataType::Float32 => Err("This HFft is for float dtypes.".into()), - HDataType::Float64 => Err("This HFft is for float dtypes.".into()), - HDataType::Complex32 => Ok(HRealFft(Arc::new(RealFftInverse::::new(length)))), - HDataType::Complex64 => Ok(HRealFft(Arc::new(RealFftInverse::::new(length)))), + HDataType::Float32 => Err("This HFft is for complex dtypes.".into()), + HDataType::Float64 => Err("This HFft is for complex dtypes.".into()), + HDataType::Complex32 => Ok(HFft(Arc::new(RealFftInverse::::new(length)))), + HDataType::Complex64 => Ok(HFft(Arc::new(RealFftInverse::::new(length)))), } } @@ -262,13 +241,14 @@ impl HFft { /// /// `process(harray: HArray)` /// - /// Computes the fast fourier transform of a complex `HArray`. - /// The FFT computed may be forward or inverse, depending on the `HFFT` created. + /// Computes the fast fourier transform of an `HArray`. + /// The FFT computed may be forward or inverse, depending on the `HFft` created. /// For a real forward FFT, transforms a real signal of length `N` to a complex-valued spectrum of length `N/2+1` (with `N/2` rounded down). /// For a real inverse FFT, transforms a complex spectrum of length `N/2+1` (with `N/2` rounded down) to a real-valued /// signal of length `N`. /// - /// The operation is done in-place. + /// The operation is done in-place for FFT. + /// The operation is done in-place for real FFT, which means the same external pointer will be used to store it. A new `HArray` is created in this case. /// /// FFT (Fast Fourier Transform) refers to a way the discrete Fourier Transform (DFT) can be calculated efficiently, /// by using symmetries in the calculated terms. The symmetry is highest when n is a power of 2, and the transform @@ -335,10 +315,8 @@ impl HFft { /// /// ```r /// library(harmonium) - /// arr = array(c(1+1i,2+2i,3+3i,4+4i,5+5i,6+6i), c(3,2)) /// dtype = HDataType$Complex32 - /// harray = HArray$new_from_values(arr, dtype) - /// hfft = HFft$new_forward(3L, harray$dtype()) + /// hfft = HFft$new_forward(3L, dtype) /// hfft$dtype() /// ``` /// @@ -361,10 +339,8 @@ impl HFft { /// /// ```r /// library(harmonium) - /// arr = array(c(1+1i,2+2i,3+3i,4+4i,5+5i,6+6i), c(3,2)) /// dtype = HDataType$Complex32 - /// harray = HArray$new_from_values(arr, dtype) - /// hfft = HFft$new_forward(3L, harray$dtype()) + /// hfft = HFft$new_forward(3L, dtype) /// hfft$print() /// /// # or similarly: @@ -397,10 +373,8 @@ impl HFft { /// /// ```r /// library(harmonium) - /// arr = array(c(1+1i,2+2i,3+3i,4+4i,5+5i,6+6i), c(3,2)) /// dtype = HDataType$Complex32 - /// harray = HArray$new_from_values(arr, dtype) - /// hfft = HFft$new_forward(3L, harray$dtype()) + /// hfft = HFft$new_forward(3L, dtype) /// hfft$clone() /// ``` /// @@ -428,10 +402,8 @@ impl HFft { /// /// ```r /// library(harmonium) - /// arr = array(c(1+1i,2+2i,3+3i,4+4i,5+5i,6+6i), c(3,2)) /// dtype = HDataType$Complex32 - /// harray = HArray$new_from_values(arr, dtype) - /// hfft = HFft$new_forward(3L, harray$dtype()) + /// hfft = HFft$new_forward(3L, dtype) /// hfft$is_unique() # TRUE. /// /// hfft2 = hfft$clone() @@ -459,283 +431,8 @@ impl HFft { /// /// ```r /// library(harmonium) - /// arr = array(c(1+1i,2+2i,3+3i,4+4i,5+5i,6+6i), c(3,2)) - /// dtype = HDataType$Complex32 - /// harray = HArray$new_from_values(arr, dtype) - /// hfft = HFft$new_forward(3L, harray$dtype()) - /// hfft$invalidate() - /// ``` - /// - /// _________ - /// - pub fn invalidate(self) -> savvy::Result<()> { - Ok(()) - } -} - -#[savvy] -impl HRealFft { - /// HRealFft - /// ## new - /// - /// `new(length: integer, dtype: HDataType) -> HRealFft` - /// - /// Creates a new `HRealFft` instance which will be used to calculate forward FFTs. - /// - /// If you plan on creating multiple FFT instances, it is recommended to reuse the same planner for all of them. This is because the planner re-uses internal data - /// across FFT instances wherever possible, saving memory and reducing setup time (FFT instances created with one planner will never re-use data and buffers with - /// FFT instances created by a different planner). - /// - /// In the constructor, the FftPlanner will detect available CPU features. If AVX, SSE, Neon, or WASM SIMD are available, it will set itself up to plan FFTs with - /// the fastest available instruction set. If no SIMD instruction sets are available, the planner will seamlessly fall back to planning non-SIMD FFTs. - /// - /// #### Arguments - /// - /// - `length` - /// - /// An integer denoting the length of the input for forward FFTs and the length of the output for inverse FFTs. For 2D `HArray`'s, nrows must - /// be provided. - /// - /// - `dtype` - /// - /// An `HDataType` to indicate the dtype that the `HRealFft` will be working with. If float, - /// will calculate the forward FFT. If complex, will calculate the inverse FFT. - /// - /// #### Returns - /// - /// An `HRealFft`. - /// - /// Will return an error if dtype is of a float type. - /// - /// #### Examples - /// - /// ```r - /// library(harmonium) - /// arr = array(c(1,2,3,4,5,6,7,8,9,10,11,12), c(3,4)) - /// dtype = HDataType$Float32 - /// harray = HArray$new_from_values(arr, dtype) - /// hfft = HRealFft$new(3L, harray$dtype()) - /// ``` - /// - /// _________ - /// - fn new(length: Sexp, dtype: &HDataType) -> savvy::Result { - let length: i32 = length.to_scalar()?; - let length: usize = length - .try_into() - .map_err(|_| savvy::Error::new("Cannot convert i32 to usize."))?; - match dtype { - HDataType::Float32 => Ok(HRealFft(Arc::new(RealFftForward::::new(length)))), - HDataType::Float64 => Ok(HRealFft(Arc::new(RealFftForward::::new(length)))), - HDataType::Complex32 => Ok(HRealFft(Arc::new(RealFftInverse::::new(length)))), - HDataType::Complex64 => Ok(HRealFft(Arc::new(RealFftInverse::::new(length)))), - } - } - - /// HRealFft - /// ## process - /// - /// `process(harray: HArray)` - /// - /// Computes the fast fourier transform of a float `HArray` or the inverse fast fourier transform of a complex `HArray`. - /// For a real forward FFT, transforms a real signal of length `N` to a complex-valued spectrum of length `N/2+1` (with `N/2` rounded down). - /// For a real inverse FFT, transforms a complex spectrum of length `N/2+1` (with `N/2` rounded down) to a real-valued - /// signal of length `N`. - /// - /// The operation is not done in-place, although the same external pointer is used to store the new HArray. - /// - /// The FFT of a real signal is Hermitian-symmetric, X[i] = conj(X[-i]) so the output contains only the positive frequencies - /// below the Nyquist frequency. - /// - /// FFT (Fast Fourier Transform) refers to a way the discrete Fourier Transform (DFT) can be calculated efficiently, - /// by using symmetries in the calculated terms. The symmetry is highest when n is a power of 2, and the transform - /// is therefore most efficient for these sizes. - /// - /// The function does not normalize outputs. Callers must manually normalize the results by scaling each element by - /// `1/sqrt(n)`. Multiple normalization steps can be merged into one via pairwise multiplication, so when doing - /// a forward FFT followed by an inverse callers can normalize once by scaling each element by `1/n`. - /// - /// Elements in the output are ordered by ascending frequency, with the first element corresponding to frequency 0. - /// - /// #### Arguments - /// - /// - `harray` - /// - /// An `HArray`. - /// - /// #### Returns - /// - /// Will return an error if: - /// - /// - The `HArray`'s dtype is incompatible with the `HRealFft`'s dtype. - /// - /// - The `HArray`'s `ndim` is greater than 2. - /// - /// #### Examples - /// - /// ```r - /// library(harmonium) - /// arr = array(c(1,2,3,4,5,6,7,8,9,10,11,12), c(3,4)) - /// dtype = HDataType$Float32 - /// harray = HArray$new_from_values(arr, dtype) - /// # Forward fft - /// fft = HRealFft$new(3L, harray$dtype()) - /// fft$process(harray) - /// # Inverse fft - /// ifft = HRealFft$new(3L, HDataType$Complex32) - /// ifft$process(harray) - /// ``` - /// - /// _________ - /// - fn process(&mut self, harray: &mut HArray) -> savvy::Result<()> { - let inner_mut = self.get_inner_mut(); - inner_mut.process(harray) - } - - /// HRealFft - /// ## dtype - /// - /// `dtype() -> HDataType` - /// - /// Gets the `HRealFft`'s dtype. - /// - /// #### Returns - /// - /// An `HDataType`. - /// - /// #### Examples - /// - /// ```r - /// library(harmonium) - /// arr = array(c(1+1i,2+2i,3+3i,4+4i,5+5i,6+6i), c(3,2)) - /// dtype = HDataType$Complex32 - /// harray = HArray$new_from_values(arr, dtype) - /// hfft = HRealFft$new(3L, HDataType$Complex32) - /// hfft$dtype() - /// ``` - /// - /// _________ - /// - fn dtype(&self) -> savvy::Result { - self.0.dtype() - } - - /// HRealFft - /// ## print - /// - /// `print()` - /// - /// Prints the `HRealFft`. - /// - /// Differently from R's normal behaviour, `print` doesn't return the value invisibly. - /// - /// #### Examples - /// - /// ```r - /// library(harmonium) - /// arr = array(c(1+1i,2+2i,3+3i,4+4i,5+5i,6+6i), c(3,2)) - /// dtype = HDataType$Complex32 - /// harray = HArray$new_from_values(arr, dtype) - /// hfft = HRealFft$new(3L, HDataType$Complex32) - /// hfft$print() - /// - /// # or similarly: - /// print(hfft) - /// ``` - /// - /// _________ - /// - fn print(&self) -> savvy::Result<()> { - r_println!("HRealFft"); - Ok(()) - } - - /// HRealFft - /// ## clone - /// - /// `clone() -> HRealFft` - /// - /// Clones the `HRealFft`. - /// - /// Creates a new `HRealFft`, with the underlying data pointing to the same place in memory. - /// When `HFFT` is cloned, having more than one reference to the same internal struct, and `process` is run, it uses the same cached `Fft` instance, but a new - /// scratch buffer will have to be allocated. - /// - /// #### Returns - /// - /// An `HRealFft`. - /// - /// #### Examples - /// - /// ```r - /// library(harmonium) - /// arr = array(c(1+1i,2+2i,3+3i,4+4i,5+5i,6+6i), c(3,2)) - /// dtype = HDataType$Complex32 - /// harray = HArray$new_from_values(arr, dtype) - /// hfft = HRealFft$new(3L, HDataType$Complex32) - /// hfft$clone() - /// ``` - /// - /// _________ - /// - fn clone(&self) -> savvy::Result { - Ok(std::clone::Clone::clone(self)) - } - - /// HRealFft - /// ## is_unique - /// - /// `is_unique() -> bool` - /// - /// Checks if the object is shared. - /// - /// Since `HRealFft` has a COW ([clone-on-write](https://doc.rust-lang.org/std/borrow/enum.Cow.html)) behaviour, this function is useful to check if a new - /// object will be created or if the change will be done in-place. - /// - /// #### Returns - /// - /// A bool. - /// - /// #### Examples - /// - /// ```r - /// library(harmonium) - /// arr = array(c(1+1i,2+2i,3+3i,4+4i,5+5i,6+6i), c(3,2)) - /// dtype = HDataType$Complex32 - /// harray = HArray$new_from_values(arr, dtype) - /// hfft = HRealFft$new(3L, HDataType$Complex32) - /// hfft$is_unique() # TRUE. - /// - /// hfft2 = hfft$clone() - /// hfft$is_unique() # FALSE, HRealFft object shared with hfft2. - /// ``` - /// - /// _________ - /// - fn is_unique(&mut self) -> savvy::Result { - // Requires &mut to avoid race condition. - let bool = Arc::strong_count(&self.0) == 1; - let logical_sexp: OwnedLogicalSexp = bool.try_into()?; - logical_sexp.into() - } - - /// HRealFft - /// ## invalidate - /// - /// `invalidate()` - /// - /// Replaces the inner value of the external pointer, invalidating it. - /// This function is useful to remove one of the shared references of the inner pointer in rust. - /// - /// #### Examples - /// - /// ```r - /// library(harmonium) - /// arr = array(c(1+1i,2+2i,3+3i,4+4i,5+5i,6+6i), c(3,2)) /// dtype = HDataType$Complex32 - /// harray = HArray$new_from_values(arr, dtype) - /// hfft = HRealFft$new(3L, HDataType$Complex32) + /// hfft = HFft$new_forward(3L, dtype) /// hfft$invalidate() /// ``` /// @@ -747,11 +444,11 @@ impl HRealFft { } macro_rules! impl_hfft { - ($(($t1:ty, $t2:ty, $e1:expr)),+) => { + ($(($t1:ty, $e1:expr)),+) => { $( - impl HFftR for $t1 { + impl HFftR for Fft<$t1> { fn process(&mut self, harray: &mut HArray) -> savvy::Result<()> { - let harray_inner = harray.get_inner_mut().as_any_mut().downcast_mut::<$t2>().ok_or_else(|| savvy::Error::new("HArray and HFft must have the same HDataType."))?; + let harray_inner = harray.get_inner_mut().as_any_mut().downcast_mut::, IxDyn>>().ok_or_else(|| savvy::Error::new("HArray and HFft must have the same HDataType."))?; harmonium_fft::fft::ProcessFft::process(self, harray_inner).map_err(|err| savvy::Error::from(HErrorR::from(err))) } @@ -767,18 +464,7 @@ macro_rules! impl_hfft { }; } -impl_hfft!( - ( - Fft, - harmonium_core::array::HArray, IxDyn>, - HDataType::Complex32 - ), - ( - Fft, - harmonium_core::array::HArray, IxDyn>, - HDataType::Complex64 - ) -); +impl_hfft!((f32, HDataType::Complex32), (f64, HDataType::Complex64)); macro_rules! impl_hrealfftforward { ($(($t1:ty, $t2:ty, $e1:expr)),+) => { @@ -838,16 +524,3 @@ impl HFft { unsafe { Arc::get_mut(&mut self.0).unwrap_unchecked() } } } - -impl HRealFft { - #[doc(hidden)] - pub fn get_inner_mut(&mut self) -> &mut dyn HFftR { - // Weak references are never used. - if Arc::strong_count(&self.0) != 1 { - self.0 = self.0.clone_inner(); - } - // Safety: reference count was checked. - // Use get_mut_unchecked when stable. - unsafe { Arc::get_mut(&mut self.0).unwrap_unchecked() } - } -} diff --git a/r-harmonium/src/rust/src/hstft.rs b/r-harmonium/src/rust/src/hstft.rs new file mode 100644 index 0000000..d4104f8 --- /dev/null +++ b/r-harmonium/src/rust/src/hstft.rs @@ -0,0 +1,430 @@ +use crate::{conversions::ToScalar, errors::HErrorR, harray::HArray, hdatatype::HDataType}; +use harmonium_fft::stft::{RealStftForward, Stft}; +use ndarray::IxDyn; +use num_complex::Complex; +use savvy::{r_println, savvy, OwnedLogicalSexp, Sexp}; +use std::{num::NonZero, sync::Arc}; + +pub trait HStftR: Send + Sync { + fn process( + &mut self, + harray: &mut HArray, + hop_length: NonZero, + window_length: NonZero, + window: Option<&HArray>, + ) -> savvy::Result<()>; + fn dtype(&self) -> savvy::Result; + fn clone_inner(&self) -> Arc; +} + +/// HStft +/// An `HStft` is used to create STFTs. It caches results internally, so when making more than one Stft it is advisable to reuse the same `HStft` instance. +/// +/// # Methods +/// +#[savvy] +#[derive(Clone)] +pub struct HStft(pub Arc); + +#[savvy] +impl HStft { + /// HStft + /// ## new_forward + /// + /// `new_forward(length: integer, dtype: HDataType) -> HStft` + /// + /// Creates a new `HStft` instance which will be used to calculate forward STFTs. + /// + /// If you plan on creating multiple STFT instances, it is recommended to reuse the same planner for all of them. This is because the planner re-uses internal data + /// across STFT instances wherever possible, saving memory and reducing setup time (STFT instances created with one planner will never re-use data and buffers with + /// STFT instances created by a different planner). + /// + /// In the constructor, the FftPlanner will detect available CPU features. If AVX, SSE, Neon, or WASM SIMD are available, it will set itself up to plan STFTs with + /// the fastest available instruction set. If no SIMD instruction sets are available, the planner will seamlessly fall back to planning non-SIMD STFTs. + /// + /// #### Arguments + /// + /// - `length` - an integer denoting the length of the input. For 2D `HArray`'s, nrows must be provided. + /// + /// - `dtype` - a complex `HDataType` to indicate the dtype that the `HStft` will be working with. + /// + /// #### Returns + /// + /// An `HStft`. + /// + /// Will return an error if dtype is of a float type. + /// + /// #### Examples + /// + /// ```r + /// library(harmonium) + /// dtype = HDataType$Complex32 + /// hstft = HStft$new_forward(3L, dtype) + /// ``` + /// + /// _________ + /// + fn new_forward(length: Sexp, dtype: &HDataType) -> savvy::Result { + let length: i32 = length.to_scalar()?; + let length: usize = length + .try_into() + .map_err(|_| savvy::Error::new("Cannot convert i32 to usize."))?; + match dtype { + HDataType::Float32 => Err("This HStft is for Complex dtypes.".into()), + HDataType::Float64 => Err("This HStft is for Complex dtypes.".into()), + HDataType::Complex32 => Ok(HStft(Arc::new(Stft::::new_forward(length)))), + HDataType::Complex64 => Ok(HStft(Arc::new(Stft::::new_forward(length)))), + } + } + + /// HStft + /// ## new_real_forward + /// + /// `new_real_forward(length: integer, dtype: HDataType) -> HStft` + /// + /// Creates a new `HStft` instance which will be used to calculate real forward STFTs. + /// + /// If you plan on creating multiple STFT instances, it is recommended to reuse the same planner for all of them. This is because the planner re-uses internal data + /// across STFT instances wherever possible, saving memory and reducing setup time (STFT instances created with one planner will never re-use data and buffers with + /// STFT instances created by a different planner). + /// + /// In the constructor, the FftPlanner will detect available CPU features. If AVX, SSE, Neon, or WASM SIMD are available, it will set itself up to plan STFTs with + /// the fastest available instruction set. If no SIMD instruction sets are available, the planner will seamlessly fall back to planning non-SIMD STFTs. + /// + /// #### Arguments + /// + /// - `length` - an integer denoting the length of the input. For 2D `HArray`'s, nrows must be provided. + /// + /// - `dtype` - a float `HDataType` to indicate the dtype that the `HStft` will be working with. + /// + /// #### Returns + /// + /// An `HStft`. + /// + /// Will return an error if dtype is of complex type. + /// + /// #### Examples + /// + /// ```r + /// library(harmonium) + /// arr = array(c(1,2,3,4,5,6,7,8,9,10,11,12), c(3,4)) + /// dtype = HDataType$Float32 + /// harray = HArray$new_from_values(arr, dtype) + /// hstft = HStft$new_real_forward(3L, harray$dtype()) + /// ``` + /// + /// _________ + /// + fn new_real_forward(length: Sexp, dtype: &HDataType) -> savvy::Result { + let length: i32 = length.to_scalar()?; + let length: usize = length + .try_into() + .map_err(|_| savvy::Error::new("Cannot convert i32 to usize."))?; + match dtype { + HDataType::Float32 => Ok(HStft(Arc::new(RealStftForward::::new(length)))), + HDataType::Float64 => Ok(HStft(Arc::new(RealStftForward::::new(length)))), + HDataType::Complex32 => Err("This HStft is for float dtypes.".into()), + HDataType::Complex64 => Err("This HStft is for float dtypes.".into()), + } + } + + /// HStft + /// ## process + /// + /// `process(harray: HArray, hop_length: Integer, window_length: Integer, window: Optional)` + /// + /// Computes the STFT of a complex `HArray`. + /// The STFT computed may be forward or inverse, depending on the `HStft` created. + /// + /// The STFT computes the Fourier transform of short overlapping windows of the input. This giving frequency components of the signal as they change over time. + /// + /// The operation is done in-place, which means, in this case, although a new `HArray` is created, the same external pointer will be used to store it. + /// + /// For a forward STFT, the `HArray` output will have the shape: + /// - `(fft_length, n_fft)` if 1D input HArray. + /// - `(ncols, fft_length, n_fft)` if 2D input HArray. + /// + /// For a real forward STFT, it will have the shape: + /// - `(fft_length / 2 + 1, n_fft)` if 1D input HArray. + /// - `(ncols, fft_length / 2 + 1, n_fft)` if 2D input HArray. + /// + /// Where `ncols` is the number of columns of the input HArray, `fft_length` is the length provided when the `HStft` is created, `n_fft` is the number of frames and `fft_length / 2` + /// is a floor division + /// + /// #### Arguments + /// + /// - `harray` - A complex 1D or 2D `HArray`. + /// + /// - `hop_length` - the distance between neighboring sliding window frames. + /// + /// - `window_length` - Each column of the HArray is windowed by window of length `window_length` and then padded with zeros to match n_fft. Padding is added on both + /// the left and the right side of the window so that the window is centered within the frame. Smaller values improve the temporal resolution of the STFT (i.e. the ability + /// to discriminate impulses that are closely spaced in time) at the expense of frequency resolution (i.e. the ability to discriminate pure tones that are closely + /// spaced in frequency). + /// + /// - `window` - A float `HArray` representing a window function. This input is optional. + /// + /// #### Returns + /// + /// Will return an error if: + /// + /// - The `HArray`'s dtype is incompatible with the `HStft`'s dtype. + /// + /// - The `HArray`'s `ndim` is greater than 2. + /// + /// #### Examples + /// + /// ```r + /// library(harmonium) + /// arr = as.array(c(1+1i,2+2i,3+3i,4+4i,5+5i,6+6i)) + /// dtype = HDataType$Complex32 + /// harray = HArray$new_from_values(arr, dtype) + /// hstft = HStft$new_forward(5L, dtype) + /// hop_length = 2L + /// window_length = 3L + /// window = HArray$new_from_values(as.array(c(1,2,3)), HDataType$Float32) + /// hstft$process(harray, hop_length, window_length, window) + /// ``` + /// + /// _________ + /// + fn process( + &mut self, + harray: &mut HArray, + hop_length: Sexp, + window_length: Sexp, + window: Option<&HArray>, + ) -> savvy::Result<()> { + let inner_mut = self.get_inner_mut(); + let hop_length: i32 = hop_length.to_scalar()?; + let hop_length: usize = hop_length + .try_into() + .map_err(|_| savvy::Error::new("Cannot convert i32 to usize."))?; + let hop_length = NonZero::new(hop_length) + .ok_or_else(|| savvy::Error::new("hop_length can't be zero."))?; + let window_length: i32 = window_length.to_scalar()?; + let window_length: usize = window_length + .try_into() + .map_err(|_| savvy::Error::new("Cannot convert i32 to usize."))?; + let window_length = NonZero::new(window_length) + .ok_or_else(|| savvy::Error::new("window_length can't be zero."))?; + inner_mut.process(harray, hop_length, window_length, window) + } + + /// HStft + /// ## dtype + /// + /// `dtype() -> HDataType` + /// + /// Gets the `HStft`'s dtype. + /// + /// #### Returns + /// + /// An `HDataType`. + /// + /// #### Examples + /// + /// ```r + /// library(harmonium) + /// dtype = HDataType$Complex32 + /// hstft = HStft$new_forward(3L, dtype) + /// hstft$dtype() + /// ``` + /// + /// _________ + /// + fn dtype(&self) -> savvy::Result { + self.0.dtype() + } + + /// HStft + /// ## print + /// + /// `print()` + /// + /// Prints the `HStft`. + /// + /// Differently from R's normal behaviour, `print` doesn't return the value invisibly. + /// + /// #### Examples + /// + /// ```r + /// library(harmonium) + /// dtype = HDataType$Complex32 + /// hstft = HStft$new_forward(3L, dtype) + /// hstft$print() + /// + /// # or similarly: + /// print(hstft) + /// ``` + /// + /// _________ + /// + fn print(&self) -> savvy::Result<()> { + r_println!("HStft"); + Ok(()) + } + + /// HStft + /// ## clone + /// + /// `clone() -> HStft` + /// + /// Clones the `HStft`. + /// + /// Creates a new `HStft`, with the underlying data pointing to the same place in memory. + /// When `HSTFT` is cloned, thus having more than one reference to the same internal struct, and `process` is run, + /// it uses the same cached `Fft` instance, but a new scratch buffer will have to be allocated. + /// + /// #### Returns + /// + /// An `HStft`. + /// + /// #### Examples + /// + /// ```r + /// library(harmonium) + /// dtype = HDataType$Complex32 + /// hstft = HStft$new_forward(3L, dtype) + /// hstft$clone() + /// ``` + /// + /// _________ + /// + fn clone(&self) -> savvy::Result { + Ok(std::clone::Clone::clone(self)) + } + + /// HStft + /// ## is_unique + /// + /// `is_unique() -> bool` + /// + /// Checks if the object is unique. + /// + /// Since `HStft` has a COW ([clone-on-write](https://doc.rust-lang.org/std/borrow/enum.Cow.html)) behaviour, this function is useful to check if a new + /// object will be created or if the change will be done in-place. + /// + /// #### Returns + /// + /// A bool. + /// + /// #### Examples + /// + /// ```r + /// library(harmonium) + /// dtype = HDataType$Complex32 + /// hstft = HStft$new_forward(3L, dtype) + /// hstft$is_unique() # TRUE. + /// + /// hstft2 = hstft$clone() + /// hstft$is_unique() # FALSE, hstft shares the same inner object with hstft2. + /// ``` + /// + /// _________ + /// + fn is_unique(&mut self) -> savvy::Result { + // Requires &mut to avoid race condition. + let bool = Arc::strong_count(&self.0) == 1; + let logical_sexp: OwnedLogicalSexp = bool.try_into()?; + logical_sexp.into() + } + + /// HStft + /// ## invalidate + /// + /// `invalidate()` + /// + /// Replaces the inner value of the external pointer, invalidating it. + /// This function is useful to remove one of the shared references of the inner pointer in rust. + /// + /// #### Examples + /// + /// ```r + /// library(harmonium) + /// dtype = HDataType$Complex32 + /// hstft = HStft$new_forward(3L, dtype) + /// hstft$invalidate() + /// ``` + /// + /// _________ + /// + pub fn invalidate(self) -> savvy::Result<()> { + Ok(()) + } +} + +macro_rules! impl_hstft { + ($(($t1:ty, $e1:expr)),+) => { + $( + impl HStftR for Stft<$t1> { + fn process(&mut self, harray: &mut HArray, hop_length: NonZero, window_length: NonZero, window: Option<&HArray>) -> savvy::Result<()> { + let harray_inner = harray.get_inner_mut().as_any_mut().downcast_mut::, IxDyn>>().ok_or_else(|| savvy::Error::new("HArray and HStft must have the same HDataType."))?; + let window = if let Some(harray_window) = window { + Some(harray_window.0.as_any().downcast_ref::>().ok_or_else(|| savvy::Error::new("HArray and HStft must have the same HDataType."))?.as_slice().unwrap()) + } else { + None + }; + let harray_output = harmonium_fft::stft::ProcessStft::process(self, harray_inner, hop_length, window_length, window).map_err(|err| savvy::Error::from(HErrorR::from(err)))?; + *harray = HArray(Arc::new(harray_output)); + Ok(()) + + } + + fn dtype(&self) -> savvy::Result { + Ok($e1) + } + + fn clone_inner(&self) -> Arc { + Arc::new(self.clone()) + } + } + )+ + }; +} + +impl_hstft!((f32, HDataType::Complex32), (f64, HDataType::Complex64)); + +macro_rules! impl_hrealstftforward { + ($(($t1:ty, $e1:expr)),+) => { + $( + impl HStftR for RealStftForward<$t1> { + fn process(&mut self, harray: &mut HArray, hop_length: NonZero, window_length: NonZero, window: Option<&HArray>) -> savvy::Result<()> { + let harray_inner = harray.get_inner_mut().as_any_mut().downcast_mut::>().ok_or_else(|| savvy::Error::new("HArray and HStft must have the same HDataType."))?; + let window = if let Some(harray_window) = window { + Some(harray_window.0.as_any().downcast_ref::>().ok_or_else(|| savvy::Error::new("HArray and HStft must have the same HDataType."))?.as_slice().unwrap()) + } else { + None + }; + let harray_output = harmonium_fft::stft::ProcessStft::process(self, harray_inner, hop_length, window_length, window).map_err(|err| savvy::Error::from(HErrorR::from(err)))?; + *harray = HArray(Arc::new(harray_output)); + Ok(()) + + } + + fn dtype(&self) -> savvy::Result { + Ok($e1) + } + + fn clone_inner(&self) -> Arc { + Arc::new(self.clone()) + } + } + )+ + }; +} + +impl_hrealstftforward!((f32, HDataType::Complex32), (f64, HDataType::Complex64)); + +impl HStft { + #[doc(hidden)] + pub fn get_inner_mut(&mut self) -> &mut dyn HStftR { + // Weak references are never used. + if Arc::strong_count(&self.0) != 1 { + self.0 = self.0.clone_inner(); + } + // Safety: reference count was checked. + // Use get_mut_unchecked when stable. + unsafe { Arc::get_mut(&mut self.0).unwrap_unchecked() } + } +} diff --git a/r-harmonium/src/rust/src/lib.rs b/r-harmonium/src/rust/src/lib.rs index 2a82773..3cac74a 100644 --- a/r-harmonium/src/rust/src/lib.rs +++ b/r-harmonium/src/rust/src/lib.rs @@ -13,6 +13,7 @@ mod hpolynomialdegree; mod hresampler; mod hresamplertype; mod hsincinterpolationparameters; +mod hstft; mod hwindow; mod hwindowtype; mod partialeq; diff --git a/r-harmonium/tests/testthat/test_hstft.R b/r-harmonium/tests/testthat/test_hstft.R new file mode 100644 index 0000000..7f5cbee --- /dev/null +++ b/r-harmonium/tests/testthat/test_hstft.R @@ -0,0 +1,68 @@ +test_that( + "stft works", + { + check_stft_1d = function() { + v = c(1+1i,2+2i,3+3i,4+4i,5+5i,6+6i) + dtype = HDataType$Complex32 + dtype_window = HDataType$Float32 + harray = HArray$new_from_values(as.array(v), dtype) + hstft = HStft$new_forward(5L, dtype) + harray_window = HArray$new_from_values(as.array(c(1,2,3)), dtype_window) + hstft$process(harray, hop_length = 2L, window_length = 3L, window = harray_window) + complex_values = c( + 20 + 20i, + -15.56887 - 12.31967i, + 10.82618 - 2.93764i, + -2.93764 + 10.82618i, + -12.31967 - 15.56887i + ) + + result = matrix(complex_values, nrow = 5, ncol = 1) + expect_equal(harray$collect(), result, tolerance = 1.0e-06) + } + + check_stft_2d = function() { + v = c(1+1i,2+2i,3+3i,4+4i,5+5i,6+6i) + v = cbind(v, v) + dtype = HDataType$Complex32 + dtype_window = HDataType$Float32 + harray = HArray$new_from_values(as.array(v), dtype) + hstft = HStft$new_forward(5L, dtype) + harray_window = HArray$new_from_values(as.array(c(1,2,3)), dtype_window) + hstft$process(harray, hop_length = 2L, window_length = 3L, window = harray_window) + complex_values = c( + 20 + 20i, + -15.56887 - 12.31967i, + 10.82618 - 2.93764i, + -2.93764 + 10.82618i, + -12.31967 - 15.56887i + ) + m = matrix(complex_values, nrow = 5, ncol = 1) + result = array(c(m, m), dim = c(5, 1, 2)) + + expect_equal(harray$collect(), result, tolerance = 1.0e-06) + } + + check_rstft_1d = function() { + v = c(1,2,3,4,5,6) + dtype = HDataType$Float32 + dtype_window = HDataType$Float32 + harray = HArray$new_from_values(as.array(v), dtype) + hstft = HStft$new_real_forward(5L, dtype) + harray_window = HArray$new_from_values(as.array(c(1,2,3)), dtype_window) + hstft$process(harray, hop_length = 2L, window_length = 3L, window = harray_window) + complex_values = c( + 20 + 0i, + -13.944272 + 1.6245984i, + 3.944272 - 6.88191i + ) + + result = matrix(complex_values, nrow = 3, ncol = 1) + expect_equal(harray$collect(), result, tolerance = 1.0e-06) + } + + check_stft_1d() + check_stft_2d() + check_rstft_1d() + } +)