diff --git a/crates/synthizer/src/array_utils.rs b/crates/synthizer/src/array_utils.rs new file mode 100644 index 0000000..206f3b3 --- /dev/null +++ b/crates/synthizer/src/array_utils.rs @@ -0,0 +1,8 @@ +/// Return an array `[0, 1, ...]`. +pub(crate) fn increasing_usize() -> [usize; N] { + let mut ret = [0; N]; + for i in 0..N { + ret[i] = i; + } + ret +} diff --git a/crates/synthizer/src/chain.rs b/crates/synthizer/src/chain.rs index 347fbdf..556360b 100644 --- a/crates/synthizer/src/chain.rs +++ b/crates/synthizer/src/chain.rs @@ -44,7 +44,7 @@ where pub fn read_slot( slot: &sigs::Slot, initial_value: T, -) -> Chain>> +) -> Chain Signal = (), Output<'a> = T>>> where T: Clone + Send + Sync + 'static, { @@ -59,7 +59,7 @@ where pub fn read_slot_and_changed( slot: &sigs::Slot, initial_value: T, -) -> Chain>> +) -> Chain Signal = (), Output<'a> = (T, bool)>>> where T: Send + Sync + Clone + 'static, { @@ -79,9 +79,13 @@ impl Chain { /// Send this chain to the audio device. pub fn to_audio_device( self, - ) -> Chain, Output = ()>>> + ) -> Chain< + impl IntoSignal< + Signal = impl for<'a> Signal = IntoSignalInput<'a, S>, Output<'a> = ()>, + >, + > where - S::Signal: Signal, + S::Signal: for<'a> Signal = f64>, { Chain { inner: sigs::AudioOutputSignalConfig::new(self.inner), @@ -102,16 +106,17 @@ impl Chain { self, ) -> Chain< impl IntoSignal< - Signal = impl Signal< - Input = NewInputType, - Output = IntoSignalOutput, + Signal = impl for<'a> Signal< + Input<'a> = NewInputType, + Output<'a> = IntoSignalOutput<'a, S>, State = IntoSignalState, Parameters = IntoSignalParameters, >, >, > where - IntoSignalInput: Default, + for<'a> IntoSignalInput<'a, S>: Default, + S::Signal: 'static, { Chain { inner: sigs::ConsumeInputSignalConfig::<_, NewInputType>::new(self.inner), @@ -123,10 +128,15 @@ impl Chain { /// This is mostly used to convert a frequency (HZ) to an increment per sample, e.g. when building sine waves. pub fn divide_by_sr( self, - ) -> Chain, Output = f64>>> + ) -> Chain< + impl IntoSignal< + Signal = impl for<'a> Signal = IntoSignalInput<'a, S>, Output<'a> = f64>, + >, + > where - S::Signal: Signal, - IntoSignalInput: Default, + for<'a> S::Signal: Signal = f64>, + for<'a> IntoSignalInput<'a, S>: Default + Clone, + for<'a> IntoSignalOutput<'a, S>: Clone, { let converted = self.output_into::(); let sr = Chain::new(config::SR as f64).discard_and_default::>(); @@ -139,16 +149,17 @@ impl Chain { self, ) -> Chain< impl IntoSignal< - Signal = impl Signal< - Input = IntoSignalInput, - Output = T, + Signal = impl for<'a> Signal< + Input<'a> = IntoSignalInput<'a, S>, + Output<'a> = T, State = IntoSignalState, Parameters = IntoSignalParameters, >, >, > where - T: From>, + for<'a> T: From>, + for<'a> IntoSignalOutput<'a, S>: Clone, { Chain { inner: sigs::ConvertOutputConfig::::new(self.inner), @@ -162,7 +173,7 @@ impl Chain { pub fn periodic_sum(self, period: f64, initial_value: f64) -> Chain> where S: IntoSignal, - S::Signal: Signal, + for<'a> S::Signal: Signal = f64>, { Chain { inner: sigs::PeriodicF64Config { @@ -177,7 +188,7 @@ impl Chain { pub fn sin(self) -> Chain> where S: IntoSignal, - S::Signal: Signal, + for<'a> S::Signal: Signal = f64>, { Chain { inner: sigs::SinSignalConfig { diff --git a/crates/synthizer/src/chain_mathops.rs b/crates/synthizer/src/chain_mathops.rs index ea70619..4f95a5d 100644 --- a/crates/synthizer/src/chain_mathops.rs +++ b/crates/synthizer/src/chain_mathops.rs @@ -1,6 +1,6 @@ //! Implements the mathematical operations between `IntoSignal`s. use std::any::Any; -use std::mem::MaybeUninit; + use std::ops::*; use std::sync::Arc; @@ -18,8 +18,8 @@ macro_rules! impl_mathop { where A: IntoSignal, B: IntoSignal, - A::Signal: Signal>, - IntoSignalOutput: $trait>, + A::Signal: for<'ol> Signal = IntoSignalOutput<'ol, B>>, + for<'ol> IntoSignalOutput<'ol, A>: $trait> + Copy, { type Output = Chain<$signal_config>; @@ -33,58 +33,42 @@ macro_rules! impl_mathop { unsafe impl Signal for $signal_name where S1: Signal, - S2: Signal>, - SignalOutput: $trait>, + S2: for<'il> Signal = SignalInput<'il, S1>>, + for<'ol> SignalOutput<'ol, S1>: $trait> + Copy, + S1: 'static, + S2: 'static, { - type Input = SignalInput; - type Output = as $trait>>::Output; + type Input<'il> = SignalInput<'il, S1>; + type Output<'ol> = as $trait>>::Output; type Parameters = (SignalParameters, SignalParameters); type State = (SignalState, SignalState); fn on_block_start( - ctx: &mut SignalExecutionContext<'_, '_, Self::State, Self::Parameters>, + ctx: &SignalExecutionContext<'_, '_>, + params: &Self::Parameters, + state: &mut Self::State, ) { - S1::on_block_start(&mut ctx.wrap(|s| &mut s.0, |p| &p.0)); - S2::on_block_start(&mut ctx.wrap(|s| &mut s.1, |p| &p.1)); + S1::on_block_start(ctx, ¶ms.0, &mut state.0); + S2::on_block_start(&ctx, ¶ms.1, &mut state.1); } - fn tick< - 'a, - I: FnMut(usize) -> &'a Self::Input, - D: SignalDestination, - const N: usize, - >( - ctx: &'_ mut SignalExecutionContext<'_, '_, Self::State, Self::Parameters>, - mut input: I, + fn tick<'il, 'ol, D, const N: usize>( + ctx: &'_ SignalExecutionContext<'_, '_>, + input: [Self::Input<'il>; N], + params: &Self::Parameters, + state: &mut Self::State, mut destination: D, ) where - Self::Input: 'a, + Self::Input<'il>: 'ol, + 'il: 'ol, + D: SignalDestination, N>, { - // When we perform the binary operation, left and right fold into each other and the drop is handled - // because either the operation dropped the values itself or the final value holds them. Dropping these - // would thus be a double drop. - let mut left: [MaybeUninit>; N] = - [const { MaybeUninit::uninit() }; N]; - let mut right: [MaybeUninit>; N] = - [const { MaybeUninit::uninit() }; N]; - let mut i = 0usize; - - S1::tick::<_, _, N>(&mut ctx.wrap(|s| &mut s.0, |p| &p.0), &mut input, |val| { - left[i].write(val); - i += 1; - }); - - i = 0; - - S2::tick::<_, _, N>(&mut ctx.wrap(|s| &mut s.1, |p| &p.1), &mut input, |val| { - right[i].write(val); - i += 1; - }); - - left.into_iter().zip(right.into_iter()).for_each(|(l, r)| { - let l = unsafe { l.assume_init() }; - let r = unsafe { r.assume_init() }; - destination.send(l.$method(r)); + S1::tick::<_, N>(ctx, input.clone(), ¶ms.0, &mut state.0, |left| { + S2::tick(ctx, input, ¶ms.1, &mut state.1, |right| { + let outgoing = crate::array_utils::increasing_usize::() + .map(|i| left[i].$method(right[i])); + destination.send(outgoing); + }); }); } diff --git a/crates/synthizer/src/context.rs b/crates/synthizer/src/context.rs index 137a75e..ed1854d 100644 --- a/crates/synthizer/src/context.rs +++ b/crates/synthizer/src/context.rs @@ -1,34 +1,15 @@ +use atomic_refcell::AtomicRefCell; + use crate::config; use crate::signals::SlotUpdateContext; -pub struct SignalExecutionContext<'a, 'shared, TState, TParameters> { - pub(crate) state: &'a mut TState, - pub(crate) parameters: &'a TParameters, - pub(crate) fixed: &'a mut FixedSignalExecutionContext<'shared>, +pub struct SignalExecutionContext<'a, 'shared> { + pub(crate) fixed: &'a FixedSignalExecutionContext<'shared>, } /// Parts of the execution context which do not contain references that need to be recast. pub(crate) struct FixedSignalExecutionContext<'a> { pub(crate) time_in_blocks: u64, - pub(crate) audio_destinationh: &'a mut [f64; config::BLOCK_SIZE], + pub(crate) audio_destinationh: AtomicRefCell<&'a mut [f64; config::BLOCK_SIZE]>, pub(crate) slots: &'a SlotUpdateContext<'a>, } - -impl<'shared, TState, TParameters> SignalExecutionContext<'_, 'shared, TState, TParameters> { - /// Convert this context into values usually derived from reborrows of this context's fields. Used to grab parts of - /// contexts when moving upstream. - pub(crate) fn wrap<'a, NewS, NewP>( - &'a mut self, - new_state: impl FnOnce(&'a mut TState) -> &'a mut NewS, - new_params: impl FnOnce(&'a TParameters) -> &'a NewP, - ) -> SignalExecutionContext<'a, 'shared, NewS, NewP> - where - 'shared: 'a, - { - SignalExecutionContext { - state: new_state(self.state), - parameters: new_params(self.parameters), - fixed: self.fixed, - } - } -} diff --git a/crates/synthizer/src/core_traits.rs b/crates/synthizer/src/core_traits.rs index 8391a50..d03757e 100644 --- a/crates/synthizer/src/core_traits.rs +++ b/crates/synthizer/src/core_traits.rs @@ -16,8 +16,8 @@ pub(crate) mod sealed { /// lets us get performance out, especially in debug builds where things like immediate unwrapping of options will /// not be optimized away. pub unsafe trait Signal: Sized + Send + Sync { - type Input: Sized; - type Output: Sized; + type Input<'il>: Sized; + type Output<'ol>: Sized; type State: Sized + Send + Sync; type Parameters: Sized + Send + Sync; @@ -33,17 +33,16 @@ pub(crate) mod sealed { /// /// Signals may choose to do work in either of those points instead, so they must be used to drive dependent /// signals. - fn tick< - 'a, - I: FnMut(usize) -> &'a Self::Input, - D: SignalDestination, - const N: usize, - >( - ctx: &'_ mut SignalExecutionContext<'_, '_, Self::State, Self::Parameters>, - input: I, + fn tick<'il, 'ol, D, const N: usize>( + ctx: &'_ SignalExecutionContext<'_, '_>, + input: [Self::Input<'il>; N], + params: &Self::Parameters, + state: &mut Self::State, destination: D, ) where - Self::Input: 'a; + D: SignalDestination, N>, + Self::Input<'il>: 'ol, + 'il: 'ol; /// Called when a signal is starting a new block. /// @@ -52,7 +51,11 @@ pub(crate) mod sealed { /// is used for many things, among them gathering references to buses or resetting block-based counters. /// /// No default impl is provided. All signals need to consider what they want to do so we forc3e the issue. - fn on_block_start(ctx: &mut SignalExecutionContext<'_, '_, Self::State, Self::Parameters>); + fn on_block_start( + ctx: &SignalExecutionContext<'_, '_>, + params: &Self::Parameters, + state: &mut Self::State, + ); /// Trace slots. /// @@ -75,8 +78,8 @@ pub(crate) mod sealed { ); } - pub trait SignalDestination { - fn send(&mut self, value: Input); + pub trait SignalDestination { + fn send(self, values: [Input; N]); } /// A frame of audio data, which can be stored on the stack. @@ -137,24 +140,24 @@ pub(crate) mod sealed { pub(crate) use sealed::*; -impl SignalDestination for F +impl SignalDestination for F where Input: Sized, - F: FnMut(Input), + F: FnOnce([Input; N]), { - fn send(&mut self, value: Input) { - (*self)(value) + fn send(self, value: [Input; N]) { + self(value) } } -pub trait Generator: Signal {} -impl Generator for T where T: Signal {} +pub trait Generator: for<'a> Signal = ()> {} +impl Generator for T where T: for<'a> Signal = ()> {} /// A mountable signal has no inputs and no outputs, and its state and parameters are 'static. pub trait Mountable where Self: Generator + Send + Sync + 'static, - Self: Signal + Generator, + Self: for<'a> Signal = ()> + Generator, SignalState: Send + Sync + 'static, SignalParameters: Send + Sync + 'static, { @@ -162,7 +165,7 @@ where impl Mountable for T where - T: Generator + Signal + Send + Sync + 'static, + T: Generator + for<'a> Signal = ()> + Send + Sync + 'static, SignalState: Send + Sync + 'static, SignalParameters: Send + Sync + 'static, { @@ -170,11 +173,11 @@ where // Workarounds for https://github.com/rust-lang/rust/issues/38078: rustc is not always able to determine when a type // isn't ambiguous, or at the very least it doesn't tell us what the options are, so we use this instead. -pub(crate) type IntoSignalOutput = <::Signal as Signal>::Output; -pub(crate) type IntoSignalInput = <::Signal as Signal>::Input; +pub(crate) type IntoSignalOutput<'a, S> = <::Signal as Signal>::Output<'a>; +pub(crate) type IntoSignalInput<'a, S> = <::Signal as Signal>::Input<'a>; pub(crate) type IntoSignalParameters = <::Signal as Signal>::Parameters; pub(crate) type IntoSignalState = <::Signal as Signal>::State; -pub(crate) type SignalInput = ::Input; -pub(crate) type SignalOutput = ::Output; +pub(crate) type SignalInput<'a, T> = ::Input<'a>; +pub(crate) type SignalOutput<'a, T> = ::Output<'a>; pub(crate) type SignalState = ::State; pub(crate) type SignalParameters = ::Parameters; diff --git a/crates/synthizer/src/lib.rs b/crates/synthizer/src/lib.rs index 53572c5..7e88796 100644 --- a/crates/synthizer/src/lib.rs +++ b/crates/synthizer/src/lib.rs @@ -7,6 +7,7 @@ mod variant; #[macro_use] mod logging; +mod array_utils; mod audio_frames; mod background_drop; pub mod biquad; diff --git a/crates/synthizer/src/mount_point.rs b/crates/synthizer/src/mount_point.rs index dec92ee..e72994c 100644 --- a/crates/synthizer/src/mount_point.rs +++ b/crates/synthizer/src/mount_point.rs @@ -22,7 +22,7 @@ pub(crate) trait ErasedMountPoint: Send + Sync + 'static { &mut self, state: &Arc, mount_id: &UniqueId, - shared_ctx: &mut FixedSignalExecutionContext, + shared_ctx: &FixedSignalExecutionContext, ); } @@ -31,21 +31,25 @@ impl ErasedMountPoint for MountPoint { &mut self, state: &Arc, mount_id: &UniqueId, - shared_ctx: &mut FixedSignalExecutionContext, + shared_ctx: &FixedSignalExecutionContext, ) { - let mut ctx = SignalExecutionContext { - fixed: shared_ctx, - state: &mut self.state, - parameters: state - .mounts - .get(mount_id) - .expect("We are in a mount that should be in this map") - .parameters - .downcast_ref::() - .expect("These are parameters for this mount"), - }; + let sig_state = &mut self.state; + let parameters = state + .mounts + .get(mount_id) + .expect("We are in a mount that should be in this map") + .parameters + .downcast_ref::() + .expect("These are parameters for this mount"); + let mut ctx = SignalExecutionContext { fixed: shared_ctx }; - S::on_block_start(&mut ctx); - S::tick::<_, _, { config::BLOCK_SIZE }>(&mut ctx, |_| &(), |_| {}); + S::on_block_start(&mut ctx, ¶meters, &mut *sig_state); + S::tick::<_, { config::BLOCK_SIZE }>( + &ctx, + [(); config::BLOCK_SIZE], + ¶meters, + &mut *sig_state, + |_: [(); config::BLOCK_SIZE]| {}, + ); } } diff --git a/crates/synthizer/src/signals/and_then.rs b/crates/synthizer/src/signals/and_then.rs index 345ae80..99232f4 100644 --- a/crates/synthizer/src/signals/and_then.rs +++ b/crates/synthizer/src/signals/and_then.rs @@ -1,5 +1,3 @@ -use std::mem::MaybeUninit; - use crate::context::*; use crate::core_traits::*; @@ -14,46 +12,38 @@ pub struct AndThen(S1, S2); unsafe impl Signal for AndThen where S1: Signal, - S2: Signal, + for<'a> S2: Signal = S1::Output<'a>>, + S1: 'static, + S2: 'static, { - type Input = S1::Input; - type Output = S2::Output; + type Input<'il> = S1::Input<'il>; + type Output<'ol> = S2::Output<'ol>; type State = (S1::State, S2::State); type Parameters = (S1::Parameters, S2::Parameters); - fn on_block_start(ctx: &mut SignalExecutionContext<'_, '_, Self::State, Self::Parameters>) { - S1::on_block_start(&mut ctx.wrap(|s| &mut s.0, |p| &p.0)); - S2::on_block_start(&mut ctx.wrap(|s| &mut s.1, |p| &p.1)); + fn on_block_start( + ctx: &SignalExecutionContext<'_, '_>, + params: &Self::Parameters, + state: &mut Self::State, + ) { + S1::on_block_start(ctx, ¶ms.0, &mut state.0); + S2::on_block_start(ctx, ¶ms.1, &mut state.1); } - fn tick< - 'a, - I: FnMut(usize) -> &'a Self::Input, - D: SignalDestination, - const N: usize, - >( - ctx: &'_ mut SignalExecutionContext<'_, '_, Self::State, Self::Parameters>, - input: I, + fn tick<'il, 'ol, D, const N: usize>( + ctx: &'_ SignalExecutionContext<'_, '_>, + input: [Self::Input<'il>; N], + params: &Self::Parameters, + state: &mut Self::State, destination: D, ) where - Self::Input: 'a, + Self::Input<'il>: 'ol, + 'il: 'ol, + D: SignalDestination, N>, { - let mut left_out: [MaybeUninit>; N] = [const { MaybeUninit::uninit() }; N]; - let mut i = 0; - S1::tick::<_, _, N>(&mut ctx.wrap(|s| &mut s.0, |p| &p.0), input, |val| { - left_out[i].write(val); - i += 1; + S1::tick::<_, N>(ctx, input, ¶ms.0, &mut state.0, |left_out| { + S2::tick::<_, N>(ctx, left_out, ¶ms.1, &mut state.1, destination); }); - - S2::tick::<_, _, N>( - &mut ctx.wrap(|s| &mut s.1, |p| &p.1), - |ind| unsafe { left_out[ind].assume_init_ref() }, - destination, - ); - - unsafe { - crate::unsafe_utils::drop_initialized_array(left_out); - } } fn trace_slots< @@ -78,9 +68,11 @@ pub struct AndThenConfig { impl IntoSignal for AndThenConfig where - S1: IntoSignal, - S2: IntoSignal, - S1::Signal: Signal::Input>, + S1: IntoSignal + 'static, + S2: IntoSignal + 'static, + for<'a> S1::Signal: Signal = ::Input<'a>>, + S1::Signal: 'static, + S2::Signal: 'static, { type Signal = AndThen; diff --git a/crates/synthizer/src/signals/audio_io.rs b/crates/synthizer/src/signals/audio_io.rs index dc7ade2..a454eab 100644 --- a/crates/synthizer/src/signals/audio_io.rs +++ b/crates/synthizer/src/signals/audio_io.rs @@ -1,4 +1,3 @@ -use crate::config; use crate::context::*; use crate::core_traits::*; @@ -7,51 +6,46 @@ pub struct AudioOutputSignalConfig(S); unsafe impl Signal for AudioOutputSignal where - S: Signal, + for<'a> S: Signal = f64>, { - type Output = (); - type Input = S::Input; + type Output<'ol> = (); + type Input<'il> = S::Input<'il>; // The parent state, with a usize tacked on for tick1's counter. type State = (S::State, usize); type Parameters = S::Parameters; - fn on_block_start(ctx: &mut SignalExecutionContext<'_, '_, Self::State, Self::Parameters>) { - S::on_block_start(&mut ctx.wrap(|s| &mut s.0, |p| p)); - - // If the caller decides to use tick1, this index will be incremented. Reset it. - ctx.state.1 = 0; + fn on_block_start( + ctx: &SignalExecutionContext<'_, '_>, + params: &Self::Parameters, + state: &mut Self::State, + ) { + S::on_block_start(ctx, params, &mut state.0); + state.1 = 0; } - fn tick< - 'a, - I: FnMut(usize) -> &'a Self::Input, - D: SignalDestination, - const N: usize, - >( - ctx: &'_ mut SignalExecutionContext<'_, '_, Self::State, Self::Parameters>, - input: I, - mut destination: D, + fn tick<'il, 'ol, D, const N: usize>( + ctx: &'_ SignalExecutionContext<'_, '_>, + input: [Self::Input<'il>; N], + params: &Self::Parameters, + state: &mut Self::State, + destination: D, ) where - Self::Input: 'a, + Self::Input<'il>: 'ol, + 'il: 'ol, + D: SignalDestination, N>, { - let mut block: [f64; config::BLOCK_SIZE] = [0.0f64; config::BLOCK_SIZE]; - - let mut i = 0; - S::tick::<_, _, N>(&mut ctx.wrap(|s| &mut s.0, |p| p), input, |x| { - block[i] = x; - i += 1; + let mut block = None; + S::tick::<_, N>(ctx, input, params, &mut state.0, |x: [f64; N]| { + block = Some(x); }); - - // This is a good place for an assert because it is a final output; if any parent signal did not call the - // destination exactly once per sample, we'll notice. - debug_assert_eq!(i, config::BLOCK_SIZE); - - ctx.fixed.audio_destinationh.copy_from_slice(&block); - - // We do have to actually use the destination, as this drives computations elsewhere. - for _ in 0..config::BLOCK_SIZE { - destination.send(()); + let block = block.unwrap(); + { + let mut dest = ctx.fixed.audio_destinationh.borrow_mut(); + dest[state.1..(state.1 + N)].copy_from_slice(&block); + state.1 += N; } + + destination.send([(); N]); } fn trace_slots< @@ -71,7 +65,7 @@ where impl IntoSignal for AudioOutputSignalConfig where S: IntoSignal, - S::Signal: Signal, + for<'ol> S::Signal: Signal = f64>, { type Signal = AudioOutputSignal; diff --git a/crates/synthizer/src/signals/consume_input.rs b/crates/synthizer/src/signals/consume_input.rs index 0fb0d2a..eccd3fb 100644 --- a/crates/synthizer/src/signals/consume_input.rs +++ b/crates/synthizer/src/signals/consume_input.rs @@ -24,38 +24,35 @@ unsafe impl Sync for ConsumeInputSignal where S: Sync {} unsafe impl Signal for ConsumeInputSignal where - S: Signal, - S::Input: Default, + S: Signal + 'static, + for<'a> S::Input<'a>: Default, { - type Input = I; - type Output = S::Output; + type Input<'il> = I; + type Output<'ol> = S::Output<'ol>; type State = S::State; type Parameters = S::Parameters; - fn on_block_start(ctx: &mut SignalExecutionContext<'_, '_, Self::State, Self::Parameters>) { - S::on_block_start(ctx); + fn on_block_start( + ctx: &SignalExecutionContext<'_, '_>, + params: &Self::Parameters, + state: &mut Self::State, + ) { + S::on_block_start(ctx, params, state); } - fn tick< - 'a, - SigI: FnMut(usize) -> &'a Self::Input, - D: SignalDestination, - const N: usize, - >( - ctx: &'_ mut SignalExecutionContext<'_, '_, Self::State, Self::Parameters>, - _input: SigI, - mut destination: D, + fn tick<'il, 'ol, D, const N: usize>( + ctx: &'_ SignalExecutionContext<'_, '_>, + _input: [Self::Input<'il>; N], + params: &Self::Parameters, + state: &mut Self::State, + destination: D, ) where - Self::Input: 'a, + Self::Input<'il>: 'ol, + 'il: 'ol, + D: SignalDestination, N>, { - let ni = Default::default(); - S::tick::<_, _, N>( - ctx, - |_| &ni, - |v| { - destination.send(v); - }, - ); + let ni = [(); N].map(|_| Default::default()); + S::tick::<_, N>(ctx, ni, params, state, destination); } fn trace_slots< @@ -75,7 +72,8 @@ where impl IntoSignal for ConsumeInputSignalConfig where S: IntoSignal, - IntoSignalInput: Default, + for<'a> IntoSignalInput<'a, S>: Default, + S::Signal: 'static, { type Signal = ConsumeInputSignal; diff --git a/crates/synthizer/src/signals/conversion.rs b/crates/synthizer/src/signals/conversion.rs index 029cdb7..237faa2 100644 --- a/crates/synthizer/src/signals/conversion.rs +++ b/crates/synthizer/src/signals/conversion.rs @@ -25,30 +25,35 @@ impl ConvertOutputConfig { unsafe impl Signal for ConvertOutput where Sig: Signal, - Sig::Output: Into, + for<'a> Sig::Output<'a>: Into + Clone, { - type Output = DType; - type Input = Sig::Input; + type Output<'ol> = DType; + type Input<'il> = Sig::Input<'il>; type Parameters = Sig::Parameters; type State = Sig::State; - fn on_block_start(ctx: &mut SignalExecutionContext<'_, '_, Self::State, Self::Parameters>) { - Sig::on_block_start(ctx); + fn on_block_start( + ctx: &SignalExecutionContext<'_, '_>, + params: &Self::Parameters, + state: &mut Self::State, + ) { + Sig::on_block_start(ctx, params, state); } - fn tick< - 'a, - I: FnMut(usize) -> &'a Self::Input, - D: SignalDestination, - const N: usize, - >( - ctx: &'_ mut SignalExecutionContext<'_, '_, Self::State, Self::Parameters>, - input: I, - mut destination: D, + fn tick<'il, 'ol, D, const N: usize>( + ctx: &'_ SignalExecutionContext<'_, '_>, + input: [Self::Input<'il>; N], + params: &Self::Parameters, + state: &mut Self::State, + destination: D, ) where - Self::Input: 'a, + Self::Input<'il>: 'ol, + 'il: 'ol, + D: SignalDestination, N>, { - Sig::tick::<_, _, N>(ctx, input, |x: Sig::Output| destination.send(x.into())); + Sig::tick::<_, N>(ctx, input, params, state, |x: [Sig::Output<'ol>; N]| { + destination.send(x.map(Into::into)); + }); } fn trace_slots< @@ -73,7 +78,7 @@ unsafe impl Sync for ConvertOutput {} impl IntoSignal for ConvertOutputConfig where Sig: IntoSignal, - IntoSignalOutput: Into, + for<'a> IntoSignalOutput<'a, Sig>: Into + Clone, { type Signal = ConvertOutput; diff --git a/crates/synthizer/src/signals/map.rs b/crates/synthizer/src/signals/map.rs index 1627c54..51d5f88 100644 --- a/crates/synthizer/src/signals/map.rs +++ b/crates/synthizer/src/signals/map.rs @@ -1,5 +1,4 @@ use std::marker::PhantomData as PD; -use std::mem::MaybeUninit; use crate::core_traits::*; @@ -15,53 +14,49 @@ pub struct MapSignalConfig { pub struct MapSignalState { closure: F, - parent_state: SignalState, } unsafe impl Signal for MapSignal where ParSig: Signal, - F: FnMut(&SignalOutput) -> O + Send + Sync + 'static, + F: FnMut(SignalOutput) -> O + Send + Sync + 'static, O: Send, { - type Input = SignalInput; - type Output = O; + type Input<'il> = SignalInput<'il, ParSig>; + type Output<'ol> = O; type Parameters = ParSig::Parameters; type State = MapSignalState; fn on_block_start( - ctx: &mut crate::context::SignalExecutionContext<'_, '_, Self::State, Self::Parameters>, + ctx: &crate::context::SignalExecutionContext<'_, '_>, + params: &Self::Parameters, + state: &mut Self::State, ) { - ParSig::on_block_start(&mut ctx.wrap(|s| &mut s.parent_state, |p| p)); + ParSig::on_block_start(ctx, params, &mut state.parent_state); } - fn tick< - 'a, - I: FnMut(usize) -> &'a Self::Input, - D: SignalDestination, - const N: usize, - >( - ctx: &'_ mut crate::context::SignalExecutionContext<'_, '_, Self::State, Self::Parameters>, - input: I, - mut destination: D, + fn tick<'il, 'ol, D, const N: usize>( + ctx: &'_ crate::context::SignalExecutionContext<'_, '_>, + input: [Self::Input<'il>; N], + params: &Self::Parameters, + state: &mut Self::State, + destination: D, ) where - Self::Input: 'a, + Self::Input<'il>: 'ol, + 'il: 'ol, + D: SignalDestination, N>, { - let mut outs: [MaybeUninit>; N] = [const { MaybeUninit::uninit() }; N]; - let mut i = 0; - ParSig::tick::<_, _, N>(&mut ctx.wrap(|s| &mut s.parent_state, |p| p), input, |x| { - outs[i].write(x); - i += 1; - }); - - outs.iter() - .for_each(|i| destination.send((ctx.state.closure)(unsafe { i.assume_init_ref() }))); - - // The mapping closure gets references, so we must drop this ourselves. - unsafe { - crate::unsafe_utils::drop_initialized_array(outs); - } + ParSig::tick::<_, N>( + ctx, + input, + params, + &mut state.parent_state, + |x: [ParSig::Output<'ol>; N]| { + let mapped = x.map(&mut state.closure); + destination.send(mapped); + }, + ); } fn trace_slots< @@ -80,7 +75,7 @@ where impl IntoSignal for MapSignalConfig where - F: FnMut(&IntoSignalOutput) -> O + Send + Sync + 'static, + F: FnMut(IntoSignalOutput) -> O + Send + Sync + 'static, ParSig: IntoSignal, O: Send + 'static, { diff --git a/crates/synthizer/src/signals/null.rs b/crates/synthizer/src/signals/null.rs index b5127d3..3b40673 100644 --- a/crates/synthizer/src/signals/null.rs +++ b/crates/synthizer/src/signals/null.rs @@ -8,30 +8,31 @@ use crate::core_traits::*; pub struct NullSignal(()); unsafe impl Signal for NullSignal { - type Input = (); - type Output = (); + type Input<'il> = (); + type Output<'ol> = (); type State = (); type Parameters = (); - fn tick< - 'a, - I: FnMut(usize) -> &'a Self::Input, - D: SignalDestination, - const N: usize, - >( - _ctx: &'_ mut SignalExecutionContext<'_, '_, Self::State, Self::Parameters>, - mut input: I, - mut destination: D, + fn tick<'il, 'ol, D, const N: usize>( + _ctx: &'_ SignalExecutionContext<'_, '_>, + _input: [Self::Input<'il>; N], + _params: &Self::Parameters, + _state: &mut Self::State, + destination: D, ) where - Self::Input: 'a, + Self::Input<'il>: 'ol, + 'il: 'ol, + D: SignalDestination, N>, { - for i in 0..N { - input(i); - destination.send(()); - } + destination.send([(); N]); } - fn on_block_start(_ctx: &mut SignalExecutionContext<'_, '_, Self::State, Self::Parameters>) {} + fn on_block_start( + _ctx: &SignalExecutionContext<'_, '_>, + _params: &Self::Parameters, + _state: &mut Self::State, + ) { + } fn trace_slots< F: FnMut( diff --git a/crates/synthizer/src/signals/periodic_f64.rs b/crates/synthizer/src/signals/periodic_f64.rs index 85d3948..87c2cdf 100644 --- a/crates/synthizer/src/signals/periodic_f64.rs +++ b/crates/synthizer/src/signals/periodic_f64.rs @@ -37,43 +37,49 @@ fn inc1( unsafe impl Signal for PeriodicF64Signal where - SIncr: Signal, + SIncr: for<'a> Signal = f64>, { - type Output = f64; - type Input = SIncr::Input; + type Output<'ol> = f64; + type Input<'il> = SIncr::Input<'il>; type State = PeriodicF64State; type Parameters = PeriodicF64Parameters; - fn on_block_start(ctx: &mut SignalExecutionContext<'_, '_, Self::State, Self::Parameters>) { - SIncr::on_block_start(&mut ctx.wrap(|s| &mut s.freq_state, |p| &p.freq_params)); + fn on_block_start( + ctx: &SignalExecutionContext<'_, '_>, + params: &Self::Parameters, + state: &mut Self::State, + ) { + SIncr::on_block_start(ctx, ¶ms.freq_params, &mut state.freq_state); } - fn tick< - 'a, - I: FnMut(usize) -> &'a Self::Input, - D: SignalDestination, - const N: usize, - >( - ctx: &'_ mut SignalExecutionContext<'_, '_, Self::State, Self::Parameters>, - input: I, - mut destination: D, + fn tick<'il, 'ol, D, const N: usize>( + ctx: &'_ SignalExecutionContext<'_, '_>, + input: [Self::Input<'il>; N], + params: &Self::Parameters, + state: &mut Self::State, + destination: D, ) where - Self::Input: 'a, + Self::Input<'il>: 'ol, + 'il: 'ol, + D: SignalDestination, N>, { let mut increments: [f64; N] = [0.0; N]; let mut i = 0; - SIncr::tick::<_, _, N>( - &mut ctx.wrap(|s| &mut s.freq_state, |p| &p.freq_params), + SIncr::tick::<_, N>( + ctx, input, - |x| { - increments[i] = x; + ¶ms.freq_params, + &mut state.freq_state, + |x: [f64; N]| { + for i in 0..N { + increments[i] = x[i]; + } i += 1; }, ); + let results = increments.map(|x| inc1(state, params, x)); - increments.into_iter().for_each(|val| { - destination.send(inc1(ctx.state, ctx.parameters, val)); - }); + destination.send(results); } fn trace_slots< @@ -93,7 +99,7 @@ where impl IntoSignal for PeriodicF64Config where SIncrCfg: IntoSignal, - SIncrCfg::Signal: Signal, + SIncrCfg::Signal: for<'ol> Signal = f64>, { type Signal = PeriodicF64Signal; diff --git a/crates/synthizer/src/signals/scalars.rs b/crates/synthizer/src/signals/scalars.rs index e1abb93..92759dd 100644 --- a/crates/synthizer/src/signals/scalars.rs +++ b/crates/synthizer/src/signals/scalars.rs @@ -9,29 +9,30 @@ use crate::unique_id::UniqueId; macro_rules! impl_scalar { ($t: ty) => { unsafe impl Signal for $t { - type Input = (); - type Output = $t; + type Input<'il> = (); + type Output<'ol> = $t; type State = (); type Parameters = $t; fn tick< - 'a, - I: FnMut(usize) -> &'a Self::Input, - D: SignalDestination, + 'il, + 'ol, + D, const N: usize, >( - ctx: &'_ mut SignalExecutionContext<'_, '_, Self::State, Self::Parameters>, - mut input: I, -mut destination: D, + _ctx: &'_ SignalExecutionContext<'_, '_>, + input: [Self::Input<'il>; N], + params: &Self::Parameters, + _state: &mut Self::State, + destination: D, ) where - Self::Input: 'a { - for i in 0..N { - input(i); - destination.send(*ctx.parameters); - } + Self::Input<'il>: 'ol, + 'il: 'ol, + D: SignalDestination, N>, { + destination.send(input.map(|_|*params)); } - fn on_block_start(_ctx: &mut SignalExecutionContext<'_, '_, Self::State, Self::Parameters>) {} + fn on_block_start(_ctx: &SignalExecutionContext<'_, '_>, _params: &Self::Parameters, _state: &mut Self::State) {} fn trace_slots)>( _state: &Self::State, diff --git a/crates/synthizer/src/signals/slots.rs b/crates/synthizer/src/signals/slots.rs index a4a4de2..6a8f43f 100644 --- a/crates/synthizer/src/signals/slots.rs +++ b/crates/synthizer/src/signals/slots.rs @@ -124,52 +124,51 @@ unsafe impl Signal for SlotSignal where T: Clone, { - type Input = (); - type Output = SlotSignalOutput; + type Input<'il> = (); + type Output<'ol> = SlotSignalOutput; type State = SlotAudioThreadState; type Parameters = SlotSignalParams; fn on_block_start( - ctx: &mut crate::context::SignalExecutionContext<'_, '_, Self::State, Self::Parameters>, + ctx: &crate::context::SignalExecutionContext<'_, '_>, + params: &Self::Parameters, + state: &mut Self::State, ) { let slot_container = ctx .fixed .slots - .resolve::(&ctx.parameters.id) + .resolve::(¶ms.id) .expect("If a slot is created, it should have previously been allocated"); - let is_change = ctx.state.last_update_id != slot_container.update_id; + let is_change = state.last_update_id != slot_container.update_id; // If no change has occurred, optimize out doing anything. if !is_change { - ctx.state.changed_this_block = false; + state.changed_this_block = false; return; } - ctx.state.value = slot_container.value.clone(); - ctx.state.last_update_id = slot_container.update_id; - ctx.state.changed_this_block = true; + state.value = slot_container.value.clone(); + state.last_update_id = slot_container.update_id; + state.changed_this_block = true; } - fn tick< - 'a, - I: FnMut(usize) -> &'a Self::Input, - D: SignalDestination, - const N: usize, - >( - ctx: &'_ mut crate::context::SignalExecutionContext<'_, '_, Self::State, Self::Parameters>, - _input: I, - mut destination: D, + fn tick<'il, 'ol, D, const N: usize>( + _ctx: &'_ crate::context::SignalExecutionContext<'_, '_>, + _input: [(); N], + _params: &Self::Parameters, + state: &mut Self::State, + destination: D, ) where - Self::Input: 'a, + Self::Input<'il>: 'ol, + 'il: 'ol, + D: SignalDestination, N>, { - let val = SlotSignalOutput { - value: (*ctx.state.value).clone(), - changed_this_block: ctx.state.changed_this_block, - }; + let sending = [(); N].map(|_| SlotSignalOutput { + value: (*state.value).clone(), + changed_this_block: state.changed_this_block, + }); - for _ in 0..N { - destination.send(val.clone()); - } + destination.send(sending); } fn trace_slots)>( @@ -216,13 +215,13 @@ where pub(crate) fn read_signal( &self, initial_value: T, - ) -> impl IntoSignal> { + ) -> impl IntoSignal Signal = (), Output<'a> = T>> { crate::signals::MapSignalConfig::new( SlotSignalConfig { initial_value, slot_id: self.slot_id, }, - |x: &SlotSignalOutput| -> T { x.value.clone() }, + |x: SlotSignalOutput| -> T { x.value.clone() }, ) } @@ -230,13 +229,13 @@ where pub(crate) fn read_signal_and_change_flag( &self, initial_value: T, - ) -> impl IntoSignal> { + ) -> impl IntoSignal Signal = (), Output<'a> = (T, bool)>> { crate::signals::MapSignalConfig::new( SlotSignalConfig { initial_value, slot_id: self.slot_id, }, - |x: &SlotSignalOutput| -> (T, bool) { (x.value.clone(), x.changed_this_block) }, + |x: SlotSignalOutput| -> (T, bool) { (x.value.clone(), x.changed_this_block) }, ) } } diff --git a/crates/synthizer/src/signals/trig.rs b/crates/synthizer/src/signals/trig.rs index c38017e..2b6e1c8 100644 --- a/crates/synthizer/src/signals/trig.rs +++ b/crates/synthizer/src/signals/trig.rs @@ -9,30 +9,35 @@ pub struct SinSignal(S); unsafe impl Signal for SinSignal where - S: Signal, + S: for<'ol> Signal = f64>, { - type Input = S::Input; - type Output = S::Output; + type Input<'il> = S::Input<'il>; + type Output<'ol> = S::Output<'ol>; type State = S::State; type Parameters = S::Parameters; - fn on_block_start(ctx: &mut SignalExecutionContext<'_, '_, Self::State, Self::Parameters>) { - S::on_block_start(ctx); + fn on_block_start( + ctx: &SignalExecutionContext<'_, '_>, + params: &Self::Parameters, + state: &mut Self::State, + ) { + S::on_block_start(ctx, params, state); } - fn tick< - 'a, - I: FnMut(usize) -> &'a Self::Input, - D: SignalDestination, - const N: usize, - >( - ctx: &'_ mut SignalExecutionContext<'_, '_, Self::State, Self::Parameters>, - input: I, - mut destination: D, + fn tick<'il, 'ol, D, const N: usize>( + ctx: &'_ SignalExecutionContext<'_, '_>, + input: [Self::Input<'il>; N], + params: &Self::Parameters, + state: &mut Self::State, + destination: D, ) where - Self::Input: 'a, + Self::Input<'il>: 'ol, + 'il: 'ol, + D: SignalDestination, N>, { - S::tick::<_, _, N>(ctx, input, |x: f64| destination.send(x.sin())); + S::tick::<_, N>(ctx, input, params, state, |x: [f64; N]| { + destination.send(x.map(|x| x.sin())) + }); } fn trace_slots< @@ -52,7 +57,7 @@ where impl IntoSignal for SinSignalConfig where S: IntoSignal, - S::Signal: Signal, + for<'a> S::Signal: Signal = f64>, { type Signal = SinSignal; diff --git a/crates/synthizer/src/synthesizer.rs b/crates/synthizer/src/synthesizer.rs index 38a5a27..760c947 100644 --- a/crates/synthizer/src/synthesizer.rs +++ b/crates/synthizer/src/synthesizer.rs @@ -322,9 +322,9 @@ fn at_iter(state: &Arc, mut dest: &mut [f32]) { m.erased_mount.borrow_mut().run( state, id, - &mut crate::context::FixedSignalExecutionContext { + &crate::context::FixedSignalExecutionContext { time_in_blocks: as_mut.time_in_blocks, - audio_destinationh: &mut as_mut.buffer, + audio_destinationh: atomic_refcell::AtomicRefCell::new(&mut as_mut.buffer), slots: &SlotUpdateContext { mount_slots: &m.slots, },