summaryrefslogtreecommitdiff
path: root/cras/client/cras_tests
diff options
context:
space:
mode:
Diffstat (limited to 'cras/client/cras_tests')
-rw-r--r--cras/client/cras_tests/.gitignore2
-rw-r--r--cras/client/cras_tests/.rustfmt.toml2
-rw-r--r--cras/client/cras_tests/Cargo.toml17
-rw-r--r--cras/client/cras_tests/src/arguments.rs462
-rw-r--r--cras/client/cras_tests/src/audio.rs414
-rw-r--r--cras/client/cras_tests/src/control.rs106
-rw-r--r--cras/client/cras_tests/src/main.rs58
7 files changed, 0 insertions, 1061 deletions
diff --git a/cras/client/cras_tests/.gitignore b/cras/client/cras_tests/.gitignore
deleted file mode 100644
index 302b31de..00000000
--- a/cras/client/cras_tests/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-target/
-.*.rustfmt
diff --git a/cras/client/cras_tests/.rustfmt.toml b/cras/client/cras_tests/.rustfmt.toml
deleted file mode 100644
index a2db3012..00000000
--- a/cras/client/cras_tests/.rustfmt.toml
+++ /dev/null
@@ -1,2 +0,0 @@
-use_field_init_shorthand = true
-use_try_shorthand = true
diff --git a/cras/client/cras_tests/Cargo.toml b/cras/client/cras_tests/Cargo.toml
deleted file mode 100644
index 108fe6c4..00000000
--- a/cras/client/cras_tests/Cargo.toml
+++ /dev/null
@@ -1,17 +0,0 @@
-[package]
-name = "cras_tests"
-version = "0.1.0"
-authors = ["The Chromium OS Authors"]
-edition = "2018"
-
-[dependencies]
-audio_streams = { path = "../../../audio_streams" } # provided by ebuild
-getopts = "0.2.18"
-hound = "3.4.0"
-libcras = { path = "../libcras" } # provided by ebuild
-sys_util = { path = "../../../../crosvm/sys_util" } # provided by ebuild
-
-[profile.release]
-lto = true
-panic = 'abort'
-overflow-checks = true
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);
- }
-}