Skip to content

Commit

Permalink
ICE
Browse files Browse the repository at this point in the history
  • Loading branch information
ahicks92 committed Dec 20, 2024
1 parent b3b4f15 commit 8f224fb
Show file tree
Hide file tree
Showing 18 changed files with 362 additions and 374 deletions.
8 changes: 8 additions & 0 deletions crates/synthizer/src/array_utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/// Return an array `[0, 1, ...]`.
pub(crate) fn increasing_usize<const N: usize>() -> [usize; N] {
let mut ret = [0; N];
for i in 0..N {
ret[i] = i;
}
ret
}
45 changes: 28 additions & 17 deletions crates/synthizer/src/chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ where
pub fn read_slot<T>(
slot: &sigs::Slot<T>,
initial_value: T,
) -> Chain<impl IntoSignal<Signal = impl Signal<Input = (), Output = T>>>
) -> Chain<impl IntoSignal<Signal = impl for<'a> Signal<Input<'a> = (), Output<'a> = T>>>
where
T: Clone + Send + Sync + 'static,
{
Expand All @@ -59,7 +59,7 @@ where
pub fn read_slot_and_changed<T>(
slot: &sigs::Slot<T>,
initial_value: T,
) -> Chain<impl IntoSignal<Signal = impl Signal<Input = (), Output = (T, bool)>>>
) -> Chain<impl IntoSignal<Signal = impl for<'a> Signal<Input<'a> = (), Output<'a> = (T, bool)>>>
where
T: Send + Sync + Clone + 'static,
{
Expand All @@ -79,9 +79,13 @@ impl<S: IntoSignal> Chain<S> {
/// Send this chain to the audio device.
pub fn to_audio_device(
self,
) -> Chain<impl IntoSignal<Signal = impl Signal<Input = IntoSignalInput<S>, Output = ()>>>
) -> Chain<
impl IntoSignal<
Signal = impl for<'a> Signal<Input<'a> = IntoSignalInput<'a, S>, Output<'a> = ()>,
>,
>
where
S::Signal: Signal<Output = f64>,
S::Signal: for<'a> Signal<Output<'a> = f64>,
{
Chain {
inner: sigs::AudioOutputSignalConfig::new(self.inner),
Expand All @@ -102,16 +106,17 @@ impl<S: IntoSignal> Chain<S> {
self,
) -> Chain<
impl IntoSignal<
Signal = impl Signal<
Input = NewInputType,
Output = IntoSignalOutput<S>,
Signal = impl for<'a> Signal<
Input<'a> = NewInputType,
Output<'a> = IntoSignalOutput<'a, S>,
State = IntoSignalState<S>,
Parameters = IntoSignalParameters<S>,
>,
>,
>
where
IntoSignalInput<S>: Default,
for<'a> IntoSignalInput<'a, S>: Default,
S::Signal: 'static,
{
Chain {
inner: sigs::ConsumeInputSignalConfig::<_, NewInputType>::new(self.inner),
Expand All @@ -123,10 +128,15 @@ impl<S: IntoSignal> Chain<S> {
/// 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<impl IntoSignal<Signal = impl Signal<Input = IntoSignalInput<S>, Output = f64>>>
) -> Chain<
impl IntoSignal<
Signal = impl for<'a> Signal<Input<'a> = IntoSignalInput<'a, S>, Output<'a> = f64>,
>,
>
where
S::Signal: Signal<Output = f64>,
IntoSignalInput<S>: Default,
for<'a> S::Signal: Signal<Output<'a> = f64>,
for<'a> IntoSignalInput<'a, S>: Default + Clone,
for<'a> IntoSignalOutput<'a, S>: Clone,
{
let converted = self.output_into::<f64>();
let sr = Chain::new(config::SR as f64).discard_and_default::<IntoSignalInput<S>>();
Expand All @@ -139,16 +149,17 @@ impl<S: IntoSignal> Chain<S> {
self,
) -> Chain<
impl IntoSignal<
Signal = impl Signal<
Input = IntoSignalInput<S>,
Output = T,
Signal = impl for<'a> Signal<
Input<'a> = IntoSignalInput<'a, S>,
Output<'a> = T,
State = IntoSignalState<S>,
Parameters = IntoSignalParameters<S>,
>,
>,
>
where
T: From<IntoSignalOutput<S>>,
for<'a> T: From<IntoSignalOutput<'a, S>>,
for<'a> IntoSignalOutput<'a, S>: Clone,
{
Chain {
inner: sigs::ConvertOutputConfig::<S, T>::new(self.inner),
Expand All @@ -162,7 +173,7 @@ impl<S: IntoSignal> Chain<S> {
pub fn periodic_sum(self, period: f64, initial_value: f64) -> Chain<sigs::PeriodicF64Config<S>>
where
S: IntoSignal,
S::Signal: Signal<Output = f64>,
for<'a> S::Signal: Signal<Output<'a> = f64>,
{
Chain {
inner: sigs::PeriodicF64Config {
Expand All @@ -177,7 +188,7 @@ impl<S: IntoSignal> Chain<S> {
pub fn sin(self) -> Chain<sigs::SinSignalConfig<S>>
where
S: IntoSignal,
S::Signal: Signal<Output = f64>,
for<'a> S::Signal: Signal<Output<'a> = f64>,
{
Chain {
inner: sigs::SinSignalConfig {
Expand Down
72 changes: 28 additions & 44 deletions crates/synthizer/src/chain_mathops.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -18,8 +18,8 @@ macro_rules! impl_mathop {
where
A: IntoSignal,
B: IntoSignal,
A::Signal: Signal<Output = IntoSignalOutput<B>>,
IntoSignalOutput<A>: $trait<IntoSignalOutput<B>>,
A::Signal: for<'ol> Signal<Output<'ol> = IntoSignalOutput<'ol, B>>,
for<'ol> IntoSignalOutput<'ol, A>: $trait<IntoSignalOutput<'ol, B>> + Copy,
{
type Output = Chain<$signal_config<A, B>>;

Expand All @@ -33,58 +33,42 @@ macro_rules! impl_mathop {
unsafe impl<S1, S2> Signal for $signal_name<S1, S2>
where
S1: Signal,
S2: Signal<Input = SignalInput<S1>>,
SignalOutput<S1>: $trait<SignalOutput<S2>>,
S2: for<'il> Signal<Input<'il> = SignalInput<'il, S1>>,
for<'ol> SignalOutput<'ol, S1>: $trait<SignalOutput<'ol, S2>> + Copy,
S1: 'static,
S2: 'static,
{
type Input = SignalInput<S1>;
type Output = <SignalOutput<S1> as $trait<SignalOutput<S2>>>::Output;
type Input<'il> = SignalInput<'il, S1>;
type Output<'ol> = <SignalOutput<'ol, S1> as $trait<SignalOutput<'ol, S2>>>::Output;
type Parameters = (SignalParameters<S1>, SignalParameters<S2>);
type State = (SignalState<S1>, SignalState<S2>);

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, &params.0, &mut state.0);
S2::on_block_start(&ctx, &params.1, &mut state.1);
}

fn tick<
'a,
I: FnMut(usize) -> &'a Self::Input,
D: SignalDestination<Self::Output>,
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<Self::Output<'ol>, 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<SignalOutput<S1>>; N] =
[const { MaybeUninit::uninit() }; N];
let mut right: [MaybeUninit<SignalOutput<S2>>; 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(), &params.0, &mut state.0, |left| {
S2::tick(ctx, input, &params.1, &mut state.1, |right| {
let outgoing = crate::array_utils::increasing_usize::<N>()
.map(|i| left[i].$method(right[i]));
destination.send(outgoing);
});
});
}

Expand Down
29 changes: 5 additions & 24 deletions crates/synthizer/src/context.rs
Original file line number Diff line number Diff line change
@@ -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,
}
}
}
55 changes: 29 additions & 26 deletions crates/synthizer/src/core_traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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<Self::Output>,
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<Self::Output<'ol>, N>,
Self::Input<'il>: 'ol,
'il: 'ol;

/// Called when a signal is starting a new block.
///
Expand All @@ -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.
///
Expand All @@ -75,8 +78,8 @@ pub(crate) mod sealed {
);
}

pub trait SignalDestination<Input: Sized> {
fn send(&mut self, value: Input);
pub trait SignalDestination<Input: Sized, const N: usize> {
fn send(self, values: [Input; N]);
}

/// A frame of audio data, which can be stored on the stack.
Expand Down Expand Up @@ -137,44 +140,44 @@ pub(crate) mod sealed {

pub(crate) use sealed::*;

impl<F, Input> SignalDestination<Input> for F
impl<F, Input, const N: usize> SignalDestination<Input, N> 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<Input = ()> {}
impl<T> Generator for T where T: Signal<Input = ()> {}
pub trait Generator: for<'a> Signal<Input<'a> = ()> {}
impl<T> Generator for T where T: for<'a> Signal<Input<'a> = ()> {}

/// 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<Output = ()> + Generator,
Self: for<'a> Signal<Output<'a> = ()> + Generator,
SignalState<Self>: Send + Sync + 'static,
SignalParameters<Self>: Send + Sync + 'static,
{
}

impl<T> Mountable for T
where
T: Generator + Signal<Output = ()> + Send + Sync + 'static,
T: Generator + for<'a> Signal<Output<'a> = ()> + Send + Sync + 'static,
SignalState<T>: Send + Sync + 'static,
SignalParameters<T>: Send + Sync + 'static,
{
}

// 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<S> = <<S as IntoSignal>::Signal as Signal>::Output;
pub(crate) type IntoSignalInput<S> = <<S as IntoSignal>::Signal as Signal>::Input;
pub(crate) type IntoSignalOutput<'a, S> = <<S as IntoSignal>::Signal as Signal>::Output<'a>;
pub(crate) type IntoSignalInput<'a, S> = <<S as IntoSignal>::Signal as Signal>::Input<'a>;
pub(crate) type IntoSignalParameters<S> = <<S as IntoSignal>::Signal as Signal>::Parameters;
pub(crate) type IntoSignalState<S> = <<S as IntoSignal>::Signal as Signal>::State;
pub(crate) type SignalInput<T> = <T as Signal>::Input;
pub(crate) type SignalOutput<T> = <T as Signal>::Output;
pub(crate) type SignalInput<'a, T> = <T as Signal>::Input<'a>;
pub(crate) type SignalOutput<'a, T> = <T as Signal>::Output<'a>;
pub(crate) type SignalState<S> = <S as Signal>::State;
pub(crate) type SignalParameters<S> = <S as Signal>::Parameters;
1 change: 1 addition & 0 deletions crates/synthizer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ mod variant;
#[macro_use]
mod logging;

mod array_utils;
mod audio_frames;
mod background_drop;
pub mod biquad;
Expand Down
Loading

0 comments on commit 8f224fb

Please sign in to comment.