Skip to main content

audiograph/
channel.rs

1use bitvec::{array::BitArray, slice::IterOnes};
2
3use crate::AudioGraphError;
4
5include!(concat!(env!("OUT_DIR"), "/constants.rs"));
6
7const WORDS: usize = (MAX_CHANNELS - 1) / 64 + 1;
8
9/// Compact representation of an audio channel selection
10///
11/// Selected channel indices are encoded using a bit array, leading to a very small memory footprint.
12/// A channel selection is meant to be copied wherever it is needed. It includes methods for checking
13/// and modifying a channel selection status, as well as iterating over selected channel indices.
14///
15/// In the context of audio processing, a channel selection is synonymous with a "channel connection" between
16/// input and output buffers.
17pub type ChannelSelection = ChannelSelectionImpl<MAX_CHANNELS, WORDS>;
18
19#[derive(Clone)]
20pub struct ChannelSelectionImpl<const NUM_CHANNELS: usize, const WORDS: usize> {
21    bits: BitArray<[u64; WORDS]>,
22}
23
24impl<const NUM_CHANNELS: usize, const WORDS: usize> Default
25    for ChannelSelectionImpl<NUM_CHANNELS, WORDS>
26{
27    /// Creates a default selection, where every channel is selected
28    fn default() -> Self {
29        Self::new(NUM_CHANNELS)
30    }
31}
32
33impl<const NUM_CHANNELS: usize, const WORDS: usize> ChannelSelectionImpl<NUM_CHANNELS, WORDS> {
34    /// Creates a channel selection with the first `num_connected` channels connected and remaining channels disconnected
35    pub fn new(num_connected: usize) -> Self {
36        let mut bits = BitArray::ZERO;
37        let count = num_connected.min(NUM_CHANNELS);
38        bits[..count].fill(true);
39        Self { bits }
40    }
41
42    /// Creates a channel selection from a slice of connected channel indices
43    ///
44    /// # Example
45    /// ```rust,ignore
46    /// let selection = ChannelSelection::from_indices(&[0, 2, 4]);
47    /// ```
48    pub fn from_indices(indices: &[usize]) -> Self {
49        let mut selection = ChannelSelectionImpl::new(0);
50        for &index in indices {
51            selection
52                .connect(index)
53                .expect("Channel index is out of bounds");
54        }
55        selection
56    }
57
58    /// Defines a specific channel as selected / connected
59    pub fn connect(&mut self, channel: usize) -> Result<(), AudioGraphError> {
60        if channel >= NUM_CHANNELS {
61            return Err("Channel index is out of bounds");
62        }
63
64        self.bits.set(channel, true);
65        Ok(())
66    }
67
68    pub fn disconnect(&mut self, channel: usize) -> Result<(), AudioGraphError> {
69        if channel >= NUM_CHANNELS {
70            return Err("Channel index is out of bounds");
71        }
72
73        self.bits.set(channel, false);
74        Ok(())
75    }
76
77    /// Checks if a specifc channel is selected / connected
78    pub fn is_connected(&self, channel: usize) -> bool {
79        if channel < NUM_CHANNELS {
80            self.bits[channel]
81        } else {
82            false
83        }
84    }
85
86    /// Create iterator over all selected / connected channels
87    pub fn iter(&self) -> IterOnes<'_, u64, bitvec::order::Lsb0> {
88        self.bits.iter_ones()
89    }
90
91    /// Unselects / disconnects all channels at index `max_channels` and above
92    pub fn clamp(&mut self, max_channels: usize) {
93        self.bits.split_at_mut(max_channels).1.fill(false);
94    }
95
96    /// Returns the highest index of all currently selected / connected channels
97    pub fn index_of_last_connected(&self) -> Option<usize> {
98        self.bits.last_one()
99    }
100
101    /// Combines this channel selection with `other` (bitwise OR operation)
102    pub fn combine(&mut self, other: &Self) {
103        self.bits |= other.bits;
104    }
105}
106
107#[cfg(test)]
108mod tests {
109    use super::*;
110
111    #[test]
112    fn test_channel_selection_creation() {
113        let selection = ChannelSelection::new(3);
114        assert!(selection.is_connected(0));
115        assert!(selection.is_connected(1));
116        assert!(selection.is_connected(2));
117        assert!(!selection.is_connected(3));
118        assert!(!selection.is_connected(63));
119        assert!(!selection.is_connected(64));
120    }
121
122    #[test]
123    fn test_channel_selection_clamping() {
124        let mut selection = ChannelSelection::new(5);
125        selection.clamp(3);
126        assert!(selection.is_connected(0));
127        assert!(selection.is_connected(1));
128        assert!(selection.is_connected(2));
129        assert!(!selection.is_connected(3));
130        assert!(!selection.is_connected(4));
131    }
132}