diff --git a/CHANGES.md b/CHANGES.md index 81c1062..9d96d2e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,6 @@ ## Changes -### Version 0.18 (Next Version) +### Version 0.18 - This release involves a major rewrite and many changes. - 64-bit sample support is gone. All samples are now `f32`. diff --git a/Cargo.toml b/Cargo.toml index 7dd90ac..c3f1a9d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,6 @@ funutd = { version = "0.16.0", default-features = false } thingbuf = { version = "0.1.6", default-features = false, features = ["alloc"] } once_cell = { version = "1.19.0", default-features = false, features = ["race", "alloc"] } symphonia = { version = "0.5.4", optional = true, features = ["all"] } -no_denormals = "0.1.2" [dev-dependencies] anyhow = "1.0.86" diff --git a/README.md b/README.md index 1e18702..6a06589 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,10 @@ Add `fundsp` to your `Cargo.toml` as a dependency. fundsp = "0.18.0" ``` +The `files` feature is enabled by default. It adds support for +loading of audio files into `Wave` objects +via the [Symphonia](https://crates.io/crates/symphonia) crate. + ### no_std Support FunDSP supports `no_std` environments. To enable `no_std`, disable diff --git a/src/feedback.rs b/src/feedback.rs index 8d94d46..f4d791c 100644 --- a/src/feedback.rs +++ b/src/feedback.rs @@ -12,18 +12,6 @@ use alloc::boxed::Box; use alloc::vec; use alloc::vec::Vec; -#[cfg(not(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64")))] -#[inline(always)] -pub fn flush_denormals T>(f: F) -> T { - f() -} - -#[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))] -#[inline(always)] -pub fn flush_denormals T>(f: F) -> T { - no_denormals::no_denormals(f) -} - /// Diffusive Hadamard feedback matrix. The number of channels must be a power of two. #[derive(Default, Clone)] pub struct FrameHadamard> { @@ -137,25 +125,21 @@ where #[inline] fn tick(&mut self, input: &Frame) -> Frame { - flush_denormals(|| { - let output = self.x.tick(&(input + self.value.clone())); - self.value = self.feedback.frame(&output); - output - }) + let output = self.x.tick(&(input + self.value.clone())); + self.value = self.feedback.frame(&output); + output } fn process(&mut self, size: usize, input: &BufferRef, output: &mut BufferMut) { - flush_denormals(|| { - for i in 0..size { - let input_frame = - Frame::generate(|channel| input.at_f32(channel, i) + self.value[channel]); - let output_frame = self.x.tick(&input_frame); - self.value = self.feedback.frame(&output_frame); - for channel in 0..self.outputs() { - output.set_f32(channel, i, output_frame[channel]); - } + for i in 0..size { + let input_frame = + Frame::generate(|channel| input.at_f32(channel, i) + self.value[channel]); + let output_frame = self.x.tick(&input_frame); + self.value = self.feedback.frame(&output_frame); + for channel in 0..self.outputs() { + output.set_f32(channel, i, output_frame[channel]); } - }) + } } fn route(&mut self, input: &SignalFrame, _frequency: f64) -> SignalFrame { @@ -251,25 +235,21 @@ where #[inline] fn tick(&mut self, input: &Frame) -> Frame { - flush_denormals(|| { - let output = self.x.tick(&(input + self.value.clone())); - self.value = self.feedback.frame(&self.y.tick(&output)); - output - }) + let output = self.x.tick(&(input + self.value.clone())); + self.value = self.feedback.frame(&self.y.tick(&output)); + output } fn process(&mut self, size: usize, input: &BufferRef, output: &mut BufferMut) { - flush_denormals(|| { - for i in 0..size { - let input_frame = - Frame::generate(|channel| input.at_f32(channel, i) + self.value[channel]); - let output_frame = self.x.tick(&input_frame); - self.value = self.feedback.frame(&self.y.tick(&output_frame)); - for channel in 0..self.outputs() { - output.set_f32(channel, i, output_frame[channel]); - } + for i in 0..size { + let input_frame = + Frame::generate(|channel| input.at_f32(channel, i) + self.value[channel]); + let output_frame = self.x.tick(&input_frame); + self.value = self.feedback.frame(&self.y.tick(&output_frame)); + for channel in 0..self.outputs() { + output.set_f32(channel, i, output_frame[channel]); } - }) + } } fn route(&mut self, input: &SignalFrame, _frequency: f64) -> SignalFrame { @@ -369,60 +349,56 @@ impl AudioUnit for FeedbackUnit { } fn tick(&mut self, input: &[f32], output: &mut [f32]) { - flush_denormals(|| { - let read_i = self.read_index(self.samples); - for (channel, (tick, i)) in self.tick_buffer.iter_mut().zip(input.iter()).enumerate() { - *tick = *i + self.feedback[channel][read_i]; - } - self.x.tick(&self.tick_buffer, output); - for (channel, i) in output.iter().enumerate() { - self.feedback[channel][self.index] = *i; - } - self.index = (self.index + 1) & self.mask; - }); + let read_i = self.read_index(self.samples); + for (channel, (tick, i)) in self.tick_buffer.iter_mut().zip(input.iter()).enumerate() { + *tick = *i + self.feedback[channel][read_i]; + } + self.x.tick(&self.tick_buffer, output); + for (channel, i) in output.iter().enumerate() { + self.feedback[channel][self.index] = *i; + } + self.index = (self.index + 1) & self.mask; } fn process(&mut self, size: usize, input: &BufferRef, output: &mut BufferMut) { - flush_denormals(|| { - if size <= self.samples { - // We have enough feedback samples to process the whole block at once. - for channel in 0..self.channels { - let mut read_i = self.read_index(self.samples); - for (b, i) in self.buffer.channel_mut_f32(channel)[0..size] - .iter_mut() - .zip(input.channel_f32(channel)[0..size].iter()) - { - *b = *i + self.feedback[channel][read_i]; - read_i = (read_i + 1) & self.mask; - } - } - self.x.process(size, &self.buffer.buffer_ref(), output); - for channel in 0..self.channels { - let mut write_i = self.index; - for i in output.channel_f32(channel)[0..size].iter() { - self.feedback[channel][write_i] = *i; - write_i = (write_i + 1) & self.mask; - } - } - } else { - // The feedback delay is small so we proceed sample by sample. + if size <= self.samples { + // We have enough feedback samples to process the whole block at once. + for channel in 0..self.channels { let mut read_i = self.read_index(self.samples); - let mut write_i = self.index; - for i in 0..size { - for (channel, tick) in self.tick_buffer.iter_mut().enumerate() { - *tick = input.at_f32(channel, i) + self.feedback[channel][read_i]; - } - self.x.tick(&self.tick_buffer, &mut self.tick_buffer2); - for (channel, tick) in self.tick_buffer2.iter().enumerate() { - output.set_f32(channel, i, *tick); - self.feedback[channel][write_i] = *tick; - } + for (b, i) in self.buffer.channel_mut_f32(channel)[0..size] + .iter_mut() + .zip(input.channel_f32(channel)[0..size].iter()) + { + *b = *i + self.feedback[channel][read_i]; read_i = (read_i + 1) & self.mask; + } + } + self.x.process(size, &self.buffer.buffer_ref(), output); + for channel in 0..self.channels { + let mut write_i = self.index; + for i in output.channel_f32(channel)[0..size].iter() { + self.feedback[channel][write_i] = *i; write_i = (write_i + 1) & self.mask; } } - self.index = (self.index + size) & self.mask; - }); + } else { + // The feedback delay is small so we proceed sample by sample. + let mut read_i = self.read_index(self.samples); + let mut write_i = self.index; + for i in 0..size { + for (channel, tick) in self.tick_buffer.iter_mut().enumerate() { + *tick = input.at_f32(channel, i) + self.feedback[channel][read_i]; + } + self.x.tick(&self.tick_buffer, &mut self.tick_buffer2); + for (channel, tick) in self.tick_buffer2.iter().enumerate() { + output.set_f32(channel, i, *tick); + self.feedback[channel][write_i] = *tick; + } + read_i = (read_i + 1) & self.mask; + write_i = (write_i + 1) & self.mask; + } + } + self.index = (self.index + size) & self.mask; } fn inputs(&self) -> usize { diff --git a/src/write.rs b/src/write.rs index e7e87f5..30b3bbe 100644 --- a/src/write.rs +++ b/src/write.rs @@ -69,7 +69,7 @@ impl Wave { for i in 0..self.length() { for channel in 0..self.channels() { let sample = round(clamp11(self.at(channel, i)) * 32767.49); - write16(&mut writer, (sample as i64) as u16)?; + write16(&mut writer, (sample as i16) as u16)?; } } std::io::Result::Ok(())