From 73eac232b18e6510cde1d4c8eef240a23e315ef8 Mon Sep 17 00:00:00 2001 From: Benjamin Saunders Date: Sat, 30 Sep 2023 16:13:44 -0700 Subject: [PATCH] Remove superfluous interior mutability --- src/adapt.rs | 11 +++----- src/cycle.rs | 14 +++++------ src/fader.rs | 34 +++++++++++-------------- src/frames.rs | 16 ++++++------ src/gain.rs | 24 ++++++++---------- src/mixer.rs | 13 ++++------ src/ring.rs | 12 ++++----- src/signal.rs | 10 +++----- src/sine.rs | 13 +++++----- src/spatial.rs | 68 ++++++++++++++++++++++---------------------------- src/stream.rs | 34 +++++++++++-------------- src/swap.rs | 3 +++ 12 files changed, 109 insertions(+), 143 deletions(-) diff --git a/src/adapt.rs b/src/adapt.rs index 5bcf8f2..9be24f2 100644 --- a/src/adapt.rs +++ b/src/adapt.rs @@ -1,5 +1,3 @@ -use core::cell::Cell; - 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, 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, } } @@ -73,9 +71,8 @@ where 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 { diff --git a/src/cycle.rs b/src/cycle.rs index 42ae3a1..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, } } @@ -26,8 +25,8 @@ impl Signal for Cycle { 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(&mut self, seconds: f32) { - let s = (self.cursor.get() + f64::from(seconds) * self.frames.rate() as f64) + self.cursor = (self.cursor + f64::from(seconds) * self.frames.rate() as f64) .rem_euclid(self.frames.len() as f64); - self.cursor.set(s); } } diff --git a/src/fader.rs b/src/fader.rs index a47f435..0cf1dc5 100644 --- a/src/fader.rs +++ b/src/fader.rs @@ -1,8 +1,5 @@ use alloc::sync::Arc; -use core::{ - cell::{Cell, UnsafeCell}, - mem, -}; +use core::mem; use crate::{frame, math::Float, Frame, Signal, Swap}; @@ -11,18 +8,18 @@ use crate::{frame, math::Float, Frame, Signal, Swap}; /// Uses constant-power fading, suitable for blending uncorrelated signals without distorting /// perceived loudness pub struct Fader { - progress: Cell, - inner: UnsafeCell, + progress: f32, next: Arc>>>, + inner: T, } impl Fader { /// Create a fader initially wrapping `inner` pub fn new(inner: T) -> (FaderControl, Self) { let signal = Self { - progress: Cell::new(1.0), - inner: UnsafeCell::new(inner), + progress: 1.0, next: Arc::new(Swap::new(|| None)), + inner, }; let control = FaderControl(signal.next.clone()); (control, signal) @@ -37,15 +34,13 @@ where #[allow(clippy::float_cmp)] fn sample(&mut self, interval: f32, mut out: &mut [T::Frame]) { - let inner = unsafe { &mut *self.inner.get() }; - - if self.progress.get() >= 1.0 { + 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; } } @@ -55,23 +50,22 @@ where 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); } } diff --git a/src/frames.rs b/src/frames.rs index ac1c2d8..0c7e46e 100644 --- a/src/frames.rs +++ b/src/frames.rs @@ -1,6 +1,5 @@ use crate::alloc::{alloc, boxed::Box, sync::Arc}; use core::{ - cell::Cell, mem, ops::{Deref, DerefMut}, ptr, @@ -142,7 +141,7 @@ 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. @@ -155,7 +154,7 @@ impl FramesSignal { /// `start_seconds` adjusts the initial playback position, and may be negative. pub fn new(data: Arc>, start_seconds: f64) -> (FramesSignalControl, Self) { let signal = Self { - t: Cell::new(start_seconds), + t: start_seconds, sample_t: Arc::new(AtomicIsize::new((start_seconds * data.rate) as isize)), data, }; @@ -169,7 +168,7 @@ impl Signal for FramesSignal { #[inline] fn sample(&mut self, interval: f32, out: &mut [T]) { - let s0 = self.t.get() * self.data.rate; + 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 { @@ -190,22 +189,21 @@ 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(&mut self, seconds: f32) { - self.t.set(self.t.get() + f64::from(seconds)); + self.t += f64::from(seconds); } } diff --git a/src/gain.rs b/src/gain.rs index 2d11abe..6466aca 100644 --- a/src/gain.rs +++ b/src/gain.rs @@ -1,8 +1,5 @@ use alloc::sync::Arc; -use core::{ - cell::RefCell, - sync::atomic::{AtomicU32, Ordering}, -}; +use core::sync::atomic::{AtomicU32, Ordering}; use crate::{frame, math::Float, Frame, Seek, Signal, Smoothed}; @@ -60,7 +57,7 @@ where /// mapping the maximum volume to 0 decibels, and the minimum to e.g. -60. pub struct Gain { shared: Arc, - gain: RefCell>, + gain: Smoothed, inner: T, } @@ -69,7 +66,7 @@ impl Gain { pub fn new(signal: T) -> (GainControl, Self) { let signal = Gain { shared: Arc::new(AtomicU32::new(1.0f32.to_bits())), - gain: RefCell::new(Smoothed::new(1.0)), + gain: Smoothed::new(1.0), inner: signal, }; let handle = GainControl(signal.shared.clone()); @@ -92,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); } } @@ -106,12 +103,11 @@ where 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); @@ -120,8 +116,8 @@ 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); } } diff --git a/src/mixer.rs b/src/mixer.rs index 4500dc4..3f30e44 100644 --- a/src/mixer.rs +++ b/src/mixer.rs @@ -1,8 +1,5 @@ use alloc::{boxed::Box, sync::Arc, vec}; -use core::{ - cell::RefCell, - sync::atomic::{AtomicBool, Ordering}, -}; +use core::sync::atomic::{AtomicBool, Ordering}; use crate::{frame, set, Frame, Set, SetHandle, Signal}; @@ -55,7 +52,7 @@ impl MixedSignal { /// A [`Signal`] that mixes a dynamic set of [`Signal`]s pub struct Mixer { - recv: RefCell>, + recv: Inner, } impl Mixer @@ -68,10 +65,10 @@ where ( MixerControl(handle), Self { - recv: RefCell::new(Inner { + recv: Inner { set, buffer: vec![T::ZERO; 1024].into(), - }), + }, }, ) } @@ -86,7 +83,7 @@ impl Signal for Mixer { type Frame = T; fn sample(&mut self, interval: f32, out: &mut [T]) { - let this = &mut *self.recv.borrow_mut(); + let this = &mut self.recv; this.set.update(); for o in out.iter_mut() { diff --git a/src/ring.rs b/src/ring.rs index 4fadd76..36c4b24 100644 --- a/src/ring.rs +++ b/src/ring.rs @@ -81,19 +81,17 @@ 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(&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; } } } @@ -107,7 +105,7 @@ mod tests { #[test] fn fill() { let mut r = Ring::new(4); - let mut s = TimeSignal(Cell::new(1.0)); + let mut s = TimeSignal(1.0); r.write(&mut s, 1, 1.0); assert_eq!(r.write, 1.0); @@ -124,7 +122,7 @@ mod tests { #[test] fn wrap() { let mut r = Ring::new(4); - let mut s = TimeSignal(Cell::new(1.0)); + let mut s = TimeSignal(1.0); r.write(&mut s, 1, 3.0); assert_eq!(r.buffer[..], [1.0, 2.0, 3.0, 0.0]); diff --git a/src/signal.rs b/src/signal.rs index 86b4976..697980c 100644 --- a/src/signal.rs +++ b/src/signal.rs @@ -92,26 +92,24 @@ impl> Seek for MonoToStereo { #[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(&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 mut 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 6128896..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,15 +17,14 @@ 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; } } @@ -35,7 +34,7 @@ impl Signal for Sine { 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); } diff --git a/src/spatial.rs b/src/spatial.rs index 060cc86..d361c8d 100644 --- a/src/spatial.rs +++ b/src/spatial.rs @@ -1,8 +1,5 @@ use alloc::{boxed::Box, sync::Arc}; -use core::{ - cell::{Cell, RefCell}, - ops::{Index, IndexMut}, -}; +use core::ops::{Index, IndexMut}; use crate::{ math::{add, dot, invert_quat, mix, norm, rotate, scale, sub, Float}, @@ -24,7 +21,7 @@ struct SpatialSignalBuffered { /// /// Accounts only for the source's velocity. Listener velocity and attenuation are handled at /// output time. - queue: RefCell, + queue: Ring, inner: T, } @@ -46,7 +43,7 @@ impl SpatialSignalBuffered { rate, max_delay, common: Common::new(radius, position, velocity), - queue: RefCell::new(queue), + queue: queue, inner, } } @@ -75,10 +72,10 @@ impl SpatialSignal { struct Common { radius: f32, motion: Arc>, - state: RefCell, + state: State, /// How long ago the signal finished, if it did - finished_for: Cell>, - stopped: Cell, + finished_for: Option, + stopped: bool, } impl Common { @@ -90,9 +87,9 @@ impl Common { velocity, discontinuity: false, })), - state: RefCell::new(State::new(position)), - finished_for: Cell::new(None), - stopped: Cell::new(false), + state: State::new(position), + finished_for: None, + stopped: false, } } } @@ -132,8 +129,8 @@ impl Spatial { /// [`Signal`] for stereo output from a spatial scene pub struct SpatialScene { rot: Arc>>, - recv_buffered: RefCell>, - recv: RefCell>, + recv_buffered: Set, + recv: Set, } impl SpatialScene { @@ -154,18 +151,16 @@ impl SpatialScene { }; let signal = SpatialScene { rot, - recv_buffered: RefCell::new(buffered_set), - recv: RefCell::new(seek_set), + recv_buffered: buffered_set, + recv: seek_set, }; (control, signal) } } -unsafe impl Send for SpatialScene {} - fn walk_set( set: &mut Set>, - get_common: impl Fn(&T) -> &Common, + get_common: impl Fn(&mut T) -> &mut Common, get_inner: impl Fn(&T) -> &U, prev_rot: &mint::Quaternion, rot: &mint::Quaternion, @@ -185,7 +180,7 @@ fn walk_set( 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(); @@ -216,21 +211,21 @@ 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 { - common.stopped.set(true); + common.stopped = true; } else { - common.finished_for.set(Some(t + elapsed)); + common.finished_for = Some(t + elapsed); } } None => { if get_inner(signal).is_finished() { - common.finished_for.set(Some(elapsed)); + get_common(signal).finished_for = Some(elapsed); } } } - if common.stopped.get() { + if get_common(signal).stopped { set.remove(i); continue; } @@ -347,7 +342,7 @@ impl Signal for SpatialScene { type Frame = [Sample; 2]; fn sample(&mut self, interval: f32, out: &mut [[Sample; 2]]) { - let set = &mut *self.recv_buffered.borrow_mut(); + let set = &mut self.recv_buffered; // Update set contents set.update(); @@ -367,7 +362,7 @@ 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, @@ -376,10 +371,7 @@ impl Signal for SpatialScene { debug_assert!(signal.max_delay >= elapsed); // Extend delay queue with new data - signal - .queue - .borrow_mut() - .write(&mut signal.inner, signal.rate, elapsed); + signal.queue.write(&mut signal.inner, signal.rate, elapsed); // Mix into output for &ear in &[Ear::Left, Ear::Right] { @@ -394,7 +386,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()]); @@ -408,12 +400,12 @@ 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, @@ -615,25 +607,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/stream.rs b/src/stream.rs index acf7f85..d699176 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -1,15 +1,13 @@ //! Streaming audio support -use core::cell::{Cell, RefCell}; - use crate::{frame, math::Float, spsc, Frame, Signal}; /// Dynamic audio from an external source pub struct Stream { rate: u32, - inner: RefCell>, + inner: spsc::Receiver, /// Offset of t=0 from the start of the buffer, in frames - t: Cell, + t: f32, /// Whether `inner` will receive no further updates stopping: bool, } @@ -27,8 +25,8 @@ impl Stream { let (send, recv) = spsc::channel(size); let signal = Self { rate, - inner: RefCell::new(recv), - t: Cell::new(0.0), + inner: recv, + t: 0.0, stopping: false, }; let control = StreamControl(send); @@ -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,12 +60,11 @@ 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(); } } @@ -76,11 +72,11 @@ impl Signal for Stream { type Frame = T; fn sample(&mut self, interval: f32, out: &mut [T]) { - self.inner.borrow_mut().update(); - if self.inner.borrow().is_closed() { + self.inner.update(); + if self.inner.is_closed() { self.stopping = true; } - let s0 = self.t.get(); + let s0 = self.t; let ds = interval * self.rate as f32; for (i, o) in out.iter_mut().enumerate() { @@ -91,7 +87,7 @@ impl Signal for Stream { #[allow(clippy::float_cmp)] fn is_finished(&self) -> bool { - self.stopping && self.t.get() == self.inner.borrow().len() as f32 + self.stopping && self.t == self.inner.len() as f32 } } diff --git a/src/swap.rs b/src/swap.rs index e9f6991..206b5cf 100644 --- a/src/swap.rs +++ b/src/swap.rs @@ -69,6 +69,9 @@ impl Default for Swap { } } +unsafe impl Send for Swap {} +unsafe impl Sync for Swap {} + const FRESH_BIT: usize = 0b100; const INDEX_MASK: usize = 0b011;