1 // Copyright 2019 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 extern crate audio_streams;
5 extern crate data_model;
6
7 use std::cmp::min;
8 use std::convert::{TryFrom, TryInto};
9 use std::error;
10 use std::fmt;
11 use std::iter::FromIterator;
12 use std::os::raw::c_char;
13 use std::str::FromStr;
14 use std::time::Duration;
15
16 #[allow(dead_code)]
17 #[allow(non_upper_case_globals)]
18 #[allow(non_camel_case_types)]
19 #[allow(non_snake_case)]
20 pub mod gen;
21 use gen::{
22 _snd_pcm_format, audio_dev_debug_info, audio_message, audio_stream_debug_info,
23 cras_audio_format_packed, cras_iodev_info, cras_ionode_info, cras_ionode_info__bindgen_ty_1,
24 cras_timespec, snd_pcm_format_t, CRAS_AUDIO_MESSAGE_ID, CRAS_CHANNEL, CRAS_CLIENT_TYPE,
25 CRAS_NODE_TYPE, CRAS_STREAM_DIRECTION, CRAS_STREAM_EFFECT, CRAS_STREAM_TYPE,
26 };
27
28 use audio_streams::{SampleFormat, StreamDirection, StreamEffect};
29
30 unsafe impl data_model::DataInit for gen::audio_message {}
31 unsafe impl data_model::DataInit for gen::audio_debug_info {}
32 unsafe impl data_model::DataInit for gen::audio_dev_debug_info {}
33 unsafe impl data_model::DataInit for gen::audio_stream_debug_info {}
34 unsafe impl data_model::DataInit for gen::cras_client_connected {}
35 unsafe impl data_model::DataInit for gen::cras_client_stream_connected {}
36 unsafe impl data_model::DataInit for gen::cras_connect_message {}
37 unsafe impl data_model::DataInit for gen::cras_disconnect_stream_message {}
38 unsafe impl data_model::DataInit for gen::cras_dump_audio_thread {}
39 unsafe impl data_model::DataInit for gen::cras_iodev_info {}
40 unsafe impl data_model::DataInit for gen::cras_ionode_info {}
41 unsafe impl data_model::DataInit for gen::cras_server_state {}
42 unsafe impl data_model::DataInit for gen::cras_set_system_mute {}
43 unsafe impl data_model::DataInit for gen::cras_set_system_volume {}
44
45 /// An enumeration of errors that can occur when converting the packed C
46 /// structs into Rust-style structs.
47 #[derive(Debug)]
48 pub enum Error {
49 InvalidChannel(i8),
50 InvalidClientType(u32),
51 InvalidClientTypeStr,
52 InvalidStreamType(u32),
53 }
54
55 impl error::Error for Error {}
56
57 impl fmt::Display for Error {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result58 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
59 use Error::*;
60 match self {
61 InvalidChannel(c) => write!(
62 f,
63 "Channel value {} is not within valid range [0, {})",
64 c,
65 CRAS_CHANNEL::CRAS_CH_MAX as u32
66 ),
67 InvalidClientType(t) => write!(
68 f,
69 "Client type {} is not within valid range [0, {})",
70 t,
71 CRAS_CLIENT_TYPE::CRAS_CLIENT_TYPE_SERVER_STREAM as u32 + 1
72 ),
73 InvalidClientTypeStr => write!(f, "Invalid client type string"),
74 InvalidStreamType(t) => write!(
75 f,
76 "Stream type {} is not within valid range [0, {})",
77 t,
78 CRAS_STREAM_TYPE::CRAS_STREAM_NUM_TYPES as u32
79 ),
80 }
81 }
82 }
83
84 impl cras_audio_format_packed {
85 /// Initializes `cras_audio_format_packed` from input parameters.
86 /// Field `channel_layout` will be assigned with default channel layout defined in
87 /// `Self::default_channel_layout`.
88 ///
89 /// # Arguments
90 /// * `format` - Format in used.
91 /// * `rate` - Rate in used.
92 /// * `num_channels` - Number of channels in used.
93 /// * `direction` - Stream direction enumeration.
94 ///
95 /// # Returns
96 /// Structure `cras_audio_format_packed`
new( format: _snd_pcm_format, rate: u32, num_channels: usize, direction: CRAS_STREAM_DIRECTION, ) -> Self97 pub fn new(
98 format: _snd_pcm_format,
99 rate: u32,
100 num_channels: usize,
101 direction: CRAS_STREAM_DIRECTION,
102 ) -> Self {
103 Self {
104 format: format as i32,
105 frame_rate: rate,
106 num_channels: num_channels as u32,
107 channel_layout: Self::default_channel_layout(num_channels, direction),
108 }
109 }
110
111 /// Generates default channel layout by given number of channels and stream direction.
112 /// ```
113 /// use cras_sys::gen::{
114 /// _snd_pcm_format,
115 /// cras_audio_format_packed,
116 /// CRAS_STREAM_DIRECTION::*
117 /// };
118 /// let test_one = | num_channels, direction, expected_results | {
119 /// let default_channel_fmt = cras_audio_format_packed::new(
120 /// _snd_pcm_format::SND_PCM_FORMAT_S16,
121 /// 48000,
122 /// num_channels,
123 /// direction
124 /// );
125 /// assert_eq!(default_channel_fmt.channel_layout, expected_results);
126 /// };
127 /// test_one(2, CRAS_STREAM_OUTPUT, [0, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1]);
128 /// test_one(4, CRAS_STREAM_OUTPUT, [0, 1, 2, 3, -1, -1, -1, -1, -1, -1, -1]);
129 /// test_one(6, CRAS_STREAM_OUTPUT, [0, 1, 4, 5, 2, 3, -1, -1, -1, -1, -1]);
130 /// test_one(2, CRAS_STREAM_INPUT, [0, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1]);
131 /// test_one(4, CRAS_STREAM_INPUT, [0, 1, 2, 3, -1, -1, -1, -1, -1, -1, -1]);
132 /// test_one(6, CRAS_STREAM_INPUT, [0, 1, 2, 3, 4, 5, -1, -1, -1, -1, -1]);
133 /// ```
default_channel_layout( num_channels: usize, direction: CRAS_STREAM_DIRECTION, ) -> [i8; CRAS_CHANNEL::CRAS_CH_MAX as usize]134 fn default_channel_layout(
135 num_channels: usize,
136 direction: CRAS_STREAM_DIRECTION,
137 ) -> [i8; CRAS_CHANNEL::CRAS_CH_MAX as usize] {
138 use {CRAS_CHANNEL::*, CRAS_STREAM_DIRECTION::*};
139
140 let mut channel_layout = [-1; CRAS_CH_MAX as usize];
141 match (num_channels, direction) {
142 (6, CRAS_STREAM_OUTPUT) => {
143 [
144 CRAS_CH_FL,
145 CRAS_CH_FR,
146 CRAS_CH_FC,
147 CRAS_CH_LFE,
148 CRAS_CH_RL,
149 CRAS_CH_RR,
150 ]
151 .iter()
152 .enumerate()
153 .for_each(|(idx, &channel)| channel_layout[channel as usize] = idx as i8);
154 }
155 _ => {
156 for (i, channel) in channel_layout
157 .iter_mut()
158 .enumerate()
159 .take(min(num_channels, CRAS_CH_MAX as usize))
160 {
161 *channel = i as i8;
162 }
163 }
164 }
165 channel_layout
166 }
167 }
168
169 impl Default for audio_message {
default() -> Self170 fn default() -> Self {
171 Self {
172 error: 0,
173 frames: 0,
174 id: CRAS_AUDIO_MESSAGE_ID::NUM_AUDIO_MESSAGES,
175 }
176 }
177 }
178
179 impl Default for cras_iodev_info {
default() -> Self180 fn default() -> Self {
181 Self {
182 idx: 0,
183 name: [0; 64usize],
184 stable_id: 0,
185 max_supported_channels: 0,
186 }
187 }
188 }
189
190 #[derive(Debug)]
191 pub struct CrasIodevInfo {
192 pub index: u32,
193 pub name: String,
194 }
195
cstring_to_string(cstring: &[c_char]) -> String196 fn cstring_to_string(cstring: &[c_char]) -> String {
197 let null_idx = match cstring.iter().enumerate().find(|(_, &c)| c == 0) {
198 Some((i, _)) => i,
199 None => return "".to_owned(),
200 };
201
202 let ptr = cstring.as_ptr() as *const u8;
203 let slice = unsafe { core::slice::from_raw_parts(ptr, null_idx) };
204 String::from_utf8_lossy(slice).to_string()
205 }
206
207 impl From<cras_iodev_info> for CrasIodevInfo {
from(info: cras_iodev_info) -> Self208 fn from(info: cras_iodev_info) -> Self {
209 Self {
210 index: info.idx,
211 name: cstring_to_string(&info.name),
212 }
213 }
214 }
215
216 impl Default for cras_ionode_info {
default() -> Self217 fn default() -> Self {
218 Self {
219 iodev_idx: 0,
220 ionode_idx: 0,
221 plugged: 0,
222 active: 0,
223 plugged_time: cras_ionode_info__bindgen_ty_1 {
224 tv_sec: 0,
225 tv_usec: 0,
226 },
227 volume: 0,
228 ui_gain_scaler: 0.0,
229 capture_gain: 0,
230 left_right_swapped: 0,
231 type_enum: 0,
232 stable_id: 0,
233 type_: [0; 32usize],
234 name: [0; 64usize],
235 active_hotword_model: [0; 16usize],
236 }
237 }
238 }
239
240 impl From<u32> for CRAS_NODE_TYPE {
from(node_type: u32) -> CRAS_NODE_TYPE241 fn from(node_type: u32) -> CRAS_NODE_TYPE {
242 use CRAS_NODE_TYPE::*;
243 match node_type {
244 0 => CRAS_NODE_TYPE_INTERNAL_SPEAKER,
245 1 => CRAS_NODE_TYPE_HEADPHONE,
246 2 => CRAS_NODE_TYPE_HDMI,
247 3 => CRAS_NODE_TYPE_HAPTIC,
248 4 => CRAS_NODE_TYPE_LINEOUT,
249 5 => CRAS_NODE_TYPE_MIC,
250 6 => CRAS_NODE_TYPE_HOTWORD,
251 7 => CRAS_NODE_TYPE_POST_MIX_PRE_DSP,
252 8 => CRAS_NODE_TYPE_POST_DSP,
253 9 => CRAS_NODE_TYPE_USB,
254 10 => CRAS_NODE_TYPE_BLUETOOTH,
255 _ => CRAS_NODE_TYPE_UNKNOWN,
256 }
257 }
258 }
259
260 #[derive(Debug)]
261 pub struct CrasIonodeInfo {
262 pub name: String,
263 pub iodev_index: u32,
264 pub ionode_index: u32,
265 pub stable_id: u32,
266 pub plugged: bool,
267 pub active: bool,
268 pub node_type: CRAS_NODE_TYPE,
269 pub type_name: String,
270 pub volume: u32,
271 pub capture_gain: i32,
272 pub plugged_time: cras_timespec,
273 }
274
275 impl From<cras_ionode_info> for CrasIonodeInfo {
from(info: cras_ionode_info) -> Self276 fn from(info: cras_ionode_info) -> Self {
277 Self {
278 name: cstring_to_string(&info.name),
279 iodev_index: info.iodev_idx,
280 ionode_index: info.ionode_idx,
281 stable_id: info.stable_id,
282 plugged: info.plugged != 0,
283 active: info.active != 0,
284 node_type: CRAS_NODE_TYPE::from(info.type_enum),
285 type_name: cstring_to_string(&info.type_),
286 volume: info.volume,
287 capture_gain: info.capture_gain,
288 plugged_time: cras_timespec {
289 tv_sec: info.plugged_time.tv_sec,
290 tv_nsec: info.plugged_time.tv_usec * 1000,
291 },
292 }
293 }
294 }
295
296 impl From<u32> for CRAS_STREAM_DIRECTION {
from(node_type: u32) -> CRAS_STREAM_DIRECTION297 fn from(node_type: u32) -> CRAS_STREAM_DIRECTION {
298 use CRAS_STREAM_DIRECTION::*;
299 match node_type {
300 0 => CRAS_STREAM_OUTPUT,
301 1 => CRAS_STREAM_INPUT,
302 2 => CRAS_STREAM_UNDEFINED,
303 3 => CRAS_STREAM_POST_MIX_PRE_DSP,
304 _ => CRAS_STREAM_UNDEFINED,
305 }
306 }
307 }
308
309 impl Default for audio_dev_debug_info {
default() -> Self310 fn default() -> Self {
311 Self {
312 dev_name: [0; 64],
313 buffer_size: 0,
314 min_buffer_level: 0,
315 min_cb_level: 0,
316 max_cb_level: 0,
317 frame_rate: 0,
318 num_channels: 0,
319 est_rate_ratio: 0.0,
320 direction: 0,
321 num_underruns: 0,
322 num_severe_underruns: 0,
323 highest_hw_level: 0,
324 runtime_sec: 0,
325 runtime_nsec: 0,
326 longest_wake_sec: 0,
327 longest_wake_nsec: 0,
328 software_gain_scaler: 0.0,
329 }
330 }
331 }
332
333 /// A rust-style representation of the server's packed audio_dev_debug_info
334 /// struct.
335 #[derive(Debug)]
336 pub struct AudioDevDebugInfo {
337 pub dev_name: String,
338 pub buffer_size: u32,
339 pub min_buffer_level: u32,
340 pub min_cb_level: u32,
341 pub max_cb_level: u32,
342 pub frame_rate: u32,
343 pub num_channels: u32,
344 pub est_rate_ratio: f64,
345 pub direction: CRAS_STREAM_DIRECTION,
346 pub num_underruns: u32,
347 pub num_severe_underruns: u32,
348 pub highest_hw_level: u32,
349 pub runtime: Duration,
350 pub longest_wake: Duration,
351 pub software_gain_scaler: f64,
352 }
353
354 impl From<audio_dev_debug_info> for AudioDevDebugInfo {
from(info: audio_dev_debug_info) -> Self355 fn from(info: audio_dev_debug_info) -> Self {
356 Self {
357 dev_name: cstring_to_string(&info.dev_name),
358 buffer_size: info.buffer_size,
359 min_buffer_level: info.min_buffer_level,
360 min_cb_level: info.min_cb_level,
361 max_cb_level: info.max_cb_level,
362 frame_rate: info.frame_rate,
363 num_channels: info.num_channels,
364 est_rate_ratio: info.est_rate_ratio,
365 direction: CRAS_STREAM_DIRECTION::from(u32::from(info.direction)),
366 num_underruns: info.num_underruns,
367 num_severe_underruns: info.num_severe_underruns,
368 highest_hw_level: info.highest_hw_level,
369 runtime: Duration::new(info.runtime_sec.into(), info.runtime_nsec),
370 longest_wake: Duration::new(info.longest_wake_sec.into(), info.longest_wake_nsec),
371 software_gain_scaler: info.software_gain_scaler,
372 }
373 }
374 }
375
376 impl fmt::Display for AudioDevDebugInfo {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result377 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
378 writeln!(f, "Device: {}", self.dev_name)?;
379 writeln!(f, " Direction: {:?}", self.direction)?;
380 writeln!(f, " Buffer size: {}", self.buffer_size)?;
381 writeln!(f, " Minimum buffer level: {}", self.min_buffer_level)?;
382 writeln!(f, " Minimum callback level: {}", self.min_cb_level)?;
383 writeln!(f, " Max callback level: {}", self.max_cb_level)?;
384 writeln!(f, " Frame rate: {}", self.frame_rate)?;
385 writeln!(f, " Number of channels: {}", self.num_channels)?;
386 writeln!(f, " Estimated rate ratio: {:.2}", self.est_rate_ratio)?;
387 writeln!(f, " Underrun count: {}", self.num_underruns)?;
388 writeln!(f, " Severe underrun count: {}", self.num_severe_underruns)?;
389 writeln!(f, " Highest hardware level: {}", self.highest_hw_level)?;
390 writeln!(f, " Runtime: {:?}", self.runtime)?;
391 writeln!(f, " Longest wake: {:?}", self.longest_wake)?;
392 writeln!(f, " Software gain scaler: {}", self.software_gain_scaler)?;
393 Ok(())
394 }
395 }
396
397 impl TryFrom<u32> for CRAS_STREAM_TYPE {
398 type Error = Error;
try_from(stream_type: u32) -> Result<Self, Self::Error>399 fn try_from(stream_type: u32) -> Result<Self, Self::Error> {
400 use CRAS_STREAM_TYPE::*;
401 match stream_type {
402 0 => Ok(CRAS_STREAM_TYPE_DEFAULT),
403 1 => Ok(CRAS_STREAM_TYPE_MULTIMEDIA),
404 2 => Ok(CRAS_STREAM_TYPE_VOICE_COMMUNICATION),
405 3 => Ok(CRAS_STREAM_TYPE_SPEECH_RECOGNITION),
406 4 => Ok(CRAS_STREAM_TYPE_PRO_AUDIO),
407 5 => Ok(CRAS_STREAM_TYPE_ACCESSIBILITY),
408 _ => Err(Error::InvalidStreamType(stream_type)),
409 }
410 }
411 }
412
413 impl TryFrom<u32> for CRAS_CLIENT_TYPE {
414 type Error = Error;
try_from(client_type: u32) -> Result<Self, Self::Error>415 fn try_from(client_type: u32) -> Result<Self, Self::Error> {
416 use CRAS_CLIENT_TYPE::*;
417 match client_type {
418 0 => Ok(CRAS_CLIENT_TYPE_UNKNOWN),
419 1 => Ok(CRAS_CLIENT_TYPE_LEGACY),
420 2 => Ok(CRAS_CLIENT_TYPE_TEST),
421 3 => Ok(CRAS_CLIENT_TYPE_PCM),
422 4 => Ok(CRAS_CLIENT_TYPE_CHROME),
423 5 => Ok(CRAS_CLIENT_TYPE_ARC),
424 6 => Ok(CRAS_CLIENT_TYPE_CROSVM),
425 7 => Ok(CRAS_CLIENT_TYPE_SERVER_STREAM),
426 8 => Ok(CRAS_CLIENT_TYPE_LACROS),
427 _ => Err(Error::InvalidClientType(client_type)),
428 }
429 }
430 }
431
432 impl FromStr for CRAS_CLIENT_TYPE {
433 type Err = Error;
from_str(s: &str) -> std::result::Result<Self, Self::Err>434 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
435 use CRAS_CLIENT_TYPE::*;
436 match s {
437 "crosvm" => Ok(CRAS_CLIENT_TYPE_CROSVM),
438 "arcvm" => Ok(CRAS_CLIENT_TYPE_ARCVM),
439 _ => Err(Error::InvalidClientTypeStr),
440 }
441 }
442 }
443
444 impl Default for audio_stream_debug_info {
default() -> Self445 fn default() -> Self {
446 Self {
447 stream_id: 0,
448 dev_idx: 0,
449 direction: 0,
450 stream_type: 0,
451 client_type: 0,
452 buffer_frames: 0,
453 cb_threshold: 0,
454 effects: 0,
455 flags: 0,
456 frame_rate: 0,
457 num_channels: 0,
458 longest_fetch_sec: 0,
459 longest_fetch_nsec: 0,
460 num_missed_cb: 0,
461 num_overruns: 0,
462 is_pinned: 0,
463 pinned_dev_idx: 0,
464 runtime_sec: 0,
465 runtime_nsec: 0,
466 stream_volume: 0.0,
467 channel_layout: [0; 11],
468 }
469 }
470 }
471
472 impl TryFrom<i8> for CRAS_CHANNEL {
473 type Error = Error;
try_from(channel: i8) -> Result<Self, Self::Error>474 fn try_from(channel: i8) -> Result<Self, Self::Error> {
475 use CRAS_CHANNEL::*;
476 match channel {
477 0 => Ok(CRAS_CH_FL),
478 1 => Ok(CRAS_CH_FR),
479 2 => Ok(CRAS_CH_RL),
480 3 => Ok(CRAS_CH_RR),
481 4 => Ok(CRAS_CH_FC),
482 5 => Ok(CRAS_CH_LFE),
483 6 => Ok(CRAS_CH_SL),
484 7 => Ok(CRAS_CH_SR),
485 8 => Ok(CRAS_CH_RC),
486 9 => Ok(CRAS_CH_FLC),
487 10 => Ok(CRAS_CH_FRC),
488 _ => Err(Error::InvalidChannel(channel)),
489 }
490 }
491 }
492
493 /// A rust-style representation of the server's packed audio_stream_debug_info
494 /// struct.
495 #[derive(Debug)]
496 pub struct AudioStreamDebugInfo {
497 pub stream_id: u64,
498 pub dev_idx: u32,
499 pub direction: CRAS_STREAM_DIRECTION,
500 pub stream_type: CRAS_STREAM_TYPE,
501 pub client_type: CRAS_CLIENT_TYPE,
502 pub buffer_frames: u32,
503 pub cb_threshold: u32,
504 pub effects: u64,
505 pub flags: u32,
506 pub frame_rate: u32,
507 pub num_channels: u32,
508 pub longest_fetch: Duration,
509 pub num_missed_cb: u32,
510 pub num_overruns: u32,
511 pub is_pinned: bool,
512 pub pinned_dev_idx: u32,
513 pub runtime: Duration,
514 pub stream_volume: f64,
515 pub channel_layout: Vec<CRAS_CHANNEL>,
516 }
517
518 impl TryFrom<audio_stream_debug_info> for AudioStreamDebugInfo {
519 type Error = Error;
try_from(info: audio_stream_debug_info) -> Result<Self, Self::Error>520 fn try_from(info: audio_stream_debug_info) -> Result<Self, Self::Error> {
521 let channel_layout = info
522 .channel_layout
523 .iter()
524 .cloned()
525 .take_while(|&c| c != -1)
526 .map(TryInto::try_into)
527 .collect::<Result<Vec<_>, _>>()?;
528 Ok(Self {
529 stream_id: info.stream_id,
530 dev_idx: info.dev_idx,
531 direction: info.direction.into(),
532 stream_type: info.stream_type.try_into()?,
533 client_type: info.client_type.try_into()?,
534 buffer_frames: info.buffer_frames,
535 cb_threshold: info.cb_threshold,
536 effects: info.effects,
537 flags: info.flags,
538 frame_rate: info.frame_rate,
539 num_channels: info.num_channels,
540 longest_fetch: Duration::new(info.longest_fetch_sec.into(), info.longest_fetch_nsec),
541 num_missed_cb: info.num_missed_cb,
542 num_overruns: info.num_overruns,
543 is_pinned: info.is_pinned != 0,
544 pinned_dev_idx: info.pinned_dev_idx,
545 runtime: Duration::new(info.runtime_sec.into(), info.runtime_nsec),
546 stream_volume: info.stream_volume,
547 channel_layout,
548 })
549 }
550 }
551
552 impl fmt::Display for AudioStreamDebugInfo {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result553 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
554 writeln!(
555 f,
556 "Stream: {}, Device index: {}",
557 self.stream_id, self.dev_idx
558 )?;
559 writeln!(f, " Direction: {:?}", self.direction)?;
560 writeln!(f, " Stream type: {:?}", self.stream_type)?;
561 writeln!(f, " Client type: {:?}", self.client_type)?;
562 writeln!(f, " Buffer frames: {}", self.buffer_frames)?;
563 writeln!(f, " Callback threshold: {}", self.cb_threshold)?;
564 writeln!(f, " Effects: {:#x}", self.effects)?;
565 writeln!(f, " Frame rate: {}", self.frame_rate)?;
566 writeln!(f, " Number of channels: {}", self.num_channels)?;
567 writeln!(f, " Longest fetch: {:?}", self.longest_fetch)?;
568 writeln!(f, " Overrun count: {}", self.num_overruns)?;
569 writeln!(f, " Pinned: {}", self.is_pinned)?;
570 writeln!(f, " Pinned device index: {}", self.pinned_dev_idx)?;
571 writeln!(f, " Missed callbacks: {}", self.num_missed_cb)?;
572 match self.direction {
573 CRAS_STREAM_DIRECTION::CRAS_STREAM_OUTPUT => {
574 writeln!(f, " Volume: {:.2}", self.stream_volume)?
575 }
576 CRAS_STREAM_DIRECTION::CRAS_STREAM_INPUT => {
577 writeln!(f, " Gain: {:.2}", self.stream_volume)?
578 }
579 _ => (),
580 };
581 writeln!(f, " Runtime: {:?}", self.runtime)?;
582 write!(f, " Channel map:")?;
583 for channel in &self.channel_layout {
584 write!(f, " {:?}", channel)?;
585 }
586 writeln!(f)?;
587 Ok(())
588 }
589 }
590
591 /// A rust-style representation of the server's audio debug info.
592 pub struct AudioDebugInfo {
593 pub devices: Vec<AudioDevDebugInfo>,
594 pub streams: Vec<AudioStreamDebugInfo>,
595 }
596
597 impl AudioDebugInfo {
new(devices: Vec<AudioDevDebugInfo>, streams: Vec<AudioStreamDebugInfo>) -> Self598 pub fn new(devices: Vec<AudioDevDebugInfo>, streams: Vec<AudioStreamDebugInfo>) -> Self {
599 Self { devices, streams }
600 }
601 }
602
603 impl Into<u64> for CRAS_STREAM_EFFECT {
into(self) -> u64604 fn into(self) -> u64 {
605 u64::from(self.0)
606 }
607 }
608
609 impl CRAS_STREAM_EFFECT {
empty() -> Self610 pub fn empty() -> Self {
611 CRAS_STREAM_EFFECT(0)
612 }
613 }
614
615 impl From<StreamDirection> for CRAS_STREAM_DIRECTION {
616 /// Convert an audio_streams StreamDirection into the corresponding CRAS_STREAM_DIRECTION.
from(direction: StreamDirection) -> Self617 fn from(direction: StreamDirection) -> Self {
618 match direction {
619 StreamDirection::Playback => CRAS_STREAM_DIRECTION::CRAS_STREAM_OUTPUT,
620 StreamDirection::Capture => CRAS_STREAM_DIRECTION::CRAS_STREAM_INPUT,
621 }
622 }
623 }
624
625 impl From<StreamEffect> for CRAS_STREAM_EFFECT {
626 /// Convert an audio_streams StreamEffect into the corresponding CRAS_STREAM_EFFECT.
from(effect: StreamEffect) -> Self627 fn from(effect: StreamEffect) -> Self {
628 match effect {
629 StreamEffect::NoEffect => CRAS_STREAM_EFFECT::empty(),
630 StreamEffect::EchoCancellation => CRAS_STREAM_EFFECT::APM_ECHO_CANCELLATION,
631 }
632 }
633 }
634
635 impl<'a> FromIterator<&'a StreamEffect> for CRAS_STREAM_EFFECT {
from_iter<I>(iter: I) -> Self where I: IntoIterator<Item = &'a StreamEffect>,636 fn from_iter<I>(iter: I) -> Self
637 where
638 I: IntoIterator<Item = &'a StreamEffect>,
639 {
640 iter.into_iter().fold(
641 CRAS_STREAM_EFFECT::empty(),
642 |cras_effect, &stream_effect| cras_effect | stream_effect.into(),
643 )
644 }
645 }
646
647 /// Convert an audio_streams SampleFormat into the corresponding pcm_format.
648 impl From<SampleFormat> for snd_pcm_format_t {
from(format: SampleFormat) -> Self649 fn from(format: SampleFormat) -> Self {
650 match format {
651 SampleFormat::U8 => snd_pcm_format_t::SND_PCM_FORMAT_U8,
652 SampleFormat::S16LE => snd_pcm_format_t::SND_PCM_FORMAT_S16_LE,
653 SampleFormat::S24LE => snd_pcm_format_t::SND_PCM_FORMAT_S24_LE,
654 SampleFormat::S32LE => snd_pcm_format_t::SND_PCM_FORMAT_S32_LE,
655 }
656 }
657 }
658