nandi/jolt-nativepublic Fork 0
cfd3e3677bed92e80cfe447469a249cfdc0b1401
Commits
Clone
git clone https://git.rickub.com/nandi/jolt-native.git
git clone ssh://git@rickub.com/nandi/jolt-native.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

device.rs · 289 lines · 10.2 KBRust Blame HistoryRaw
Lift freeq's AV media plane out of sleek 90f8b89 nandi 19d ago1use crate::traits::DeviceTrait;
2use crate::{
3 BackendSpecificError, BuildStreamError, Data, DefaultStreamConfigError, DeviceDescription,
4 DeviceDescriptionBuilder, DeviceDirection, DeviceId, DeviceIdError, DeviceNameError,
5 InputCallbackInfo, OutputCallbackInfo, SampleFormat, SampleRate, StreamConfig, StreamError,
6 SupportedBufferSize, SupportedStreamConfig, SupportedStreamConfigRange,
7 SupportedStreamConfigsError,
8};
9use std::hash::{Hash, Hasher};
10use std::time::Duration;
11
12use super::stream::Stream;
13use super::JACK_SAMPLE_FORMAT;
14
15pub use crate::iter::{SupportedInputConfigs, SupportedOutputConfigs};
16
17const DEFAULT_NUM_CHANNELS: u16 = 2;
18const DEFAULT_SUPPORTED_CHANNELS: [u16; 10] = [1, 2, 4, 6, 8, 16, 24, 32, 48, 64];
19
20#[derive(Clone, Debug)]
21pub struct Device {
22 name: String,
23 sample_rate: SampleRate,
24 buffer_size: SupportedBufferSize,
25 direction: DeviceDirection,
26 start_server_automatically: bool,
27 connect_ports_automatically: bool,
28}
29
30impl Device {
31 fn new_device(
32 name: String,
33 connect_ports_automatically: bool,
34 start_server_automatically: bool,
35 direction: DeviceDirection,
36 ) -> Result<Self, String> {
37 // ClientOptions are bit flags that you can set with the constants provided
38 let client_options = super::get_client_options(start_server_automatically);
39
40 // Create a dummy client to find out the sample rate of the server to be able to provide it as a possible config.
41 // This client will be dropped, and a new one will be created when making the stream.
42 // This is a hack due to the fact that the Client must be moved to create the AsyncClient.
43 match super::get_client(&name, client_options) {
44 Ok(client) => Ok(Device {
45 // The name given to the client by JACK, could potentially be different from the name supplied e.g.if there is a name collision
46 name: client.name().to_string(),
47 sample_rate: client.sample_rate(),
48 buffer_size: SupportedBufferSize::Range {
49 min: client.buffer_size(),
50 max: client.buffer_size(),
51 },
52 direction,
53 start_server_automatically,
54 connect_ports_automatically,
55 }),
56 Err(e) => Err(e),
57 }
58 }
59
60 fn id(&self) -> Result<DeviceId, DeviceIdError> {
61 Ok(DeviceId(crate::platform::HostId::Jack, self.name.clone()))
62 }
63
64 pub fn default_output_device(
65 name: &str,
66 connect_ports_automatically: bool,
67 start_server_automatically: bool,
68 ) -> Result<Self, String> {
69 let output_client_name = format!("{}_out", name);
70 Device::new_device(
71 output_client_name,
72 connect_ports_automatically,
73 start_server_automatically,
74 DeviceDirection::Output,
75 )
76 }
77
78 pub fn default_input_device(
79 name: &str,
80 connect_ports_automatically: bool,
81 start_server_automatically: bool,
82 ) -> Result<Self, String> {
83 let input_client_name = format!("{}_in", name);
84 Device::new_device(
85 input_client_name,
86 connect_ports_automatically,
87 start_server_automatically,
88 DeviceDirection::Input,
89 )
90 }
91
92 pub fn default_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
93 let channels = DEFAULT_NUM_CHANNELS;
94 let sample_rate = self.sample_rate;
95 let buffer_size = self.buffer_size;
96 // The sample format for JACK audio ports is always "32-bit float mono audio" in the current implementation.
97 // Custom formats are allowed within JACK, but this is of niche interest.
98 // The format can be found programmatically by calling jack::PortSpec::port_type() on a created port.
99 let sample_format = JACK_SAMPLE_FORMAT;
100 Ok(SupportedStreamConfig {
101 channels,
102 sample_rate,
103 buffer_size,
104 sample_format,
105 })
106 }
107
108 pub fn supported_configs(&self) -> Vec<SupportedStreamConfigRange> {
109 let f = match self.default_config() {
110 Err(_) => return vec![],
111 Ok(f) => f,
112 };
113
114 let mut supported_configs = vec![];
115
116 for &channels in DEFAULT_SUPPORTED_CHANNELS.iter() {
117 supported_configs.push(SupportedStreamConfigRange {
118 channels,
119 min_sample_rate: f.sample_rate,
120 max_sample_rate: f.sample_rate,
121 buffer_size: f.buffer_size,
122 sample_format: f.sample_format,
123 });
124 }
125 supported_configs
126 }
127
128 pub fn is_input(&self) -> bool {
129 matches!(self.direction, DeviceDirection::Input)
130 }
131
132 pub fn is_output(&self) -> bool {
133 matches!(self.direction, DeviceDirection::Output)
134 }
135
136 /// Validate buffer size if Fixed is specified. This is necessary because JACK buffer size
137 /// is controlled by the JACK server and cannot be changed by clients. Without validation,
138 /// cpal would silently use the server's buffer size even if a different value was requested.
139 fn validate_buffer_size(&self, conf: StreamConfig) -> Result<(), BuildStreamError> {
140 if let crate::BufferSize::Fixed(requested_size) = conf.buffer_size {
141 if let SupportedBufferSize::Range { min, max } = self.buffer_size {
142 if !(min..=max).contains(&requested_size) {
143 return Err(BuildStreamError::StreamConfigNotSupported);
144 }
145 }
146 }
147 Ok(())
148 }
149}
150
151impl DeviceTrait for Device {
152 type SupportedInputConfigs = SupportedInputConfigs;
153 type SupportedOutputConfigs = SupportedOutputConfigs;
154 type Stream = Stream;
155
156 fn description(&self) -> Result<DeviceDescription, DeviceNameError> {
157 Ok(DeviceDescriptionBuilder::new(self.name.clone())
158 .direction(self.direction)
159 .build())
160 }
161
162 fn id(&self) -> Result<DeviceId, DeviceIdError> {
163 Device::id(self)
164 }
165
166 fn supported_input_configs(
167 &self,
168 ) -> Result<Self::SupportedInputConfigs, SupportedStreamConfigsError> {
169 Ok(self.supported_configs().into_iter())
170 }
171
172 fn supported_output_configs(
173 &self,
174 ) -> Result<Self::SupportedOutputConfigs, SupportedStreamConfigsError> {
175 Ok(self.supported_configs().into_iter())
176 }
177
178 /// Returns the default input config
179 /// The sample format for JACK audio ports is always "32-bit float mono audio" unless using a custom type.
180 /// The sample rate is set by the JACK server.
181 fn default_input_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
182 self.default_config()
183 }
184
185 /// Returns the default output config
186 /// The sample format for JACK audio ports is always "32-bit float mono audio" unless using a custom type.
187 /// The sample rate is set by the JACK server.
188 fn default_output_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
189 self.default_config()
190 }
191
192 fn build_input_stream_raw<D, E>(
193 &self,
194 conf: StreamConfig,
195 sample_format: SampleFormat,
196 data_callback: D,
197 error_callback: E,
198 _timeout: Option<Duration>,
199 ) -> Result<Self::Stream, BuildStreamError>
200 where
201 D: FnMut(&Data, &InputCallbackInfo) + Send + 'static,
202 E: FnMut(StreamError) + Send + 'static,
203 {
204 if self.is_output() {
205 // Trying to create an input stream from an output device
206 return Err(BuildStreamError::StreamConfigNotSupported);
207 }
208 if conf.sample_rate != self.sample_rate || sample_format != JACK_SAMPLE_FORMAT {
209 return Err(BuildStreamError::StreamConfigNotSupported);
210 }
211 self.validate_buffer_size(conf)?;
212
213 // The settings should be fine, create a Client
214 let client_options = super::get_client_options(self.start_server_automatically);
215 let client;
216 match super::get_client(&self.name, client_options) {
217 Ok(c) => client = c,
218 Err(e) => {
219 return Err(BuildStreamError::BackendSpecific {
220 err: BackendSpecificError { description: e },
221 })
222 }
223 };
224 let mut stream = Stream::new_input(client, conf.channels, data_callback, error_callback);
225
226 if self.connect_ports_automatically {
227 stream.connect_to_system_inputs();
228 }
229
230 Ok(stream)
231 }
232
233 fn build_output_stream_raw<D, E>(
234 &self,
235 conf: StreamConfig,
236 sample_format: SampleFormat,
237 data_callback: D,
238 error_callback: E,
239 _timeout: Option<Duration>,
240 ) -> Result<Self::Stream, BuildStreamError>
241 where
242 D: FnMut(&mut Data, &OutputCallbackInfo) + Send + 'static,
243 E: FnMut(StreamError) + Send + 'static,
244 {
245 if self.is_input() {
246 // Trying to create an output stream from an input device
247 return Err(BuildStreamError::StreamConfigNotSupported);
248 }
249 if conf.sample_rate != self.sample_rate || sample_format != JACK_SAMPLE_FORMAT {
250 return Err(BuildStreamError::StreamConfigNotSupported);
251 }
252 self.validate_buffer_size(conf)?;
253
254 // The settings should be fine, create a Client
255 let client_options = super::get_client_options(self.start_server_automatically);
256 let client;
257 match super::get_client(&self.name, client_options) {
258 Ok(c) => client = c,
259 Err(e) => {
260 return Err(BuildStreamError::BackendSpecific {
261 err: BackendSpecificError { description: e },
262 })
263 }
264 };
265 let mut stream = Stream::new_output(client, conf.channels, data_callback, error_callback);
266
267 if self.connect_ports_automatically {
268 stream.connect_to_system_outputs();
269 }
270
271 Ok(stream)
272 }
273}
274
275impl PartialEq for Device {
276 fn eq(&self, other: &Self) -> bool {
277 // Device::id() can never fail in this implementation
278 self.id().unwrap() == other.id().unwrap()
279 }
280}
281
282impl Eq for Device {}
283
284impl Hash for Device {
285 fn hash<H: Hasher>(&self, state: &mut H) {
286 // Device::id() can never fail in this implementation
287 self.id().unwrap().hash(state);
288 }
289}