Skip to main content

clack_common/process/
constant_mask.rs

1use std::fmt::{Debug, Formatter};
2use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign};
3
4/// A hint that indicates which channels of an audio port are constant.
5///
6/// Channels are defined as constant when all of their samples have the exact same value. This
7/// however doesn't mean they are _silent_: the value could be different from `0`.
8///
9/// This type is internally by a 64-bit bitmask, as per the CLAP specification, hence its name. This
10/// also means a constant mask can only hold constant hints for up to 64 channels: information about
11/// any extra channels is lost, and this implementation considers those channels to never be
12/// constant.
13///
14/// # Example
15///
16/// ```
17/// use clack_common::process::ConstantMask;
18///
19/// let mut constant_mask = ConstantMask::from_bits(0b101);
20///
21/// assert!(constant_mask.is_channel_constant(0));
22/// assert!(!constant_mask.is_channel_constant(1));
23/// assert!(constant_mask.is_channel_constant(2));
24///
25/// constant_mask.set_channel_constant(1, true);
26/// assert!(constant_mask.is_channel_constant(1));
27/// assert_eq!(0b111, constant_mask.to_bits());
28///
29/// ```
30#[derive(Copy, Clone, Eq, PartialEq)]
31#[repr(transparent)]
32pub struct ConstantMask(u64);
33
34impl ConstantMask {
35    /// The capacity, in bits, of a [`ConstantMask`].
36    ///
37    /// This is how many channels can be represented in a [`ConstantMask`].
38    pub const CAPACITY: u8 = 64;
39
40    /// A [`ConstantMask`] that represents all 64 possible channels as being fully constant.
41    /// This constant mask has all its bits set to `1`.
42    pub const FULLY_CONSTANT: ConstantMask = ConstantMask(u64::MAX);
43    /// A [`ConstantMask`] that represents all 64 possible channels as being fully dynamic.
44    /// This constant mask has all its bits set to `0`.
45    pub const FULLY_DYNAMIC: ConstantMask = ConstantMask(0);
46
47    /// Creates a new constant mask from its inner bitmask representation.
48    #[inline]
49    pub const fn from_bits(bits: u64) -> Self {
50        ConstantMask(bits)
51    }
52
53    /// Gets the constant mask's inner bitmask representation.
54    #[inline]
55    pub const fn to_bits(&self) -> u64 {
56        self.0
57    }
58
59    /// Returns an iterator over the constant status of each channel in this constant mask.
60    ///
61    /// # Example
62    ///
63    /// ```
64    /// use clack_common::process::ConstantMask;
65    ///
66    /// let mut iter = ConstantMask::from_bits(0b101).iter();
67    ///
68    /// assert_eq!(Some(true), iter.next());
69    /// assert_eq!(Some(false), iter.next());
70    /// assert_eq!(Some(true), iter.next());
71    /// assert_eq!(Some(false), iter.next());
72    /// assert_eq!(Some(false), iter.next());
73    /// ```
74    #[inline]
75    pub const fn iter(&self) -> ConstantMaskIter {
76        ConstantMaskIter(self.0)
77    }
78
79    /// Returns `true` if the channel at the given index is constant, `false` otherwise.
80    ///
81    /// This function will always return `false` when given any index over `63`.
82    #[inline]
83    pub const fn is_channel_constant(&self, channel_index: u64) -> bool {
84        if channel_index > 63 {
85            return false;
86        }
87
88        (self.0 & (1 << channel_index)) != 0
89    }
90
91    /// Sets whether the channel at the given index is constant.
92    ///
93    /// This function does nothing when given any index over `63`.
94    #[inline]
95    pub const fn set_channel_constant(&mut self, channel_index: u64, value: bool) {
96        if channel_index > 63 {
97            return;
98        }
99
100        if value {
101            self.0 |= 1 << channel_index
102        } else {
103            self.0 &= !(1 << channel_index)
104        }
105    }
106}
107
108impl Debug for ConstantMask {
109    #[inline]
110    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
111        core::fmt::Binary::fmt(&self.0, f)
112    }
113}
114
115impl Default for ConstantMask {
116    /// Returns an empty constant mask, i.e. one where every channel is considered dynamic.
117    #[inline]
118    fn default() -> Self {
119        ConstantMask::FULLY_DYNAMIC
120    }
121}
122
123impl IntoIterator for ConstantMask {
124    type Item = bool;
125    type IntoIter = ConstantMaskIter;
126
127    #[inline]
128    fn into_iter(self) -> Self::IntoIter {
129        self.iter()
130    }
131}
132
133impl IntoIterator for &ConstantMask {
134    type Item = bool;
135    type IntoIter = ConstantMaskIter;
136
137    #[inline]
138    fn into_iter(self) -> Self::IntoIter {
139        self.iter()
140    }
141}
142
143impl IntoIterator for &mut ConstantMask {
144    type Item = bool;
145    type IntoIter = ConstantMaskIter;
146
147    #[inline]
148    fn into_iter(self) -> Self::IntoIter {
149        self.iter()
150    }
151}
152
153impl BitAnd for ConstantMask {
154    type Output = ConstantMask;
155
156    #[inline]
157    fn bitand(self, rhs: Self) -> Self::Output {
158        ConstantMask(self.0 & rhs.0)
159    }
160}
161
162impl BitOr for ConstantMask {
163    type Output = ConstantMask;
164
165    #[inline]
166    fn bitor(self, rhs: Self) -> Self::Output {
167        ConstantMask(self.0 | rhs.0)
168    }
169}
170
171impl BitXor for ConstantMask {
172    type Output = ConstantMask;
173
174    #[inline]
175    fn bitxor(self, rhs: Self) -> Self::Output {
176        ConstantMask(self.0 ^ rhs.0)
177    }
178}
179
180impl BitAndAssign for ConstantMask {
181    #[inline]
182    fn bitand_assign(&mut self, rhs: Self) {
183        self.0 &= rhs.0
184    }
185}
186
187impl BitOrAssign for ConstantMask {
188    #[inline]
189    fn bitor_assign(&mut self, rhs: Self) {
190        self.0 |= rhs.0
191    }
192}
193
194impl BitXorAssign for ConstantMask {
195    #[inline]
196    fn bitxor_assign(&mut self, rhs: Self) {
197        self.0 ^= rhs.0
198    }
199}
200
201/// An iterator over the constant status of each channel in a [`ConstantMask`].
202///
203/// This iterator is infinite: once all channels have been iterated over, it will return `false`
204/// continuously. Use it alongside a limiting iterator adapter, such as [`zip`](Iterator::zip) or
205/// [`take`](Iterator::take).
206///
207/// # Example
208///
209/// ```
210/// use clack_common::process::ConstantMask;
211///
212/// let mut iter = ConstantMask::from_bits(0b101).iter();
213///
214/// assert_eq!(Some(true), iter.next());
215/// assert_eq!(Some(false), iter.next());
216/// assert_eq!(Some(true), iter.next());
217/// assert_eq!(Some(false), iter.next());
218/// assert_eq!(Some(false), iter.next());
219/// ```
220#[derive(Copy, Clone)]
221pub struct ConstantMaskIter(u64);
222
223impl Iterator for ConstantMaskIter {
224    type Item = bool;
225
226    #[inline]
227    fn next(&mut self) -> Option<Self::Item> {
228        let is_constant = self.0 & 1 != 0;
229        self.0 >>= 1;
230        Some(is_constant)
231    }
232
233    #[inline]
234    fn size_hint(&self) -> (usize, Option<usize>) {
235        (usize::MAX, None)
236    }
237
238    #[inline]
239    fn nth(&mut self, n: usize) -> Option<bool> {
240        self.0 >>= n;
241
242        self.next()
243    }
244}