From 913b9e2a76ddaa3985dcae3c74b8a8dbeb5001b8 Mon Sep 17 00:00:00 2001 From: daniellga Date: Mon, 16 Sep 2024 20:55:23 -0300 Subject: [PATCH] more stft --- CHANGELOG.md | 7 + Cargo.toml | 12 +- harmonium-core/src/array.rs | 5 - harmonium-fft/src/fft.rs | 275 +++---- harmonium-stft/src/stft.rs | 695 ++++++++++-------- r-harmonium/.testtest.R | 15 +- r-harmonium/NAMESPACE | 4 +- r-harmonium/R/000-wrappers.R | 88 ++- r-harmonium/R/structs.R | 7 +- r-harmonium/R/zzz.R | 1 + r-harmonium/src/init.c | 72 +- r-harmonium/src/rust/Cargo.toml | 32 +- r-harmonium/src/rust/api.h | 22 +- r-harmonium/src/rust/src/harray.rs | 70 +- r-harmonium/src/rust/src/harrayr.rs | 102 +-- r-harmonium/src/rust/src/haudioop.rs | 109 ++- r-harmonium/src/rust/src/hfft.rs | 262 ++++--- .../tests/testthat/test_db_to_amplitude.R | 2 +- r-harmonium/tests/testthat/test_harray.R | 2 +- r-harmonium/tests/testthat/test_hfft.R | 12 +- r-harmonium/tests/testthat/test_hresampler.R | 16 +- r-harmonium/tests/testthat/test_to_mono.R | 4 +- 22 files changed, 1027 insertions(+), 787 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1aed9e2..1e23425 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ 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 +### Added +Forward and inverse STFT. + +### Fixed +`HArray`'s `is_shared` is now `is_unique` with a new implementation. + # [0.2.0] - 2024-07-14 ### Added - new FFT implementation diff --git a/Cargo.toml b/Cargo.toml index 7f345be..23d854d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,14 +10,14 @@ members = [ ] [workspace.dependencies] -num-traits = "0.2" -rustfft = "6.2" -realfft = "3.3" -rubato = "0.15.0" -ndarray = "0.15.6" +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"] } +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" } +num-complex = { version = "0.4", default-features = false } harmonium-core = { path = "harmonium-core", default-features = false } harmonium-io = { path = "harmonium-io", default-features = false } diff --git a/harmonium-core/src/array.rs b/harmonium-core/src/array.rs index c260105..007f900 100644 --- a/harmonium-core/src/array.rs +++ b/harmonium-core/src/array.rs @@ -59,11 +59,6 @@ where pub fn as_slice_mut(&mut self) -> Option<&mut [T]> { self.0.as_slice_mut() } - - /// Returns `true` if the `HArray` shares the inner arc with another one. - pub fn is_shared(&self) -> bool { - todo!() - } } #[cfg(test)] diff --git a/harmonium-fft/src/fft.rs b/harmonium-fft/src/fft.rs index 380605b..1be4e2e 100644 --- a/harmonium-fft/src/fft.rs +++ b/harmonium-fft/src/fft.rs @@ -2,7 +2,7 @@ use harmonium_core::{array::HArray, errors::HError, errors::HResult}; use ndarray::{ArcArray1, ArcArray2, Axis, Dimension, Ix1, Ix2, IxDyn, Zip}; use realfft::{ComplexToReal, RealFftPlanner, RealToComplex}; use rustfft::{ - num_complex::Complex, + num_complex::{Complex, ComplexFloat}, num_traits::{ConstZero, Float, FloatConst}, FftNum, FftPlanner, }; @@ -27,7 +27,7 @@ pub struct RealFftInverse { } impl Fft { - pub fn new_fft_forward(length: usize) -> Self { + pub fn new_forward(length: usize) -> Self { let mut planner = FftPlanner::new(); let fft = planner.plan_fft_forward(length); let scratch_len = fft.get_inplace_scratch_len(); @@ -40,7 +40,7 @@ impl Fft { } } - pub fn new_fft_inverse(length: usize) -> Self { + pub fn new_inverse(length: usize) -> Self { let mut planner = FftPlanner::new(); let fft = planner.plan_fft_inverse(length); let scratch_len = fft.get_inplace_scratch_len(); @@ -55,7 +55,7 @@ impl Fft { } impl RealFftForward { - pub fn new_real_fft_forward(length: usize) -> Self { + pub fn new(length: usize) -> Self { let mut planner = RealFftPlanner::new(); let fft = planner.plan_fft_forward(length); let scratch_len = fft.get_scratch_len(); @@ -70,7 +70,7 @@ impl RealFftForward { } impl RealFftInverse { - pub fn new_real_fft_inverse(length: usize) -> Self { + pub fn new(length: usize) -> Self { let mut planner = RealFftPlanner::new(); let fft = planner.plan_fft_inverse(length); let scratch_len = fft.get_scratch_len(); @@ -84,69 +84,89 @@ impl RealFftInverse { } } -pub trait ProcessFft +pub trait ProcessFft<'a, D> where - T: FftNum + Float + FloatConst, D: Dimension, { - fn process(&mut self, harray: &mut HArray, D>) -> HResult<()>; -} + type U: ComplexFloat; + type Output; -pub trait ProcessRealFftForward -where - T: FftNum + Float + FloatConst, - D: Dimension, -{ - fn process(&mut self, harray: &mut HArray) -> HResult, D>>; + fn process(&mut self, harray: &'a mut HArray) -> HResult; } -pub trait ProcessRealFftInverse +impl<'a, T> ProcessFft<'a, Ix1> for Fft where T: FftNum + Float + FloatConst, - D: Dimension, { - fn process(&mut self, harray: &mut HArray, D>) -> HResult>; + type U = Complex; + type Output = (); + + fn process(&mut self, harray: &'a mut HArray, Ix1>) -> HResult<()> { + let scratch_buffer = make_mut_slice(&mut self.scratch_buffer); + self.fft + .process_with_scratch(harray.as_slice_mut().unwrap(), scratch_buffer); + Ok(()) + } } -impl ProcessFft for Fft +impl<'a, T> ProcessFft<'a, Ix2> for Fft where T: FftNum + Float + FloatConst, { - fn process(&mut self, harray: &mut HArray, Ix1>) -> HResult<()> { + type U = Complex; + type Output = (); + + fn process(&mut self, harray: &'a mut HArray, Ix2>) -> HResult<()> { let scratch_buffer = make_mut_slice(&mut self.scratch_buffer); - self.fft - .process_with_scratch(harray.as_slice_mut().unwrap(), scratch_buffer); + + for mut row in harray.0.lanes_mut(Axis(1)) { + self.fft + .process_with_scratch(row.as_slice_mut().unwrap(), scratch_buffer); + } Ok(()) } } -impl ProcessRealFftForward for RealFftForward +impl<'a, T> ProcessFft<'a, IxDyn> for Fft where - T: FftNum + Float + FloatConst + ConstZero, + T: FftNum + Float + FloatConst, { - fn process(&mut self, harray: &mut HArray) -> HResult, Ix1>> { - let length = harray.len(); - let mut ndarray = ArcArray1::from_elem(length / 2 + 1, Complex::::ZERO); + type U = Complex; + type Output = (); + + fn process(&mut self, harray: &mut HArray, IxDyn>) -> HResult<()> { let scratch_buffer = make_mut_slice(&mut self.scratch_buffer); - self.fft - .process_with_scratch( - harray.as_slice_mut().unwrap(), - ndarray.as_slice_mut().unwrap(), - scratch_buffer, - ) - .unwrap(); - Ok(HArray(ndarray)) + match harray.ndim() { + 1 => { + self.fft + .process_with_scratch(harray.as_slice_mut().unwrap(), scratch_buffer); + Ok(()) + } + 2 => { + for mut row in harray.0.lanes_mut(Axis(1)) { + self.fft + .process_with_scratch(row.as_slice_mut().unwrap(), scratch_buffer); + } + Ok(()) + } + _ => Err(HError::OutOfSpecError( + "The HArray's ndim should be 1 or 2.".into(), + )), + } } } -impl ProcessRealFftInverse for RealFftInverse +impl<'a, T> ProcessFft<'a, Ix1> for RealFftForward where T: FftNum + Float + FloatConst + ConstZero, { - fn process(&mut self, harray: &mut HArray, Ix1>) -> HResult> { - let length = self.fft.len(); + type U = T; + type Output = HArray, Ix1>; + + fn process(&mut self, harray: &'a mut HArray) -> HResult, Ix1>> { + let length = harray.len(); + let mut ndarray = ArcArray1::from_elem(length / 2 + 1, Complex::::ZERO); let scratch_buffer = make_mut_slice(&mut self.scratch_buffer); - let mut ndarray = ArcArray1::from_elem(length, T::ZERO); self.fft .process_with_scratch( harray.as_slice_mut().unwrap(), @@ -158,26 +178,14 @@ where } } -impl ProcessFft for Fft -where - T: FftNum + Float + FloatConst, -{ - fn process(&mut self, harray: &mut HArray, Ix2>) -> HResult<()> { - let scratch_buffer = make_mut_slice(&mut self.scratch_buffer); - - for mut row in harray.0.lanes_mut(Axis(1)) { - self.fft - .process_with_scratch(row.as_slice_mut().unwrap(), scratch_buffer); - } - Ok(()) - } -} - -impl ProcessRealFftForward for RealFftForward +impl<'a, T> ProcessFft<'a, Ix2> for RealFftForward where T: FftNum + Float + FloatConst + ConstZero, { - fn process(&mut self, harray: &mut HArray) -> HResult, Ix2>> { + type U = T; + type Output = HArray, Ix2>; + + fn process(&mut self, harray: &'a mut HArray) -> HResult, Ix2>> { let nrows = harray.0.nrows(); let ncols = harray.0.ncols(); let scratch_buffer = make_mut_slice(&mut self.scratch_buffer); @@ -199,63 +207,14 @@ where } } -impl ProcessRealFftInverse for RealFftInverse +impl<'a, T> ProcessFft<'a, IxDyn> for RealFftForward where T: FftNum + Float + FloatConst + ConstZero, { - fn process(&mut self, harray: &mut HArray, Ix2>) -> HResult> { - let length = self.fft.len(); - let nrows = harray.0.nrows(); - let scratch_buffer = make_mut_slice(&mut self.scratch_buffer); - let mut ndarray = ArcArray2::from_elem((nrows, length), T::ZERO); + type U = T; + type Output = HArray, IxDyn>; - Zip::from(ndarray.lanes_mut(Axis(1))) - .and(harray.0.lanes_mut(Axis(1))) - .for_each(|mut row_output, mut row_input| { - self.fft - .process_with_scratch( - row_input.as_slice_mut().unwrap(), - row_output.as_slice_mut().unwrap(), - scratch_buffer, - ) - .unwrap(); - }); - - Ok(HArray(ndarray)) - } -} - -impl ProcessFft for Fft -where - T: FftNum + Float + FloatConst, -{ - fn process(&mut self, harray: &mut HArray, IxDyn>) -> HResult<()> { - let scratch_buffer = make_mut_slice(&mut self.scratch_buffer); - match harray.ndim() { - 1 => { - self.fft - .process_with_scratch(harray.as_slice_mut().unwrap(), scratch_buffer); - Ok(()) - } - 2 => { - Zip::from(harray.0.lanes_mut(Axis(1))).for_each(|mut row| { - self.fft - .process_with_scratch(row.as_slice_mut().unwrap(), scratch_buffer); - }); - Ok(()) - } - _ => Err(HError::OutOfSpecError( - "The HArray's ndim should be 1 or 2.".into(), - )), - } - } -} - -impl ProcessRealFftForward for RealFftForward -where - T: FftNum + Float + FloatConst + ConstZero, -{ - fn process(&mut self, harray: &mut HArray) -> HResult, IxDyn>> { + fn process(&mut self, harray: &'a mut HArray) -> HResult, IxDyn>> { let scratch_buffer = make_mut_slice(&mut self.scratch_buffer); match harray.ndim() { 1 => { @@ -297,10 +256,64 @@ where } } -impl ProcessRealFftInverse for RealFftInverse +impl<'a, T> ProcessFft<'a, Ix1> for RealFftInverse +where + T: FftNum + Float + FloatConst + ConstZero, +{ + type U = Complex; + type Output = HArray; + + fn process(&mut self, harray: &'a mut HArray, Ix1>) -> HResult> { + let length = self.fft.len(); + let scratch_buffer = make_mut_slice(&mut self.scratch_buffer); + let mut ndarray = ArcArray1::from_elem(length, T::ZERO); + self.fft + .process_with_scratch( + harray.as_slice_mut().unwrap(), + ndarray.as_slice_mut().unwrap(), + scratch_buffer, + ) + .unwrap(); + Ok(HArray(ndarray)) + } +} + +impl<'a, T> ProcessFft<'a, Ix2> for RealFftInverse +where + T: FftNum + Float + FloatConst + ConstZero, +{ + type U = Complex; + type Output = HArray; + + fn process(&mut self, harray: &mut HArray, Ix2>) -> HResult> { + let length = self.fft.len(); + let nrows = harray.0.nrows(); + let scratch_buffer = make_mut_slice(&mut self.scratch_buffer); + let mut ndarray = ArcArray2::from_elem((nrows, length), T::ZERO); + + Zip::from(ndarray.lanes_mut(Axis(1))) + .and(harray.0.lanes_mut(Axis(1))) + .for_each(|mut row_output, mut row_input| { + self.fft + .process_with_scratch( + row_input.as_slice_mut().unwrap(), + row_output.as_slice_mut().unwrap(), + scratch_buffer, + ) + .unwrap(); + }); + + Ok(HArray(ndarray)) + } +} + +impl<'a, T> ProcessFft<'a, IxDyn> for RealFftInverse where T: FftNum + Float + FloatConst + ConstZero, { + type U = Complex; + type Output = HArray; + fn process(&mut self, harray: &mut HArray, IxDyn>) -> HResult> { let length = self.fft.len(); let scratch_buffer = make_mut_slice(&mut self.scratch_buffer); @@ -372,7 +385,7 @@ mod tests { ]; let mut lhs = HArray::new_from_shape_vec(6, v).unwrap(); let length = lhs.len(); - let mut fft = Fft::new_fft_forward(length); + let mut fft = Fft::new_forward(length); fft.process(&mut lhs).unwrap(); let result = vec![ Complex::new(36.0, 42.0), @@ -397,7 +410,7 @@ mod tests { ]; let mut lhs = HArray::new_from_shape_vec((3, 2), v).unwrap(); let ncols = lhs.0.ncols(); - let mut fft = Fft::new_fft_forward(ncols); + let mut fft = Fft::new_forward(ncols); fft.process(&mut lhs).unwrap(); let result = vec![ Complex::new(4_f32, 6_f32), @@ -424,7 +437,7 @@ mod tests { .unwrap() .into_dynamic(); let ncols = lhs.0.len_of(Axis(1)); - let mut fft = Fft::new_fft_forward(ncols); + let mut fft = Fft::new_forward(ncols); fft.process(&mut lhs).unwrap(); let result = vec![ Complex::new(4_f32, 6_f32), @@ -452,7 +465,7 @@ mod tests { ]; let mut lhs = HArray::new_from_shape_vec(6, v.clone()).unwrap(); let length = lhs.len(); - let mut fft = Fft::new_fft_inverse(length); + let mut fft = Fft::new_inverse(length); fft.process(&mut lhs).unwrap(); let result = vec![ Complex::new(36.0, 42.0), @@ -478,7 +491,7 @@ mod tests { ]; let mut lhs = HArray::new_from_shape_vec((3, 2), v).unwrap(); let ncols = lhs.0.ncols(); - let mut fft = Fft::new_fft_forward(ncols); + let mut fft = Fft::new_forward(ncols); fft.process(&mut lhs).unwrap(); let result = vec![ Complex::new(4.0, 6.0), @@ -506,7 +519,7 @@ mod tests { .unwrap() .into_dynamic(); let ncols = lhs.0.len_of(Axis(1)); - let mut fft = Fft::new_fft_forward(ncols); + let mut fft = Fft::new_forward(ncols); fft.process(&mut lhs).unwrap(); let result = vec![ Complex::new(4.0, 6.0), @@ -527,7 +540,7 @@ mod tests { let v = vec![1_f32, 2_f32, 3_f32, 4_f32, 5_f32, 6_f32]; let mut harray = HArray::new_from_shape_vec(6, v).unwrap(); let length = harray.len(); - let mut rfft = RealFftForward::new_real_fft_forward(length); + let mut rfft = RealFftForward::new(length); let lhs = rfft.process(&mut harray).unwrap(); let result = vec![ Complex::new(21.0, 0.0), @@ -546,7 +559,7 @@ mod tests { ]; let mut harray = HArray::new_from_shape_vec((3, 4), v).unwrap(); let ncols = harray.0.ncols(); - let mut rfft = RealFftForward::new_real_fft_forward(ncols); + let mut rfft = RealFftForward::new(ncols); let lhs = rfft.process(&mut harray).unwrap(); let result = vec![ Complex::new(10_f32, 0_f32), @@ -572,7 +585,7 @@ mod tests { .unwrap() .into_dynamic(); let ncols = harray.0.len_of(Axis(1)); - let mut rfft = RealFftForward::new_real_fft_forward(ncols); + let mut rfft = RealFftForward::new(ncols); let lhs = rfft.process(&mut harray).unwrap(); let result = vec![ Complex::new(10_f32, 0_f32), @@ -597,9 +610,9 @@ mod tests { let v = vec![1_f32, 2., 3., 4., 5., 6.]; let mut harray = HArray::new_from_shape_vec(6, v).unwrap(); let length = harray.len(); - let mut rfft = RealFftForward::new_real_fft_forward(length); + let mut rfft = RealFftForward::new(length); let mut spectrum = rfft.process(&mut harray).unwrap(); - let mut irfft = RealFftInverse::new_real_fft_inverse(length); + let mut irfft = RealFftInverse::new(length); let lhs = irfft.process(&mut spectrum).unwrap(); let result = vec![6_f32, 12., 18., 24., 30., 36.]; let rhs = HArray::new_from_shape_vec(length, result).unwrap(); @@ -608,9 +621,9 @@ mod tests { let v = vec![1_f32, 2., 3., 4., 5., 6.]; let mut harray = HArray::new_from_shape_vec(6, v).unwrap(); let length = harray.len() + 1; - let mut rfft = RealFftForward::new_real_fft_forward(length - 1); + let mut rfft = RealFftForward::new(length - 1); let mut spectrum = rfft.process(&mut harray).unwrap(); - let mut irfft = RealFftInverse::new_real_fft_inverse(length); + let mut irfft = RealFftInverse::new(length); let lhs = irfft.process(&mut spectrum).unwrap(); let result = vec![ 3.000000000000007_f32, @@ -631,9 +644,9 @@ mod tests { let v = vec![1_f32, 2., 3., 4., 5., 6.]; let mut harray = HArray::new_from_shape_vec((3, 2), v).unwrap(); let length = harray.0.ncols(); - let mut rfft = RealFftForward::new_real_fft_forward(length); + let mut rfft = RealFftForward::new(length); let mut spectrum = rfft.process(&mut harray).unwrap(); - let mut irfft = RealFftInverse::new_real_fft_inverse(length); + let mut irfft = RealFftInverse::new(length); let lhs = irfft.process(&mut spectrum).unwrap(); let result = vec![2., 4., 6., 8., 10., 12.]; let rhs = HArray::new_from_shape_vec((3, length), result).unwrap(); @@ -642,9 +655,9 @@ mod tests { let v = vec![1_f32, 2., 3., 4., 5., 6.]; let mut harray = HArray::new_from_shape_vec((3, 2), v).unwrap(); let length = harray.0.ncols() + 1; - let mut rfft = RealFftForward::new_real_fft_forward(length - 1); + let mut rfft = RealFftForward::new(length - 1); let mut spectrum = rfft.process(&mut harray).unwrap(); - let mut irfft = RealFftInverse::new_real_fft_inverse(length); + let mut irfft = RealFftInverse::new(length); let lhs = irfft.process(&mut spectrum).unwrap(); let result = vec![1., 4., 4., 5., 8., 8., 9., 12., 12.]; let rhs = HArray::new_from_shape_vec((3, length), result).unwrap(); @@ -659,9 +672,9 @@ mod tests { .unwrap() .into_dynamic(); let length = harray.0.len_of(Axis(1)); - let mut rfft = RealFftForward::new_real_fft_forward(length); + let mut rfft = RealFftForward::new(length); let mut spectrum = rfft.process(&mut harray).unwrap(); - let mut irfft = RealFftInverse::new_real_fft_inverse(length); + let mut irfft = RealFftInverse::new(length); let lhs = irfft.process(&mut spectrum).unwrap(); let result = vec![2., 4., 6., 8., 10., 12.]; let rhs = HArray::new_from_shape_vec((3, length), result) @@ -674,9 +687,9 @@ mod tests { .unwrap() .into_dynamic(); let length = harray.0.len_of(Axis(1)) + 1; - let mut rfft = RealFftForward::new_real_fft_forward(length - 1); + let mut rfft = RealFftForward::new(length - 1); let mut spectrum = rfft.process(&mut harray).unwrap(); - let mut irfft = RealFftInverse::new_real_fft_inverse(length); + let mut irfft = RealFftInverse::new(length); let lhs = irfft.process(&mut spectrum).unwrap(); let result = vec![1., 4., 4., 5., 8., 8., 9., 12., 12.]; let rhs = HArray::new_from_shape_vec((3, length), result) diff --git a/harmonium-stft/src/stft.rs b/harmonium-stft/src/stft.rs index 863ef51..8796f7e 100644 --- a/harmonium-stft/src/stft.rs +++ b/harmonium-stft/src/stft.rs @@ -2,30 +2,30 @@ use harmonium_core::{ array::HArray, errors::{HError, HResult}, }; -use harmonium_fft::fft::{make_mut_slice, Fft, ProcessFft}; +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}, num_traits::{ConstZero, Float, FloatConst}, FftNum, }; -use std::num::NonZero; +use std::{num::NonZero, sync::Arc}; pub struct Stft(Fft); -//pub struct RealStftForward { -// inner: RealFftForward, -// scratch_real_buffer: Arc<[T]>, -//} +pub struct RealStftForward { + inner: RealFftForward, + scratch_real_buffer: Arc<[T]>, +} #[allow(clippy::len_without_is_empty)] -/// An `Stft` is used to process stft. It caches results internally, so when making more than one stft it is advisable to reuse the same `Stft` instance. +/// A `Stft` is used to process stft. It caches results internally, so when making more than one stft it is advisable to reuse the same `Stft` instance. impl Stft where T: FftNum + Float + FloatConst + ConstZero, { pub fn new_stft_forward(length: usize) -> Self { - Stft(Fft::new_fft_forward(length)) + Stft(Fft::new_forward(length)) } pub fn len(&self) -> usize { @@ -33,75 +33,62 @@ where } } -/// An `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. -//impl RealStftForward -//where -// T: FftNum + Float + FloatConst + ConstZero, -//{ -// pub fn new_real_stft_forward(length: usize) -> Self { -// let scratch_real_buffer = vec![T::ZERO; length]; -// let scratch_real_buffer: Arc<[T]> = Arc::from(scratch_real_buffer); -// -// RealStftForward { -// inner: RealFftForward::new_real_fft_forward(length), -// scratch_real_buffer, -// } -// } -// -// pub fn len(&self) -> usize { -// self.inner.fft.len() -// } -//} - -pub trait ProcessStft +#[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. +impl RealStftForward where - T: FftNum + Float + FloatConst, - D: Dimension, + T: FftNum + Float + FloatConst + ConstZero, { - /// Computes the Fourier transform of short overlapping windows of the input. - /// The function does not normalize outputs. - /// - /// # Arguments - /// `harray` - A complex 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`. - fn process( - &mut self, - harray: &HArray, D>, - hop_length: NonZero, - window_length: NonZero, - window: Option<&[T]>, - ) -> HResult, D::Larger>>; + pub fn new_real_stft_forward(length: usize) -> Self { + let scratch_real_buffer = vec![T::ZERO; length]; + let scratch_real_buffer: Arc<[T]> = Arc::from(scratch_real_buffer); + + RealStftForward { + inner: RealFftForward::new(length), + scratch_real_buffer, + } + } + + pub fn len(&self) -> usize { + self.inner.fft.len() + } } -pub trait ProcessRealStftForward +pub trait ProcessStft where T: FftNum + Float + FloatConst, D: Dimension, { + type U: ComplexFloat; + type Output; + /// Computes the Fourier transform of short overlapping windows of the input. - /// For each forward FFT, transforms a real signal of length `N` to a complex-valued spectrum of length `N/2+1` (with `N/2` rounded down). /// The function does not normalize outputs. /// + /// If real-valued input, for each forward FFT, transforms a real signal of length `N` to a complex-valued spectrum of length `N/2+1` (with `N/2` rounded down). + /// /// # Arguments - /// `harray` - A real-valued 1D or 2D HArray to be used as input. + /// `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`. fn process( &mut self, - harray: &HArray, + harray: &HArray, hop_length: NonZero, window_length: NonZero, window: Option<&[T]>, - ) -> HResult, D::Larger>>; + ) -> HResult; } impl ProcessStft for Stft where T: FftNum + Float + FloatConst + ConstZero, { + type U = Complex; + type Output = HArray, Ix2>; + fn process( &mut self, harray: &HArray, Ix1>, @@ -109,7 +96,8 @@ where window_length: NonZero, window: Option<&[T]>, ) -> HResult, Ix2>> { - 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. + // 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(); @@ -159,6 +147,9 @@ impl ProcessStft for Stft where T: FftNum + Float + FloatConst + ConstZero, { + type U = Complex; + type Output = HArray, Ix3>; + fn process( &mut self, harray: &HArray, Ix2>, @@ -166,7 +157,8 @@ where window_length: NonZero, window: Option<&[T]>, ) -> HResult, Ix3>> { - 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. + // 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 nrows = harray.0.len_of(Axis(0)); @@ -191,30 +183,24 @@ where // Center PAD the window if fft_length > window_length. let left = (fft_length - window_length) / 2; let right = left + window_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( + for (mut col, win_col) in stft_ndarray.lanes_mut(Axis(2)).into_iter().zip( harray .0 - .windows((nrows, fft_length)) + .windows((1, 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 col_slice = col.as_slice_mut().unwrap(); + let col_slice_sliced = &mut col_slice[left..right]; + + col_slice_sliced.copy_from_slice(&win_col.as_slice().unwrap()[left..right]); + + if let Some(w) = window { + col_slice_sliced.apply_window(w); } + self.0.fft.process_with_scratch(col_slice, scratch_buffer); } let output = HArray(stft_ndarray); @@ -227,6 +213,9 @@ impl ProcessStft for Stft where T: FftNum + Float + FloatConst + ConstZero, { + type U = Complex; + type Output = HArray, IxDyn>; + fn process( &mut self, harray: &HArray, IxDyn>, @@ -234,7 +223,8 @@ where 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. + // 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(); @@ -346,135 +336,148 @@ 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, Ix2>> { -// 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 real_fft_length = fft_length / 2 + 1; -// let window_length = window_length.get(); -// let hop_length = hop_length.get(); -// let length = harray.len(); -// 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(), -// )); -// } -// } -// -// let n_fft = 1 + (length - fft_length) / hop_length; -// let mut stft_ndarray = ArcArray2::>::zeros((n_fft, real_fft_length)); -// -// // Center PAD the window if fft_length > window_length. -// let left = (fft_length - window_length) / 2; -// let right = left + window_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(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.slice(slice_info_1d).as_slice().unwrap()); -// 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); -// -// Ok(output) -// } -//} +impl ProcessStft for RealStftForward +where + T: FftNum + Float + FloatConst + ConstZero, +{ + type U = T; + type Output = HArray, Ix2>; + + fn process( + &mut self, + harray: &HArray, + hop_length: NonZero, + window_length: NonZero, + window: Option<&[T]>, + ) -> HResult, Ix2>> { + // 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(); + let length = harray.len(); + 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(), + )); + } + } + + let n_fft = 1 + (length - fft_length) / hop_length; + let mut stft_ndarray = ArcArray2::>::zeros((n_fft, real_fft_length)); + + // Center PAD the window if fft_length > window_length. + let left = (fft_length - window_length) / 2; + let right = left + window_length; + + for (mut row, win) in stft_ndarray + .lanes_mut(Axis(1)) + .into_iter() + .zip(harray.0.windows(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); + + Ok(output) + } +} + +impl ProcessStft for RealStftForward +where + T: FftNum + Float + FloatConst + ConstZero, +{ + type U = T; + type Output = HArray, Ix3>; + + fn process( + &mut self, + harray: &HArray, + hop_length: NonZero, + window_length: NonZero, + window: Option<&[T]>, + ) -> HResult, Ix3>> { + // 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(); + let nrows = harray.0.len_of(Axis(0)); + let ncols = harray.0.len_of(Axis(1)); + 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(), + )); + } + } + + let n_fft = 1 + (ncols - fft_length) / hop_length; + let mut stft_ndarray = ArcArray::, Ix3>::zeros((nrows, n_fft, real_fft_length)); + + // Center PAD the window if fft_length > window_length. + let left = (fft_length - window_length) / 2; + let right = left + window_length; + + for (mut col, win_col) in stft_ndarray.lanes_mut(Axis(2)).into_iter().zip( + harray + .0 + .windows((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); + + Ok(output) + } +} -//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, Ix3>> { -// 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(); -// 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)); -// -// // Center PAD the window if fft_length > window_length. -// let left = (fft_length - window_length) / 2; -// let right = left + window_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((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); -// -// Ok(output) -// } -//} -// //impl ProcessRealStftForward for RealStftForward //where // T: FftNum + Float + FloatConst + ConstZero, @@ -857,8 +860,8 @@ mod tests { .unwrap(); let ncols = harray.0.len_of(Axis(1)); let n_fft = 1 + (ncols - fft_length) / hop_length; - let lhs = HArray::new_from_shape_vec((2, n_fft, fft_length), result.clone()).unwrap(); - assert!(compare_harray_complex(&stft_harray, &lhs)); + let rhs = HArray::new_from_shape_vec((2, n_fft, fft_length), result.clone()).unwrap(); + assert!(compare_harray_complex(&stft_harray, &rhs)); // IxDyn test. let harray = HArray::new_from_shape_vec((2, length / 2), input.clone()) @@ -870,107 +873,203 @@ mod tests { .unwrap(); let ncols = harray.0.len_of(Axis(1)); let n_fft = 1 + (ncols - fft_length) / hop_length; - let lhs = HArray::new_from_shape_vec((2, n_fft, fft_length), result.clone()) + let rhs = HArray::new_from_shape_vec((2, n_fft, fft_length), result.clone()) .unwrap() .into_dynamic(); - assert!(compare_harray_complex(&stft_harray, &lhs)); + assert!(compare_harray_complex(&stft_harray, &rhs)); + } + } + + #[test] + fn real_stft_1d_test() { + let fft_length = [3_usize, 5, 5, 5]; + let one_hop_length = NonZero::::new(1).unwrap(); + let two_hop_length = NonZero::::new(2).unwrap(); + let hop_length = [ + one_hop_length, + one_hop_length, + two_hop_length, + two_hop_length, + ]; + let result_no_pad = vec![ + Complex::new(6.0, 0.0), + Complex::new(-1.5, 0.8660254), + Complex::new(9.0, 0.0), + Complex::new(-1.5, 0.8660254), + Complex::new(12.0, 0.0), + Complex::new(-1.5, 0.8660254), + Complex::new(15.0, 0.0), + Complex::new(-1.5, 0.8660254), + ]; + let result_pad = vec![ + Complex::new(9.0, 0.0), + Complex::new(-5.045085, -1.314328), + Complex::new(0.5450851, -2.126627), + Complex::new(12.0, 0.0), + Complex::new(-6.354102, -2.265384), + Complex::new(0.3541019, -2.714412), + ]; + let result_pad_hop_length = vec![ + Complex::new(9.0, 0.0), + Complex::new(-5.045085, -1.314328), + Complex::new(0.5450851, -2.126627), + ]; + let result_pad_hop_length_window = vec![ + Complex::new(20.0, 0.0), + Complex::new(-13.94427, 1.624598), + Complex::new(3.944272, -6.88191), + ]; + + let result = [ + result_no_pad, + result_pad, + result_pad_hop_length, + result_pad_hop_length_window, + ]; + + let window = [None, None, None, Some([1., 2., 3.].as_slice())]; + + let input = vec![1., 2., 3., 4., 5., 6.]; + let length = input.len(); + let window_length = NonZero::new(3).unwrap(); + + for (((fft_length, hop_length), result), window) in fft_length + .into_iter() + .zip(hop_length.into_iter()) + .zip(result.iter()) + .zip(window.into_iter()) + { + // Ix1 test. + let harray = HArray::new_from_shape_vec(length, input.clone()).unwrap(); + 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(); + 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)); } } - //#[test] - //fn real_stft_1d_test() { - // let fft_length = [3_usize, 5, 5, 5]; - // let one_hop_length = NonZero::::new(1).unwrap(); - // let two_hop_length = NonZero::::new(2).unwrap(); - // let hop_length = [ - // one_hop_length, - // one_hop_length, - // two_hop_length, - // two_hop_length, - // ]; - // let result_no_pad = vec![ - // Complex::new(9.0, 12.0), - // Complex::new(-4.732051, -1.2679492), - // Complex::new(-1.2679492, -4.732051), - // Complex::new(15.0, 18.0), - // Complex::new(-4.732051, -1.2679492), - // Complex::new(-1.2679492, -4.732051), - // Complex::new(21.0, 24.0), - // Complex::new(-4.732051, -1.2679492), - // Complex::new(-1.2679492, -4.732051), - // Complex::new(27.0, 30.0), - // Complex::new(-4.732051, -1.2679492), - // Complex::new(-1.2679492, -4.732051), - // ]; - // let result_pad = vec![ - // Complex::new(15.0, 18.0), - // Complex::new(-6.15250, -11.76777), - // Complex::new(5.534407, -2.575299), - // Complex::new(-2.972101, 4.755639), - // Complex::new(-11.40981, -8.41257), - // Complex::new(21.0, 24.0), - // Complex::new(-6.86842, -16.28792), - // Complex::new(6.328012, -4.132835), - // Complex::new(-4.529637, 5.549243), - // Complex::new(-15.92996, -9.12849), - // ]; - // let result_pad_hop_length = vec![ - // Complex::new(15.0, 18.0), - // Complex::new(-6.1525, -11.76777), - // Complex::new(5.534407, -2.575299), - // Complex::new(-2.972101, 4.755639), - // Complex::new(-11.40981, -8.41257), - // ]; - // let result_pad_hop_length_window = vec![ - // Complex::new(34.0, 40.0), - // Complex::new(-27.40167, -24.27608), - // Complex::new(20.9163, -4.33643), - // Complex::new(-6.61134, 20.11352), - // Complex::new(-20.90328, -31.50101), - // ]; - - // let result = [ - // result_no_pad, - // result_pad, - // result_pad_hop_length, - // result_pad_hop_length_window, - // ]; - - // let window = [None, None, None, Some([1., 2., 3.].as_slice())]; - - // let input = vec![1.,2.,3.,4.,5.,6.]; - // let length = input.len(); - // let window_length = NonZero::new(3).unwrap(); - - // for (((fft_length, hop_length), result), window) in fft_length - // .into_iter() - // .zip(hop_length.into_iter()) - // .zip(result.iter()) - // .zip(window.into_iter()) - // { - // // Ix1 test. - // let harray = HArray::new_from_shape_vec(length, input.clone()).unwrap(); - // 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(); - // 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)); - // } - //} + #[test] + fn real_stft_2d_test() { + let fft_length = [3_usize, 5, 5, 5]; + let one_hop_length = NonZero::::new(1).unwrap(); + let two_hop_length = NonZero::::new(2).unwrap(); + let hop_length = [ + one_hop_length, + one_hop_length, + two_hop_length, + two_hop_length, + ]; + let result_no_pad = vec![ + Complex::new(6.0, 0.0), + Complex::new(-1.5, 0.8660254), + Complex::new(9.0, 0.0), + Complex::new(-1.5, 0.8660254), + Complex::new(12.0, 0.0), + Complex::new(-1.5, 0.8660254), + Complex::new(15.0, 0.0), + Complex::new(-1.5, 0.8660254), + Complex::new(6.0, 0.0), + Complex::new(-1.5, 0.8660254), + Complex::new(9.0, 0.0), + Complex::new(-1.5, 0.8660254), + Complex::new(12.0, 0.0), + Complex::new(-1.5, 0.8660254), + Complex::new(15.0, 0.0), + Complex::new(-1.5, 0.8660254), + ]; + let result_pad = vec![ + Complex::new(9.0, 0.0), + Complex::new(-5.045085, -1.314328), + Complex::new(0.5450851, -2.126627), + Complex::new(12.0, 0.0), + Complex::new(-6.354102, -2.265384), + Complex::new(0.3541019, -2.714412), + Complex::new(9.0, 0.0), + Complex::new(-5.045085, -1.314328), + Complex::new(0.5450851, -2.126627), + Complex::new(12.0, 0.0), + Complex::new(-6.354102, -2.265384), + Complex::new(0.3541019, -2.714412), + ]; + let result_pad_hop_length = vec![ + Complex::new(9.0, 0.0), + Complex::new(-5.045085, -1.314328), + Complex::new(0.5450851, -2.126627), + Complex::new(9.0, 0.0), + Complex::new(-5.045085, -1.314328), + Complex::new(0.5450851, -2.126627), + ]; + let result_pad_hop_length_window = vec![ + Complex::new(20.0, 0.0), + Complex::new(-13.94427, 1.624598), + Complex::new(3.944272, -6.88191), + Complex::new(20.0, 0.0), + Complex::new(-13.94427, 1.624598), + Complex::new(3.944272, -6.88191), + ]; + + let result = [ + result_no_pad, + result_pad, + result_pad_hop_length, + result_pad_hop_length_window, + ]; + + let window = [None, None, None, Some([1., 2., 3.].as_slice())]; + + let input = vec![1., 2., 3., 4., 5., 6., 1., 2., 3., 4., 5., 6.]; + let length = input.len(); + let window_length = NonZero::new(3).unwrap(); + + for (((fft_length, hop_length), result), window) in fft_length + .into_iter() + .zip(hop_length.into_iter()) + .zip(result.iter()) + .zip(window.into_iter()) + { + // 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 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(); + 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)); + } + } } diff --git a/r-harmonium/.testtest.R b/r-harmonium/.testtest.R index 084e557..7b96d9e 100644 --- a/r-harmonium/.testtest.R +++ b/r-harmonium/.testtest.R @@ -4,14 +4,7 @@ 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( - complex(real = 1, imaginary = 2), - complex(real = 3, imaginary = 4), - complex(real = 5, imaginary = 6), - complex(real = 7, imaginary = 8), - complex(real = 9, imaginary = 10), - complex(real = 11, imaginary = 12) -) +v <- c(1,2,3,4,5,6) complex_tensor = torch_tensor(v) a = torch_stft( @@ -19,9 +12,9 @@ a = torch_stft( n_fft = 5, hop_length = 2, win_length = 3, - window = torch_tensor(c(1,2,3)), + window = c(1.,2.,3.), center = FALSE, - onesided = FALSE, + onesided = TRUE, return_complex = TRUE ) -t(as_array(a)) \ No newline at end of file +t(as_array(a)) diff --git a/r-harmonium/NAMESPACE b/r-harmonium/NAMESPACE index 44a7a58..239034d 100644 --- a/r-harmonium/NAMESPACE +++ b/r-harmonium/NAMESPACE @@ -12,8 +12,7 @@ export(HWindow) export(HMetadataType) export(HFile) export(HFft) -export(HRealFft) -export(HAudioOp) +export(HArrayAudio) export(hdocs) export(HDecoderStream) export(HDecodedAudio) @@ -44,6 +43,5 @@ S3method(print,HWindowType) S3method(print,HInterpolationType) S3method(print,HSincInterpolationParameters) S3method(print,HFft) -S3method(print,HRealFft) useDynLib(harmonium, .registration = TRUE) diff --git a/r-harmonium/R/000-wrappers.R b/r-harmonium/R/000-wrappers.R index cf1279d..f193e89 100644 --- a/r-harmonium/R/000-wrappers.R +++ b/r-harmonium/R/000-wrappers.R @@ -88,12 +88,6 @@ NULL } } -`HArray_is_shared` <- function(self) { - function() { - .Call(savvy_HArray_is_shared__impl, `self`) - } -} - `HArray_mem_adress` <- function(self) { function() { .Call(savvy_HArray_mem_adress__impl, `self`) @@ -106,6 +100,12 @@ NULL } } +`HArray_is_unique` <- function(self) { + function() { + .Call(savvy_HArray_is_unique__impl, `self`) + } +} + `HArray_invalidate` <- function(self) { function() { invisible(.Call(savvy_HArray_invalidate__impl, `self`)) @@ -125,9 +125,9 @@ NULL e$`clone` <- `HArray_clone`(ptr) e$`collect` <- `HArray_collect`(ptr) e$`dtype` <- `HArray_dtype`(ptr) - e$`is_shared` <- `HArray_is_shared`(ptr) e$`mem_adress` <- `HArray_mem_adress`(ptr) e$`is_standard_layout` <- `HArray_is_standard_layout`(ptr) + e$`is_unique` <- `HArray_is_unique`(ptr) e$`invalidate` <- `HArray_invalidate`(ptr) class(e) <- "HArray" @@ -175,73 +175,73 @@ class(`HArray`) <- "HArray__bundle" #' @export `[[<-.HArray__bundle` <- function(x, i, value) stop("HArray cannot be modified", call. = FALSE) -### wrapper functions for HAudioOp +### wrapper functions for HArrayAudio -`.savvy_wrap_HAudioOp` <- function(ptr) { +`.savvy_wrap_HArrayAudio` <- function(ptr) { e <- new.env(parent = emptyenv()) e$.ptr <- ptr - class(e) <- "HAudioOp" + class(e) <- "HArrayAudio" e } #' @export -`$<-.HAudioOp` <- function(x, name, value) stop("HAudioOp cannot be modified", call. = FALSE) +`$<-.HArrayAudio` <- function(x, name, value) stop("HArrayAudio cannot be modified", call. = FALSE) #' @export -`[[<-.HAudioOp` <- function(x, i, value) stop("HAudioOp cannot be modified", call. = FALSE) +`[[<-.HArrayAudio` <- function(x, i, value) stop("HArrayAudio cannot be modified", call. = FALSE) -#' HAudioOp +#' HArrayAudio #' A collection of methods that can be applied to float 1D or 2D `HArray`s which represents audio data. #' #' # Methods #' -`HAudioOp` <- new.env(parent = emptyenv()) +`HArrayAudio` <- new.env(parent = emptyenv()) #' @export -`$<-.HAudioOp` <- function(x, name, value) stop("HAudioOp cannot be modified", call. = FALSE) +`$<-.HArrayAudio` <- function(x, name, value) stop("HArrayAudio cannot be modified", call. = FALSE) #' @export -`[[<-.HAudioOp` <- function(x, i, value) stop("HAudioOp cannot be modified", call. = FALSE) +`[[<-.HArrayAudio` <- function(x, i, value) stop("HArrayAudio cannot be modified", call. = FALSE) -### associated functions for HAudioOp +### associated functions for HArrayAudio -`HAudioOp`$`nchannels` <- function(`harray`) { +`HArrayAudio`$`nchannels` <- function(`harray`) { `harray` <- .savvy_extract_ptr(`harray`, "HArray") - .Call(savvy_HAudioOp_nchannels__impl, `harray`) + .Call(savvy_HArrayAudio_nchannels__impl, `harray`) } -`HAudioOp`$`nframes` <- function(`harray`) { +`HArrayAudio`$`nframes` <- function(`harray`) { `harray` <- .savvy_extract_ptr(`harray`, "HArray") - .Call(savvy_HAudioOp_nframes__impl, `harray`) + .Call(savvy_HArrayAudio_nframes__impl, `harray`) } -`HAudioOp`$`db_to_amplitude` <- function(`harray`, `reference`, `power`) { +`HArrayAudio`$`db_to_amplitude` <- function(`harray`, `reference`, `power`) { `harray` <- .savvy_extract_ptr(`harray`, "HArray") - invisible(.Call(savvy_HAudioOp_db_to_amplitude__impl, `harray`, `reference`, `power`)) + invisible(.Call(savvy_HArrayAudio_db_to_amplitude__impl, `harray`, `reference`, `power`)) } -`HAudioOp`$`to_mono` <- function(`harray`) { +`HArrayAudio`$`to_mono` <- function(`harray`) { `harray` <- .savvy_extract_ptr(`harray`, "HArray") - invisible(.Call(savvy_HAudioOp_to_mono__impl, `harray`)) + invisible(.Call(savvy_HArrayAudio_to_mono__impl, `harray`)) } -class(`HAudioOp`) <- "HAudioOp__bundle" +class(`HArrayAudio`) <- "HArrayAudio__bundle" #' @export -`print.HAudioOp__bundle` <- function(x, ...) { - cat('HAudioOp') +`print.HArrayAudio__bundle` <- function(x, ...) { + cat('HArrayAudio') } #' @export -`$<-.HAudioOp__bundle` <- function(x, name, value) stop("HAudioOp cannot be modified", call. = FALSE) +`$<-.HArrayAudio__bundle` <- function(x, name, value) stop("HArrayAudio cannot be modified", call. = FALSE) #' @export -`[[<-.HAudioOp__bundle` <- function(x, i, value) stop("HAudioOp cannot be modified", call. = FALSE) +`[[<-.HArrayAudio__bundle` <- function(x, i, value) stop("HArrayAudio cannot be modified", call. = FALSE) ### wrapper functions for HAudioSink @@ -690,9 +690,9 @@ class(`HDecoderStream`) <- "HDecoderStream__bundle" } } -`HFft_is_shared` <- function(self) { +`HFft_is_unique` <- function(self) { function() { - .Call(savvy_HFft_is_shared__impl, `self`) + .Call(savvy_HFft_is_unique__impl, `self`) } } @@ -709,7 +709,7 @@ class(`HDecoderStream`) <- "HDecoderStream__bundle" e$`dtype` <- `HFft_dtype`(ptr) e$`print` <- `HFft_print`(ptr) e$`clone` <- `HFft_clone`(ptr) - e$`is_shared` <- `HFft_is_shared`(ptr) + e$`is_unique` <- `HFft_is_unique`(ptr) e$`invalidate` <- `HFft_invalidate`(ptr) class(e) <- "HFft" @@ -738,14 +738,24 @@ class(`HDecoderStream`) <- "HDecoderStream__bundle" ### associated functions for HFft -`HFft`$`new_fft_forward` <- function(`length`, `dtype`) { +`HFft`$`new_forward` <- function(`length`, `dtype`) { + `dtype` <- .savvy_extract_ptr(`dtype`, "HDataType") + .savvy_wrap_HFft(.Call(savvy_HFft_new_forward__impl, `length`, `dtype`)) +} + +`HFft`$`new_inverse` <- function(`length`, `dtype`) { + `dtype` <- .savvy_extract_ptr(`dtype`, "HDataType") + .savvy_wrap_HFft(.Call(savvy_HFft_new_inverse__impl, `length`, `dtype`)) +} + +`HFft`$`new_real_forward` <- function(`length`, `dtype`) { `dtype` <- .savvy_extract_ptr(`dtype`, "HDataType") - .savvy_wrap_HFft(.Call(savvy_HFft_new_fft_forward__impl, `length`, `dtype`)) + .savvy_wrap_HRealFft(.Call(savvy_HFft_new_real_forward__impl, `length`, `dtype`)) } -`HFft`$`new_fft_inverse` <- function(`length`, `dtype`) { +`HFft`$`new_real_inverse` <- function(`length`, `dtype`) { `dtype` <- .savvy_extract_ptr(`dtype`, "HDataType") - .savvy_wrap_HFft(.Call(savvy_HFft_new_fft_inverse__impl, `length`, `dtype`)) + .savvy_wrap_HRealFft(.Call(savvy_HFft_new_real_inverse__impl, `length`, `dtype`)) } @@ -1249,9 +1259,9 @@ class(`HPolynomialDegree`) <- "HPolynomialDegree__bundle" ### associated functions for HRealFft -`HRealFft`$`new_real_fft` <- function(`length`, `dtype`) { +`HRealFft`$`new` <- function(`length`, `dtype`) { `dtype` <- .savvy_extract_ptr(`dtype`, "HDataType") - .savvy_wrap_HRealFft(.Call(savvy_HRealFft_new_real_fft__impl, `length`, `dtype`)) + .savvy_wrap_HRealFft(.Call(savvy_HRealFft_new__impl, `length`, `dtype`)) } diff --git a/r-harmonium/R/structs.R b/r-harmonium/R/structs.R index 4ed2ae1..74063f2 100644 --- a/r-harmonium/R/structs.R +++ b/r-harmonium/R/structs.R @@ -52,9 +52,4 @@ print.HInterpolationType = function(x, ...) { print.HFft = function(x, ...) { x$print() -} - -print.HRealFft = 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 22eb4c1..8bc62df 100644 --- a/r-harmonium/R/zzz.R +++ b/r-harmonium/R/zzz.R @@ -6,6 +6,7 @@ lockEnvironment(HInterpolationType, bindings = TRUE) lockEnvironment(HPolynomialDegree, bindings = TRUE) lockEnvironment(HArray, bindings = TRUE) + lockEnvironment(HArrayAudio, bindings = TRUE) lockEnvironment(HAudioSink, bindings = TRUE) lockEnvironment(HWindow, bindings = TRUE) lockEnvironment(HFile, bindings = TRUE) diff --git a/r-harmonium/src/init.c b/r-harmonium/src/init.c index 35d5749..874cbb2 100644 --- a/r-harmonium/src/init.c +++ b/r-harmonium/src/init.c @@ -90,11 +90,6 @@ SEXP savvy_HArray_dtype__impl(SEXP self__) { return handle_result(res); } -SEXP savvy_HArray_is_shared__impl(SEXP self__) { - SEXP res = savvy_HArray_is_shared__ffi(self__); - return handle_result(res); -} - SEXP savvy_HArray_mem_adress__impl(SEXP self__) { SEXP res = savvy_HArray_mem_adress__ffi(self__); return handle_result(res); @@ -105,28 +100,33 @@ SEXP savvy_HArray_is_standard_layout__impl(SEXP self__) { return handle_result(res); } +SEXP savvy_HArray_is_unique__impl(SEXP self__) { + SEXP res = savvy_HArray_is_unique__ffi(self__); + return handle_result(res); +} + SEXP savvy_HArray_invalidate__impl(SEXP self__) { SEXP res = savvy_HArray_invalidate__ffi(self__); return handle_result(res); } -SEXP savvy_HAudioOp_nchannels__impl(SEXP harray) { - SEXP res = savvy_HAudioOp_nchannels__ffi(harray); +SEXP savvy_HArrayAudio_nchannels__impl(SEXP harray) { + SEXP res = savvy_HArrayAudio_nchannels__ffi(harray); return handle_result(res); } -SEXP savvy_HAudioOp_nframes__impl(SEXP harray) { - SEXP res = savvy_HAudioOp_nframes__ffi(harray); +SEXP savvy_HArrayAudio_nframes__impl(SEXP harray) { + SEXP res = savvy_HArrayAudio_nframes__ffi(harray); return handle_result(res); } -SEXP savvy_HAudioOp_db_to_amplitude__impl(SEXP harray, SEXP reference, SEXP power) { - SEXP res = savvy_HAudioOp_db_to_amplitude__ffi(harray, reference, power); +SEXP savvy_HArrayAudio_db_to_amplitude__impl(SEXP harray, SEXP reference, SEXP power) { + SEXP res = savvy_HArrayAudio_db_to_amplitude__ffi(harray, reference, power); return handle_result(res); } -SEXP savvy_HAudioOp_to_mono__impl(SEXP harray) { - SEXP res = savvy_HAudioOp_to_mono__ffi(harray); +SEXP savvy_HArrayAudio_to_mono__impl(SEXP harray) { + SEXP res = savvy_HArrayAudio_to_mono__ffi(harray); return handle_result(res); } @@ -275,13 +275,23 @@ SEXP savvy_HDecoderStream_stream__impl(SEXP self__) { return handle_result(res); } -SEXP savvy_HFft_new_fft_forward__impl(SEXP length, SEXP dtype) { - SEXP res = savvy_HFft_new_fft_forward__ffi(length, dtype); +SEXP savvy_HFft_new_forward__impl(SEXP length, SEXP dtype) { + SEXP res = savvy_HFft_new_forward__ffi(length, dtype); + return handle_result(res); +} + +SEXP savvy_HFft_new_inverse__impl(SEXP length, SEXP dtype) { + SEXP res = savvy_HFft_new_inverse__ffi(length, 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); return handle_result(res); } -SEXP savvy_HFft_new_fft_inverse__impl(SEXP length, SEXP dtype) { - SEXP res = savvy_HFft_new_fft_inverse__ffi(length, dtype); +SEXP savvy_HFft_new_real_inverse__impl(SEXP length, SEXP dtype) { + SEXP res = savvy_HFft_new_real_inverse__ffi(length, dtype); return handle_result(res); } @@ -305,8 +315,8 @@ SEXP savvy_HFft_clone__impl(SEXP self__) { return handle_result(res); } -SEXP savvy_HFft_is_shared__impl(SEXP self__) { - SEXP res = savvy_HFft_is_shared__ffi(self__); +SEXP savvy_HFft_is_unique__impl(SEXP self__) { + SEXP res = savvy_HFft_is_unique__ffi(self__); return handle_result(res); } @@ -385,8 +395,8 @@ SEXP savvy_HPolynomialDegree_ne__impl(SEXP self__, SEXP other) { return handle_result(res); } -SEXP savvy_HRealFft_new_real_fft__impl(SEXP length, SEXP dtype) { - SEXP res = savvy_HRealFft_new_real_fft__ffi(length, dtype); +SEXP savvy_HRealFft_new__impl(SEXP length, SEXP dtype) { + SEXP res = savvy_HRealFft_new__ffi(length, dtype); return handle_result(res); } @@ -564,14 +574,14 @@ static const R_CallMethodDef CallEntries[] = { {"savvy_HArray_clone__impl", (DL_FUNC) &savvy_HArray_clone__impl, 1}, {"savvy_HArray_collect__impl", (DL_FUNC) &savvy_HArray_collect__impl, 1}, {"savvy_HArray_dtype__impl", (DL_FUNC) &savvy_HArray_dtype__impl, 1}, - {"savvy_HArray_is_shared__impl", (DL_FUNC) &savvy_HArray_is_shared__impl, 1}, {"savvy_HArray_mem_adress__impl", (DL_FUNC) &savvy_HArray_mem_adress__impl, 1}, {"savvy_HArray_is_standard_layout__impl", (DL_FUNC) &savvy_HArray_is_standard_layout__impl, 1}, + {"savvy_HArray_is_unique__impl", (DL_FUNC) &savvy_HArray_is_unique__impl, 1}, {"savvy_HArray_invalidate__impl", (DL_FUNC) &savvy_HArray_invalidate__impl, 1}, - {"savvy_HAudioOp_nchannels__impl", (DL_FUNC) &savvy_HAudioOp_nchannels__impl, 1}, - {"savvy_HAudioOp_nframes__impl", (DL_FUNC) &savvy_HAudioOp_nframes__impl, 1}, - {"savvy_HAudioOp_db_to_amplitude__impl", (DL_FUNC) &savvy_HAudioOp_db_to_amplitude__impl, 3}, - {"savvy_HAudioOp_to_mono__impl", (DL_FUNC) &savvy_HAudioOp_to_mono__impl, 1}, + {"savvy_HArrayAudio_nchannels__impl", (DL_FUNC) &savvy_HArrayAudio_nchannels__impl, 1}, + {"savvy_HArrayAudio_nframes__impl", (DL_FUNC) &savvy_HArrayAudio_nframes__impl, 1}, + {"savvy_HArrayAudio_db_to_amplitude__impl", (DL_FUNC) &savvy_HArrayAudio_db_to_amplitude__impl, 3}, + {"savvy_HArrayAudio_to_mono__impl", (DL_FUNC) &savvy_HArrayAudio_to_mono__impl, 1}, {"savvy_HAudioSink_new__impl", (DL_FUNC) &savvy_HAudioSink_new__impl, 0}, {"savvy_HAudioSink_append_from_harray__impl", (DL_FUNC) &savvy_HAudioSink_append_from_harray__impl, 3}, {"savvy_HAudioSink_append_from_file__impl", (DL_FUNC) &savvy_HAudioSink_append_from_file__impl, 2}, @@ -601,13 +611,15 @@ static const R_CallMethodDef CallEntries[] = { {"savvy_HDecodedAudio_sr__impl", (DL_FUNC) &savvy_HDecodedAudio_sr__impl, 1}, {"savvy_HDecodedAudio_invalidate__impl", (DL_FUNC) &savvy_HDecodedAudio_invalidate__impl, 1}, {"savvy_HDecoderStream_stream__impl", (DL_FUNC) &savvy_HDecoderStream_stream__impl, 1}, - {"savvy_HFft_new_fft_forward__impl", (DL_FUNC) &savvy_HFft_new_fft_forward__impl, 2}, - {"savvy_HFft_new_fft_inverse__impl", (DL_FUNC) &savvy_HFft_new_fft_inverse__impl, 2}, + {"savvy_HFft_new_forward__impl", (DL_FUNC) &savvy_HFft_new_forward__impl, 2}, + {"savvy_HFft_new_inverse__impl", (DL_FUNC) &savvy_HFft_new_inverse__impl, 2}, + {"savvy_HFft_new_real_forward__impl", (DL_FUNC) &savvy_HFft_new_real_forward__impl, 2}, + {"savvy_HFft_new_real_inverse__impl", (DL_FUNC) &savvy_HFft_new_real_inverse__impl, 2}, {"savvy_HFft_process__impl", (DL_FUNC) &savvy_HFft_process__impl, 2}, {"savvy_HFft_dtype__impl", (DL_FUNC) &savvy_HFft_dtype__impl, 1}, {"savvy_HFft_print__impl", (DL_FUNC) &savvy_HFft_print__impl, 1}, {"savvy_HFft_clone__impl", (DL_FUNC) &savvy_HFft_clone__impl, 1}, - {"savvy_HFft_is_shared__impl", (DL_FUNC) &savvy_HFft_is_shared__impl, 1}, + {"savvy_HFft_is_unique__impl", (DL_FUNC) &savvy_HFft_is_unique__impl, 1}, {"savvy_HFft_invalidate__impl", (DL_FUNC) &savvy_HFft_invalidate__impl, 1}, {"savvy_HFile_decode__impl", (DL_FUNC) &savvy_HFile_decode__impl, 2}, {"savvy_HFile_decode_stream__impl", (DL_FUNC) &savvy_HFile_decode_stream__impl, 3}, @@ -623,7 +635,7 @@ 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_real_fft__impl", (DL_FUNC) &savvy_HRealFft_new_real_fft__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}, diff --git a/r-harmonium/src/rust/Cargo.toml b/r-harmonium/src/rust/Cargo.toml index 8691060..7770516 100644 --- a/r-harmonium/src/rust/Cargo.toml +++ b/r-harmonium/src/rust/Cargo.toml @@ -17,22 +17,22 @@ panic = "unwind" # otherwise savvy crashes R when panic. # prevents package from thinking it's in the workspace. [dependencies] -#harmonium-core = { path = "../../../harmonium-core" } -#harmonium-io = { path = "../../../harmonium-io" } -#harmonium-resample = { path = "../../../harmonium-resample" } -#harmonium-fft = { path = "../../../harmonium-fft" } -#harmonium-window = { path = "../../../harmonium-window" } -harmonium-core = { git = "https://github.com/daniellga/harmonium.git" } -harmonium-io = { git = "https://github.com/daniellga/harmonium.git", features = ["all", "opt-simd"] } -harmonium-resample = { git = "https://github.com/daniellga/harmonium.git" } -harmonium-fft = { git = "https://github.com/daniellga/harmonium.git" } -harmonium-window = { git = "https://github.com/daniellga/harmonium.git" } -rubato = "0.15.0" -rustfft = "6.2" -realfft = "3.3" -ndarray = "0.15.6" -num-complex = "0.4" -savvy = { version = "0.6.5", features = ["complex"] } +harmonium-core = { path = "../../../harmonium-core" } +harmonium-io = { path = "../../../harmonium-io" } +harmonium-resample = { path = "../../../harmonium-resample" } +harmonium-fft = { path = "../../../harmonium-fft" } +harmonium-window = { path = "../../../harmonium-window" } +#harmonium-core = { git = "https://github.com/daniellga/harmonium.git" } +#harmonium-io = { git = "https://github.com/daniellga/harmonium.git", features = ["all", "opt-simd"] } +#harmonium-resample = { git = "https://github.com/daniellga/harmonium.git" } +#harmonium-fft = { git = "https://github.com/daniellga/harmonium.git" } +#harmonium-window = { git = "https://github.com/daniellga/harmonium.git" } +rubato = { version = "0.15.0", default-features = false, features = ["fft_resampler"] } +rustfft = { version = "6.2", default-features = false, features = ["avx", "sse", "neon"] } +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"] } [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 e0fad9f..68e6991 100644 --- a/r-harmonium/src/rust/api.h +++ b/r-harmonium/src/rust/api.h @@ -12,16 +12,16 @@ SEXP savvy_HArray_ne__ffi(SEXP self__, SEXP other); SEXP savvy_HArray_clone__ffi(SEXP self__); SEXP savvy_HArray_collect__ffi(SEXP self__); SEXP savvy_HArray_dtype__ffi(SEXP self__); -SEXP savvy_HArray_is_shared__ffi(SEXP self__); SEXP savvy_HArray_mem_adress__ffi(SEXP self__); SEXP savvy_HArray_is_standard_layout__ffi(SEXP self__); +SEXP savvy_HArray_is_unique__ffi(SEXP self__); SEXP savvy_HArray_invalidate__ffi(SEXP self__); -// methods and associated functions for HAudioOp -SEXP savvy_HAudioOp_nchannels__ffi(SEXP harray); -SEXP savvy_HAudioOp_nframes__ffi(SEXP harray); -SEXP savvy_HAudioOp_db_to_amplitude__ffi(SEXP harray, SEXP reference, SEXP power); -SEXP savvy_HAudioOp_to_mono__ffi(SEXP harray); +// 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); // methods and associated functions for HAudioSink SEXP savvy_HAudioSink_new__ffi(void); @@ -61,13 +61,15 @@ SEXP savvy_HDecodedAudio_invalidate__ffi(SEXP self__); SEXP savvy_HDecoderStream_stream__ffi(SEXP self__); // methods and associated functions for HFft -SEXP savvy_HFft_new_fft_forward__ffi(SEXP length, SEXP dtype); -SEXP savvy_HFft_new_fft_inverse__ffi(SEXP length, SEXP dtype); +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_dtype__ffi(SEXP self__); SEXP savvy_HFft_print__ffi(SEXP self__); SEXP savvy_HFft_clone__ffi(SEXP self__); -SEXP savvy_HFft_is_shared__ffi(SEXP self__); +SEXP savvy_HFft_is_unique__ffi(SEXP self__); SEXP savvy_HFft_invalidate__ffi(SEXP self__); // methods and associated functions for HFile @@ -93,7 +95,7 @@ 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_real_fft__ffi(SEXP length, SEXP dtype); +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__); diff --git a/r-harmonium/src/rust/src/harray.rs b/r-harmonium/src/rust/src/harray.rs index cbce5f9..81b30c3 100644 --- a/r-harmonium/src/rust/src/harray.rs +++ b/r-harmonium/src/rust/src/harray.rs @@ -488,41 +488,6 @@ impl HArray { Ok(self.0.dtype()) } - /// HArray - /// ## is_shared - /// - /// `is_shared() -> bool` - /// - /// Checks if the object is shared. - /// - /// Since `HArray` 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,2,3,4,5,6,7,8,9,10,11,12), c(3,4)) - /// dtype = HDataType$Float32 - /// harray1 = HArray$new_from_values(arr, dtype) - /// harray1$is_shared() # FALSE. - /// - /// harray2 = harray1$clone() - /// harray1$is_shared() # TRUE, HArray object shared with harray2. - /// ``` - /// - /// _________ - /// - fn is_shared(&self) -> savvy::Result { - let bool = Arc::weak_count(&self.0) + Arc::strong_count(&self.0) != 1; - let logical_sexp: OwnedLogicalSexp = bool.try_into()?; - logical_sexp.into() - } - /// HArray /// ## mem_adress /// @@ -586,6 +551,41 @@ impl HArray { self.0.is_standard_layout() } + /// HArray + /// ## is_unique + /// + /// `is_unique() -> bool` + /// + /// Checks if the object is shared. + /// + /// Since `HArray` 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,2,3,4,5,6,7,8,9,10,11,12), c(3,4)) + /// dtype = HDataType$Float32 + /// harray1 = HArray$new_from_values(arr, dtype) + /// harray1$is_unique() # TRUE. + /// + /// harray2 = harray1$clone() + /// harray1$is_unique() # FALSE, HArray object shared with harray2. + /// ``` + /// + /// _________ + /// + fn is_unique(&mut self) -> savvy::Result { + // Requires &mut to avoid race condition. + let inner_mut = self.get_inner_mut(); + inner_mut.is_unique() + } + /// HArray /// ## invalidate /// diff --git a/r-harmonium/src/rust/src/harrayr.rs b/r-harmonium/src/rust/src/harrayr.rs index e4ed4ae..1e3ac9d 100644 --- a/r-harmonium/src/rust/src/harrayr.rs +++ b/r-harmonium/src/rust/src/harrayr.rs @@ -1,11 +1,10 @@ -use crate::{errors::HErrorR, hdatatype::HDataType}; -use harmonium_core::audioop::AudioOp; +use crate::{haudioop::HAudioOp, hdatatype::HDataType}; use ndarray::{IxDyn, SliceInfo, SliceInfoElem}; use num_complex::Complex; use savvy::{r_println, OwnedComplexSexp, OwnedIntegerSexp, OwnedLogicalSexp, OwnedRealSexp, Sexp}; use std::{any::Any, sync::Arc}; -pub trait HArrayR: Send + Sync { +pub trait HArrayR: Send + Sync + HAudioOp { fn as_any(&self) -> &dyn Any; fn as_any_mut(&mut self) -> &mut dyn Any; fn len(&self) -> usize; @@ -17,11 +16,8 @@ pub trait HArrayR: Send + Sync { fn dtype(&self) -> HDataType; fn mem_adress(&self) -> String; fn is_standard_layout(&self) -> savvy::Result; + fn is_unique(&mut self) -> savvy::Result; fn clone_inner(&self) -> Arc; - fn nchannels(&self) -> savvy::Result; - fn nframes(&self) -> savvy::Result; - fn db_to_amplitude(&mut self, reference: f64, power: f64) -> savvy::Result<()>; - fn to_mono(&mut self) -> savvy::Result<()>; } impl HArrayR for harmonium_core::array::HArray { @@ -90,26 +86,14 @@ impl HArrayR for harmonium_core::array::HArray { logical_sexp.into() } - fn clone_inner(&self) -> Arc { - Arc::new(self.clone()) - } - - fn nchannels(&self) -> savvy::Result { - Ok(AudioOp::nchannels(self)) - } - - fn nframes(&self) -> savvy::Result { - Ok(AudioOp::nframes(self)) - } - - fn db_to_amplitude(&mut self, reference: f64, power: f64) -> savvy::Result<()> { - AudioOp::db_to_amplitude(self, reference as f32, power as f32); - Ok(()) + fn is_unique(&mut self) -> savvy::Result { + let is_unique = self.0.is_unique(); + let logical_sexp: OwnedLogicalSexp = is_unique.try_into()?; + logical_sexp.into() } - fn to_mono(&mut self) -> savvy::Result<()> { - *self = AudioOp::to_mono(self).map_err(HErrorR::from)?; - Ok(()) + fn clone_inner(&self) -> Arc { + Arc::new(self.clone()) } } @@ -178,26 +162,14 @@ impl HArrayR for harmonium_core::array::HArray { logical_sexp.into() } - fn clone_inner(&self) -> Arc { - Arc::new(self.clone()) - } - - fn nchannels(&self) -> savvy::Result { - Ok(AudioOp::nchannels(self)) - } - - fn nframes(&self) -> savvy::Result { - Ok(AudioOp::nframes(self)) - } - - fn db_to_amplitude(&mut self, reference: f64, power: f64) -> savvy::Result<()> { - AudioOp::db_to_amplitude(self, reference, power); - Ok(()) + fn is_unique(&mut self) -> savvy::Result { + let is_unique = self.0.is_unique(); + let logical_sexp: OwnedLogicalSexp = is_unique.try_into()?; + logical_sexp.into() } - fn to_mono(&mut self) -> savvy::Result<()> { - *self = AudioOp::to_mono(self).map_err(HErrorR::from)?; - Ok(()) + fn clone_inner(&self) -> Arc { + Arc::new(self.clone()) } } @@ -266,24 +238,14 @@ impl HArrayR for harmonium_core::array::HArray, IxDyn> { logical_sexp.into() } - fn clone_inner(&self) -> Arc { - Arc::new(self.clone()) - } - - fn nchannels(&self) -> savvy::Result { - Err("Operation only allowed for float HArrays.".into()) - } - - fn nframes(&self) -> savvy::Result { - Err("Operation only allowed for float HArrays.".into()) - } - - fn db_to_amplitude(&mut self, _: f64, _: f64) -> savvy::Result<()> { - Err("Operation only allowed for float HArrays.".into()) + fn is_unique(&mut self) -> savvy::Result { + let is_unique = self.0.is_unique(); + let logical_sexp: OwnedLogicalSexp = is_unique.try_into()?; + logical_sexp.into() } - fn to_mono(&mut self) -> savvy::Result<()> { - Err("Operation only allowed for float HArrays.".into()) + fn clone_inner(&self) -> Arc { + Arc::new(self.clone()) } } @@ -352,23 +314,13 @@ impl HArrayR for harmonium_core::array::HArray, IxDyn> { logical_sexp.into() } - fn clone_inner(&self) -> Arc { - Arc::new(self.clone()) - } - - fn nchannels(&self) -> savvy::Result { - Err("Operation only allowed for float HArrays.".into()) - } - - fn nframes(&self) -> savvy::Result { - Err("Operation only allowed for float HArrays.".into()) - } - - fn db_to_amplitude(&mut self, _: f64, _: f64) -> savvy::Result<()> { - Err("Operation only allowed for float HArrays.".into()) + fn is_unique(&mut self) -> savvy::Result { + let is_unique = self.0.is_unique(); + let logical_sexp: OwnedLogicalSexp = is_unique.try_into()?; + logical_sexp.into() } - fn to_mono(&mut self) -> savvy::Result<()> { - Err("Operation only allowed for float HArrays.".into()) + fn clone_inner(&self) -> Arc { + Arc::new(self.clone()) } } diff --git a/r-harmonium/src/rust/src/haudioop.rs b/r-harmonium/src/rust/src/haudioop.rs index 2348c0d..8f25667 100644 --- a/r-harmonium/src/rust/src/haudioop.rs +++ b/r-harmonium/src/rust/src/haudioop.rs @@ -1,20 +1,24 @@ use crate::{ conversions::{try_from_usize_to_int_sexp, ToScalar}, + errors::HErrorR, harray::HArray, }; +use harmonium_core::audioop::AudioOp; +use ndarray::IxDyn; +use num_complex::Complex; use savvy::{savvy, Sexp}; -/// HAudioOp +/// HArrayAudio /// A collection of methods that can be applied to float 1D or 2D `HArray`s which represents audio data. /// /// # Methods /// #[savvy] -pub struct HAudioOp; +pub struct HArrayAudio; #[savvy] -impl HAudioOp { - /// HAudioOp +impl HArrayAudio { + /// HArrayAudio /// ## nchannels /// /// `nchannels() -> integer` @@ -34,7 +38,7 @@ impl HAudioOp { /// 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) - /// HAudioOp$nchannels(harray) + /// HArrayAudio$nchannels(harray) /// ``` /// /// _________ @@ -45,7 +49,7 @@ impl HAudioOp { integer_sexp.into() } - /// HAudioOp + /// HArrayAudio /// ## nframes /// /// `nframes() -> integer` @@ -67,7 +71,7 @@ impl HAudioOp { /// 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) - /// HAudioOp$nframes(harray) + /// HArrayAudio$nframes(harray) /// ``` /// /// _________ @@ -78,7 +82,7 @@ impl HAudioOp { integer_sexp.into() } - /// HAudioOp + /// HArrayAudio /// ## db_to_amplitude /// /// `db_to_amplitude(harray: HArray, reference: double)` @@ -110,7 +114,7 @@ impl HAudioOp { /// 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) - /// HAudioOp$db_to_amplitude(harray, 2, 1) + /// HArrayAudio$db_to_amplitude(harray, 2, 1) /// ``` /// /// _________ @@ -122,7 +126,7 @@ impl HAudioOp { inner_mut.db_to_amplitude(reference, power) } - /// HAudioOp + /// HArrayAudio /// ## to_mono /// /// `to_mono(harray: HArray)` @@ -144,7 +148,7 @@ impl HAudioOp { /// 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) - /// HAudioOp$to_mono(harray) + /// HArrayAudio$to_mono(harray) /// ``` /// /// _________ @@ -154,3 +158,86 @@ impl HAudioOp { inner_mut.to_mono() } } + +pub trait HAudioOp { + fn nchannels(&self) -> savvy::Result; + fn nframes(&self) -> savvy::Result; + fn db_to_amplitude(&mut self, reference: f64, power: f64) -> savvy::Result<()>; + fn to_mono(&mut self) -> savvy::Result<()>; +} + +impl HAudioOp for harmonium_core::array::HArray { + fn nchannels(&self) -> savvy::Result { + Ok(AudioOp::nchannels(self)) + } + + fn nframes(&self) -> savvy::Result { + Ok(AudioOp::nframes(self)) + } + + fn db_to_amplitude(&mut self, reference: f64, power: f64) -> savvy::Result<()> { + AudioOp::db_to_amplitude(self, reference as f32, power as f32); + Ok(()) + } + + fn to_mono(&mut self) -> savvy::Result<()> { + *self = AudioOp::to_mono(self).map_err(HErrorR::from)?; + Ok(()) + } +} + +impl HAudioOp for harmonium_core::array::HArray { + fn nchannels(&self) -> savvy::Result { + Ok(AudioOp::nchannels(self)) + } + + fn nframes(&self) -> savvy::Result { + Ok(AudioOp::nframes(self)) + } + + fn db_to_amplitude(&mut self, reference: f64, power: f64) -> savvy::Result<()> { + AudioOp::db_to_amplitude(self, reference, power); + Ok(()) + } + + fn to_mono(&mut self) -> savvy::Result<()> { + *self = AudioOp::to_mono(self).map_err(HErrorR::from)?; + Ok(()) + } +} + +impl HAudioOp for harmonium_core::array::HArray, IxDyn> { + fn nchannels(&self) -> savvy::Result { + Err("Operation only allowed for float HArrays.".into()) + } + + fn nframes(&self) -> savvy::Result { + Err("Operation only allowed for float HArrays.".into()) + } + + fn db_to_amplitude(&mut self, _: f64, _: f64) -> savvy::Result<()> { + Err("Operation only allowed for float HArrays.".into()) + } + + fn to_mono(&mut self) -> savvy::Result<()> { + Err("Operation only allowed for float HArrays.".into()) + } +} + +impl HAudioOp for harmonium_core::array::HArray, IxDyn> { + fn nchannels(&self) -> savvy::Result { + Err("Operation only allowed for float HArrays.".into()) + } + + fn nframes(&self) -> savvy::Result { + Err("Operation only allowed for float HArrays.".into()) + } + + fn db_to_amplitude(&mut self, _: f64, _: f64) -> savvy::Result<()> { + Err("Operation only allowed for float HArrays.".into()) + } + + fn to_mono(&mut self) -> savvy::Result<()> { + Err("Operation only allowed for float HArrays.".into()) + } +} diff --git a/r-harmonium/src/rust/src/hfft.rs b/r-harmonium/src/rust/src/hfft.rs index eadfe3e..42dcc7e 100644 --- a/r-harmonium/src/rust/src/hfft.rs +++ b/r-harmonium/src/rust/src/hfft.rs @@ -11,12 +11,6 @@ pub trait HFftR: Send + Sync { fn clone_inner(&self) -> Arc; } -pub trait HRealFftR: Send + Sync { - fn process(&mut self, harray: &mut HArray) -> savvy::Result<()>; - fn dtype(&self) -> savvy::Result; - fn clone_inner(&self) -> Arc; -} - /// HFft /// An `HFft` is used to create FFTs. It caches results internally, so when making more than one FFT it is advisable to reuse the same `HFft` instance. /// @@ -35,14 +29,14 @@ pub struct HFft(pub Arc); ///// #[savvy] #[derive(Clone)] -pub struct HRealFft(pub Arc); +pub struct HRealFft(pub Arc); #[savvy] impl HFft { /// HFft - /// ## new_fft_forward + /// ## new_forward /// - /// `new_fft_forward(length: integer, dtype: HDataType) -> HFft` + /// `new_forward(length: integer, dtype: HDataType) -> HFft` /// /// Creates a new `HFft` instance which will be used to calculate forward FFTs. /// @@ -77,28 +71,28 @@ impl HFft { /// 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_fft_forward(3L, harray$dtype()) + /// hfft = HFft$new_forward(3L, harray$dtype()) /// ``` /// /// _________ /// - fn new_fft_forward(length: Sexp, dtype: &HDataType) -> savvy::Result { + 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("The HFft is for Complex dtypes.".into()), - HDataType::Float64 => Err("The HFft is for Complex dtypes.".into()), - HDataType::Complex32 => Ok(HFft(Arc::new(Fft::::new_fft_forward(length)))), - HDataType::Complex64 => Ok(HFft(Arc::new(Fft::::new_fft_forward(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(Fft::::new_forward(length)))), + HDataType::Complex64 => Ok(HFft(Arc::new(Fft::::new_forward(length)))), } } /// HFft - /// ## new_fft_inverse + /// ## new_inverse /// - /// `new_fft_inverse(length: integer, dtype: HDataType) -> HFft` + /// `new_inverse(length: integer, dtype: HDataType) -> HFft` /// /// Creates a new `HFft` instance which will be used to calculate inverse FFTs. /// @@ -133,21 +127,133 @@ impl HFft { /// 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_fft_inverse(3L, harray$dtype()) + /// hfft = HFft$new_inverse(3L, harray$dtype()) + /// ``` + /// + /// _________ + /// + fn new_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 Complex dtypes.".into()), + HDataType::Float64 => Err("This HFft is for Complex dtypes.".into()), + HDataType::Complex32 => Ok(HFft(Arc::new(Fft::::new_inverse(length)))), + HDataType::Complex64 => Ok(HFft(Arc::new(Fft::::new_inverse(length)))), + } + } + + /// HFft + /// ## new_real_forward + /// + /// `new_real_forward(length: integer, dtype: HDataType) -> HFft` + /// + /// Creates a new `HFft` instance which will be used to calculate real 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` + /// + /// A float `HDataType` to indicate the dtype that the `HFft` will be working with. + /// + /// #### Returns + /// + /// An `HFft`. + /// + /// 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) + /// hfft = HFft$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(HRealFft(Arc::new(RealFftForward::::new(length)))), + HDataType::Float64 => Ok(HRealFft(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()), + } + } + + /// HFft + /// ## new_real_inverse + /// + /// `new_real_inverse(length: integer, dtype: HDataType) -> HFft` + /// + /// Creates a new `HFft` instance which will be used to calculate real inverse 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` + /// + /// A complex `HDataType` to indicate the dtype that the `HFft` will be working with. + /// + /// #### Returns + /// + /// An `HFft`. + /// + /// Will return an error if dtype is of 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 = HFft$new_real_inverse(3L, harray$dtype()) /// ``` /// /// _________ /// - fn new_fft_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("The HFft is for Complex dtypes.".into()), - HDataType::Float64 => Err("The HFft is for Complex dtypes.".into()), - HDataType::Complex32 => Ok(HFft(Arc::new(Fft::::new_fft_inverse(length)))), - HDataType::Complex64 => Ok(HFft(Arc::new(Fft::::new_fft_inverse(length)))), + 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)))), } } @@ -158,6 +264,9 @@ impl HFft { /// /// Computes the fast fourier transform of a complex `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. /// @@ -193,14 +302,14 @@ impl HFft { /// 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_fft_forward(3L, harray$dtype()) + /// hfft = HFft$new_forward(3L, harray$dtype()) /// hfft$process(harray) /// /// # Inverse FFT. /// 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_fft_inverse(3L, harray$dtype()) + /// hfft = HFft$new_inverse(3L, harray$dtype()) /// hfft$process(harray) /// ``` /// @@ -229,7 +338,7 @@ impl HFft { /// 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_fft_forward(3L, harray$dtype()) + /// hfft = HFft$new_forward(3L, harray$dtype()) /// hfft$dtype() /// ``` /// @@ -255,7 +364,7 @@ impl HFft { /// 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_fft_forward(3L, harray$dtype()) + /// hfft = HFft$new_forward(3L, harray$dtype()) /// hfft$print() /// /// # or similarly: @@ -291,7 +400,7 @@ impl HFft { /// 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_fft_forward(3L, harray$dtype()) + /// hfft = HFft$new_forward(3L, harray$dtype()) /// hfft$clone() /// ``` /// @@ -302,11 +411,11 @@ impl HFft { } /// HFft - /// ## is_shared + /// ## is_unique /// - /// `is_shared() -> bool` + /// `is_unique() -> bool` /// - /// Checks if the object is shared. + /// Checks if the object is unique. /// /// Since `HFft` 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. @@ -322,17 +431,18 @@ impl HFft { /// 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_fft_forward(3L, harray$dtype()) - /// hfft$is_shared() # FALSE. + /// hfft = HFft$new_forward(3L, harray$dtype()) + /// hfft$is_unique() # TRUE. /// /// hfft2 = hfft$clone() - /// hfft$is_shared() # TRUE, hfft shares the same inner object with hfft2. + /// hfft$is_unique() # FALSE, hfft shares the same inner object with hfft2. /// ``` /// /// _________ /// - fn is_shared(&self) -> savvy::Result { - let bool = Arc::weak_count(&self.0) + Arc::strong_count(&self.0) != 1; + 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() } @@ -352,7 +462,7 @@ impl HFft { /// 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_fft_forward(3L, harray$dtype()) + /// hfft = HFft$new_forward(3L, harray$dtype()) /// hfft$invalidate() /// ``` /// @@ -366,9 +476,9 @@ impl HFft { #[savvy] impl HRealFft { /// HRealFft - /// ## new_real_fft + /// ## new /// - /// `new_real_fft(length: integer, dtype: HDataType) -> HRealFft` + /// `new(length: integer, dtype: HDataType) -> HRealFft` /// /// Creates a new `HRealFft` instance which will be used to calculate forward FFTs. /// @@ -404,29 +514,21 @@ impl HRealFft { /// 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_real_fft(3L, harray$dtype()) + /// hfft = HRealFft$new(3L, harray$dtype()) /// ``` /// /// _________ /// - fn new_real_fft(length: Sexp, dtype: &HDataType) -> savvy::Result { + 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_real_fft_forward(length), - ))), - HDataType::Float64 => Ok(HRealFft(Arc::new( - RealFftForward::::new_real_fft_forward(length), - ))), - HDataType::Complex32 => Ok(HRealFft(Arc::new( - RealFftInverse::::new_real_fft_inverse(length), - ))), - HDataType::Complex64 => Ok(HRealFft(Arc::new( - RealFftInverse::::new_real_fft_inverse(length), - ))), + 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)))), } } @@ -436,8 +538,8 @@ impl HRealFft { /// `process(harray: HArray)` /// /// Computes the fast fourier transform of a float `HArray` or the inverse fast fourier transform of a complex `HArray`. - /// For a 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 an inverse FFT, transforms a complex spectrum of length `N/2+1` (with `N/2` rounded down) to a real-valued + /// 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. @@ -477,10 +579,10 @@ impl HRealFft { /// dtype = HDataType$Float32 /// harray = HArray$new_from_values(arr, dtype) /// # Forward fft - /// fft = HRealFft$new_real_fft(3L, harray$dtype()) + /// fft = HRealFft$new(3L, harray$dtype()) /// fft$process(harray) /// # Inverse fft - /// ifft = HRealFft$new_real_fft(3L, HDataType$Complex32) + /// ifft = HRealFft$new(3L, HDataType$Complex32) /// ifft$process(harray) /// ``` /// @@ -509,7 +611,7 @@ impl HRealFft { /// 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_real_fft(3L, HDataType$Complex32) + /// hfft = HRealFft$new(3L, HDataType$Complex32) /// hfft$dtype() /// ``` /// @@ -535,7 +637,7 @@ impl HRealFft { /// 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_real_fft(3L, HDataType$Complex32) + /// hfft = HRealFft$new(3L, HDataType$Complex32) /// hfft$print() /// /// # or similarly: @@ -571,7 +673,7 @@ impl HRealFft { /// 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_real_fft(3L, HDataType$Complex32) + /// hfft = HRealFft$new(3L, HDataType$Complex32) /// hfft$clone() /// ``` /// @@ -602,7 +704,7 @@ impl HRealFft { /// 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_real_fft(3L, HDataType$Complex32) + /// hfft = HRealFft$new(3L, HDataType$Complex32) /// hfft$is_shared() # FALSE. /// /// hfft2 = hfft$clone() @@ -632,7 +734,7 @@ impl HRealFft { /// 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_real_fft(3L, HDataType$Complex32) + /// hfft = HRealFft$new(3L, HDataType$Complex32) /// hfft$invalidate() /// ``` /// @@ -680,10 +782,10 @@ impl_hfft!( macro_rules! impl_hrealfftforward { ($(($t1:ty, $t2:ty, $e1:expr)),+) => { $( - impl HRealFftR for $t1 { + impl HFftR for $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 result = harmonium_fft::fft::ProcessRealFftForward::process(self, harray_inner).map_err(|err| savvy::Error::from(HErrorR::from(err)))?; + let result = harmonium_fft::fft::ProcessFft::process(self, harray_inner).map_err(|err| savvy::Error::from(HErrorR::from(err)))?; *harray = HArray(Arc::new(result)); Ok(()) } @@ -692,7 +794,7 @@ macro_rules! impl_hrealfftforward { Ok($e1) } - fn clone_inner(&self) -> Arc { + fn clone_inner(&self) -> Arc { Arc::new(self.clone()) } } @@ -710,33 +812,7 @@ impl_hrealfftforward!( RealFftForward, harmonium_core::array::HArray, HDataType::Float64 - ) -); - -macro_rules! impl_hrealfftinverse { - ($(($t1:ty, $t2:ty, $e1:expr)),+) => { - $( - impl HRealFftR for $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 result = harmonium_fft::fft::ProcessRealFftInverse::process(self, harray_inner).map_err(|err| savvy::Error::from(HErrorR::from(err)))?; - *harray = HArray(Arc::new(result)); - Ok(()) - } - - fn dtype(&self) -> savvy::Result { - Ok($e1) - } - - fn clone_inner(&self) -> Arc { - Arc::new(self.clone()) - } - } - )+ - }; -} - -impl_hrealfftinverse!( + ), ( RealFftInverse, harmonium_core::array::HArray, IxDyn>, @@ -764,7 +840,7 @@ impl HFft { impl HRealFft { #[doc(hidden)] - pub fn get_inner_mut(&mut self) -> &mut dyn HRealFftR { + 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(); diff --git a/r-harmonium/tests/testthat/test_db_to_amplitude.R b/r-harmonium/tests/testthat/test_db_to_amplitude.R index 590c33f..296e00d 100644 --- a/r-harmonium/tests/testthat/test_db_to_amplitude.R +++ b/r-harmonium/tests/testthat/test_db_to_amplitude.R @@ -3,7 +3,7 @@ test_that( { check_db_to_amplitude = function(dtype, input, result) { harray = HArray$new_from_values(input, dtype) - HAudioOp$db_to_amplitude(harray, 1, 1) + HArrayAudio$db_to_amplitude(harray, 1, 1) expect_true(all.equal(harray$collect(), result, tolerance = 1e-4)) } diff --git a/r-harmonium/tests/testthat/test_harray.R b/r-harmonium/tests/testthat/test_harray.R index b33c7e6..33b20a2 100644 --- a/r-harmonium/tests/testthat/test_harray.R +++ b/r-harmonium/tests/testthat/test_harray.R @@ -10,7 +10,7 @@ test_that( expect_equal(harray$ndim(), length(dim(arr))) expect_true(harray$eq(harray)) expect_false(harray$ne(harray)) - expect_false(harray$is_shared()) + expect_true(harray$is_unique()) expect_equal(harray$collect(), arr) } diff --git a/r-harmonium/tests/testthat/test_hfft.R b/r-harmonium/tests/testthat/test_hfft.R index 43828cb..63990b2 100644 --- a/r-harmonium/tests/testthat/test_hfft.R +++ b/r-harmonium/tests/testthat/test_hfft.R @@ -8,7 +8,7 @@ test_that( dtype = HDataType$Complex64 harray = HArray$new_from_values(x, dtype) - hfft = HFft$new_fft_forward(30L, dtype) + hfft = HFft$new_forward(30L, dtype) hfft$process(harray) expect_equal(stats::fft(x), harray$collect(), tolerance = 1.0e-06) } @@ -20,7 +20,7 @@ test_that( dtype = HDataType$Complex64 harray = HArray$new_from_values(x, dtype) - hfft = HFft$new_fft_inverse(30L, dtype) + hfft = HFft$new_inverse(30L, dtype) hfft$process(harray) expect_equal(stats::fft(x, inverse = TRUE), harray$collect(), tolerance = 1.0e-06) } @@ -33,7 +33,7 @@ test_that( dtype = HDataType$Complex64 harray = HArray$new_from_values(x, dtype) - hfft = HFft$new_fft_forward(as.integer(30/10), dtype) + hfft = HFft$new_forward(as.integer(30/10), dtype) hfft$process(harray) expect_equal(stats::mvfft(x), harray$collect(), tolerance = 1.0e-06) } @@ -46,7 +46,7 @@ test_that( dtype = HDataType$Complex64 harray = HArray$new_from_values(x, dtype) - hfft = HFft$new_fft_inverse(as.integer(30/10), dtype) + hfft = HFft$new_inverse(as.integer(30/10), dtype) hfft$process(harray) expect_equal(stats::mvfft(x, inverse = TRUE), harray$collect(), tolerance = 1.0e-06) } @@ -55,7 +55,7 @@ test_that( x = as.array(as.double(1:6)) dtype = HDataType$Float64 harray = HArray$new_from_values(x, dtype) - hfft = HRealFft$new_real_fft(as.integer(6), dtype) + hfft = HFft$new_real_forward(as.integer(6), dtype) hfft$process(harray) result = as.array(c(21+0i, -3+5.196152i, -3+1.732051i, -3+0i)) expect_equal(harray$collect(), result, tolerance = 1.0e-06) @@ -66,7 +66,7 @@ test_that( x = matrix(x, ncol = 3) dtype = HDataType$Float64 harray = HArray$new_from_values(x, dtype) - hfft = HRealFft$new_real_fft(as.integer(4), dtype) + hfft = HFft$new_real_forward(as.integer(4), dtype) hfft$process(harray) result = matrix(c(10+0i, -2+2i, -2+0i, 26+0i, -2+2i, -2+0i, 42+0i, -2+2i, -2+0i), ncol = 3) expect_equal(harray$collect(), result, tolerance = 1.0e-06) diff --git a/r-harmonium/tests/testthat/test_hresampler.R b/r-harmonium/tests/testthat/test_hresampler.R index cadbb35..7186fab 100644 --- a/r-harmonium/tests/testthat/test_hresampler.R +++ b/r-harmonium/tests/testthat/test_hresampler.R @@ -6,10 +6,10 @@ test_that( harray = HArray$new_from_values(arr, dtype = HDataType$Float64) hparams = HSincInterpolationParameters$new(256L, 0.95, 256L, HInterpolationType$Linear, HWindowType$BlackmanHarris2) - res = HResampler$new_sinc(48000 / 44100, 2, hparams, 512L, HAudioOp$nchannels(harray), HResamplerType$SincFixedIn, HDataType$Float32) + res = HResampler$new_sinc(48000 / 44100, 2, hparams, 512L, HArrayAudio$nchannels(harray), HResamplerType$SincFixedIn, HDataType$Float32) expect_error(res$process(harray)) - res = HResampler$new_sinc(48000 / 44100, 2, hparams, 512L, HAudioOp$nchannels(harray), HResamplerType$SincFixedIn, HDataType$Float64) + res = HResampler$new_sinc(48000 / 44100, 2, hparams, 512L, HArrayAudio$nchannels(harray), HResamplerType$SincFixedIn, HDataType$Float64) expect_true(res$res_type() == HResamplerType$SincFixedIn) expect_true(res$dtype() == HDataType$Float64) expect_no_error(res$process(harray)) @@ -28,10 +28,10 @@ test_that( harray = HArray$new_from_values(arr, dtype = HDataType$Float64) hparams = HSincInterpolationParameters$new(256L, 0.95, 256L, HInterpolationType$Linear, HWindowType$BlackmanHarris2) - res = HResampler$new_sinc(48000 / 44100, 2, hparams, 512L, HAudioOp$nchannels(harray), HResamplerType$SincFixedOut, HDataType$Float32) + res = HResampler$new_sinc(48000 / 44100, 2, hparams, 512L, HArrayAudio$nchannels(harray), HResamplerType$SincFixedOut, HDataType$Float32) expect_error(res$process(haudio, sr_out = 48000)) - res = HResampler$new_sinc(48000 / 44100, 2, hparams, 512L, HAudioOp$nchannels(harray), HResamplerType$SincFixedOut, HDataType$Float64) + res = HResampler$new_sinc(48000 / 44100, 2, hparams, 512L, HArrayAudio$nchannels(harray), HResamplerType$SincFixedOut, HDataType$Float64) expect_true(res$res_type() == HResamplerType$SincFixedOut) expect_true(res$dtype() == HDataType$Float64) expect_no_error(res$process(harray)) @@ -112,10 +112,10 @@ test_that( arr = matrix(0, nrow = 1024, ncol = 2) harray = HArray$new_from_values(arr, dtype = HDataType$Float64) - res = HResampler$new_fast(48000 / 44100, 2, HPolynomialDegree$Linear, 512L, HAudioOp$nchannels(harray), HResamplerType$FastFixedIn, HDataType$Float32) + res = HResampler$new_fast(48000 / 44100, 2, HPolynomialDegree$Linear, 512L, HArrayAudio$nchannels(harray), HResamplerType$FastFixedIn, HDataType$Float32) expect_error(res$process(harray)) - res = HResampler$new_fast(48000 / 44100, 2, HPolynomialDegree$Linear, 512L, HAudioOp$nchannels(harray), HResamplerType$FastFixedIn, HDataType$Float64) + res = HResampler$new_fast(48000 / 44100, 2, HPolynomialDegree$Linear, 512L, HArrayAudio$nchannels(harray), HResamplerType$FastFixedIn, HDataType$Float64) expect_true(res$res_type() == HResamplerType$FastFixedIn) expect_true(res$dtype() == HDataType$Float64) expect_no_error(res$process(harray)) @@ -133,10 +133,10 @@ test_that( arr = matrix(0, nrow = 512, ncol = 2) harray = HArray$new_from_values(arr, dtype = HDataType$Float64) - res = HResampler$new_fast(48000 / 44100, 2, HPolynomialDegree$Linear, 512L, HAudioOp$nchannels(harray), HResamplerType$FastFixedOut, HDataType$Float32) + res = HResampler$new_fast(48000 / 44100, 2, HPolynomialDegree$Linear, 512L, HArrayAudio$nchannels(harray), HResamplerType$FastFixedOut, HDataType$Float32) expect_error(res$process(harray)) - res = HResampler$new_fast(48000 / 44100, 2, HPolynomialDegree$Linear, 512L, HAudioOp$nchannels(harray), HResamplerType$FastFixedOut, HDataType$Float64) + res = HResampler$new_fast(48000 / 44100, 2, HPolynomialDegree$Linear, 512L, HArrayAudio$nchannels(harray), HResamplerType$FastFixedOut, HDataType$Float64) expect_true(res$res_type() == HResamplerType$FastFixedOut) expect_true(res$dtype() == HDataType$Float64) expect_no_error(res$process(harray)) diff --git a/r-harmonium/tests/testthat/test_to_mono.R b/r-harmonium/tests/testthat/test_to_mono.R index f03d3a7..ae03355 100644 --- a/r-harmonium/tests/testthat/test_to_mono.R +++ b/r-harmonium/tests/testthat/test_to_mono.R @@ -5,14 +5,14 @@ test_that( values = c(1,2,3,4,5,6,7,8,9,10,11,12) arr = matrix(values, ncol = 3) harray = HArray$new_from_values(arr, HDataType$Float32) - HAudioOp$to_mono(harray) + HArrayAudio$to_mono(harray) expect_equal(harray$collect(), as.array(rowMeans(arr))) # f64 values = c(1,2,3,4,5,6,7,8,9,10,11,12) arr = matrix(values, ncol = 3) harray = HArray$new_from_values(arr, HDataType$Float64) - HAudioOp$to_mono(harray) + HArrayAudio$to_mono(harray) expect_equal(harray$collect(), as.array(rowMeans(arr))) } )