firewheel_core/
channel_config.rs

1use core::{error::Error, fmt, num::NonZeroU32};
2
3pub const MAX_CHANNELS: usize = 64;
4
5/// A supported number of channels on an audio node.
6///
7/// This number cannot be greater than `64`.
8#[repr(transparent)]
9#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
10#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
11#[cfg_attr(feature = "bevy_reflect", reflect(opaque))]
12pub struct ChannelCount(u32);
13
14impl ChannelCount {
15    pub const ZERO: Self = Self(0);
16    pub const MONO: Self = Self(1);
17    pub const STEREO: Self = Self(2);
18    pub const MAX: Self = Self(MAX_CHANNELS as u32);
19
20    /// Create a new [`ChannelCount`].
21    ///
22    /// Returns `None` if `count` is greater than `64`.
23    #[inline]
24    pub const fn new(count: u32) -> Option<Self> {
25        if count <= 64 {
26            Some(Self(count))
27        } else {
28            None
29        }
30    }
31
32    #[inline]
33    pub const fn get(&self) -> u32 {
34        if self.0 <= 64 {
35            self.0
36        } else {
37            // SAFETY:
38            // The constructor ensures that the value is less than or
39            // equal to `64`.
40            unsafe { core::hint::unreachable_unchecked() }
41        }
42    }
43}
44
45impl From<usize> for ChannelCount {
46    fn from(value: usize) -> Self {
47        Self::new(value as u32).unwrap()
48    }
49}
50
51impl From<ChannelCount> for u32 {
52    fn from(value: ChannelCount) -> Self {
53        value.get()
54    }
55}
56
57impl From<ChannelCount> for usize {
58    fn from(value: ChannelCount) -> Self {
59        value.get() as usize
60    }
61}
62
63/// A supported number of channels on an audio node.
64///
65/// This number cannot be `0` or greater than `64`.
66#[repr(transparent)]
67#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
68#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
69#[cfg_attr(feature = "bevy_reflect", reflect(opaque))]
70pub struct NonZeroChannelCount(NonZeroU32);
71
72impl NonZeroChannelCount {
73    pub const MONO: Self = Self(NonZeroU32::new(1).unwrap());
74    pub const STEREO: Self = Self(NonZeroU32::new(2).unwrap());
75    pub const MAX: Self = Self(NonZeroU32::new(64).unwrap());
76
77    /// Create a new [`NonZeroChannelCount`].
78    ///
79    /// Returns `None` if `count` is greater than `64`.
80    #[inline]
81    pub const fn new(count: u32) -> Option<Self> {
82        if count > 0 && count <= 64 {
83            Some(Self(NonZeroU32::new(count).unwrap()))
84        } else {
85            None
86        }
87    }
88
89    #[inline]
90    pub const fn get(&self) -> ChannelCount {
91        if self.0.get() <= 64 {
92            ChannelCount(self.0.get())
93        } else {
94            // SAFETY:
95            // The constructor ensures that the value is less than or
96            // equal to `64`.
97            unsafe { core::hint::unreachable_unchecked() }
98        }
99    }
100}
101
102impl Default for NonZeroChannelCount {
103    fn default() -> Self {
104        Self::STEREO
105    }
106}
107
108impl From<usize> for NonZeroChannelCount {
109    fn from(value: usize) -> Self {
110        Self::new(value as u32).unwrap()
111    }
112}
113
114impl From<NonZeroChannelCount> for NonZeroU32 {
115    fn from(value: NonZeroChannelCount) -> Self {
116        NonZeroU32::new(value.get().get()).unwrap()
117    }
118}
119
120impl From<NonZeroChannelCount> for usize {
121    fn from(value: NonZeroChannelCount) -> Self {
122        value.get().get() as usize
123    }
124}
125
126/// A supported number of channels on an audio node.
127#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
128#[cfg_attr(feature = "bevy", derive(bevy_ecs::prelude::Component))]
129pub struct ChannelConfig {
130    pub num_inputs: ChannelCount,
131    pub num_outputs: ChannelCount,
132}
133
134impl ChannelConfig {
135    pub fn new(num_inputs: impl Into<ChannelCount>, num_outputs: impl Into<ChannelCount>) -> Self {
136        Self {
137            num_inputs: num_inputs.into(),
138            num_outputs: num_outputs.into(),
139        }
140    }
141
142    pub fn verify(
143        &self,
144        min_num_inputs: ChannelCount,
145        max_num_inputs: ChannelCount,
146        min_num_outputs: ChannelCount,
147        max_num_outputs: ChannelCount,
148        equal_num_ins_outs: bool,
149    ) -> Result<(), ChannelConfigError> {
150        if self.num_inputs.get() < min_num_inputs.get()
151            || self.num_inputs.get() > max_num_inputs.get()
152        {
153            Err(ChannelConfigError::InvalidNumInputs {
154                min: min_num_inputs,
155                max: max_num_inputs,
156                got: self.num_inputs,
157            })
158        } else if self.num_outputs.get() < min_num_outputs.get()
159            || self.num_outputs.get() > max_num_outputs.get()
160        {
161            Err(ChannelConfigError::InvalidNumOutputs {
162                min: min_num_outputs,
163                max: max_num_outputs,
164                got: self.num_outputs,
165            })
166        } else if equal_num_ins_outs && self.num_inputs.get() != self.num_outputs.get() {
167            Err(ChannelConfigError::NumInOutNotEqual {
168                got_in: self.num_inputs,
169                got_out: self.num_outputs,
170            })
171        } else {
172            Ok(())
173        }
174    }
175}
176
177impl From<(usize, usize)> for ChannelConfig {
178    fn from(value: (usize, usize)) -> Self {
179        Self::new(value.0, value.1)
180    }
181}
182
183/// An invalid channel configuration
184#[derive(Debug)]
185pub enum ChannelConfigError {
186    InvalidNumInputs {
187        min: ChannelCount,
188        max: ChannelCount,
189        got: ChannelCount,
190    },
191    InvalidNumOutputs {
192        min: ChannelCount,
193        max: ChannelCount,
194        got: ChannelCount,
195    },
196    NumInOutNotEqual {
197        got_in: ChannelCount,
198        got_out: ChannelCount,
199    },
200}
201
202impl Error for ChannelConfigError {}
203
204impl fmt::Display for ChannelConfigError {
205    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
206        match self {
207            Self::InvalidNumInputs { min, max, got } => {
208                write!(
209                    f,
210                    "Invalid number of input channels on audio node | got: {}, min: {}, max: {}",
211                    got.get(),
212                    min.get(),
213                    max.get()
214                )
215            }
216            Self::InvalidNumOutputs { min, max, got } => {
217                write!(
218                    f,
219                    "Invalid number of output channels on audio node | got: {}, min: {}, max: {}",
220                    got.get(),
221                    min.get(),
222                    max.get()
223                )
224            }
225            Self::NumInOutNotEqual { got_in, got_out } => {
226                write!(f, "Number of input channels does not equal number of output channels | in: {}, out: {}", got_in.get(), got_out.get())
227            }
228        }
229    }
230}