firewheel_pool/
sampler.rs

1use firewheel_core::{
2    channel_config::NonZeroChannelCount,
3    diff::{Diff, PathBuilder},
4    node::NodeID,
5};
6use firewheel_graph::{backend::AudioBackend, ContextQueue, FirewheelCtx};
7use firewheel_nodes::sampler::{PlaybackState, SamplerConfig, SamplerNode, SamplerState};
8
9use crate::{PoolError, PoolableNode};
10
11/// A struct which uses a [`SamplerNode`] as the first node in an
12/// [`AudioNodePool`](crate::AudioNodePool).
13pub struct SamplerPool;
14
15impl PoolableNode for SamplerPool {
16    type AudioNode = SamplerNode;
17
18    /// Return the number of output channels for the given configuration.
19    fn num_output_channels(config: Option<&SamplerConfig>) -> NonZeroChannelCount {
20        config
21            .map(|c| c.channels)
22            .unwrap_or(SamplerConfig::default().channels)
23    }
24
25    /// Return `true` if the given parameters signify that the sequence is stopped,
26    /// `false` otherwise.
27    fn params_stopped(params: &SamplerNode) -> bool {
28        if let PlaybackState::Stop = *params.playback {
29            true
30        } else {
31            false
32        }
33    }
34
35    /// Return `true` if the node state of the given node is stopped.
36    ///
37    /// Return an error if the given `node_id` is invalid.
38    fn node_is_stopped<B: AudioBackend>(
39        node_id: NodeID,
40        cx: &FirewheelCtx<B>,
41    ) -> Result<bool, PoolError> {
42        cx.node_state::<SamplerState>(node_id)
43            .map(|s| s.stopped())
44            .ok_or(PoolError::InvalidNodeID(node_id))
45    }
46
47    /// Return a score of how ready this node is to accept new work.
48    ///
49    /// The worker with the highest worker score will be chosen for the new work.
50    ///
51    /// Return an error if the given `node_id` is invalid.
52    fn worker_score<B: AudioBackend>(
53        params: &SamplerNode,
54        node_id: NodeID,
55        cx: &mut FirewheelCtx<B>,
56    ) -> Result<u64, PoolError> {
57        cx.node_state::<SamplerState>(node_id)
58            .map(|s| s.worker_score(params))
59            .ok_or(PoolError::InvalidNodeID(node_id))
60    }
61
62    /// Diff the new parameters and push the changes into the event queue.
63    fn diff<B: AudioBackend>(
64        baseline: &SamplerNode,
65        new: &SamplerNode,
66        event_queue: &mut ContextQueue<B>,
67    ) {
68        new.diff(baseline, PathBuilder::default(), event_queue);
69    }
70
71    /// Notify the node state that a sequence is playing/stopped.
72    ///
73    /// If `stopped` is `true`, then the sequence has been stopped. If `stopped` is
74    /// `false`, then a new sequence has been started.
75    ///
76    /// This is used to account for the delay between sending an event to the node
77    /// and the node receiving the event.
78    ///
79    /// Return an error if the given `node_id` is invalid.
80    fn mark_stopped<B: AudioBackend>(
81        stopped: bool,
82        node_id: NodeID,
83        cx: &mut FirewheelCtx<B>,
84    ) -> Result<(), PoolError> {
85        cx.node_state_mut::<SamplerState>(node_id)
86            .map(|s| s.mark_stopped(stopped))
87            .ok_or(PoolError::InvalidNodeID(node_id))
88    }
89
90    /// Pause the sequence in the node parameters
91    fn pause(params: &mut SamplerNode) {
92        params.pause();
93    }
94    /// Resume the sequence in the node parameters
95    fn resume(params: &mut SamplerNode) {
96        params.resume();
97    }
98    /// Stop the sequence in the node parameters
99    fn stop(params: &mut SamplerNode) {
100        params.stop();
101    }
102}