diff --git a/examples/adapt.rs b/examples/adapt.rs index 3bdcf71..9037704 100644 --- a/examples/adapt.rs +++ b/examples/adapt.rs @@ -3,8 +3,9 @@ const RATE: u32 = 44100; const BLOCK_SIZE: usize = 512; fn main() { - let mixer = oddio::Adapt::new( - oddio::Mixer::new(), + let (mut mixer, signal) = oddio::Mixer::new(); + let mut signal = oddio::Adapt::new( + signal, 1e-3 / 2.0f32.sqrt(), oddio::AdaptOptions { tau: 0.1, @@ -13,7 +14,6 @@ fn main() { high: 0.5 / 2.0f32.sqrt(), }, ); - let (mut mixer, split) = oddio::split(mixer); let spec = hound::WavSpec { channels: 1, @@ -26,7 +26,7 @@ fn main() { let mut drive = || { for _ in 0..(RATE * DURATION_SECS / BLOCK_SIZE as u32) { let mut block = [0.0; BLOCK_SIZE]; - oddio::run(&split, RATE, &mut block); + oddio::run(&mut signal, RATE, &mut block); for &sample in &block { writer .write_sample((sample * i16::MAX as f32) as i16) @@ -38,11 +38,11 @@ fn main() { let quiet = oddio::FixedGain::new(oddio::Sine::new(0.0, 5e2), -60.0); let loud = oddio::FixedGain::new(oddio::Sine::new(0.0, 4e2), -2.0); - mixer.control::, _>().play(quiet); + mixer.play(quiet); drive(); - let mut handle = mixer.control::, _>().play(loud); + let mut handle = mixer.play(loud); drive(); - handle.control::, _>().stop(); + handle.stop(); drive(); writer.finalize().unwrap(); diff --git a/examples/offline.rs b/examples/offline.rs index 50367f5..7355cb5 100644 --- a/examples/offline.rs +++ b/examples/offline.rs @@ -12,8 +12,8 @@ fn main() { (t * 500.0 * 2.0 * core::f32::consts::PI).sin() * 80.0 }), ); - let (mut scene_handle, scene) = oddio::split(oddio::SpatialScene::new()); - scene_handle.control::().play( + let (mut scene_handle, mut scene) = oddio::SpatialScene::new(); + scene_handle.play( oddio::FramesSignal::from(boop), oddio::SpatialOptions { position: [-SPEED, 10.0, 0.0].into(), @@ -32,7 +32,7 @@ fn main() { for _ in 0..(RATE * DURATION_SECS / BLOCK_SIZE as u32) { let mut block = [[0.0; 2]; BLOCK_SIZE]; - oddio::run(&scene, RATE, &mut block); + oddio::run(&mut scene, RATE, &mut block); for &frame in &block { for &sample in &frame { writer diff --git a/examples/realtime.rs b/examples/realtime.rs index b35db15..05020ea 100644 --- a/examples/realtime.rs +++ b/examples/realtime.rs @@ -21,7 +21,7 @@ fn main() { // create our oddio handles for a `SpatialScene`. We could also use a `Mixer`, // which doesn't spatialize signals. - let (mut scene_handle, scene) = oddio::split(oddio::SpatialScene::new()); + let (mut scene_handle, mut scene) = oddio::SpatialScene::new(); // We send `scene` into this closure, where changes to `scene_handle` are reflected. // `scene_handle` is how we add new sounds and modify the scene live. @@ -30,7 +30,7 @@ fn main() { &config, move |data: &mut [f32], _: &cpal::OutputCallbackInfo| { let frames = oddio::frame_stereo(data); - oddio::run(&scene, sample_rate.0, frames); + oddio::run(&mut scene, sample_rate.0, frames); }, move |err| { eprintln!("{}", err); @@ -59,32 +59,24 @@ fn main() { // We can also add filters around our `FramesSignal` to make our sound more controllable. // A common one is `Gain`, which lets us modulate the gain of the `Signal` (how loud it is) - let gain = oddio::Gain::new(basic_signal); - - // The type given out from `.play_buffered` reflects the controls we placed in it. It will be a - // very complex type, so it can be useful to newtype or typedef. Notice the `Gain`, which is - // there because we wrapped our `FramesSignal` above with `Gain`. - type AudioHandle = - oddio::Handle>>>>; + let (mut gain_control, gain) = oddio::Gain::new(basic_signal); // the speed at which we'll be moving around const SPEED: f32 = 50.0; - // `_play_buffered` is used because the dynamically adjustable `Gain` filter makes sample values + // `play_buffered` is used because the dynamically adjustable `Gain` filter makes sample values // non-deterministic. For immutable signals like a bare `FramesSignal`, the regular `play` is // more efficient. - let mut signal: AudioHandle = scene_handle - .control::() - .play_buffered( - gain, - oddio::SpatialOptions { - position: [-SPEED, 10.0, 0.0].into(), - velocity: [SPEED, 0.0, 0.0].into(), - radius: 0.1, - }, - 1000.0, - sample_rate.0, - 0.1, - ); + let mut spatial_control = scene_handle.play_buffered( + gain, + oddio::SpatialOptions { + position: [-SPEED, 10.0, 0.0].into(), + velocity: [SPEED, 0.0, 0.0].into(), + radius: 0.1, + }, + 1000.0, + sample_rate.0, + 0.1, + ); let start = Instant::now(); @@ -95,9 +87,6 @@ fn main() { break; } - // Access our Spatial Controls - let mut spatial_control = signal.control::, _>(); - // This has no noticable effect because it matches the initial velocity, but serves to // demonstrate that `Spatial` can smooth over the inevitable small timing inconsistencies // between the main thread and the audio thread without glitching. @@ -108,9 +97,6 @@ fn main() { ); // We also could adjust the Gain here in the same way: - let mut gain_control = signal.control::, _>(); - - // Just leave the gain at its natural volume. (sorry this can be a bit loud!) gain_control.set_gain(1.0); } } diff --git a/examples/simple.rs b/examples/simple.rs index 611ef50..7fd536e 100644 --- a/examples/simple.rs +++ b/examples/simple.rs @@ -20,7 +20,7 @@ fn main() { // Create the root mixer, and divide it into two parts: a handle that we can use to add new // signals to play, and an object we can pass to `oddio::run` in cpal's callback to generate // output frames. - let (mut mixer_handle, mixer) = oddio::split(oddio::Mixer::new()); + let (mut mixer_handle, mut mixer) = oddio::Mixer::new(); // Start cpal, taking care not to drop its stream early let stream = device @@ -28,7 +28,7 @@ fn main() { &config, move |out_flat: &mut [f32], _: &cpal::OutputCallbackInfo| { let out_stereo: &mut [[f32; 2]] = oddio::frame_stereo(out_flat); - oddio::run(&mixer, sample_rate.0, out_stereo); + oddio::run(&mut mixer, sample_rate.0, out_stereo); }, move |err| { eprintln!("{}", err); @@ -39,9 +39,7 @@ fn main() { // Start a 200Hz sine wave. We can do this as many times as we like, whenever we like, with // different types of signals as needed. - mixer_handle - .control::, _>() - .play(oddio::MonoToStereo::new(oddio::Sine::new(0.0, 400.0))); + mixer_handle.play(oddio::MonoToStereo::new(oddio::Sine::new(0.0, 400.0))); // Wait a bit before exiting thread::sleep(Duration::from_secs(3)); diff --git a/examples/wav.rs b/examples/wav.rs index 063a47f..247f3af 100644 --- a/examples/wav.rs +++ b/examples/wav.rs @@ -45,7 +45,7 @@ fn main() { let samples_stereo = oddio::frame_stereo(&mut samples); let sound_frames = oddio::Frames::from_slice(source_sample_rate, samples_stereo); - let (mut mixer_handle, mixer) = oddio::split(oddio::Mixer::new()); + let (mut mixer_handle, mut mixer) = oddio::Mixer::new(); let config = cpal::StreamConfig { channels: 2, @@ -58,7 +58,7 @@ fn main() { &config, move |out_flat: &mut [f32], _: &cpal::OutputCallbackInfo| { let out_stereo = oddio::frame_stereo(out_flat); - oddio::run(&mixer, device_sample_rate, out_stereo); + oddio::run(&mut mixer, device_sample_rate, out_stereo); }, move |err| { eprintln!("{}", err); @@ -67,9 +67,7 @@ fn main() { .unwrap(); stream.play().unwrap(); - mixer_handle - .control::, _>() - .play(oddio::FramesSignal::from(sound_frames)); + mixer_handle.play(oddio::FramesSignal::from(sound_frames)); thread::sleep(Duration::from_secs_f32(length_seconds)); } diff --git a/src/adapt.rs b/src/adapt.rs index 1e5303f..9be24f2 100644 --- a/src/adapt.rs +++ b/src/adapt.rs @@ -1,6 +1,4 @@ -use core::cell::Cell; - -use crate::{math::Float, Filter, Frame, Signal}; +use crate::{math::Float, Frame, Signal}; /// Smoothly adjusts gain over time to keep average (RMS) signal level within a target range /// @@ -15,7 +13,7 @@ use crate::{math::Float, Filter, Frame, Signal}; /// perception of loudness is logarithmic. pub struct Adapt { options: AdaptOptions, - avg_squared: Cell, + avg_squared: f32, inner: T, } @@ -27,7 +25,7 @@ impl Adapt { pub fn new(signal: T, initial_rms: f32, options: AdaptOptions) -> Self { Self { options, - avg_squared: Cell::new(initial_rms * initial_rms), + avg_squared: initial_rms * initial_rms, inner: signal, } } @@ -68,14 +66,13 @@ where { type Frame = T::Frame; - fn sample(&self, interval: f32, out: &mut [T::Frame]) { + fn sample(&mut self, interval: f32, out: &mut [T::Frame]) { let alpha = 1.0 - (-interval / self.options.tau).exp(); self.inner.sample(interval, out); for x in out { let sample = x.channels().iter().sum::(); - self.avg_squared - .set(sample * sample * alpha + self.avg_squared.get() * (1.0 - alpha)); - let avg_peak = self.avg_squared.get().sqrt() * 2.0f32.sqrt(); + self.avg_squared = sample * sample * alpha + self.avg_squared * (1.0 - alpha); + let avg_peak = self.avg_squared.sqrt() * 2.0f32.sqrt(); let gain = if avg_peak < self.options.low { (self.options.low / avg_peak).min(self.options.max_gain) } else if avg_peak > self.options.high { @@ -94,13 +91,6 @@ where } } -impl Filter for Adapt { - type Inner = T; - fn inner(&self) -> &T { - &self.inner - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/src/constant.rs b/src/constant.rs index 30396c1..7cee08a 100644 --- a/src/constant.rs +++ b/src/constant.rs @@ -13,11 +13,11 @@ impl Constant { impl Signal for Constant { type Frame = T; - fn sample(&self, _interval: f32, out: &mut [T]) { + fn sample(&mut self, _interval: f32, out: &mut [T]) { out.fill(self.0.clone()); } } impl Seek for Constant { - fn seek(&self, _: f32) {} + fn seek(&mut self, _: f32) {} } diff --git a/src/cycle.rs b/src/cycle.rs index 70c480d..2aa4385 100644 --- a/src/cycle.rs +++ b/src/cycle.rs @@ -1,12 +1,11 @@ use alloc::sync::Arc; -use core::cell::Cell; use crate::{frame, math::Float, Frame, Frames, Seek, Signal}; /// Loops [`Frames`] end-to-end to construct a repeating signal pub struct Cycle { /// Current playback time, in samples - cursor: Cell, + cursor: f64, frames: Arc>, } @@ -15,7 +14,7 @@ impl Cycle { // TODO: Crossfade pub fn new(frames: Arc>) -> Self { Self { - cursor: Cell::new(0.0), + cursor: 0.0, frames, } } @@ -24,10 +23,10 @@ impl Cycle { impl Signal for Cycle { type Frame = T; - fn sample(&self, interval: f32, out: &mut [T]) { + fn sample(&mut self, interval: f32, out: &mut [T]) { let ds = interval * self.frames.rate() as f32; - let mut base = self.cursor.get() as usize; - let mut offset = (self.cursor.get() - base as f64) as f32; + let mut base = self.cursor as usize; + let mut offset = (self.cursor - base as f64) as f32; for o in out { let trunc = unsafe { offset.to_int_unchecked::() }; let fract = offset - trunc as f32; @@ -50,15 +49,14 @@ impl Signal for Cycle { *o = frame::lerp(&a, &b, fract); offset += ds; } - self.cursor.set(base as f64 + offset as f64); + self.cursor = base as f64 + offset as f64; } } impl Seek for Cycle { - fn seek(&self, seconds: f32) { - let s = (self.cursor.get() + f64::from(seconds) * self.frames.rate() as f64) + fn seek(&mut self, seconds: f32) { + self.cursor = (self.cursor + f64::from(seconds) * self.frames.rate() as f64) .rem_euclid(self.frames.len() as f64); - self.cursor.set(s); } } @@ -70,7 +68,7 @@ mod tests { #[test] fn wrap_single() { - let s = Cycle::new(Frames::from_slice(1, FRAMES)); + let mut s = Cycle::new(Frames::from_slice(1, FRAMES)); let mut buf = [0.0; 5]; s.sample(1.0, &mut buf); assert_eq!(buf, [1.0, 2.0, 3.0, 1.0, 2.0]); @@ -78,7 +76,7 @@ mod tests { #[test] fn wrap_multi() { - let s = Cycle::new(Frames::from_slice(1, FRAMES)); + let mut s = Cycle::new(Frames::from_slice(1, FRAMES)); let mut buf = [0.0; 5]; s.sample(1.0, &mut buf[..2]); s.sample(1.0, &mut buf[2..]); @@ -87,7 +85,7 @@ mod tests { #[test] fn wrap_fract() { - let s = Cycle::new(Frames::from_slice(1, FRAMES)); + let mut s = Cycle::new(Frames::from_slice(1, FRAMES)); let mut buf = [0.0; 8]; s.sample(0.5, &mut buf[..2]); s.sample(0.5, &mut buf[2..]); @@ -96,7 +94,7 @@ mod tests { #[test] fn wrap_fract_offset() { - let s = Cycle::new(Frames::from_slice(1, FRAMES)); + let mut s = Cycle::new(Frames::from_slice(1, FRAMES)); s.seek(0.25); let mut buf = [0.0; 7]; s.sample(0.5, &mut buf[..2]); @@ -106,7 +104,7 @@ mod tests { #[test] fn wrap_single_frame() { - let s = Cycle::new(Frames::from_slice(1, &[1.0])); + let mut s = Cycle::new(Frames::from_slice(1, &[1.0])); s.seek(0.25); let mut buf = [0.0; 3]; s.sample(1.0, &mut buf[..2]); @@ -116,7 +114,7 @@ mod tests { #[test] fn wrap_large_interval() { - let s = Cycle::new(Frames::from_slice(1, FRAMES)); + let mut s = Cycle::new(Frames::from_slice(1, FRAMES)); let mut buf = [0.0; 3]; s.sample(10.0, &mut buf[..2]); s.sample(10.0, &mut buf[2..]); diff --git a/src/downmix.rs b/src/downmix.rs index 57671f6..3933625 100644 --- a/src/downmix.rs +++ b/src/downmix.rs @@ -1,4 +1,4 @@ -use crate::{Filter, Frame, Sample, Signal}; +use crate::{Frame, Sample, Seek, Signal}; /// Sums all channels together /// @@ -20,7 +20,7 @@ where { type Frame = Sample; - fn sample(&self, interval: f32, out: &mut [Sample]) { + fn sample(&mut self, interval: f32, out: &mut [Sample]) { const CHUNK_SIZE: usize = 256; let mut buf = [Frame::ZERO; CHUNK_SIZE]; @@ -35,17 +35,14 @@ where fn is_finished(&self) -> bool { self.0.is_finished() } - - fn handle_dropped(&self) { - self.0.handle_dropped(); - } } -impl Filter for Downmix { - type Inner = T; - - fn inner(&self) -> &Self::Inner { - &self.0 +impl Seek for Downmix +where + T::Frame: Frame, +{ + fn seek(&mut self, seconds: f32) { + self.0.seek(seconds); } } @@ -56,7 +53,7 @@ mod tests { #[test] fn smoke() { - let signal = Downmix::new(Constant::new([1.0, 2.0])); + let mut signal = Downmix::new(Constant::new([1.0, 2.0])); let mut out = [0.0; 384]; signal.sample(1.0, &mut out); assert_eq!(out, [3.0; 384]); diff --git a/src/fader.rs b/src/fader.rs index 2c67f5c..807f235 100644 --- a/src/fader.rs +++ b/src/fader.rs @@ -1,28 +1,29 @@ -use core::{ - cell::{Cell, UnsafeCell}, - mem, -}; +use alloc::sync::Arc; +use core::mem; -use crate::{frame, math::Float, Controlled, Filter, Frame, Signal, Swap}; +use crate::{frame, math::Float, swap, Frame, Signal}; /// Cross-fades smoothly between dynamically-supplied signals /// /// Uses constant-power fading, suitable for blending uncorrelated signals without distorting /// perceived loudness pub struct Fader { - progress: Cell, - inner: UnsafeCell, - next: Swap>>, + progress: f32, + next: swap::Receiver>>, + inner: T, } -impl Fader { +impl Fader { /// Create a fader initially wrapping `inner` - pub fn new(inner: T) -> Self { - Self { - progress: Cell::new(1.0), - inner: UnsafeCell::new(inner), - next: Swap::new(|| None), - } + pub fn new(inner: T) -> (FaderControl, Self) { + let (send, recv) = swap::swap(|| None); + let signal = Self { + progress: 1.0, + next: recv, + inner, + }; + let control = FaderControl(send); + (control, signal) } } @@ -33,42 +34,39 @@ where type Frame = T::Frame; #[allow(clippy::float_cmp)] - fn sample(&self, interval: f32, mut out: &mut [T::Frame]) { - let inner = unsafe { &mut *self.inner.get() }; - - if self.progress.get() >= 1.0 { + fn sample(&mut self, interval: f32, mut out: &mut [T::Frame]) { + if self.progress >= 1.0 { // A fade must complete before a new one begins if self.next.refresh() { - self.progress.set(0.0); + self.progress = 0.0; } else { // Fast path - inner.sample(interval, out); + self.inner.sample(interval, out); return; } } - let next = unsafe { (*self.next.received()).as_mut().unwrap() }; + let next = (*self.next.received()).as_mut().unwrap(); let increment = interval / next.duration; while !out.is_empty() { let mut buffer = [(); 1024].map(|()| T::Frame::ZERO); let n = buffer.len().min(out.len()); - inner.sample(interval, &mut buffer); + self.inner.sample(interval, &mut buffer); next.fade_to.sample(interval, out); for (o, x) in out.iter_mut().zip(&buffer) { - let fade_out = (1.0 - self.progress.get()).sqrt(); - let fade_in = self.progress.get().sqrt(); + let fade_out = (1.0 - self.progress).sqrt(); + let fade_in = self.progress.sqrt(); *o = frame::mix(&frame::scale(x, fade_out), &frame::scale(o, fade_in)); - self.progress - .set((self.progress.get() + increment).min(1.0)); + self.progress = (self.progress + increment).min(1.0); } out = &mut out[n..]; } - if self.progress.get() >= 1.0 { + if self.progress >= 1.0 { // We've finished fading; move the new signal into `self`, and stash the old one back in // `next` to be dropped by a future `fade_to` call. - mem::swap(inner, &mut next.fade_to); + mem::swap(&mut self.inner, &mut next.fade_to); } } @@ -76,44 +74,20 @@ where fn is_finished(&self) -> bool { false } - - #[inline] - fn handle_dropped(&self) { - unsafe { - (*self.inner.get()).handle_dropped(); - } - } -} - -impl Filter for Fader { - type Inner = T; - fn inner(&self) -> &T { - unsafe { &*self.inner.get() } - } } /// Thread-safe control for a [`Fader`] filter -pub struct FaderControl<'a, T>(&'a Swap>>); +pub struct FaderControl(swap::Sender>>); -unsafe impl<'a, T: 'a> Controlled<'a> for Fader { - type Control = FaderControl<'a, T>; - - unsafe fn make_control(signal: &'a Fader) -> Self::Control { - FaderControl(&signal.next) - } -} - -impl<'a, T> FaderControl<'a, T> { +impl FaderControl { /// Crossfade to `signal` over `duration`. If a fade is already in progress, it will complete /// before a fading to the new signal begins. If another signal is already waiting for a current /// fade to complete, the waiting signal is replaced. pub fn fade_to(&mut self, signal: T, duration: f32) { - unsafe { - *self.0.pending() = Some(Command { - fade_to: signal, - duration, - }); - } + *self.0.pending() = Some(Command { + fade_to: signal, + duration, + }); self.0.flush() } } @@ -131,11 +105,11 @@ mod tests { #[test] fn smoke() { - let s = Fader::new(Constant(1.0)); + let (mut c, mut s) = Fader::new(Constant(1.0)); let mut buf = [42.0; 12]; s.sample(0.1, &mut buf); assert_eq!(buf, [1.0; 12]); - FaderControl(&s.next).fade_to(Constant(0.0), 1.0); + c.fade_to(Constant(0.0), 1.0); s.sample(0.1, &mut buf); assert_eq!(buf[0], 1.0); assert_eq!(buf[11], 0.0); diff --git a/src/filter.rs b/src/filter.rs deleted file mode 100644 index b8df2a9..0000000 --- a/src/filter.rs +++ /dev/null @@ -1,151 +0,0 @@ -use alloc::sync::Arc; -use core::marker::PhantomData; - -/// Handle for manipulating a signal owned elsewhere -/// -/// Handle types are typically verbose. Consider using type aliases or newtypes as shorthand for -/// those that arise commonly in your application. -pub struct Handle { - shared: Arc, -} - -impl Handle { - /// Construct a handle enclosing `signal` - /// - /// Used to implement signals like [`Mixer`](crate::Mixer). - /// - /// # Safety - /// - /// There must never be more than one other `Arc` referencing the same `T`. - pub unsafe fn from_arc(signal: Arc) -> Self { - Self { shared: signal } - } - - /// Get the control for [`Controlled`] signal `S` in a chain of signals - /// - /// Allows a signal to be controlled while playing. Prefer [`Filter::control`] for initial - /// configuration. - /// - /// `Index` can usually be inferred. - /// - /// # Example - /// ``` - /// # use oddio::*; - /// fn quiet(signal: &mut Handle>>>) { - /// signal.control::, _>().set_gain(-3.0); - /// } - /// ``` - pub fn control<'a, S, Index>(&'a mut self) -> S::Control - where - T: FilterHaving, - S: Controlled<'a>, - { - let signal: &S = (*self.shared).get(); - unsafe { S::make_control(signal) } - } -} - -// Sound because `T` is not accessible except via `unsafe trait Controlled` -unsafe impl Send for Handle {} -unsafe impl Sync for Handle {} - -/// A wrapper which transforms a [`Signal`](crate::Signal) -/// -/// Allows [`Handle::control`] to expose the transformed signal as well as the transformer. For -/// example, a `Handle>>` allows both gain and motion state to be updated. -pub trait Filter { - /// Type of signal transformed by this filter - type Inner: ?Sized; - - /// Access the inner signal - fn inner(&self) -> &Self::Inner; - - /// Get the control for owned [`Controlled`] signal `S` in a chain of signals - /// - /// Useful for initial configuration of a signal before it's passed on to be played. See - /// [`Handle::control`] for live control. - /// - /// `Index` can usually be inferred. - /// - /// # Example - /// ``` - /// # use oddio::*; - /// fn configure(signal: &mut MonoToStereo>>) { - /// signal.control::, _>().set_gain(-3.0); - /// } - /// ``` - fn control<'a, S, Index>(&'a mut self) -> S::Control - where - Self: FilterHaving, - S: Controlled<'a>, - { - let signal: &S = (*self).get(); - unsafe { S::make_control(signal) } - } -} - -/// A [`Signal`] or transformer that can be safely controlled from another thread -/// -/// # Safety -/// -/// `make_control` and `Control` must not permit access to `&Self` that constitutes a data race with -/// concurrent invocation of any [`Signal`] method even if `Self: !Sync`. For example, an -/// implementation could restrict itself to atomic operations. -/// -/// [`Signal`]: crate::Signal -pub unsafe trait Controlled<'a>: Sized + 'a { - /// The interface through which this signal can be safely controlled - type Control; - - /// Construct a `Control` for `signal` - /// - /// # Safety - /// - /// Must not be invoked while another `Control` for this signal exists - unsafe fn make_control(signal: &'a Self) -> Self::Control; -} - -/// Filter chains that contain a `T` at any position -/// -/// Helper trait for [`Handle::control()`]. `Index` is [`Here`] or [`There`], and can generally be -/// inferred. -pub trait FilterHaving { - /// Get the `T` element of a filter chain - fn get(&self) -> &T; -} - -/// `Index` value for [`FilterHaving`] representing the first filter in the chain -pub struct Here(()); - -/// `Index` value for [`FilterHaving`] representing the filter at position `T+1` -pub struct There(PhantomData); - -impl FilterHaving for T { - fn get(&self) -> &T { - self - } -} - -impl FilterHaving> for T -where - T::Inner: FilterHaving, -{ - fn get(&self) -> &U { - self.inner().get() - } -} - -#[cfg(test)] -mod tests { - use super::*; - - fn _foo(x: &A) - where - A: Filter, - B: Filter, - { - let _: &A = x.get(); - let _: &B = x.get(); - let _: &C = x.get(); - } -} diff --git a/src/frames.rs b/src/frames.rs index e8defd4..0c7e46e 100644 --- a/src/frames.rs +++ b/src/frames.rs @@ -1,13 +1,12 @@ use crate::alloc::{alloc, boxed::Box, sync::Arc}; use core::{ - cell::Cell, mem, ops::{Deref, DerefMut}, ptr, sync::atomic::{AtomicIsize, Ordering}, }; -use crate::{frame, math::Float, Controlled, Frame, Seek, Signal}; +use crate::{frame, math::Float, Frame, Seek, Signal}; /// A sequence of static audio frames at a particular sample rate /// @@ -142,23 +141,25 @@ pub struct FramesSignal { /// Frames to play data: Arc>, /// Playback position in seconds - t: Cell, + t: f64, /// Approximation of t in samples, for reading from the control. We could store t's bits in an /// AtomicU64 here, but that would sacrifice portability to platforms that don't have it, /// e.g. mips32. - sample_t: AtomicIsize, + sample_t: Arc, } impl FramesSignal { /// Create an audio signal from some samples /// /// `start_seconds` adjusts the initial playback position, and may be negative. - pub fn new(data: Arc>, start_seconds: f64) -> Self { - Self { - t: Cell::new(start_seconds), - sample_t: AtomicIsize::new((start_seconds * data.rate) as isize), + pub fn new(data: Arc>, start_seconds: f64) -> (FramesSignalControl, Self) { + let signal = Self { + t: start_seconds, + sample_t: Arc::new(AtomicIsize::new((start_seconds * data.rate) as isize)), data, - } + }; + let control = FramesSignalControl(signal.sample_t.clone(), signal.data.rate); + (control, signal) } } @@ -166,8 +167,8 @@ impl Signal for FramesSignal { type Frame = T; #[inline] - fn sample(&self, interval: f32, out: &mut [T]) { - let s0 = self.t.get() * self.data.rate; + fn sample(&mut self, interval: f32, out: &mut [T]) { + let s0 = self.t * self.data.rate; let ds = interval * self.data.rate as f32; let base = s0 as isize; if (ds - 1.0).abs() <= f32::EPSILON { @@ -188,53 +189,34 @@ impl Signal for FramesSignal { offset += ds; } } - self.t - .set(self.t.get() + f64::from(interval) * out.len() as f64); + self.t += f64::from(interval) * out.len() as f64; self.sample_t - .store((self.t.get() * self.data.rate) as isize, Ordering::Relaxed); + .store((self.t * self.data.rate) as isize, Ordering::Relaxed); } #[inline] fn is_finished(&self) -> bool { - self.t.get() >= self.data.samples.len() as f64 / self.data.rate + self.t >= self.data.samples.len() as f64 / self.data.rate } } impl Seek for FramesSignal { #[inline] - fn seek(&self, seconds: f32) { - self.t.set(self.t.get() + f64::from(seconds)); - } -} - -impl Clone for FramesSignal { - fn clone(&self) -> Self { - Self { - data: self.data.clone(), - t: self.t.clone(), - sample_t: AtomicIsize::new(self.sample_t.load(Ordering::Relaxed)), - } + fn seek(&mut self, seconds: f32) { + self.t += f64::from(seconds); } } impl From>> for FramesSignal { fn from(samples: Arc>) -> Self { - Self::new(samples, 0.0) + Self::new(samples, 0.0).1 } } /// Thread-safe control for a [`FramesSignal`], giving access to current playback location. -pub struct FramesSignalControl<'a>(&'a AtomicIsize, f64); - -unsafe impl<'a, T: 'a> Controlled<'a> for FramesSignal { - type Control = FramesSignalControl<'a>; - - unsafe fn make_control(signal: &'a FramesSignal) -> Self::Control { - FramesSignalControl(&signal.sample_t, signal.data.rate) - } -} +pub struct FramesSignalControl(Arc, f64); -impl<'a> FramesSignalControl<'a> { +impl FramesSignalControl { /// Get the current playback position. /// /// This number may be negative if the starting time was negative, @@ -252,7 +234,7 @@ mod tests { use super::*; use ::alloc::vec; - fn assert_out(stream: &FramesSignal, interval: f32, expected: &[f32]) { + fn assert_out(stream: &mut FramesSignal, interval: f32, expected: &[f32]) { let mut output = vec![0.0; expected.len()]; stream.sample(interval, &mut output); assert_eq!(&output, expected); @@ -267,49 +249,35 @@ mod tests { #[test] fn sample() { - let signal = FramesSignal::new(Frames::from_slice(1, &[1.0, 2.0, 3.0, 4.0]), -2.0); + let (_, mut signal) = FramesSignal::new(Frames::from_slice(1, &[1.0, 2.0, 3.0, 4.0]), -2.0); - assert_out(&signal, 0.25, &[0.0, 0.0, 0.0, 0.0]); - assert_out(&signal, 0.5, &[0.0, 0.5, 1.0]); - assert_out(&signal, 1.0, &[1.5, 2.5, 3.5, 2.0, 0.0]); + assert_out(&mut signal, 0.25, &[0.0, 0.0, 0.0, 0.0]); + assert_out(&mut signal, 0.5, &[0.0, 0.5, 1.0]); + assert_out(&mut signal, 1.0, &[1.5, 2.5, 3.5, 2.0, 0.0]); } #[test] fn playback_position() { - let signal = FramesSignal::new(Frames::from_slice(1, &[1.0, 2.0, 3.0]), -2.0); + let (control, mut signal) = + FramesSignal::new(Frames::from_slice(1, &[1.0, 2.0, 3.0]), -2.0); // negatives are fine - let init = FramesSignalControl(&signal.sample_t, signal.data.rate).playback_position(); + let init = control.playback_position(); assert_eq!(init, -2.0); let mut buf = [0.0; 10]; // get back to positive signal.sample(0.2, &mut buf); - assert_eq!( - 0.0, - FramesSignalControl(&signal.sample_t, signal.data.rate).playback_position() - ); + assert_eq!(0.0, control.playback_position()); signal.sample(0.1, &mut buf); - assert_eq!( - 1.0, - FramesSignalControl(&signal.sample_t, signal.data.rate).playback_position() - ); + assert_eq!(1.0, control.playback_position()); signal.sample(0.1, &mut buf); - assert_eq!( - 2.0, - FramesSignalControl(&signal.sample_t, signal.data.rate).playback_position() - ); + assert_eq!(2.0, control.playback_position()); signal.sample(0.2, &mut buf); - assert_eq!( - 4.0, - FramesSignalControl(&signal.sample_t, signal.data.rate).playback_position() - ); + assert_eq!(4.0, control.playback_position()); signal.sample(0.5, &mut buf); - assert_eq!( - 9.0, - FramesSignalControl(&signal.sample_t, signal.data.rate).playback_position() - ); + assert_eq!(9.0, control.playback_position()); } } diff --git a/src/gain.rs b/src/gain.rs index fb8bab9..643f75a 100644 --- a/src/gain.rs +++ b/src/gain.rs @@ -1,9 +1,7 @@ -use core::{ - cell::RefCell, - sync::atomic::{AtomicU32, Ordering}, -}; +use alloc::sync::Arc; +use core::sync::atomic::{AtomicU32, Ordering}; -use crate::{frame, math::Float, Controlled, Filter, Frame, Seek, Signal, Smoothed}; +use crate::{frame, math::Float, Frame, Seek, Signal, Smoothed}; /// Amplifies a signal by a constant amount /// @@ -31,7 +29,7 @@ where { type Frame = T::Frame; - fn sample(&self, interval: f32, out: &mut [T::Frame]) { + fn sample(&mut self, interval: f32, out: &mut [T::Frame]) { self.inner.sample(interval, out); for x in out { *x = frame::scale(x, self.gain); @@ -41,18 +39,13 @@ where fn is_finished(&self) -> bool { self.inner.is_finished() } - - #[inline] - fn handle_dropped(&self) { - self.inner.handle_dropped(); - } } impl Seek for FixedGain where T::Frame: Frame, { - fn seek(&self, seconds: f32) { + fn seek(&mut self, seconds: f32) { self.inner.seek(seconds) } } @@ -63,23 +56,23 @@ where /// input amplitude is initially in the range [0, 1] and pass decibels to [`GainControl::set_gain`], /// mapping the maximum volume to 0 decibels, and the minimum to e.g. -60. pub struct Gain { - shared: AtomicU32, - gain: RefCell>, + shared: Arc, + gain: Smoothed, inner: T, } impl Gain { /// Apply dynamic amplification to `signal` - pub fn new(signal: T) -> Self { - Self { - shared: AtomicU32::new(1.0f32.to_bits()), - gain: RefCell::new(Smoothed::new(1.0)), + pub fn new(signal: T) -> (GainControl, Self) { + let signal = Gain { + shared: Arc::new(AtomicU32::new(1.0f32.to_bits())), + gain: Smoothed::new(1.0), inner: signal, - } + }; + let handle = GainControl(signal.shared.clone()); + (handle, signal) } -} -impl Gain { /// Set the initial amplification to `db` decibels /// /// Perceptually linear. Negative values make the signal quieter. @@ -96,7 +89,7 @@ impl Gain { /// needed, or even have its phase inverted with a negative factor. pub fn set_amplitude_ratio(&mut self, factor: f32) { self.shared.store(factor.to_bits(), Ordering::Relaxed); - *self.gain.get_mut() = Smoothed::new(factor); + self.gain = Smoothed::new(factor); } } @@ -107,15 +100,14 @@ where type Frame = T::Frame; #[allow(clippy::float_cmp)] - fn sample(&self, interval: f32, out: &mut [T::Frame]) { + fn sample(&mut self, interval: f32, out: &mut [T::Frame]) { self.inner.sample(interval, out); let shared = f32::from_bits(self.shared.load(Ordering::Relaxed)); - let mut gain = self.gain.borrow_mut(); - if gain.target() != &shared { - gain.set(shared); + if self.gain.target() != &shared { + self.gain.set(shared); } - if gain.progress() == 1.0 { - let g = gain.get(); + if self.gain.progress() == 1.0 { + let g = self.gain.get(); if g != 1.0 { for x in out { *x = frame::scale(x, g); @@ -124,40 +116,20 @@ where return; } for x in out { - *x = frame::scale(x, gain.get()); - gain.advance(interval / SMOOTHING_PERIOD); + *x = frame::scale(x, self.gain.get()); + self.gain.advance(interval / SMOOTHING_PERIOD); } } fn is_finished(&self) -> bool { self.inner.is_finished() } - - #[inline] - fn handle_dropped(&self) { - self.inner.handle_dropped(); - } -} - -impl Filter for Gain { - type Inner = T; - fn inner(&self) -> &T { - &self.inner - } } /// Thread-safe control for a [`Gain`] filter -pub struct GainControl<'a>(&'a AtomicU32); - -unsafe impl<'a, T: 'a> Controlled<'a> for Gain { - type Control = GainControl<'a>; - - unsafe fn make_control(signal: &'a Gain) -> Self::Control { - GainControl(&signal.shared) - } -} +pub struct GainControl(Arc); -impl<'a> GainControl<'a> { +impl GainControl { /// Get the current amplification in decibels pub fn gain(&self) -> f32 { 20.0 * self.amplitude_ratio().log10() @@ -197,9 +169,9 @@ mod tests { #[test] fn smoothing() { - let mut s = Gain::new(Constant(1.0)); + let (mut c, mut s) = Gain::new(Constant(1.0)); let mut buf = [0.0; 6]; - s.control::, _>().set_amplitude_ratio(5.0); + c.set_amplitude_ratio(5.0); s.sample(0.025, &mut buf); assert_eq!(buf, [1.0, 2.0, 3.0, 4.0, 5.0, 5.0]); s.sample(0.025, &mut buf); diff --git a/src/lib.rs b/src/lib.rs index 8ab6aaf..b9d9478 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,13 +1,13 @@ //! Lightweight game audio //! //! ```no_run -//! let (mut scene_handle, scene) = oddio::split(oddio::SpatialScene::new()); +//! let (mut scene_handle, mut scene) = oddio::SpatialScene::new(); //! //! // In audio callback: //! # let data = &mut [][..]; //! # let output_sample_rate = 44100; //! let out_frames = oddio::frame_stereo(data); -//! oddio::run(&scene, output_sample_rate, out_frames); +//! oddio::run(&mut scene, output_sample_rate, out_frames); //! //! // In game logic: //! # let frames = []; @@ -15,11 +15,11 @@ //! # let position = [0.0, 0.0, 0.0].into(); //! # let velocity = [0.0, 0.0, 0.0].into(); //! let frames = oddio::FramesSignal::from(oddio::Frames::from_slice(sample_rate, &frames)); -//! let mut handle = scene_handle.control::() +//! let mut handle = scene_handle //! .play(frames, oddio::SpatialOptions { position, velocity, ..Default::default() }); //! //! // When position/velocity changes: -//! handle.control::, _>().set_motion(position, velocity, false); +//! handle.set_motion(position, velocity, false); //! ``` //! //! To get started, review [the `examples` @@ -29,7 +29,6 @@ //! - [`Frames`] stores static audio data, which can be played with a [`FramesSignal`] //! - [`Mixer`] allows multiple signals to be played concurrently and controlled during playback //! - [`SpatialScene`] is a mixer that spatializes its signals -//! - [`Handle`] allows control of a signal while it's playing, from a mixer or [`split`] //! - [`run`] writes frames from a [`Signal`] into an output buffer #![allow(unused_imports)] @@ -45,7 +44,6 @@ mod constant; mod cycle; mod downmix; mod fader; -mod filter; mod frame; mod frames; mod gain; @@ -60,7 +58,6 @@ mod smooth; mod spatial; mod speed; mod spsc; -mod stop; mod stream; mod swap; mod tanh; @@ -70,7 +67,6 @@ pub use constant::Constant; pub use cycle::Cycle; pub use downmix::Downmix; pub use fader::{Fader, FaderControl}; -pub use filter::*; pub use frame::Frame; pub use frames::*; pub use gain::{FixedGain, Gain, GainControl}; @@ -82,9 +78,7 @@ pub use sine::*; pub use smooth::{Interpolate, Smoothed}; pub use spatial::*; pub use speed::{Speed, SpeedControl}; -pub use stop::*; pub use stream::{Stream, StreamControl}; -pub use swap::Swap; pub use tanh::Tanh; /// Unitless instantaneous sound wave amplitude measurement @@ -93,42 +87,11 @@ pub type Sample = f32; /// Populate `out` with frames from `signal` at `sample_rate` /// /// Convenience wrapper for [`Signal::sample`]. -pub fn run(signal: &S, sample_rate: u32, out: &mut [S::Frame]) { +pub fn run(signal: &mut S, sample_rate: u32, out: &mut [S::Frame]) { let interval = 1.0 / sample_rate as f32; signal.sample(interval, out); } -/// Split concurrent controls out of a signal -/// -/// The [`Handle`] can be used to control the signal concurrent with the [`SplitSignal`] being -/// played -pub fn split(signal: S) -> (Handle, SplitSignal) { - let signal = alloc::sync::Arc::new(signal); - let handle = unsafe { Handle::from_arc(signal.clone()) }; - (handle, SplitSignal(signal)) -} - -/// A concurrently controlled [`Signal`] -pub struct SplitSignal(alloc::sync::Arc); - -impl Signal for SplitSignal -where - S: Signal + ?Sized, -{ - type Frame = S::Frame; - - fn sample(&self, interval: f32, out: &mut [Self::Frame]) { - self.0.sample(interval, out); - } - - fn is_finished(&self) -> bool { - self.0.is_finished() - } -} - -// Safe due to constraints on [`Controlled`] -unsafe impl Send for SplitSignal {} - /// Convert a slice of interleaved stereo data into a slice of stereo frames /// /// Useful for adapting output buffers obtained externally. diff --git a/src/mixer.rs b/src/mixer.rs index 604fa2b..3f30e44 100644 --- a/src/mixer.rs +++ b/src/mixer.rs @@ -1,12 +1,12 @@ use alloc::{boxed::Box, sync::Arc, vec}; -use core::cell::RefCell; +use core::sync::atomic::{AtomicBool, Ordering}; -use crate::{frame, set, Controlled, Frame, Handle, Set, SetHandle, Signal, Stop}; +use crate::{frame, set, Frame, Set, SetHandle, Signal}; /// Handle for controlling a [`Mixer`] from another thread -pub struct MixerControl<'a, T>(&'a Mixer); +pub struct MixerControl(SetHandle>); -impl MixerControl<'_, T> { +impl MixerControl { /// Begin playing `signal`, returning a handle that can be used to pause or stop it and access /// other controls /// @@ -15,54 +15,62 @@ impl MixerControl<'_, T> { /// /// The type of signal given determines what additional controls can be used. See the /// examples for a detailed guide. - pub fn play(&mut self, signal: S) -> Handle> + pub fn play(&mut self, signal: S) -> Mixed where S: Signal + Send + 'static, { - let signal = Arc::new(Stop::new(signal)); - let handle = unsafe { Handle::from_arc(signal.clone()) }; - self.0.send.borrow_mut().insert(signal); - handle + let signal = Box::new(MixedSignal::new(signal)); + let control = Mixed(signal.stop.clone()); + self.0.insert(signal); + control } } -/// A [`Signal`] that mixes a dynamic set of [`Signal`]s -pub struct Mixer { - send: RefCell>>, - recv: RefCell>, +/// Handle to a signal playing in a [`Mixer`] +pub struct Mixed(Arc); + +impl Mixed { + /// Immediately halt playback of the associated signal by its [`Mixer`] + pub fn stop(&mut self) { + self.0.store(true, Ordering::Relaxed); + } } -impl Mixer -where - T: Frame + Clone, -{ - /// Construct a new mixer - pub fn new() -> Self { - let (handle, set) = set(); +struct MixedSignal { + stop: Arc, + inner: T, +} + +impl MixedSignal { + fn new(signal: T) -> Self { Self { - send: RefCell::new(handle), - recv: RefCell::new(Inner { - set, - buffer: vec![T::ZERO; 1024].into(), - }), + stop: Arc::new(AtomicBool::new(false)), + inner: signal, } } } -impl Default for Mixer +/// A [`Signal`] that mixes a dynamic set of [`Signal`]s +pub struct Mixer { + recv: Inner, +} + +impl Mixer where T: Frame + Clone, { - fn default() -> Self { - Self::new() - } -} - -unsafe impl<'a, T: 'a> Controlled<'a> for Mixer { - type Control = MixerControl<'a, T>; - - unsafe fn make_control(signal: &'a Mixer) -> Self::Control { - MixerControl(signal) + /// Construct a new mixer + pub fn new() -> (MixerControl, Self) { + let (handle, set) = set(); + ( + MixerControl(handle), + Self { + recv: Inner { + set, + buffer: vec![T::ZERO; 1024].into(), + }, + }, + ) } } @@ -74,8 +82,8 @@ struct Inner { impl Signal for Mixer { type Frame = T; - fn sample(&self, interval: f32, out: &mut [T]) { - let this = &mut *self.recv.borrow_mut(); + fn sample(&mut self, interval: f32, out: &mut [T]) { + let this = &mut self.recv; this.set.update(); for o in out.iter_mut() { @@ -83,27 +91,18 @@ impl Signal for Mixer { } for i in (0..this.set.len()).rev() { - let signal = &this.set[i]; - if Arc::strong_count(signal) == 1 { - signal.handle_dropped(); - } - if signal.is_finished() { - signal.stop(); - } - if signal.is_stopped() { + let signal = &mut this.set[i]; + if signal.stop.load(Ordering::Relaxed) || signal.inner.is_finished() { this.set.remove(i); continue; } - if signal.is_paused() { - continue; - } // Sample into `buffer`, then mix into `out` let mut iter = out.iter_mut(); while iter.len() > 0 { let n = iter.len().min(this.buffer.len()); let staging = &mut this.buffer[..n]; - signal.sample(interval, staging); + signal.inner.sample(interval, staging); for (staged, o) in staging.iter().zip(&mut iter) { *o = frame::mix(o, staged); } @@ -112,4 +111,4 @@ impl Signal for Mixer { } } -type ErasedSignal = Arc>>; +type ErasedSignal = Box>>; diff --git a/src/reinhard.rs b/src/reinhard.rs index d1a1057..2505b0f 100644 --- a/src/reinhard.rs +++ b/src/reinhard.rs @@ -1,4 +1,4 @@ -use crate::{math::Float, Filter, Frame, Seek, Signal}; +use crate::{math::Float, Frame, Seek, Signal}; /// Smoothly maps a signal of any range into (-1, 1) /// @@ -25,7 +25,7 @@ where { type Frame = T::Frame; - fn sample(&self, interval: f32, out: &mut [T::Frame]) { + fn sample(&mut self, interval: f32, out: &mut [T::Frame]) { self.0.sample(interval, out); for x in out { for channel in x.channels_mut() { @@ -37,18 +37,6 @@ where fn is_finished(&self) -> bool { self.0.is_finished() } - - #[inline] - fn handle_dropped(&self) { - self.0.handle_dropped(); - } -} - -impl Filter for Reinhard { - type Inner = T; - fn inner(&self) -> &T { - &self.0 - } } impl Seek for Reinhard @@ -56,7 +44,7 @@ where T: Signal + Seek, T::Frame: Frame, { - fn seek(&self, seconds: f32) { + fn seek(&mut self, seconds: f32) { self.0.seek(seconds); } } diff --git a/src/ring.rs b/src/ring.rs index 04f5a33..36c4b24 100644 --- a/src/ring.rs +++ b/src/ring.rs @@ -15,7 +15,12 @@ impl Ring { } /// Fill buffer from `signal` - pub fn write + ?Sized>(&mut self, signal: &S, rate: u32, dt: f32) { + pub fn write + ?Sized>( + &mut self, + signal: &mut S, + rate: u32, + dt: f32, + ) { debug_assert!( dt * rate as f32 <= self.buffer.len() as f32, "output range exceeds capacity" @@ -76,24 +81,22 @@ impl Ring { #[cfg(test)] mod tests { - use core::cell::Cell; - use super::*; - struct TimeSignal(Cell); + struct TimeSignal(f32); impl Signal for TimeSignal { type Frame = Sample; - fn sample(&self, interval: f32, out: &mut [Sample]) { + fn sample(&mut self, interval: f32, out: &mut [Sample]) { for x in out { - let t = self.0.get(); + let t = self.0; *x = t as f32; - self.0.set(t + interval); + self.0 = t + interval; } } } - fn assert_out(r: &Ring, rate: u32, t: f32, interval: f32, expected: &[f32]) { + fn assert_out(r: &mut Ring, rate: u32, t: f32, interval: f32, expected: &[f32]) { let mut output = vec![0.0; expected.len()]; r.sample(rate, t, interval, &mut output); assert_eq!(&output, expected); @@ -102,31 +105,31 @@ mod tests { #[test] fn fill() { let mut r = Ring::new(4); - let s = TimeSignal(Cell::new(1.0)); + let mut s = TimeSignal(1.0); - r.write(&s, 1, 1.0); + r.write(&mut s, 1, 1.0); assert_eq!(r.write, 1.0); assert_eq!(r.buffer[..], [1.0, 0.0, 0.0, 0.0]); - r.write(&s, 1, 2.0); + r.write(&mut s, 1, 2.0); assert_eq!(r.write, 3.0); assert_eq!(r.buffer[..], [1.0, 2.0, 3.0, 0.0]); - assert_out(&r, 1, -1.5, 1.0, &[2.5, 1.5]); - assert_out(&r, 1, -1.5, 0.25, &[2.5, 2.75, 3.0, 2.25]); + assert_out(&mut r, 1, -1.5, 1.0, &[2.5, 1.5]); + assert_out(&mut r, 1, -1.5, 0.25, &[2.5, 2.75, 3.0, 2.25]); } #[test] fn wrap() { let mut r = Ring::new(4); - let s = TimeSignal(Cell::new(1.0)); + let mut s = TimeSignal(1.0); - r.write(&s, 1, 3.0); + r.write(&mut s, 1, 3.0); assert_eq!(r.buffer[..], [1.0, 2.0, 3.0, 0.0]); - r.write(&s, 1, 3.0); + r.write(&mut s, 1, 3.0); assert_eq!(r.buffer[..], [5.0, 6.0, 3.0, 4.0]); - assert_out(&r, 1, -2.75, 0.5, &[4.25, 4.75, 5.25, 5.75, 5.25, 3.75]); + assert_out(&mut r, 1, -2.75, 0.5, &[4.25, 4.75, 5.25, 5.75, 5.25, 3.75]); } } diff --git a/src/set.rs b/src/set.rs index 1a8abe9..04f0f36 100644 --- a/src/set.rs +++ b/src/set.rs @@ -1,5 +1,9 @@ use alloc::{collections::vec_deque::VecDeque, vec::Vec}; -use core::{cell::UnsafeCell, mem, ops::Deref}; +use core::{ + cell::UnsafeCell, + mem, + ops::{Deref, DerefMut}, +}; use crate::spsc; @@ -192,6 +196,13 @@ impl Deref for Set { } } +impl DerefMut for Set { + fn deref_mut(&mut self) -> &mut [T] { + let this = unsafe { &mut (*self.0.get()) }; + &mut this.signals + } +} + type SignalTable = Vec; enum Msg { @@ -215,9 +226,9 @@ mod tests { #[test] fn realloc_signals() { let (mut remote, mut s) = set(); - let signal = FramesSignal::from(Frames::from_slice(RATE, &[[0.0; 2]; RATE as usize])); + let frames = Frames::from_slice(RATE, &[[0.0; 2]; RATE as usize]); for i in 1..=(INITIAL_SIGNALS_CAPACITY + 2) { - remote.insert(signal.clone()); + remote.insert(FramesSignal::from(frames.clone())); s.update(); assert_eq!(unsafe { (*s.0.get()).signals.len() }, i); } @@ -226,9 +237,9 @@ mod tests { #[test] fn realloc_channel() { let (mut remote, mut s) = set(); - let signal = FramesSignal::from(Frames::from_slice(RATE, &[[0.0; 2]; RATE as usize])); + let frames = Frames::from_slice(RATE, &[[0.0; 2]; RATE as usize]); for _ in 0..(INITIAL_CHANNEL_CAPACITY + 2) { - remote.insert(signal.clone()); + remote.insert(FramesSignal::from(frames.clone())); } assert_eq!(remote.sender.capacity(), 1 + 2 * INITIAL_CHANNEL_CAPACITY); assert_eq!(unsafe { (*s.0.get()).signals.len() }, 0); diff --git a/src/signal.rs b/src/signal.rs index de742f0..4edfe6b 100644 --- a/src/signal.rs +++ b/src/signal.rs @@ -1,12 +1,12 @@ -use crate::{flatten_stereo, Filter, Sample}; +use crate::{flatten_stereo, Sample}; /// An audio signal /// /// This interface is intended for use only from the code actually generating an audio signal for /// output. For example, in a real-time application, `Signal`s will typically be owned by the /// real-time audio thread and not directly accessible from elsewhere. Access to an active signal -/// for other purposes (e.g. to adjust parameters) is generally through [`Handle`](crate::Handle), -/// using signal-specific interfaces that implement wait-free inter-thread communication. +/// for other purposes (e.g. to adjust parameters) is generally through a control handle returned by +/// its constructor. /// /// To ensure glitch-free audio, none of these methods should perform any operation that may /// wait. This includes locks, memory allocation or freeing, and even unbounded compare-and-swap @@ -16,7 +16,7 @@ pub trait Signal { type Frame; /// Sample frames separated by `interval` seconds each - fn sample(&self, interval: f32, out: &mut [Self::Frame]); + fn sample(&mut self, interval: f32, out: &mut [Self::Frame]); /// Whether future calls to `sample` with a nonnegative `interval` will only produce zeroes /// @@ -25,19 +25,12 @@ pub trait Signal { fn is_finished(&self) -> bool { false } - - /// Called when the signal's handle is dropped - /// - /// Useful for e.g. allowing [`Stream`](crate::Stream) to clean itself up when no more data can - /// be supplied - #[inline] - fn handle_dropped(&self) {} } impl Signal for alloc::boxed::Box { type Frame = T::Frame; - fn sample(&self, interval: f32, out: &mut [T::Frame]) { + fn sample(&mut self, interval: f32, out: &mut [T::Frame]) { (**self).sample(interval, out); } @@ -45,11 +38,6 @@ impl Signal for alloc::boxed::Box { fn is_finished(&self) -> bool { (**self).is_finished() } - - #[inline] - fn handle_dropped(&self) { - (**self).handle_dropped(); - } } /// Audio signals which support seeking @@ -59,12 +47,12 @@ impl Signal for alloc::boxed::Box { /// code. pub trait Seek: Signal { /// Shift the starting point of the next `sample` call by `seconds` - fn seek(&self, seconds: f32); + fn seek(&mut self, seconds: f32); } impl Seek for alloc::boxed::Box { #[inline] - fn seek(&self, seconds: f32) { + fn seek(&mut self, seconds: f32) { (**self).seek(seconds); } } @@ -82,7 +70,7 @@ impl MonoToStereo { impl> Signal for MonoToStereo { type Frame = [Sample; 2]; - fn sample(&self, interval: f32, out: &mut [[Sample; 2]]) { + fn sample(&mut self, interval: f32, out: &mut [[Sample; 2]]) { let n = out.len(); let buf = flatten_stereo(out); self.0.sample(interval, &mut buf[..n]); @@ -94,49 +82,34 @@ impl> Signal for MonoToStereo { fn is_finished(&self) -> bool { self.0.is_finished() } - - #[inline] - fn handle_dropped(&self) { - self.0.handle_dropped(); - } -} - -impl Filter for MonoToStereo { - type Inner = T; - - fn inner(&self) -> &Self::Inner { - &self.0 - } } impl> Seek for MonoToStereo { - fn seek(&self, seconds: f32) { + fn seek(&mut self, seconds: f32) { self.0.seek(seconds) } } #[cfg(test)] mod tests { - use core::cell::Cell; - use super::*; - struct CountingSignal(Cell); + struct CountingSignal(u32); impl Signal for CountingSignal { type Frame = Sample; - fn sample(&self, _: f32, out: &mut [Sample]) { + fn sample(&mut self, _: f32, out: &mut [Sample]) { for x in out { - let i = self.0.get(); + let i = self.0; *x = i as f32; - self.0.set(i + 1); + self.0 = i + 1; } } } #[test] fn mono_to_stereo() { - let signal = MonoToStereo::new(CountingSignal(Cell::new(0))); + let mut signal = MonoToStereo::new(CountingSignal(0)); let mut buf = [[0.0; 2]; 4]; signal.sample(1.0, (&mut buf[..]).into()); assert_eq!(buf, [[0.0, 0.0], [1.0, 1.0], [2.0, 2.0], [3.0, 3.0]]); diff --git a/src/sine.rs b/src/sine.rs index 39aa0e2..b986ed8 100644 --- a/src/sine.rs +++ b/src/sine.rs @@ -1,10 +1,10 @@ -use core::{cell::Cell, f32::consts::TAU}; +use core::f32::consts::TAU; use crate::{math::Float, Sample, Seek, Signal}; /// A trivial [`Signal`] that produces a sine wave of a particular frequency, forever pub struct Sine { - phase: Cell, + phase: f32, /// Radians per second frequency: f32, } @@ -17,32 +17,31 @@ impl Sine { /// may produce weird interference effects if their phases align. pub fn new(phase: f32, frequency_hz: f32) -> Self { Self { - phase: Cell::new(phase), + phase, frequency: frequency_hz * TAU, } } - fn seek_to(&self, t: f32) { + fn seek_to(&mut self, t: f32) { // Advance time, but wrap for numerical stability no matter how long we play for - self.phase - .set((self.phase.get() + t * self.frequency) % TAU); + self.phase = (self.phase + t * self.frequency) % TAU; } } impl Signal for Sine { type Frame = Sample; - fn sample(&self, interval: f32, out: &mut [Sample]) { + fn sample(&mut self, interval: f32, out: &mut [Sample]) { for (i, x) in out.iter_mut().enumerate() { let t = interval * i as f32; - *x = (t * self.frequency + self.phase.get()).sin(); + *x = (t * self.frequency + self.phase).sin(); } self.seek_to(interval * out.len() as f32); } } impl Seek for Sine { - fn seek(&self, seconds: f32) { + fn seek(&mut self, seconds: f32) { self.seek_to(seconds); } } diff --git a/src/spatial.rs b/src/spatial.rs index 2d9813a..10d434c 100644 --- a/src/spatial.rs +++ b/src/spatial.rs @@ -1,22 +1,18 @@ -use alloc::sync::Arc; -use core::{ - cell::{Cell, RefCell}, - ops::{Index, IndexMut}, -}; +use alloc::{boxed::Box, sync::Arc}; +use core::ops::{Index, IndexMut}; use crate::{ math::{add, dot, invert_quat, mix, norm, rotate, scale, sub, Float}, ring::Ring, set::{set, Set, SetHandle}, - swap::Swap, - Controlled, Filter, FilterHaving, Handle, Sample, Seek, Signal, Stop, + swap, Sample, Seek, Signal, }; -type ErasedSpatialBuffered = Arc + Send>>>; -type ErasedSpatial = Arc + Send>>>; +type ErasedSpatialBuffered = Box + Send>>; +type ErasedSpatial = Box + Send>>; /// An individual buffered spatialized signal -pub struct SpatialBuffered { +struct SpatialSignalBuffered { rate: u32, max_delay: f32, common: Common, @@ -24,11 +20,11 @@ pub struct SpatialBuffered { /// /// Accounts only for the source's velocity. Listener velocity and attenuation are handled at /// output time. - queue: RefCell, + queue: Ring, inner: T, } -impl SpatialBuffered { +impl SpatialSignalBuffered { fn new( rate: u32, inner: T, @@ -36,99 +32,87 @@ impl SpatialBuffered { velocity: mint::Vector3, max_delay: f32, radius: f32, - ) -> Self { + ) -> (swap::Sender, Self) { let mut queue = Ring::new((max_delay * rate as f32).ceil() as usize + 1); queue.delay( rate, (norm(position.into()) / SPEED_OF_SOUND).min(max_delay), ); - Self { - rate, - max_delay, - common: Common::new(radius, position, velocity), - queue: RefCell::new(queue), - inner, - } - } -} - -impl Filter for SpatialBuffered { - type Inner = T; - fn inner(&self) -> &T { - &self.inner - } -} - -unsafe impl<'a, T: 'a> Controlled<'a> for SpatialBuffered { - type Control = SpatialControl<'a>; - - unsafe fn make_control(signal: &'a SpatialBuffered) -> Self::Control { - SpatialControl(&signal.common.motion) + let (send, recv) = Common::new(radius, position, velocity); + ( + send, + Self { + rate, + max_delay, + common: recv, + queue, + inner, + }, + ) } } /// An individual seekable spatialized signal -pub struct Spatial { +struct SpatialSignal { common: Common, inner: T, } -impl Spatial { +impl SpatialSignal { fn new( inner: T, position: mint::Point3, velocity: mint::Vector3, radius: f32, - ) -> Self { - Self { - common: Common::new(radius, position, velocity), - inner, - } - } -} - -impl Filter for Spatial { - type Inner = T; - fn inner(&self) -> &T { - &self.inner - } -} - -unsafe impl<'a, T: 'a> Controlled<'a> for Spatial { - type Control = SpatialControl<'a>; - - unsafe fn make_control(signal: &'a Spatial) -> Self::Control { - SpatialControl(&signal.common.motion) + ) -> (swap::Sender, Self) { + let (send, recv) = Common::new(radius, position, velocity); + ( + send, + Self { + common: recv, + inner, + }, + ) } } struct Common { radius: f32, - motion: Swap, - state: RefCell, + motion: swap::Receiver, + state: State, /// How long ago the signal finished, if it did - finished_for: Cell>, + finished_for: Option, + stopped: bool, } impl Common { - fn new(radius: f32, position: mint::Point3, velocity: mint::Vector3) -> Self { - Self { - radius, - motion: Swap::new(|| Motion { - position, - velocity, - discontinuity: false, - }), - state: RefCell::new(State::new(position)), - finished_for: Cell::new(None), - } + fn new( + radius: f32, + position: mint::Point3, + velocity: mint::Vector3, + ) -> (swap::Sender, Self) { + let (send, recv) = swap::swap(|| Motion { + position, + velocity, + discontinuity: false, + }); + ( + send, + Self { + radius, + motion: recv, + state: State::new(position), + finished_for: None, + stopped: false, + }, + ) } } /// Control for updating the motion of a spatial signal -pub struct SpatialControl<'a>(&'a Swap); +pub struct Spatial(swap::Sender); -impl<'a> SpatialControl<'a> { +impl Spatial { /// Update the position and velocity of the signal /// /// Coordinates should be in world space, translated such that the listener is at the origin, @@ -146,102 +130,91 @@ impl<'a> SpatialControl<'a> { velocity: mint::Vector3, discontinuity: bool, ) { - unsafe { - *self.0.pending() = Motion { - position, - velocity, - discontinuity, - }; - } + *self.0.pending() = Motion { + position, + velocity, + discontinuity, + }; self.0.flush(); } } /// [`Signal`] for stereo output from a spatial scene pub struct SpatialScene { - send_buffered: RefCell>, - send: RefCell>, - rot: Swap>, - recv_buffered: RefCell>, - recv: RefCell>, + rot: swap::Receiver>, + recv_buffered: Set, + recv: Set, } impl SpatialScene { /// Create a [`Signal`] for spatializing mono signals for stereo output /// /// Samples its component signals at `rate`. - pub fn new() -> Self { + pub fn new() -> (SpatialSceneControl, Self) { let (seek_handle, seek_set) = set(); let (buffered_handle, buffered_set) = set(); - let rot = Swap::new(|| mint::Quaternion { + let (rot_send, rot_recv) = swap::swap(|| mint::Quaternion { s: 1.0, v: [0.0; 3].into(), }); - SpatialScene { - send_buffered: RefCell::new(buffered_handle), - send: RefCell::new(seek_handle), - rot, - recv_buffered: RefCell::new(buffered_set), - recv: RefCell::new(seek_set), - } - } -} - -unsafe impl Send for SpatialScene {} - -impl Default for SpatialScene { - fn default() -> Self { - Self::new() + let control = SpatialSceneControl { + rot: rot_send, + seek: seek_handle, + buffered: buffered_handle, + }; + let signal = SpatialScene { + rot: rot_recv, + recv_buffered: buffered_set, + recv: seek_set, + }; + (control, signal) } } -fn walk_set( - set: &mut Set>, - get_common: impl Fn(&T) -> &Common, +fn walk_set( + set: &mut Set>, + get_common: impl Fn(&mut T) -> &mut Common, + get_inner: impl Fn(&T) -> &U, prev_rot: &mint::Quaternion, rot: &mint::Quaternion, elapsed: f32, - mut mix_signal: impl FnMut(&T, mint::Point3, mint::Point3), + mut mix_signal: impl FnMut(&mut T, mint::Point3, mint::Point3), ) where - T: FilterHaving, I> + ?Sized, + T: ?Sized, U: Signal + ?Sized, { set.update(); for i in (0..set.len()).rev() { - let signal = &set[i]; - let stop = , _>>::get(signal); + let signal = &mut set[i]; let common = get_common(signal); - if Arc::strong_count(signal) == 1 { - stop.handle_dropped(); - } let prev_position; let next_position; - unsafe { + { // Compute the signal's smoothed start/end positions over the sampled period // TODO: Use historical positions - let mut state = common.state.borrow_mut(); + let state = &mut common.state; // Update motion let orig_next = *common.motion.received(); if common.motion.refresh() { - state.prev_position = if (*common.motion.received()).discontinuity { - (*common.motion.received()).position + state.prev_position = if common.motion.received().discontinuity { + common.motion.received().position } else { state.smoothed_position(0.0, &orig_next) }; state.dt = 0.0; } else { - debug_assert_eq!(orig_next.position, (*common.motion.received()).position); + debug_assert_eq!(orig_next.position, common.motion.received().position); } prev_position = rotate( prev_rot, - &state.smoothed_position(0.0, &*common.motion.received()), + &state.smoothed_position(0.0, common.motion.received()), ); next_position = rotate( rot, - &state.smoothed_position(elapsed, &*common.motion.received()), + &state.smoothed_position(elapsed, common.motion.received()), ); // Set up for next time @@ -251,45 +224,37 @@ fn walk_set( // Discard finished sources. If a source is moving away faster than the speed of sound, you // might get a pop. let distance = norm(prev_position.into()); - match common.finished_for.get() { + match common.finished_for { Some(t) => { if t > distance / SPEED_OF_SOUND { - stop.stop(); + common.stopped = true; } else { - common.finished_for.set(Some(t + elapsed)); + common.finished_for = Some(t + elapsed); } } None => { - if stop.is_finished() { - common.finished_for.set(Some(elapsed)); + if get_inner(signal).is_finished() { + get_common(signal).finished_for = Some(elapsed); } } } - if stop.is_stopped() { + if get_common(signal).stopped { set.remove(i); continue; } - if stop.is_paused() { - continue; - } - mix_signal(signal, prev_position, next_position); } } /// Control for modifying a [`SpatialScene`] -pub struct SpatialSceneControl<'a>(&'a SpatialScene); - -unsafe impl<'a> Controlled<'a> for SpatialScene { - type Control = SpatialSceneControl<'a>; - - unsafe fn make_control(signal: &'a SpatialScene) -> Self::Control { - SpatialSceneControl(signal) - } +pub struct SpatialSceneControl { + rot: swap::Sender>, + seek: SetHandle, + buffered: SetHandle, } -impl<'a> SpatialSceneControl<'a> { +impl SpatialSceneControl { /// Begin playing `signal` /// /// Note that `signal` must be single-channel. Signals in a spatial scene are modeled as @@ -299,23 +264,20 @@ impl<'a> SpatialSceneControl<'a> { /// but not rotated, with velocity relative to the listener. Units are meters and meters per /// second. /// - /// Returns a [`Handle`] that can be used to adjust the signal's movement in the future, pause - /// or stop it, and access other controls. + /// Returns a handle that can be used to adjust the signal's movement in the future, pause or + /// stop it, and access other controls. /// /// The type of signal given determines what additional controls can be used. See the /// examples for a detailed guide. - pub fn play(&mut self, signal: S, options: SpatialOptions) -> Handle>> + pub fn play(&mut self, signal: S, options: SpatialOptions) -> Spatial where S: Seek + Send + 'static, { - let signal = Arc::new(Spatial::new( - Stop::new(signal), - options.position, - options.velocity, - options.radius, - )); - let handle = unsafe { Handle::from_arc(signal.clone()) }; - self.0.send.borrow_mut().insert(signal); + let (send, recv) = + SpatialSignal::new(signal, options.position, options.velocity, options.radius); + let signal = Box::new(recv); + let handle = Spatial(send); + self.seek.insert(signal); handle } @@ -336,20 +298,21 @@ impl<'a> SpatialSceneControl<'a> { max_distance: f32, rate: u32, buffer_duration: f32, - ) -> Handle>> + ) -> Spatial where S: Signal + Send + 'static, { - let signal = Arc::new(SpatialBuffered::new( + let (send, recv) = SpatialSignalBuffered::new( rate, - Stop::new(signal), + signal, options.position, options.velocity, max_distance / SPEED_OF_SOUND + buffer_duration, options.radius, - )); - let handle = unsafe { Handle::from_arc(signal.clone()) }; - self.0.send_buffered.borrow_mut().insert(signal); + ); + let signal = Box::new(recv); + let handle = Spatial(send); + self.buffered.insert(signal); handle } @@ -358,10 +321,8 @@ impl<'a> SpatialSceneControl<'a> { /// An unrotated listener faces -Z, with +X to the right and +Y up. pub fn set_listener_rotation(&mut self, rotation: mint::Quaternion) { let signal_rotation = invert_quat(&rotation); - unsafe { - *self.0.rot.pending() = signal_rotation; - } - self.0.rot.flush(); + *self.rot.pending() = signal_rotation; + self.rot.flush(); } } @@ -389,13 +350,13 @@ impl Default for SpatialOptions { impl Signal for SpatialScene { type Frame = [Sample; 2]; - fn sample(&self, interval: f32, out: &mut [[Sample; 2]]) { - let set = &mut *self.recv_buffered.borrow_mut(); + fn sample(&mut self, interval: f32, out: &mut [[Sample; 2]]) { + let set = &mut self.recv_buffered; // Update set contents set.update(); // Update listener rotation - let (prev_rot, rot) = unsafe { + let (prev_rot, rot) = { let prev = *self.rot.received(); self.rot.refresh(); (prev, *self.rot.received()) @@ -410,7 +371,8 @@ impl Signal for SpatialScene { let elapsed = interval * out.len() as f32; walk_set( set, - |signal| &signal.common, + |signal| &mut signal.common, + |signal| &signal.inner, &prev_rot, &rot, elapsed, @@ -418,10 +380,7 @@ impl Signal for SpatialScene { debug_assert!(signal.max_delay >= elapsed); // Extend delay queue with new data - signal - .queue - .borrow_mut() - .write(&signal.inner, signal.rate, elapsed); + signal.queue.write(&mut signal.inner, signal.rate, elapsed); // Mix into output for &ear in &[Ear::Left, Ear::Right] { @@ -436,7 +395,7 @@ impl Signal for SpatialScene { let d_gain = (next_state.gain - prev_state.gain) / out.len() as f32; let mut i = 0; - let queue = signal.queue.borrow(); + let queue = &mut signal.queue; for chunk in out.chunks_mut(buf.len()) { let t = prev_offset + i as f32 * dt; queue.sample(signal.rate, t, dt, &mut buf[..chunk.len()]); @@ -450,12 +409,13 @@ impl Signal for SpatialScene { }, ); - let set = &mut *self.recv.borrow_mut(); + let set = &mut self.recv; // Update set contents set.update(); walk_set( set, - |signal| &signal.common, + |signal| &mut signal.common, + |signal| &signal.inner, &prev_rot, &rot, elapsed, @@ -629,7 +589,7 @@ mod tests { impl Signal for FinishedSignal { type Frame = f32; - fn sample(&self, _: f32, out: &mut [Self::Frame]) { + fn sample(&mut self, _: f32, out: &mut [Self::Frame]) { out.fill(0.0); } @@ -639,14 +599,14 @@ mod tests { } impl Seek for FinishedSignal { - fn seek(&self, _: f32) {} + fn seek(&mut self, _: f32) {} } /// Verify that a signal is dropped only after accounting for propagation delay #[test] fn signal_finished() { - let scene = SpatialScene::new(); - SpatialSceneControl(&scene).play( + let (mut control, mut scene) = SpatialScene::new(); + control.play( FinishedSignal, SpatialOptions { // Exactly one second of propagation delay @@ -656,25 +616,25 @@ mod tests { ); scene.sample(0.0, &mut []); assert_eq!( - scene.recv.borrow().len(), + scene.recv.len(), 1, "signal remains after no time has passed" ); scene.sample(0.6, &mut [[0.0; 2]]); assert_eq!( - scene.recv.borrow().len(), + scene.recv.len(), 1, "signal remains partway through propagation" ); scene.sample(0.6, &mut [[0.0; 2]]); assert_eq!( - scene.recv.borrow().len(), + scene.recv.len(), 1, "signal remains immediately after propagation delay expires" ); scene.sample(0.0, &mut []); assert_eq!( - scene.recv.borrow().len(), + scene.recv.len(), 0, "signal dropped on first past after propagation delay expires" ); diff --git a/src/speed.rs b/src/speed.rs index a299e67..5907110 100644 --- a/src/speed.rs +++ b/src/speed.rs @@ -1,22 +1,25 @@ +use alloc::sync::Arc; use core::sync::atomic::{AtomicU32, Ordering}; -use crate::{Controlled, Filter, Frame, Signal}; +use crate::{Frame, Signal}; /// Scales rate of playback by a dynamically-adjustable factor /// /// Higher/lower speeds will naturally result in higher/lower pitched sound respectively. pub struct Speed { - speed: AtomicU32, + speed: Arc, inner: T, } impl Speed { /// Apply dynamic speed to `signal` - pub fn new(signal: T) -> Self { - Self { - speed: AtomicU32::new(1.0f32.to_bits()), + pub fn new(signal: T) -> (SpeedControl, Self) { + let signal = Self { + speed: Arc::new(AtomicU32::new(1.0f32.to_bits())), inner: signal, - } + }; + let control = SpeedControl(signal.speed.clone()); + (control, signal) } } @@ -26,7 +29,7 @@ where { type Frame = T::Frame; - fn sample(&self, interval: f32, out: &mut [T::Frame]) { + fn sample(&mut self, interval: f32, out: &mut [T::Frame]) { let speed = f32::from_bits(self.speed.load(Ordering::Relaxed)); self.inner.sample(interval * speed, out); } @@ -34,32 +37,12 @@ where fn is_finished(&self) -> bool { self.inner.is_finished() } - - #[inline] - fn handle_dropped(&self) { - self.inner.handle_dropped(); - } -} - -impl Filter for Speed { - type Inner = T; - fn inner(&self) -> &T { - &self.inner - } } /// Thread-safe control for a [`Speed`] filter -pub struct SpeedControl<'a>(&'a AtomicU32); - -unsafe impl<'a, T: 'a> Controlled<'a> for Speed { - type Control = SpeedControl<'a>; - - unsafe fn make_control(signal: &'a Speed) -> Self::Control { - SpeedControl(&signal.speed) - } -} +pub struct SpeedControl(Arc); -impl<'a> SpeedControl<'a> { +impl SpeedControl { /// Get the current speed pub fn speed(&self) -> f32 { f32::from_bits(self.0.load(Ordering::Relaxed)) diff --git a/src/spsc.rs b/src/spsc.rs index dbf6b52..193bb3d 100644 --- a/src/spsc.rs +++ b/src/spsc.rs @@ -144,10 +144,8 @@ impl Receiver { } /// Whether the sender has been dropped - // Could be `&self` since we don't allow new references to be created, but :effort: - #[allow(clippy::wrong_self_convention)] - pub fn is_closed(&mut self) -> bool { - Arc::get_mut(&mut self.shared).is_some() + pub fn is_closed(&self) -> bool { + Arc::strong_count(&self.shared) == 1 } pub fn pop(&mut self) -> Option { diff --git a/src/stop.rs b/src/stop.rs deleted file mode 100644 index 9008723..0000000 --- a/src/stop.rs +++ /dev/null @@ -1,107 +0,0 @@ -use core::sync::atomic::{AtomicUsize, Ordering}; - -use crate::{Controlled, Filter, Seek, Signal}; - -const PLAY: usize = 0; -const PAUSE: usize = 1; -const STOP: usize = 2; - -/// A source that can be paused or permanently stopped -pub struct Stop { - state: AtomicUsize, - inner: T, -} - -impl Stop { - pub(crate) fn new(signal: T) -> Self { - Self { - state: AtomicUsize::new(PLAY), - inner: signal, - } - } -} - -impl Stop { - /// Stop the source for good - pub(crate) fn stop(&self) { - self.state.store(STOP, Ordering::Relaxed); - } - - pub(crate) fn is_paused(&self) -> bool { - self.state.load(Ordering::Relaxed) == PAUSE - } - - pub(crate) fn is_stopped(&self) -> bool { - self.state.load(Ordering::Relaxed) == STOP - } -} - -impl Signal for Stop { - type Frame = T::Frame; - - fn sample(&self, interval: f32, out: &mut [T::Frame]) { - self.inner.sample(interval, out); - } - - fn is_finished(&self) -> bool { - let state = self.state.load(Ordering::Relaxed); - state == STOP || self.inner.is_finished() - } - - #[inline] - fn handle_dropped(&self) { - self.inner.handle_dropped(); - } -} - -impl Filter for Stop { - type Inner = T; - fn inner(&self) -> &T { - &self.inner - } -} - -impl Seek for Stop { - fn seek(&self, seconds: f32) { - self.inner.seek(seconds); - } -} - -/// Thread-safe control for a [`Stop`] filter -#[derive(Copy, Clone)] -pub struct StopControl<'a>(&'a AtomicUsize); - -unsafe impl<'a, T: 'a> Controlled<'a> for Stop { - type Control = StopControl<'a>; - - unsafe fn make_control(signal: &'a Stop) -> Self::Control { - StopControl(&signal.state) - } -} - -impl<'a> StopControl<'a> { - /// Suspend playback of the source - pub fn pause(&self) { - self.0.store(PAUSE, Ordering::Relaxed); - } - - /// Resume the paused source - pub fn resume(&self) { - self.0.store(PLAY, Ordering::Relaxed); - } - - /// Stop the source for good - pub fn stop(&self) { - self.0.store(STOP, Ordering::Relaxed); - } - - /// Whether the source is paused - pub fn is_paused(&self) -> bool { - self.0.load(Ordering::Relaxed) == PAUSE - } - - /// Whether the source has stopped - pub fn is_stopped(&self) -> bool { - self.0.load(Ordering::Relaxed) == STOP - } -} diff --git a/src/stream.rs b/src/stream.rs index ee7f115..fd63780 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -1,38 +1,36 @@ //! Streaming audio support -use core::cell::{Cell, RefCell}; - -use crate::{frame, math::Float, spsc, Controlled, Frame, Signal}; +use crate::{frame, math::Float, spsc, Frame, Signal}; /// Dynamic audio from an external source pub struct Stream { - send: RefCell>, rate: u32, - inner: RefCell>, + inner: spsc::Receiver, /// Offset of t=0 from the start of the buffer, in frames - t: Cell, - /// Whether the handle has been dropped - closed: Cell, + t: f32, + /// Whether `inner` will receive no further updates + stopping: bool, } impl Stream { /// Construct a stream of dynamic audio /// - /// Samples can be appended to the stream through its [`Handle`](crate::Handle). This allows the + /// Samples can be appended to the stream through its [`StreamControl`]. This allows the /// business of obtaining streaming audio, e.g. from a streaming decoder or the network, to take /// place without interfering with the low-latency requirements of audio output. /// /// - `rate` is the stream's sample rate /// - `size` dictates the maximum number of buffered frames - pub fn new(rate: u32, size: usize) -> Self { + pub fn new(rate: u32, size: usize) -> (StreamControl, Self) { let (send, recv) = spsc::channel(size); - Self { - send: RefCell::new(send), + let signal = Self { rate, - inner: RefCell::new(recv), - t: Cell::new(0.0), - closed: Cell::new(false), - } + inner: recv, + t: 0.0, + stopping: false, + }; + let control = StreamControl(send); + (control, signal) } #[inline] @@ -44,11 +42,10 @@ impl Stream { return T::ZERO; } let sample = sample as usize; - let inner = self.inner.borrow(); - if sample >= inner.len() { + if sample >= self.inner.len() { return T::ZERO; } - inner[sample] + self.inner[sample] } fn sample_single(&self, s: f32) -> T @@ -63,21 +60,23 @@ impl Stream { frame::lerp(&a, &b, fract) } - fn advance(&self, dt: f32) { - let mut inner = self.inner.borrow_mut(); - let next = self.t.get() + dt * self.rate as f32; - let t = next.min(inner.len() as f32); - inner.release(t as usize); - self.t.set(t.fract()); + fn advance(&mut self, dt: f32) { + let next = self.t + dt * self.rate as f32; + let t = next.min(self.inner.len() as f32); + self.inner.release(t as usize); + self.t = t.fract(); } } impl Signal for Stream { type Frame = T; - fn sample(&self, interval: f32, out: &mut [T]) { - self.inner.borrow_mut().update(); - let s0 = self.t.get(); + fn sample(&mut self, interval: f32, out: &mut [T]) { + self.inner.update(); + if self.inner.is_closed() { + self.stopping = true; + } + let s0 = self.t; let ds = interval * self.rate as f32; for (i, o) in out.iter_mut().enumerate() { @@ -88,37 +87,17 @@ impl Signal for Stream { #[allow(clippy::float_cmp)] fn is_finished(&self) -> bool { - if !self.closed.get() { - return false; - } - self.t.get() == self.inner.borrow().len() as f32 - } - - fn handle_dropped(&self) { - self.closed.set(true); - // Make sure a following `is_finished` can see data was sent at the last moment - self.inner.borrow_mut().update(); + self.stopping && self.t == self.inner.len() as f32 } } /// Thread-safe control for a [`Stream`] -pub struct StreamControl<'a, T>(&'a Stream); - -unsafe impl<'a, T> Controlled<'a> for Stream -where - T: 'static, -{ - type Control = StreamControl<'a, T>; - - unsafe fn make_control(signal: &'a Stream) -> Self::Control { - StreamControl(signal) - } -} +pub struct StreamControl(spsc::Sender); -impl<'a, T> StreamControl<'a, T> { +impl StreamControl { /// Lower bound to the number of samples that the next `write` call will successfully consume pub fn free(&mut self) -> usize { - self.0.send.borrow().free() + self.0.free() } /// Add more samples. Returns the number of samples consumed. Remaining samples should be passed @@ -127,7 +106,7 @@ impl<'a, T> StreamControl<'a, T> { where T: Copy, { - self.0.send.borrow_mut().send_from_slice(samples) + self.0.send_from_slice(samples) } } @@ -136,7 +115,7 @@ mod tests { use super::*; use alloc::vec; - fn assert_out(stream: &Stream, expected: &[f32]) { + fn assert_out(stream: &mut Stream, expected: &[f32]) { let mut output = vec![0.0; expected.len()]; stream.sample(1.0, &mut output); assert_eq!(output, expected); @@ -144,22 +123,22 @@ mod tests { #[test] fn smoke() { - let s = Stream::::new(1, 3); - assert_eq!(StreamControl(&s).write(&[1.0, 2.0]), 2); - assert_eq!(StreamControl(&s).write(&[3.0, 4.0]), 1); - assert_out(&s, &[1.0, 2.0, 3.0, 0.0, 0.0]); - assert_eq!(StreamControl(&s).write(&[5.0, 6.0, 7.0, 8.0]), 3); - assert_out(&s, &[5.0]); - assert_out(&s, &[6.0, 7.0, 0.0, 0.0]); - assert_out(&s, &[0.0, 0.0]); + let (mut c, mut s) = Stream::::new(1, 3); + assert_eq!(c.write(&[1.0, 2.0]), 2); + assert_eq!(c.write(&[3.0, 4.0]), 1); + assert_out(&mut s, &[1.0, 2.0, 3.0, 0.0, 0.0]); + assert_eq!(c.write(&[5.0, 6.0, 7.0, 8.0]), 3); + assert_out(&mut s, &[5.0]); + assert_out(&mut s, &[6.0, 7.0, 0.0, 0.0]); + assert_out(&mut s, &[0.0, 0.0]); } #[test] fn cleanup() { - let s = Stream::::new(1, 4); - assert_eq!(StreamControl(&s).write(&[1.0, 2.0]), 2); + let (mut c, mut s) = Stream::::new(1, 4); + assert_eq!(c.write(&[1.0, 2.0]), 2); assert!(!s.is_finished()); - s.handle_dropped(); + drop(c); assert!(!s.is_finished()); s.sample(1.0, &mut [0.0]); assert!(!s.is_finished()); diff --git a/src/swap.rs b/src/swap.rs index e9f6991..28c7694 100644 --- a/src/swap.rs +++ b/src/swap.rs @@ -3,98 +3,103 @@ use core::{ sync::atomic::{AtomicUsize, Ordering}, }; +use alloc::sync::Arc; + /// SPSC queue that only retains the last element sent /// /// Useful for custom controllable signals. -pub struct Swap { - slots: [UnsafeCell; 3], - send: Cell, - shared: AtomicUsize, - recv: Cell, +pub fn swap(mut init: impl FnMut() -> T) -> (Sender, Receiver) { + let shared = Arc::new(Shared { + slots: [ + UnsafeCell::new(init()), + UnsafeCell::new(init()), + UnsafeCell::new(init()), + ], + index: AtomicUsize::new(1), + }); + ( + Sender { + index: 0, + shared: shared.clone(), + }, + Receiver { index: 2, shared }, + ) } -impl Swap { - /// Create a channel initially holding `x` - pub fn new(mut init: impl FnMut() -> T) -> Self { - Self { - slots: [ - UnsafeCell::new(init()), - UnsafeCell::new(init()), - UnsafeCell::new(init()), - ], - send: Cell::new(0), - shared: AtomicUsize::new(1), - recv: Cell::new(2), - } - } +pub struct Sender { + index: usize, + shared: Arc>, +} - /// Access the value that will be sent next. Producer only. - pub fn pending(&self) -> *mut T { - self.slots[self.send.get()].get() +impl Sender { + /// Access the value that will be sent next + pub fn pending(&mut self) -> &mut T { + unsafe { &mut *self.shared.slots[self.index].get() } } - /// Send the value from `pending`. Producer only. - pub fn flush(&self) { - self.send.set( - self.shared - .swap(self.send.get() | FRESH_BIT, Ordering::AcqRel) - & INDEX_MASK, - ); + /// Send the value from `pending` + pub fn flush(&mut self) { + self.index = self + .shared + .index + .swap(self.index | FRESH_BIT, Ordering::AcqRel) + & INDEX_MASK; } +} +pub struct Receiver { + index: usize, + shared: Arc>, +} + +impl Receiver { /// Update the value exposed by `recv`. Returns whether new data was obtained. Consumer only. - pub fn refresh(&self) -> bool { - if self.shared.load(Ordering::Relaxed) & FRESH_BIT == 0 { + pub fn refresh(&mut self) -> bool { + if self.shared.index.load(Ordering::Relaxed) & FRESH_BIT == 0 { return false; } - self.recv - .set(self.shared.swap(self.recv.get(), Ordering::AcqRel) & INDEX_MASK); + self.index = self.shared.index.swap(self.index, Ordering::AcqRel) & INDEX_MASK; true } /// Access the most recent data as of the last `refresh` call. Consumer only. - pub fn received(&self) -> *mut T { - self.slots[self.recv.get()].get() + pub fn received(&mut self) -> &mut T { + unsafe { &mut *self.shared.slots[self.index].get() } } } -impl Default for Swap { - fn default() -> Self { - Self { - slots: Default::default(), - send: Cell::new(0), - shared: AtomicUsize::new(1), - recv: Cell::new(2), - } - } +struct Shared { + slots: [UnsafeCell; 3], + index: AtomicUsize, } +unsafe impl Send for Shared {} +unsafe impl Sync for Shared {} + const FRESH_BIT: usize = 0b100; const INDEX_MASK: usize = 0b011; #[cfg(test)] mod tests { - use super::Swap; + use super::*; #[test] fn smoke() { - let s = Swap::new(|| 0); - unsafe { - *s.pending() = 1; - assert_eq!(*s.received(), 0); - s.flush(); - assert_eq!(*s.received(), 0); - assert!(s.refresh()); - assert_eq!(*s.received(), 1); - assert!(!s.refresh()); - assert_eq!(*s.received(), 1); - *s.pending() = 2; - assert!(!s.refresh()); - assert_eq!(*s.received(), 1); - s.flush(); - assert_eq!(*s.received(), 1); - assert!(s.refresh()); - assert_eq!(*s.received(), 2); - } + let (mut s, mut r) = swap(|| 0); + *s.pending() = 1; + assert_eq!(*r.received(), 0); + s.flush(); + assert_eq!(*r.received(), 0); + assert!(r.refresh()); + assert_eq!(*r.received(), 1); + assert!(!r.refresh()); + assert_eq!(*r.received(), 1); + *s.pending() = 2; + assert!(!r.refresh()); + assert_eq!(*r.received(), 1); + s.flush(); + assert_eq!(*r.received(), 1); + assert!(r.refresh()); + assert_eq!(*r.received(), 2); } } diff --git a/src/tanh.rs b/src/tanh.rs index 0b15a26..16e9f40 100644 --- a/src/tanh.rs +++ b/src/tanh.rs @@ -1,4 +1,4 @@ -use crate::{math::Float, Filter, Frame, Seek, Signal}; +use crate::{math::Float, Frame, Seek, Signal}; /// Smoothly maps a signal of any range into (-1, 1) /// @@ -19,7 +19,7 @@ where { type Frame = T::Frame; - fn sample(&self, interval: f32, out: &mut [T::Frame]) { + fn sample(&mut self, interval: f32, out: &mut [T::Frame]) { self.0.sample(interval, out); for x in out { for channel in x.channels_mut() { @@ -31,18 +31,6 @@ where fn is_finished(&self) -> bool { self.0.is_finished() } - - #[inline] - fn handle_dropped(&self) { - self.0.handle_dropped(); - } -} - -impl Filter for Tanh { - type Inner = T; - fn inner(&self) -> &T { - &self.0 - } } impl Seek for Tanh @@ -50,7 +38,7 @@ where T: Signal + Seek, T::Frame: Frame, { - fn seek(&self, seconds: f32) { + fn seek(&mut self, seconds: f32) { self.0.seek(seconds); } }