diff options
Diffstat (limited to 'cras/client/cras_tests/src')
-rw-r--r-- | cras/client/cras_tests/src/arguments.rs | 462 | ||||
-rw-r--r-- | cras/client/cras_tests/src/audio.rs | 414 | ||||
-rw-r--r-- | cras/client/cras_tests/src/control.rs | 106 | ||||
-rw-r--r-- | cras/client/cras_tests/src/main.rs | 58 |
4 files changed, 0 insertions, 1040 deletions
diff --git a/cras/client/cras_tests/src/arguments.rs b/cras/client/cras_tests/src/arguments.rs deleted file mode 100644 index 59e9ec26..00000000 --- a/cras/client/cras_tests/src/arguments.rs +++ /dev/null @@ -1,462 +0,0 @@ -// Copyright 2019 The Chromium OS Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. -use std::error; -use std::fmt; -use std::path::PathBuf; - -use audio_streams::SampleFormat; -use getopts::{self, Matches, Options}; - -#[derive(Debug)] -pub enum Error { - GetOpts(getopts::Fail), - InvalidArgument(String, String, String), - InvalidFiletype(String), - MissingArgument(String), - MissingCommand, - MissingFilename, - UnknownCommand(String), -} - -impl error::Error for Error {} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use Error::*; - match self { - GetOpts(e) => write!(f, "Getopts Error: {}", e), - InvalidArgument(flag, value, error_msg) => { - write!(f, "Invalid {} argument '{}': {}", flag, value, error_msg) - } - InvalidFiletype(extension) => write!( - f, - "Invalid file extension '{}'. Supported types are 'wav' and 'raw'", - extension - ), - MissingArgument(subcommand) => write!(f, "Missing argument for {}", subcommand), - MissingCommand => write!(f, "A command must be provided"), - MissingFilename => write!(f, "A file name must be provided"), - UnknownCommand(s) => write!(f, "Unknown command '{}'", s), - } - } -} - -type Result<T> = std::result::Result<T, Error>; - -/// The different types of commands that can be given to cras_tests. -/// Any options for those commands are passed as parameters to the enum values. -#[derive(Debug, PartialEq)] -pub enum Command { - Capture(AudioOptions), - Playback(AudioOptions), - Control(ControlCommand), -} - -impl Command { - pub fn parse<T: AsRef<str>>(args: &[T]) -> Result<Option<Self>> { - let program_name = args.get(0).map(|s| s.as_ref()).unwrap_or("cras_tests"); - let remaining_args = args.get(2..).unwrap_or(&[]); - match args.get(1).map(|s| s.as_ref()) { - None => { - show_usage(program_name); - Err(Error::MissingCommand) - } - Some("help") => { - show_usage(program_name); - Ok(None) - } - Some("capture") => Ok( - AudioOptions::parse(program_name, "capture", remaining_args)?.map(Command::Capture), - ), - Some("playback") => Ok( - AudioOptions::parse(program_name, "playback", remaining_args)? - .map(Command::Playback), - ), - Some("control") => { - Ok(ControlCommand::parse(program_name, remaining_args)?.map(Command::Control)) - } - Some(s) => { - show_usage(program_name); - Err(Error::UnknownCommand(s.to_string())) - } - } - } -} - -#[derive(Debug, PartialEq)] -pub enum FileType { - Raw, - Wav, -} - -impl fmt::Display for FileType { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - FileType::Raw => write!(f, "raw data"), - FileType::Wav => write!(f, "WAVE"), - } - } -} - -fn show_usage(program_name: &str) { - eprintln!("Usage: {} [command] <command args>", program_name); - eprintln!("\nCommands:\n"); - eprintln!("capture - Capture to a file from CRAS"); - eprintln!("playback - Playback to CRAS from a file"); - eprintln!("control - Get and set server settings"); - eprintln!("\nhelp - Print help message"); -} - -fn show_audio_command_usage(program_name: &str, command: &str, opts: &Options) { - let brief = format!("Usage: {} {} [options] [filename]", program_name, command); - eprint!("{}", opts.usage(&brief)); -} - -/// The possible command line options that can be passed to the 'playback' and -/// 'capture' commands. Optional values will be `Some(_)` only if a value was -/// explicitly provided by the user. -/// -/// This struct will be passed to `playback()` and `capture()`. -#[derive(Debug, PartialEq)] -pub enum LoopbackType { - PreDsp, - PostDsp, -} - -#[derive(Debug, PartialEq)] -pub struct AudioOptions { - pub file_name: PathBuf, - pub loopback_type: Option<LoopbackType>, - pub file_type: FileType, - pub buffer_size: Option<usize>, - pub num_channels: Option<usize>, - pub format: Option<SampleFormat>, - pub frame_rate: Option<u32>, -} - -fn get_u32_param(matches: &Matches, option_name: &str) -> Result<Option<u32>> { - matches.opt_get::<u32>(option_name).map_err(|e| { - let argument = matches.opt_str(option_name).unwrap_or_default(); - Error::InvalidArgument(option_name.to_string(), argument, e.to_string()) - }) -} - -fn get_usize_param(matches: &Matches, option_name: &str) -> Result<Option<usize>> { - matches.opt_get::<usize>(option_name).map_err(|e| { - let argument = matches.opt_str(option_name).unwrap_or_default(); - Error::InvalidArgument(option_name.to_string(), argument, e.to_string()) - }) -} - -impl AudioOptions { - fn parse<T: AsRef<str>>( - program_name: &str, - command_name: &str, - args: &[T], - ) -> Result<Option<Self>> { - let mut opts = Options::new(); - opts.optopt("b", "buffer_size", "Buffer size in frames", "SIZE") - .optopt("c", "channels", "Number of channels", "NUM") - .optopt( - "f", - "format", - "Sample format (U8, S16_LE, S24_LE, or S32_LE)", - "FORMAT", - ) - .optopt("r", "rate", "Audio frame rate (Hz)", "RATE") - .optflag("h", "help", "Print help message"); - - if command_name == "capture" { - opts.optopt( - "", - "loopback", - "Capture from loopback device ('pre_dsp' or 'post_dsp')", - "DEVICE", - ); - } - - let args = args.iter().map(|s| s.as_ref()); - let matches = match opts.parse(args) { - Ok(m) => m, - Err(e) => { - show_audio_command_usage(program_name, command_name, &opts); - return Err(Error::GetOpts(e)); - } - }; - if matches.opt_present("h") { - show_audio_command_usage(program_name, command_name, &opts); - return Ok(None); - } - - let loopback_type = if matches.opt_defined("loopback") { - match matches.opt_str("loopback").as_deref() { - Some("pre_dsp") => Some(LoopbackType::PreDsp), - Some("post_dsp") => Some(LoopbackType::PostDsp), - Some(s) => { - return Err(Error::InvalidArgument( - "loopback".to_string(), - s.to_string(), - "Loopback type must be 'pre_dsp' or 'post_dsp'".to_string(), - )) - } - None => None, - } - } else { - None - }; - - let file_name = match matches.free.get(0) { - None => { - show_audio_command_usage(program_name, command_name, &opts); - return Err(Error::MissingFilename); - } - Some(file_name) => PathBuf::from(file_name), - }; - - let extension = file_name - .extension() - .map(|s| s.to_string_lossy().into_owned()); - let file_type = match extension.as_deref() { - Some("wav") | Some("wave") => FileType::Wav, - Some("raw") | None => FileType::Raw, - Some(extension) => return Err(Error::InvalidFiletype(extension.to_string())), - }; - - let buffer_size = get_usize_param(&matches, "buffer_size")?; - let num_channels = get_usize_param(&matches, "channels")?; - let frame_rate = get_u32_param(&matches, "rate")?; - let format = match matches.opt_str("format").as_deref() { - Some("U8") => Some(SampleFormat::U8), - Some("S16_LE") => Some(SampleFormat::S16LE), - Some("S24_LE") => Some(SampleFormat::S24LE), - Some("S32_LE") => Some(SampleFormat::S32LE), - Some(s) => { - show_audio_command_usage(program_name, command_name, &opts); - return Err(Error::InvalidArgument( - "format".to_string(), - s.to_string(), - "Format must be 'U8', 'S16_LE', 'S24_LE', or 'S32_LE'".to_string(), - )); - } - None => None, - }; - - Ok(Some(AudioOptions { - loopback_type, - file_name, - file_type, - buffer_size, - num_channels, - format, - frame_rate, - })) - } -} - -fn show_control_command_usage(program_name: &str) { - eprintln!("Usage: {} control [command] <command args>", program_name); - eprintln!(""); - eprintln!("Commands:"); - let commands = [ - ("help", "", "Print help message"), - ("", "", ""), - ("get_volume", "", "Get the system volume (0 - 100)"), - ( - "set_volume", - "VOLUME", - "Set the system volume to VOLUME (0 - 100)", - ), - ("get_mute", "", "Get the system mute state (true or false)"), - ( - "set_mute", - "MUTE", - "Set the system mute state to MUTE (true or false)", - ), - ("", "", ""), - ("list_output_devices", "", "Print list of output devices"), - ("list_input_devices", "", "Print list of input devices"), - ("list_output_nodes", "", "Print list of output nodes"), - ("list_input_nodes", "", "Print list of input nodes"), - ( - "dump_audio_debug_info", - "", - "Print stream info, device info, and audio thread log.", - ), - ]; - for command in &commands { - let command_string = format!("{} {}", command.0, command.1); - eprintln!("\t{: <23} {}", command_string, command.2); - } -} - -#[derive(Debug, PartialEq)] -pub enum ControlCommand { - GetSystemVolume, - SetSystemVolume(u32), - GetSystemMute, - SetSystemMute(bool), - ListOutputDevices, - ListInputDevices, - ListOutputNodes, - ListInputNodes, - DumpAudioDebugInfo, -} - -impl ControlCommand { - fn parse<T: AsRef<str>>(program_name: &str, args: &[T]) -> Result<Option<Self>> { - let mut args = args.iter().map(|s| s.as_ref()); - match args.next() { - Some("help") => { - show_control_command_usage(program_name); - Ok(None) - } - Some("get_volume") => Ok(Some(ControlCommand::GetSystemVolume)), - Some("set_volume") => { - let volume_str = args - .next() - .ok_or_else(|| Error::MissingArgument("set_volume".to_string()))?; - - let volume = volume_str.parse::<u32>().map_err(|e| { - Error::InvalidArgument( - "set_volume".to_string(), - volume_str.to_string(), - e.to_string(), - ) - })?; - - Ok(Some(ControlCommand::SetSystemVolume(volume))) - } - Some("get_mute") => Ok(Some(ControlCommand::GetSystemMute)), - Some("set_mute") => { - let mute_str = args - .next() - .ok_or_else(|| Error::MissingArgument("set_mute".to_string()))?; - - let mute = mute_str.parse::<bool>().map_err(|e| { - Error::InvalidArgument( - "set_mute".to_string(), - mute_str.to_string(), - e.to_string(), - ) - })?; - Ok(Some(ControlCommand::SetSystemMute(mute))) - } - Some("list_output_devices") => Ok(Some(ControlCommand::ListOutputDevices)), - Some("list_input_devices") => Ok(Some(ControlCommand::ListInputDevices)), - Some("list_output_nodes") => Ok(Some(ControlCommand::ListOutputNodes)), - Some("list_input_nodes") => Ok(Some(ControlCommand::ListInputNodes)), - Some("dump_audio_debug_info") => Ok(Some(ControlCommand::DumpAudioDebugInfo)), - Some(s) => { - show_control_command_usage(program_name); - Err(Error::UnknownCommand(s.to_string())) - } - None => { - show_control_command_usage(program_name); - Err(Error::MissingCommand) - } - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn parse_command() { - let command = Command::parse(&["cras_tests", "playback", "output.wav"]) - .unwrap() - .unwrap(); - assert_eq!( - command, - Command::Playback(AudioOptions { - file_name: PathBuf::from("output.wav"), - loopback_type: None, - file_type: FileType::Wav, - frame_rate: None, - num_channels: None, - format: None, - buffer_size: None, - }) - ); - let command = Command::parse(&["cras_tests", "capture", "input.raw"]) - .unwrap() - .unwrap(); - assert_eq!( - command, - Command::Capture(AudioOptions { - file_name: PathBuf::from("input.raw"), - loopback_type: None, - file_type: FileType::Raw, - frame_rate: None, - num_channels: None, - format: None, - buffer_size: None, - }) - ); - - let command = Command::parse(&[ - "cras_tests", - "playback", - "-r", - "44100", - "output.wave", - "-c", - "2", - ]) - .unwrap() - .unwrap(); - assert_eq!( - command, - Command::Playback(AudioOptions { - file_name: PathBuf::from("output.wave"), - loopback_type: None, - file_type: FileType::Wav, - frame_rate: Some(44100), - num_channels: Some(2), - format: None, - buffer_size: None, - }) - ); - - let command = - Command::parse(&["cras_tests", "playback", "-r", "44100", "output", "-c", "2"]) - .unwrap() - .unwrap(); - assert_eq!( - command, - Command::Playback(AudioOptions { - file_name: PathBuf::from("output"), - loopback_type: None, - file_type: FileType::Raw, - frame_rate: Some(44100), - num_channels: Some(2), - format: None, - buffer_size: None, - }) - ); - - assert!(Command::parse(&["cras_tests"]).is_err()); - assert!(Command::parse(&["cras_tests", "capture"]).is_err()); - assert!(Command::parse(&["cras_tests", "capture", "input.mp3"]).is_err()); - assert!(Command::parse(&["cras_tests", "capture", "input.ogg"]).is_err()); - assert!(Command::parse(&["cras_tests", "capture", "input.flac"]).is_err()); - assert!(Command::parse(&["cras_tests", "playback"]).is_err()); - assert!(Command::parse(&["cras_tests", "loopback"]).is_err()); - assert!(Command::parse(&["cras_tests", "loopback", "file.ogg"]).is_err()); - assert!(Command::parse(&["cras_tests", "filename.wav"]).is_err()); - assert!(Command::parse(&["cras_tests", "filename.wav", "capture"]).is_err()); - assert!(Command::parse(&["cras_tests", "help"]).is_ok()); - assert!(Command::parse(&[ - "cras_tests", - "-c", - "2", - "playback", - "output.wav", - "-r", - "44100" - ]) - .is_err()); - } -} diff --git a/cras/client/cras_tests/src/audio.rs b/cras/client/cras_tests/src/audio.rs deleted file mode 100644 index 23018fd7..00000000 --- a/cras/client/cras_tests/src/audio.rs +++ /dev/null @@ -1,414 +0,0 @@ -// Copyright 2019 The Chromium OS Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -use std::error; -use std::fmt; -use std::fs::File; -use std::io::{self, BufReader, BufWriter, Read, Write}; -use std::os::raw::c_int; -use std::path::Path; -use std::sync::atomic::{AtomicBool, Ordering}; - -use audio_streams::{SampleFormat, StreamSource}; -use hound::{WavReader, WavSpec, WavWriter}; -use libcras::{BoxError, CrasClient, CrasNodeType}; -use sys_util::{register_signal_handler, set_rt_prio_limit, set_rt_round_robin}; - -use crate::arguments::{AudioOptions, FileType, LoopbackType}; - -#[derive(Debug)] -pub enum Error { - CreateStream(BoxError), - FetchStream(BoxError), - FloatingPointSamples, - InvalidWavFile(hound::Error), - Io(io::Error), - Libcras(libcras::Error), - NoLoopbackNode(CrasNodeType), - OpenFile(hound::Error), - SampleBits(u16), - SysUtil(sys_util::Error), -} - -impl error::Error for Error {} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use Error::*; - match self { - CreateStream(e) => write!(f, "Failed to create stream: {}", e), - FetchStream(e) => write!(f, "Failed to fetch buffer from stream: {}", e), - FloatingPointSamples => write!(f, "Floating point audio samples are not supported"), - InvalidWavFile(e) => write!(f, "Could not open file as WAV file: {}", e), - Io(e) => write!(f, "IO Error: {}", e), - Libcras(e) => write!(f, "Libcras Error: {}", e), - NoLoopbackNode(typ) => write!(f, "No loopback node found with type {:?}", typ), - OpenFile(e) => write!(f, "Could not open WAV file for writing: {}", e), - SampleBits(bits) => write!( - f, - "Sample size {} is not supported, only 8, 16, 24, and 32 bit samples are supported", - bits - ), - SysUtil(e) => write!(f, "SysUtil Error: {}", e), - } - } -} - -type Result<T> = std::result::Result<T, Error>; - -static INTERRUPTED: AtomicBool = AtomicBool::new(false); - -extern "C" fn sigint_handler(_: c_int) { - // Check if we've already received one SIGINT. If we have, the program may - // be misbehaving and not terminating, so to be safe we'll forcefully exit. - if INTERRUPTED.load(Ordering::Acquire) { - std::process::exit(1); - } - INTERRUPTED.store(true, Ordering::Release); -} - -fn add_sigint_handler() -> Result<()> { - const SIGINT: c_int = 2; - let result = unsafe { register_signal_handler(SIGINT, sigint_handler) }; - result.map_err(Error::SysUtil) -} - -fn set_priority_to_realtime() { - const AUDIO_THREAD_RTPRIO: u16 = 10; - if set_rt_prio_limit(AUDIO_THREAD_RTPRIO as u64).is_err() - || set_rt_round_robin(AUDIO_THREAD_RTPRIO as i32).is_err() - { - println!("Attempt to use real-time priority failed, running with default scheduler."); - } -} - -fn channel_string(num_channels: usize) -> String { - match num_channels { - 1 => "Mono".to_string(), - 2 => "Stereo".to_string(), - _ => format!("{} Channels", num_channels), - } -} - -struct WavSource { - wav_reader: WavReader<BufReader<File>>, - format: SampleFormat, - num_channels: usize, - frame_rate: u32, -} - -impl WavSource { - fn try_new(opts: &AudioOptions) -> Result<Self> { - let wav_reader = WavReader::open(&opts.file_name).map_err(Error::InvalidWavFile)?; - let spec = wav_reader.spec(); - if spec.sample_format == hound::SampleFormat::Float { - return Err(Error::FloatingPointSamples); - } - - let format = match spec.bits_per_sample { - 8 => SampleFormat::U8, - 16 => SampleFormat::S16LE, - 24 => SampleFormat::S24LE, - 32 => SampleFormat::S32LE, - s => return Err(Error::SampleBits(s)), - }; - if opts.format.is_some() && Some(format) != opts.format { - eprintln!("Warning: format changed to {:?}", format); - } - - let num_channels = spec.channels as usize; - if opts.num_channels.is_some() && Some(num_channels) != opts.num_channels { - eprintln!("Warning: number of channels changed to {}", num_channels); - } - - let frame_rate = spec.sample_rate; - if opts.frame_rate.is_some() && Some(frame_rate) != opts.frame_rate { - eprintln!("Warning: frame rate changed to {}", frame_rate); - } - - Ok(Self { - wav_reader, - format, - num_channels, - frame_rate, - }) - } - - fn format(&self) -> SampleFormat { - self.format - } - - fn num_channels(&self) -> usize { - self.num_channels - } - - fn frame_rate(&self) -> u32 { - self.frame_rate - } -} - -impl Read for WavSource { - fn read(&mut self, mut buf: &mut [u8]) -> io::Result<usize> { - let frame_size = self.format.sample_bytes() * self.num_channels; - let read_len = buf.len() - buf.len() % frame_size; - let num_samples = read_len / self.format.sample_bytes(); - let samples = self.wav_reader.samples::<i32>(); - let mut read = 0; - for s in samples.take(num_samples) { - match s { - Ok(sample) => { - let result = match self.format { - SampleFormat::U8 => buf.write_all(&((sample + 128) as u8).to_le_bytes()), - SampleFormat::S16LE => buf.write_all(&(sample as i16).to_le_bytes()), - SampleFormat::S24LE | SampleFormat::S32LE => { - buf.write_all(&sample.to_le_bytes()) - } - }; - - match result { - Ok(()) => read += self.format.sample_bytes(), - Err(_) => return Ok(read), - }; - } - Err(_) => return Ok(read), - }; - } - Ok(read) - } -} - -pub fn playback(opts: AudioOptions) -> Result<()> { - let num_channels; - let frame_rate; - let format; - let mut sample_source: Box<dyn Read> = match opts.file_type { - FileType::Wav => { - let wav_source = WavSource::try_new(&opts)?; - num_channels = wav_source.num_channels(); - frame_rate = wav_source.frame_rate(); - format = wav_source.format(); - Box::new(wav_source) - } - FileType::Raw => { - num_channels = opts.num_channels.unwrap_or(2); - frame_rate = opts.frame_rate.unwrap_or(48000); - format = opts.format.unwrap_or(SampleFormat::S16LE); - Box::new(BufReader::new( - File::open(&opts.file_name).map_err(Error::Io)?, - )) - } - }; - - println!( - "Playing {} '{}' : {}, Rate {} Hz, {}", - opts.file_type, - opts.file_name.display(), - format, - frame_rate, - channel_string(num_channels) - ); - - let mut cras_client = CrasClient::new().map_err(Error::Libcras)?; - let (_control, mut stream) = cras_client - .new_playback_stream( - num_channels, - format, - frame_rate, - opts.buffer_size.unwrap_or(256), - ) - .map_err(Error::CreateStream)?; - set_priority_to_realtime(); - - add_sigint_handler()?; - while !INTERRUPTED.load(Ordering::Acquire) { - let mut buffer = stream.next_playback_buffer().map_err(Error::FetchStream)?; - - let frame_size = num_channels * format.sample_bytes(); - let frames = buffer.frame_capacity(); - - let mut chunk = (&mut sample_source).take((frames * frame_size) as u64); - let transferred = io::copy(&mut chunk, &mut buffer).map_err(Error::Io)?; - if transferred == 0 { - break; - } - } - // Stream and client should gracefully be closed out of this scope - - Ok(()) -} - -struct WavSink { - wav_writer: WavWriter<BufWriter<File>>, - format: SampleFormat, -} - -impl WavSink { - fn try_new<P: AsRef<Path>>( - path: P, - num_channels: usize, - format: SampleFormat, - frame_rate: u32, - ) -> Result<Self> { - let spec = WavSpec { - channels: num_channels as u16, - sample_rate: frame_rate, - bits_per_sample: (format.sample_bytes() * 8) as u16, - sample_format: hound::SampleFormat::Int, - }; - let wav_writer = WavWriter::create(path, spec).map_err(Error::OpenFile)?; - Ok(Self { wav_writer, format }) - } -} - -impl Write for WavSink { - fn write(&mut self, samples: &[u8]) -> io::Result<usize> { - let sample_bytes = self.format.sample_bytes(); - if samples.len() % sample_bytes != 0 { - return Err(io::Error::new( - io::ErrorKind::InvalidInput, - format!( - "u8 samples vector of length {} cannot be interpreted as {:?} samples", - samples.len(), - self.format - ), - )); - } - let num_samples = samples.len() / sample_bytes; - match self.format { - SampleFormat::U8 => { - for sample in samples { - self.wav_writer.write_sample(*sample as i8).map_err(|e| { - io::Error::new( - io::ErrorKind::Other, - format!("Failed to write sample: {}", e), - ) - })?; - } - } - SampleFormat::S16LE => { - // hound offers an optimized i16 writer, so special case here. - let mut writer = self.wav_writer.get_i16_writer(num_samples as u32); - for i in 0..num_samples { - let sample = i16::from_le_bytes([ - samples[sample_bytes * i], - samples[sample_bytes * i + 1], - ]); - writer.write_sample(sample); - } - // I16Writer buffers internally and must be explicitly flushed to write - // samples to the backing writer. Flush is not called automatically - // on drop. - // The flush method only writes data from the i16_writer to the underlying - // WavWriter, it does not actually guarantee a flush to disk. - writer.flush().map_err(|e| { - io::Error::new( - io::ErrorKind::Other, - format!("Failed to flush SampleWriter: {}", e), - ) - })?; - } - SampleFormat::S24LE | SampleFormat::S32LE => { - for i in 0..num_samples { - let mut sample = i32::from_le_bytes([ - samples[sample_bytes * i], - samples[sample_bytes * i + 1], - samples[sample_bytes * i + 2], - samples[sample_bytes * i + 3], - ]); - - // Upsample to 32 bit since CRAS doesn't support S24_3LE. - // Our wav encoder/decoder, hound, does have support for - // S24_LE, but it hasn't released a new version since the - // support was added. If getting that support is an issue, - // push upstream to cut a new a release. - if self.format == SampleFormat::S24LE { - sample <<= 8; - } - - self.wav_writer.write_sample(sample).map_err(|e| { - io::Error::new( - io::ErrorKind::Other, - format!("Failed to write sample: {}", e), - ) - })?; - } - } - } - - Ok(samples.len()) - } - - fn flush(&mut self) -> io::Result<()> { - self.wav_writer.flush().map_err(|e| { - io::Error::new( - io::ErrorKind::Other, - format!("Failed to flush WavWriter: {}", e), - ) - }) - } -} - -pub fn capture(opts: AudioOptions) -> Result<()> { - let num_channels = opts.num_channels.unwrap_or(2); - let format = opts.format.unwrap_or(SampleFormat::S16LE); - let frame_rate = opts.frame_rate.unwrap_or(48000); - let buffer_size = opts.buffer_size.unwrap_or(256); - - let mut sample_sink: Box<dyn Write> = match opts.file_type { - FileType::Raw => Box::new(BufWriter::new( - File::create(&opts.file_name).map_err(Error::Io)?, - )), - FileType::Wav => Box::new(WavSink::try_new( - &opts.file_name, - num_channels, - format, - frame_rate, - )?), - }; - - println!( - "Recording {} '{}' : {}, Rate {} Hz, {}", - opts.file_type, - opts.file_name.display(), - format, - frame_rate, - channel_string(num_channels) - ); - - let mut cras_client = CrasClient::new().map_err(Error::Libcras)?; - cras_client.enable_cras_capture(); - let (_control, mut stream) = match opts.loopback_type { - Some(loopback_type) => { - let node_type = match loopback_type { - LoopbackType::PreDsp => CrasNodeType::CRAS_NODE_TYPE_POST_MIX_PRE_DSP, - LoopbackType::PostDsp => CrasNodeType::CRAS_NODE_TYPE_POST_DSP, - }; - - let loopback_node = cras_client - .input_nodes() - .find(|node| node.node_type == node_type) - .ok_or(Error::NoLoopbackNode(node_type))?; - - cras_client - .new_pinned_capture_stream( - loopback_node.iodev_index, - num_channels, - format, - frame_rate, - buffer_size, - ) - .map_err(Error::CreateStream)? - } - None => cras_client - .new_capture_stream(num_channels, format, frame_rate, buffer_size) - .map_err(Error::CreateStream)?, - }; - set_priority_to_realtime(); - add_sigint_handler()?; - while !INTERRUPTED.load(Ordering::Acquire) { - let mut buf = stream.next_capture_buffer().map_err(Error::FetchStream)?; - io::copy(&mut buf, &mut sample_sink).map_err(Error::Io)?; - } - Ok(()) -} diff --git a/cras/client/cras_tests/src/control.rs b/cras/client/cras_tests/src/control.rs deleted file mode 100644 index 3a98ec98..00000000 --- a/cras/client/cras_tests/src/control.rs +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright 2019 The Chromium OS Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -use std::error; -use std::fmt; - -use libcras::{AudioDebugInfo, CrasClient, CrasIonodeInfo}; - -use crate::arguments::ControlCommand; - -/// An enumeration of errors that can occur when running `ControlCommand` using -/// the `control()` function. -#[derive(Debug)] -pub enum Error { - Libcras(libcras::Error), -} - -impl error::Error for Error {} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use Error::*; - match self { - Libcras(e) => write!(f, "Libcras Error: {}", e), - } - } -} - -type Result<T> = std::result::Result<T, Error>; - -fn print_nodes(nodes: impl Iterator<Item = CrasIonodeInfo>) { - println!( - "{: <13}{: <7}{: <6}{: <10}{: <13}{: <20} {: <10}", - "Stable ID", "ID", "Vol", "Plugged", "Time", "Type", "Name" - ); - for node in nodes { - let id = format!("{}:{}", node.iodev_index, node.ionode_index); - let stable_id = format!("({:08x})", node.stable_id); - let plugged_time = node.plugged_time.tv_sec; - let active = if node.active { "*" } else { " " }; - println!( - "{: <13}{: <7}{: <6}{: <10}{: <13}{: <20}{}{: <10}", - stable_id, - id, - node.volume, - node.plugged, - plugged_time, - node.type_name, - active, - node.name - ); - } -} - -fn print_audio_debug_info(info: &AudioDebugInfo) { - println!("Audio Debug Stats:"); - println!("-------------devices------------"); - for device in &info.devices { - println!("{}", device); - println!(); - } - - println!("-------------stream_dump------------"); - for stream in &info.streams { - println!("{}", stream); - println!(); - } -} - -/// Connect to CRAS and run the given `ControlCommand`. -pub fn control(command: ControlCommand) -> Result<()> { - use ControlCommand::*; - let mut cras_client = CrasClient::new().map_err(Error::Libcras)?; - match command { - GetSystemVolume => println!("{}", cras_client.get_system_volume()), - SetSystemVolume(volume) => { - cras_client - .set_system_volume(volume) - .map_err(Error::Libcras)?; - } - GetSystemMute => println!("{}", cras_client.get_system_mute()), - SetSystemMute(mute) => { - cras_client.set_system_mute(mute).map_err(Error::Libcras)?; - } - ListOutputDevices => { - println!("{: <5}{: <10}", "ID", "Name"); - for dev in cras_client.output_devices() { - println!("{: <5}{: <10}", dev.index, dev.name); - } - } - ListInputDevices => { - println!("{: <5}{: <10}", "ID", "Name"); - for dev in cras_client.input_devices() { - println!("{: <5}{: <10}", dev.index, dev.name); - } - } - ListOutputNodes => print_nodes(cras_client.output_nodes()), - ListInputNodes => print_nodes(cras_client.input_nodes()), - DumpAudioDebugInfo => { - let debug_info = cras_client.get_audio_debug_info().map_err(Error::Libcras)?; - print_audio_debug_info(&debug_info); - } - }; - Ok(()) -} diff --git a/cras/client/cras_tests/src/main.rs b/cras/client/cras_tests/src/main.rs deleted file mode 100644 index 50ffd090..00000000 --- a/cras/client/cras_tests/src/main.rs +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2019 The Chromium OS Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -mod arguments; -mod audio; -mod control; - -use std::error; -use std::fmt; - -use crate::arguments::Command; -use crate::audio::{capture, playback}; -use crate::control::control; - -#[derive(Debug)] -pub enum Error { - Audio(audio::Error), - ParseArgs(arguments::Error), - Control(control::Error), -} - -impl error::Error for Error {} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use Error::*; - match self { - Audio(e) => e.fmt(f), - ParseArgs(e) => write!(f, "Failed to parse arguments: {}", e), - Control(e) => e.fmt(f), - } - } -} - -type Result<T> = std::result::Result<T, Error>; - -fn run() -> Result<()> { - let args: Vec<String> = std::env::args().collect(); - let command = match Command::parse(&args).map_err(Error::ParseArgs)? { - None => return Ok(()), - Some(v) => v, - }; - - match command { - Command::Capture(audio_opts) => capture(audio_opts).map_err(Error::Audio), - Command::Control(command) => control(command).map_err(Error::Control), - Command::Playback(audio_opts) => playback(audio_opts).map_err(Error::Audio), - } -} - -fn main() { - // Use run() instead of returning a Result from main() so that we can print - // errors using Display instead of Debug. - if let Err(e) = run() { - eprintln!("{}", e); - } -} |