1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
use alloc::{boxed::Box, sync::Arc, vec};
use core::sync::atomic::{AtomicBool, Ordering};

use crate::{frame, set, Frame, Set, SetHandle, Signal};

/// Handle for controlling a [`Mixer`] from another thread
pub struct MixerControl<T>(SetHandle<ErasedSignal<T>>);

impl<T> MixerControl<T> {
    /// Begin playing `signal`, returning a handle that can be used to pause or stop it and access
    /// other controls
    ///
    /// Finished signals are automatically stopped, and their storage reused for future `play`
    /// calls.
    ///
    /// The type of signal given determines what additional controls can be used. See the
    /// examples for a detailed guide.
    pub fn play<S>(&mut self, signal: S) -> Mixed
    where
        S: Signal<Frame = T> + Send + 'static,
    {
        let signal = Box::new(MixedSignal::new(signal));
        let control = Mixed(signal.stop.clone());
        self.0.insert(signal);
        control
    }
}

/// Handle to a signal playing in a [`Mixer`]
pub struct Mixed(Arc<AtomicBool>);

impl Mixed {
    /// Immediately halt playback of the associated signal by its [`Mixer`]
    pub fn stop(&mut self) {
        self.0.store(true, Ordering::Relaxed);
    }

    /// Whether the signal's playback
    ///
    /// Set by both `is_stopped` and signals naturally finishing.
    pub fn is_stopped(&self) -> bool {
        self.0.load(Ordering::Relaxed)
    }
}

struct MixedSignal<T: ?Sized> {
    stop: Arc<AtomicBool>,
    inner: T,
}

impl<T> MixedSignal<T> {
    fn new(signal: T) -> Self {
        Self {
            stop: Arc::new(AtomicBool::new(false)),
            inner: signal,
        }
    }
}

/// A [`Signal`] that mixes a dynamic set of [`Signal`]s
pub struct Mixer<T> {
    recv: Inner<T>,
}

impl<T> Mixer<T>
where
    T: Frame + Clone,
{
    /// Construct a new mixer
    pub fn new() -> (MixerControl<T>, Self) {
        let (handle, set) = set();
        (
            MixerControl(handle),
            Self {
                recv: Inner {
                    set,
                    buffer: vec![T::ZERO; 1024].into(),
                },
            },
        )
    }
}

struct Inner<T> {
    set: Set<ErasedSignal<T>>,
    buffer: Box<[T]>,
}

impl<T: Frame> Signal for Mixer<T> {
    type Frame = T;

    fn sample(&mut self, interval: f32, out: &mut [T]) {
        let this = &mut self.recv;
        this.set.update();

        for o in out.iter_mut() {
            *o = T::ZERO;
        }

        for i in (0..this.set.len()).rev() {
            let signal = &mut this.set[i];
            if signal.stop.load(Ordering::Relaxed) || signal.inner.is_finished() {
                signal.stop.store(true, Ordering::Relaxed);
                this.set.remove(i);
                continue;
            }

            // Sample into `buffer`, then mix into `out`
            let mut iter = out.iter_mut();
            while iter.len() > 0 {
                let n = iter.len().min(this.buffer.len());
                let staging = &mut this.buffer[..n];
                signal.inner.sample(interval, staging);
                for (staged, o) in staging.iter().zip(&mut iter) {
                    *o = frame::mix(o, staged);
                }
            }
        }
    }
}

type ErasedSignal<T> = Box<MixedSignal<dyn Signal<Frame = T>>>;

#[cfg(test)]
mod tests {
    use super::*;
    use crate::{Frames, FramesSignal};

    #[test]
    fn is_stopped() {
        let (mut mixer_control, mut mixer) = Mixer::new();
        let (_, signal) = FramesSignal::new(Frames::from_slice(1, &[0.0, 0.0]), 0.0);
        let handle = mixer_control.play(signal);
        assert!(!handle.is_stopped());

        let mut out = [0.0];

        mixer.sample(0.6, &mut out);
        assert!(!handle.is_stopped());

        mixer.sample(0.6, &mut out);
        // Signal is finished, but we won't actually notice until the next scan
        assert!(!handle.is_stopped());

        mixer.sample(0.0, &mut out);
        assert!(handle.is_stopped());
    }
}