diff --git a/crates/synthizer/src/chain_mathops.rs b/crates/synthizer/src/chain_mathops.rs index eb1cec4..6bbbd92 100644 --- a/crates/synthizer/src/chain_mathops.rs +++ b/crates/synthizer/src/chain_mathops.rs @@ -5,7 +5,6 @@ use std::ops::*; use std::sync::Arc; use crate::chain::Chain; -use crate::config; use crate::context::SignalExecutionContext; use crate::core_traits::*; use crate::unique_id::UniqueId; @@ -49,10 +48,11 @@ macro_rules! impl_mathop { S2::on_block_start(&mut ctx.wrap(|s| &mut s.1, |p| &p.1)); } - fn tick_block< + fn tick< 'a, I: FnMut(usize) -> &'a Self::Input, D: ReusableSignalDestination, + const N: usize, >( ctx: &'_ mut SignalExecutionContext<'_, '_, Self::State, Self::Parameters>, mut input: I, @@ -63,20 +63,20 @@ macro_rules! impl_mathop { // 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>; config::BLOCK_SIZE] = - [const { MaybeUninit::uninit() }; config::BLOCK_SIZE]; - let mut right: [MaybeUninit>; config::BLOCK_SIZE] = - [const { MaybeUninit::uninit() }; config::BLOCK_SIZE]; + let mut left: [MaybeUninit>; N] = + [const { MaybeUninit::uninit() }; N]; + let mut right: [MaybeUninit>; N] = + [const { MaybeUninit::uninit() }; N]; let mut i = 0usize; - S1::tick_block(&mut ctx.wrap(|s| &mut s.0, |p| &p.0), &mut input, |val| { + 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_block(&mut ctx.wrap(|s| &mut s.1, |p| &p.1), &mut input, |val| { + S2::tick::<_, _, N>(&mut ctx.wrap(|s| &mut s.1, |p| &p.1), &mut input, |val| { right[i].write(val); i += 1; }); diff --git a/crates/synthizer/src/core_traits.rs b/crates/synthizer/src/core_traits.rs index 508dc10..1f91177 100644 --- a/crates/synthizer/src/core_traits.rs +++ b/crates/synthizer/src/core_traits.rs @@ -21,22 +21,23 @@ pub(crate) mod sealed { type State: Sized + Send + Sync; type Parameters: Sized + Send + Sync; - /// Tick this signal [config::BLOCK_SIZE] times. Implemented via `tick1` by default. + /// Tick this signal. /// - /// The default implementation is only suitable for signals which are "endpoints" e.g. signals that produce - /// values from nothing. Combinators must implement this in terms of their combined signals. Signals which are - /// not combinators may ignore this if they are wrapping a signal, but cannot meaningfully benefit from or store - /// output data. The complexity here, though: when reading from files and external sources, we are not able to - /// do so efficiently sample by sample. Anywhere which does not forward block-based calls to their parent - /// de-optimizes this path. + /// Exactly `BLOCK_SIZE` ticks will occur between calls to `on_block_start`. They may be broken up into smaller + /// blocks, possibly down to 1 sample (for example, in recursive structures). /// - /// Callers will choose to use tick1 or to use tick_block, and will not mix them for the duration of a block. - /// Callers may change their mind between blocks. Wrapper signals should also preserve this property, though - /// since this is an entire block of data, it's hard to break this rule. - fn tick_block< + /// This method must uphold two important invariants: + /// + /// - `input(i)` must be called at minimum once on each input index in the range `0..N`. + /// - Exactly `N` outputs are sent to the destination. + /// + /// 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: ReusableSignalDestination, + const N: usize, >( ctx: &'_ mut SignalExecutionContext<'_, '_, Self::State, Self::Parameters>, input: I, diff --git a/crates/synthizer/src/mount_point.rs b/crates/synthizer/src/mount_point.rs index 9840d46..dec92ee 100644 --- a/crates/synthizer/src/mount_point.rs +++ b/crates/synthizer/src/mount_point.rs @@ -1,5 +1,6 @@ use std::sync::Arc; +use crate::config; use crate::context::*; use crate::core_traits::*; use crate::synthesizer::SynthesizerState; @@ -45,6 +46,6 @@ impl ErasedMountPoint for MountPoint { }; S::on_block_start(&mut ctx); - S::tick_block(&mut ctx, |_| &(), |_| {}); + S::tick::<_, _, { config::BLOCK_SIZE }>(&mut ctx, |_| &(), |_| {}); } } diff --git a/crates/synthizer/src/signals/and_then.rs b/crates/synthizer/src/signals/and_then.rs index ee47dab..7020e42 100644 --- a/crates/synthizer/src/signals/and_then.rs +++ b/crates/synthizer/src/signals/and_then.rs @@ -1,6 +1,5 @@ use std::mem::MaybeUninit; -use crate::config; use crate::context::*; use crate::core_traits::*; @@ -27,10 +26,11 @@ where S2::on_block_start(&mut ctx.wrap(|s| &mut s.1, |p| &p.1)); } - fn tick_block< + fn tick< 'a, I: FnMut(usize) -> &'a Self::Input, D: ReusableSignalDestination, + const N: usize, >( ctx: &'_ mut SignalExecutionContext<'_, '_, Self::State, Self::Parameters>, input: I, @@ -38,15 +38,14 @@ where ) where Self::Input: 'a, { - let mut left_out: [MaybeUninit>; config::BLOCK_SIZE] = - [const { MaybeUninit::uninit() }; config::BLOCK_SIZE]; + let mut left_out: [MaybeUninit>; N] = [const { MaybeUninit::uninit() }; N]; let mut i = 0; - S1::tick_block(&mut ctx.wrap(|s| &mut s.0, |p| &p.0), input, |val| { + S1::tick::<_, _, N>(&mut ctx.wrap(|s| &mut s.0, |p| &p.0), input, |val| { left_out[i].write(val); i += 1; }); - S2::tick_block( + S2::tick::<_, _, N>( &mut ctx.wrap(|s| &mut s.1, |p| &p.1), |ind| unsafe { left_out[ind].assume_init_ref() }, destination, diff --git a/crates/synthizer/src/signals/audio_io.rs b/crates/synthizer/src/signals/audio_io.rs index 70a6d7c..a88cc0e 100644 --- a/crates/synthizer/src/signals/audio_io.rs +++ b/crates/synthizer/src/signals/audio_io.rs @@ -22,10 +22,11 @@ where ctx.state.1 = 0; } - fn tick_block< + fn tick< 'a, I: FnMut(usize) -> &'a Self::Input, D: ReusableSignalDestination, + const N: usize, >( ctx: &'_ mut SignalExecutionContext<'_, '_, Self::State, Self::Parameters>, input: I, @@ -36,7 +37,7 @@ where let mut block: [f64; config::BLOCK_SIZE] = [0.0f64; config::BLOCK_SIZE]; let mut i = 0; - S::tick_block(&mut ctx.wrap(|s| &mut s.0, |p| p), input, |x| { + S::tick::<_, _, N>(&mut ctx.wrap(|s| &mut s.0, |p| p), input, |x| { block[i] = x; i += 1; }); diff --git a/crates/synthizer/src/signals/consume_input.rs b/crates/synthizer/src/signals/consume_input.rs index 9d026c6..22425e4 100644 --- a/crates/synthizer/src/signals/consume_input.rs +++ b/crates/synthizer/src/signals/consume_input.rs @@ -36,10 +36,11 @@ where S::on_block_start(ctx); } - fn tick_block< + fn tick< 'a, SigI: FnMut(usize) -> &'a Self::Input, D: ReusableSignalDestination, + const N: usize, >( ctx: &'_ mut SignalExecutionContext<'_, '_, Self::State, Self::Parameters>, _input: SigI, @@ -48,7 +49,7 @@ where Self::Input: 'a, { let ni = Default::default(); - S::tick_block( + S::tick::<_, _, N>( ctx, |_| &ni, |v| { diff --git a/crates/synthizer/src/signals/conversion.rs b/crates/synthizer/src/signals/conversion.rs index 733fb5b..552ea54 100644 --- a/crates/synthizer/src/signals/conversion.rs +++ b/crates/synthizer/src/signals/conversion.rs @@ -36,10 +36,11 @@ where Sig::on_block_start(ctx); } - fn tick_block< + fn tick< 'a, I: FnMut(usize) -> &'a Self::Input, D: ReusableSignalDestination, + const N: usize, >( ctx: &'_ mut SignalExecutionContext<'_, '_, Self::State, Self::Parameters>, input: I, @@ -47,7 +48,7 @@ where ) where Self::Input: 'a, { - Sig::tick_block(ctx, input, |x: Sig::Output| { + Sig::tick::<_, _, N>(ctx, input, |x: Sig::Output| { destination.send_reusable(x.into()) }); } diff --git a/crates/synthizer/src/signals/map.rs b/crates/synthizer/src/signals/map.rs index 53cc7dc..63ab9cb 100644 --- a/crates/synthizer/src/signals/map.rs +++ b/crates/synthizer/src/signals/map.rs @@ -1,7 +1,6 @@ use std::marker::PhantomData as PD; use std::mem::MaybeUninit; -use crate::config; use crate::core_traits::*; pub struct MapSignal(PD<*const (ParSig, F, O)>); @@ -37,10 +36,11 @@ where ParSig::on_block_start(&mut ctx.wrap(|s| &mut s.parent_state, |p| p)); } - fn tick_block< + fn tick< 'a, I: FnMut(usize) -> &'a Self::Input, D: ReusableSignalDestination, + const N: usize, >( ctx: &'_ mut crate::context::SignalExecutionContext<'_, '_, Self::State, Self::Parameters>, input: I, @@ -48,10 +48,9 @@ where ) where Self::Input: 'a, { - let mut outs: [MaybeUninit>; config::BLOCK_SIZE] = - [const { MaybeUninit::uninit() }; config::BLOCK_SIZE]; + let mut outs: [MaybeUninit>; N] = [const { MaybeUninit::uninit() }; N]; let mut i = 0; - ParSig::tick_block(&mut ctx.wrap(|s| &mut s.parent_state, |p| p), input, |x| { + ParSig::tick::<_, _, N>(&mut ctx.wrap(|s| &mut s.parent_state, |p| p), input, |x| { outs[i].write(x); i += 1; }); diff --git a/crates/synthizer/src/signals/null.rs b/crates/synthizer/src/signals/null.rs index b206ace..6cfa148 100644 --- a/crates/synthizer/src/signals/null.rs +++ b/crates/synthizer/src/signals/null.rs @@ -13,10 +13,11 @@ unsafe impl Signal for NullSignal { type State = (); type Parameters = (); - fn tick_block< + fn tick< 'a, I: FnMut(usize) -> &'a Self::Input, D: ReusableSignalDestination, + const N: usize, >( _ctx: &'_ mut SignalExecutionContext<'_, '_, Self::State, Self::Parameters>, mut input: I, @@ -24,7 +25,7 @@ unsafe impl Signal for NullSignal { ) where Self::Input: 'a, { - for i in 0..crate::config::BLOCK_SIZE { + for i in 0..N { input(i); destination.send_reusable(()); } diff --git a/crates/synthizer/src/signals/periodic_f64.rs b/crates/synthizer/src/signals/periodic_f64.rs index 6ad634f..9ffcd65 100644 --- a/crates/synthizer/src/signals/periodic_f64.rs +++ b/crates/synthizer/src/signals/periodic_f64.rs @@ -1,4 +1,3 @@ -use crate::config; use crate::context::*; use crate::core_traits::*; @@ -49,10 +48,11 @@ where SIncr::on_block_start(&mut ctx.wrap(|s| &mut s.freq_state, |p| &p.freq_params)); } - fn tick_block< + fn tick< 'a, I: FnMut(usize) -> &'a Self::Input, D: ReusableSignalDestination, + const N: usize, >( ctx: &'_ mut SignalExecutionContext<'_, '_, Self::State, Self::Parameters>, input: I, @@ -60,9 +60,9 @@ where ) where Self::Input: 'a, { - let mut increments: [f64; config::BLOCK_SIZE] = [0.0; config::BLOCK_SIZE]; + let mut increments: [f64; N] = [0.0; N]; let mut i = 0; - SIncr::tick_block( + SIncr::tick::<_, _, N>( &mut ctx.wrap(|s| &mut s.freq_state, |p| &p.freq_params), input, |x| { diff --git a/crates/synthizer/src/signals/scalars.rs b/crates/synthizer/src/signals/scalars.rs index e36fa3d..fa4a0a1 100644 --- a/crates/synthizer/src/signals/scalars.rs +++ b/crates/synthizer/src/signals/scalars.rs @@ -14,17 +14,18 @@ macro_rules! impl_scalar { type State = (); type Parameters = $t; - fn tick_block< + fn tick< 'a, I: FnMut(usize) -> &'a Self::Input, D: ReusableSignalDestination, + const N: usize, >( ctx: &'_ mut SignalExecutionContext<'_, '_, Self::State, Self::Parameters>, mut input: I, mut destination: D, ) where Self::Input: 'a { - for i in 0..crate::config::BLOCK_SIZE { + for i in 0..N { input(i); destination.send_reusable(*ctx.parameters); } diff --git a/crates/synthizer/src/signals/slots.rs b/crates/synthizer/src/signals/slots.rs index 651dbfc..f86728a 100644 --- a/crates/synthizer/src/signals/slots.rs +++ b/crates/synthizer/src/signals/slots.rs @@ -2,10 +2,9 @@ use std::any::Any; use std::marker::PhantomData as PD; use std::sync::Arc; +use crate::core_traits::*; use rpds::HashTrieMapSync; -use crate::config; -use crate::core_traits::*; use crate::unique_id::UniqueId; pub(crate) type SlotMap = HashTrieMapSync; @@ -151,10 +150,11 @@ where ctx.state.changed_this_block = true; } - fn tick_block< + fn tick< 'a, I: FnMut(usize) -> &'a Self::Input, D: ReusableSignalDestination, + const N: usize, >( ctx: &'_ mut crate::context::SignalExecutionContext<'_, '_, Self::State, Self::Parameters>, _input: I, @@ -167,7 +167,7 @@ where changed_this_block: ctx.state.changed_this_block, }; - for _ in 0..config::BLOCK_SIZE { + for _ in 0..N { destination.send_reusable(val.clone()); } } diff --git a/crates/synthizer/src/signals/trig.rs b/crates/synthizer/src/signals/trig.rs index 75551c7..624a981 100644 --- a/crates/synthizer/src/signals/trig.rs +++ b/crates/synthizer/src/signals/trig.rs @@ -20,10 +20,11 @@ where S::on_block_start(ctx); } - fn tick_block< + fn tick< 'a, I: FnMut(usize) -> &'a Self::Input, D: ReusableSignalDestination, + const N: usize, >( ctx: &'_ mut SignalExecutionContext<'_, '_, Self::State, Self::Parameters>, input: I, @@ -31,7 +32,7 @@ where ) where Self::Input: 'a, { - S::tick_block(ctx, input, |x: f64| destination.send_reusable(x.sin())); + S::tick::<_, _, N>(ctx, input, |x: f64| destination.send_reusable(x.sin())); } fn trace_slots<