Skip to content

Commit

Permalink
Encapsulate triple buffer unsafety
Browse files Browse the repository at this point in the history
  • Loading branch information
Ralith committed Sep 30, 2023
1 parent 73eac23 commit 334ef33
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 136 deletions.
25 changes: 12 additions & 13 deletions src/fader.rs
Original file line number Diff line number Diff line change
@@ -1,27 +1,28 @@
use alloc::sync::Arc;
use core::mem;

use crate::{frame, math::Float, 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<T> {
progress: f32,
next: Arc<Swap<Option<Command<T>>>>,
next: swap::Receiver<Option<Command<T>>>,
inner: T,
}

impl<T> Fader<T> {
impl<T: Send> Fader<T> {
/// Create a fader initially wrapping `inner`
pub fn new(inner: T) -> (FaderControl<T>, Self) {
let (send, recv) = swap::swap(|| None);
let signal = Self {
progress: 1.0,
next: Arc::new(Swap::new(|| None)),
next: recv,
inner,
};
let control = FaderControl(signal.next.clone());
let control = FaderControl(send);
(control, signal)
}
}
Expand All @@ -45,7 +46,7 @@ where
}
}

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);
Expand Down Expand Up @@ -76,19 +77,17 @@ where
}

/// Thread-safe control for a [`Fader`] filter
pub struct FaderControl<T>(Arc<Swap<Option<Command<T>>>>);
pub struct FaderControl<T>(swap::Sender<Option<Command<T>>>);

impl<T> FaderControl<T> {
/// 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()
}
}
Expand Down
1 change: 0 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@ pub use smooth::{Interpolate, Smoothed};
pub use spatial::*;
pub use speed::{Speed, SpeedControl};
pub use stream::{Stream, StreamControl};
pub use swap::Swap;
pub use tanh::Tanh;

/// Unitless instantaneous sound wave amplitude measurement
Expand Down
123 changes: 66 additions & 57 deletions src/spatial.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ use crate::{
math::{add, dot, invert_quat, mix, norm, rotate, scale, sub, Float},
ring::Ring,
set::{set, Set, SetHandle},
swap::Swap,
Sample, Seek, Signal,
swap, Sample, Seek, Signal,
};

type ErasedSpatialBuffered = Box<SpatialSignalBuffered<dyn Signal<Frame = Sample> + Send>>;
Expand All @@ -33,19 +32,23 @@ impl<T> SpatialSignalBuffered<T> {
velocity: mint::Vector3<f32>,
max_delay: f32,
radius: f32,
) -> Self {
) -> (swap::Sender<Motion>, 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: queue,
inner,
}
let (send, recv) = Common::new(radius, position, velocity);
(
send,
Self {
rate,
max_delay,
common: recv,
queue,
inner,
},
)
}
}

Expand All @@ -61,41 +64,53 @@ impl<T> SpatialSignal<T> {
position: mint::Point3<f32>,
velocity: mint::Vector3<f32>,
radius: f32,
) -> Self {
Self {
common: Common::new(radius, position, velocity),
inner,
}
) -> (swap::Sender<Motion>, Self) {
let (send, recv) = Common::new(radius, position, velocity);
(
send,
Self {
common: recv,
inner,
},
)
}
}

struct Common {
radius: f32,
motion: Arc<Swap<Motion>>,
motion: swap::Receiver<Motion>,
state: State,
/// How long ago the signal finished, if it did
finished_for: Option<f32>,
stopped: bool,
}

impl Common {
fn new(radius: f32, position: mint::Point3<f32>, velocity: mint::Vector3<f32>) -> Self {
Self {
radius,
motion: Arc::new(Swap::new(|| Motion {
position,
velocity,
discontinuity: false,
})),
state: State::new(position),
finished_for: None,
stopped: false,
}
fn new(
radius: f32,
position: mint::Point3<f32>,
velocity: mint::Vector3<f32>,
) -> (swap::Sender<Motion>, 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 Spatial(Arc<Swap<Motion>>);
pub struct Spatial(swap::Sender<Motion>);

impl Spatial {
/// Update the position and velocity of the signal
Expand All @@ -115,20 +130,18 @@ impl Spatial {
velocity: mint::Vector3<f32>,
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 {
rot: Arc<Swap<mint::Quaternion<f32>>>,
rot: swap::Receiver<mint::Quaternion<f32>>,
recv_buffered: Set<ErasedSpatialBuffered>,
recv: Set<ErasedSpatial>,
}
Expand All @@ -140,17 +153,17 @@ impl SpatialScene {
pub fn new() -> (SpatialSceneControl, Self) {
let (seek_handle, seek_set) = set();
let (buffered_handle, buffered_set) = set();
let rot = Arc::new(Swap::new(|| mint::Quaternion {
let (rot_send, rot_recv) = swap::swap(|| mint::Quaternion {
s: 1.0,
v: [0.0; 3].into(),
}));
});
let control = SpatialSceneControl {
rot: rot.clone(),
rot: rot_send,
seek: seek_handle,
buffered: buffered_handle,
};
let signal = SpatialScene {
rot,
rot: rot_recv,
recv_buffered: buffered_set,
recv: seek_set,
};
Expand All @@ -177,7 +190,7 @@ fn walk_set<T, U>(

let prev_position;
let next_position;
unsafe {
{
// Compute the signal's smoothed start/end positions over the sampled period
// TODO: Use historical positions
let state = &mut common.state;
Expand Down Expand Up @@ -236,7 +249,7 @@ fn walk_set<T, U>(

/// Control for modifying a [`SpatialSceneSignal`]

Check failure on line 250 in src/spatial.rs

View workflow job for this annotation

GitHub Actions / lint

unresolved link to `SpatialSceneSignal`
pub struct SpatialSceneControl {
rot: Arc<Swap<mint::Quaternion<f32>>>,
rot: swap::Sender<mint::Quaternion<f32>>,
seek: SetHandle<ErasedSpatial>,
buffered: SetHandle<ErasedSpatialBuffered>,
}
Expand All @@ -260,13 +273,10 @@ impl SpatialSceneControl {
where
S: Seek<Frame = Sample> + Send + 'static,
{
let signal = Box::new(SpatialSignal::new(
signal,
options.position,
options.velocity,
options.radius,
));
let handle = Spatial(signal.common.motion.clone());
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
}
Expand All @@ -292,15 +302,16 @@ impl SpatialSceneControl {
where
S: Signal<Frame = Sample> + Send + 'static,
{
let signal = Box::new(SpatialSignalBuffered::new(
let (send, recv) = SpatialSignalBuffered::new(
rate,
signal,
options.position,
options.velocity,
max_distance / SPEED_OF_SOUND + buffer_duration,
options.radius,
));
let handle = Spatial(signal.common.motion.clone());
);
let signal = Box::new(recv);
let handle = Spatial(send);
self.buffered.insert(signal);
handle
}
Expand All @@ -310,9 +321,7 @@ impl SpatialSceneControl {
/// An unrotated listener faces -Z, with +X to the right and +Y up.
pub fn set_listener_rotation(&mut self, rotation: mint::Quaternion<f32>) {
let signal_rotation = invert_quat(&rotation);
unsafe {
*self.rot.pending() = signal_rotation;
}
*self.rot.pending() = signal_rotation;
self.rot.flush();
}
}
Expand Down Expand Up @@ -347,7 +356,7 @@ impl Signal for SpatialScene {
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())
Expand Down
Loading

0 comments on commit 334ef33

Please sign in to comment.