1use crate::format::StreamFormat;
13use crate::property::PropertyScope;
14
15#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
17pub enum StreamDirection {
18 Input,
21 Output,
24}
25
26impl StreamDirection {
27 #[inline]
30 #[must_use]
31 pub const fn as_property_value(self) -> u32 {
32 match self {
33 Self::Input => 1,
34 Self::Output => 0,
35 }
36 }
37
38 #[inline]
42 #[must_use]
43 pub const fn from_property_value(value: u32) -> Self {
44 if value == 0 {
45 Self::Output
46 } else {
47 Self::Input
48 }
49 }
50
51 #[inline]
54 #[must_use]
55 pub const fn scope(self) -> PropertyScope {
56 match self {
57 Self::Input => PropertyScope::INPUT,
58 Self::Output => PropertyScope::OUTPUT,
59 }
60 }
61
62 #[inline]
64 #[must_use]
65 pub const fn opposite(self) -> Self {
66 match self {
67 Self::Input => Self::Output,
68 Self::Output => Self::Input,
69 }
70 }
71}
72
73#[derive(Copy, Clone, PartialEq, Debug)]
83pub struct StreamSpec {
84 direction: StreamDirection,
85 starting_channel: u32,
86 format: StreamFormat,
87}
88
89impl StreamSpec {
90 #[inline]
92 #[must_use]
93 pub const fn input(format: StreamFormat) -> Self {
94 Self {
95 direction: StreamDirection::Input,
96 starting_channel: 1,
97 format,
98 }
99 }
100
101 #[inline]
103 #[must_use]
104 pub const fn output(format: StreamFormat) -> Self {
105 Self {
106 direction: StreamDirection::Output,
107 starting_channel: 1,
108 format,
109 }
110 }
111
112 #[inline]
115 #[must_use]
116 pub const fn with_starting_channel(mut self, channel: u32) -> Self {
117 self.starting_channel = channel;
118 self
119 }
120
121 #[inline]
123 #[must_use]
124 pub const fn with_format(mut self, format: StreamFormat) -> Self {
125 self.format = format;
126 self
127 }
128
129 #[inline]
131 #[must_use]
132 pub const fn direction(&self) -> StreamDirection {
133 self.direction
134 }
135
136 #[inline]
139 #[must_use]
140 pub const fn starting_channel(&self) -> u32 {
141 self.starting_channel
142 }
143
144 #[inline]
147 #[must_use]
148 pub const fn format(&self) -> StreamFormat {
149 self.format
150 }
151
152 #[inline]
154 #[must_use]
155 pub const fn channels(&self) -> u32 {
156 self.format.channels()
157 }
158}
159
160#[cfg(test)]
161mod tests {
162 use super::*;
163
164 #[test]
165 fn direction_property_values_match_core_audio() {
166 assert_eq!(StreamDirection::Input.as_property_value(), 1);
167 assert_eq!(StreamDirection::Output.as_property_value(), 0);
168 }
169
170 #[test]
171 fn direction_round_trips_through_property_value() {
172 assert_eq!(
173 StreamDirection::from_property_value(1),
174 StreamDirection::Input
175 );
176 assert_eq!(
177 StreamDirection::from_property_value(0),
178 StreamDirection::Output
179 );
180 assert_eq!(
182 StreamDirection::from_property_value(99),
183 StreamDirection::Input
184 );
185 }
186
187 #[test]
188 fn direction_scopes_and_opposites() {
189 assert_eq!(StreamDirection::Input.scope(), PropertyScope::INPUT);
190 assert_eq!(StreamDirection::Output.scope(), PropertyScope::OUTPUT);
191 assert_eq!(StreamDirection::Input.opposite(), StreamDirection::Output);
192 assert_eq!(StreamDirection::Output.opposite(), StreamDirection::Input);
193 }
194
195 #[test]
196 fn input_stream_defaults() {
197 let s = StreamSpec::input(StreamFormat::float32(48_000.0, 2));
198 assert_eq!(s.direction(), StreamDirection::Input);
199 assert_eq!(s.starting_channel(), 1);
200 assert_eq!(s.channels(), 2);
201 assert!(s.format().is_canonical());
202 }
203
204 #[test]
205 fn output_stream_defaults() {
206 let s = StreamSpec::output(StreamFormat::float32(44_100.0, 1));
207 assert_eq!(s.direction(), StreamDirection::Output);
208 assert_eq!(s.starting_channel(), 1);
209 assert_eq!(s.channels(), 1);
210 }
211
212 #[test]
213 fn builder_setters_apply() {
214 let s = StreamSpec::output(StreamFormat::float32(48_000.0, 2))
215 .with_starting_channel(3)
216 .with_format(StreamFormat::float32(96_000.0, 4));
217 assert_eq!(s.starting_channel(), 3);
218 assert_eq!(s.format().sample_rate(), 96_000.0);
219 assert_eq!(s.channels(), 4);
220 }
221}