• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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