firewheel_core/
channel_config.rs

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