| // Copyright 2023 The Fuchsia 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 argh::{ArgsInfo, FromArgs}; |
| use ffx_core::ffx_command; |
| use fidl_fuchsia_audio_device as fadevice; |
| use fuchsia_audio::{ |
| device::{Direction, HardwareType}, |
| Format, |
| }; |
| |
| #[ffx_command()] |
| #[derive(ArgsInfo, FromArgs, Debug, PartialEq)] |
| #[argh( |
| subcommand, |
| name = "device", |
| description = "Interact directly with device hardware.", |
| example = "Show information about a specific device: |
| |
| $ ffx audio device --id 3d99d780 --direction input info |
| |
| Play a WAV file directly to device hardware: |
| |
| $ cat ~/sine.wav | ffx audio device --id a70075f2 play |
| $ ffx audio device --id a70075f2 play --file ~/sine.wav |
| |
| Record a WAV file directly from device hardware: |
| |
| $ ffx audio device --id 3d99d780 record --format 48000,uint8,1ch --duration 1s |
| |
| Mute the stream of an output device: |
| |
| $ ffx audio device --id a70075f2 --direction output mute |
| |
| Set the gain of an output device to -20 dB: |
| |
| $ ffx audio device --id a70075f2 --direction output gain -20 |
| |
| Turn AGC on for an input device: |
| |
| $ ffx audio device --id 3d99d780 --direction input agc on" |
| )] |
| pub struct DeviceCommand { |
| #[argh(subcommand)] |
| pub subcommand: SubCommand, |
| |
| #[argh( |
| option, |
| description = "device ID. Specify a devfs node name, \ |
| e.g. 3d99d780 for /dev/class/audio-input/3d99d780. |
| If not specified, defaults to the first device alphabetically listed." |
| )] |
| pub id: Option<String>, |
| |
| #[argh( |
| option, |
| long = "direction", |
| description = "device direction. Accepted values: input, output. \ |
| Play and record will use output and input respectively by default." |
| )] |
| pub device_direction: Option<Direction>, |
| |
| #[argh( |
| option, |
| long = "type", |
| description = "device type. Accepted values: StreamConfig, Composite." |
| )] |
| pub device_type: Option<HardwareType>, |
| } |
| |
| #[derive(ArgsInfo, FromArgs, Debug, PartialEq)] |
| #[argh(subcommand)] |
| pub enum SubCommand { |
| List(ListCommand), |
| Info(InfoCommand), |
| Play(PlayCommand), |
| Record(RecordCommand), |
| Gain(GainCommand), |
| Mute(MuteCommand), |
| Unmute(UnmuteCommmand), |
| Agc(AgcCommand), |
| } |
| |
| #[derive(ArgsInfo, FromArgs, Debug, PartialEq)] |
| #[argh( |
| subcommand, |
| name = "list", |
| description = "Lists audio devices.", |
| example = "ffx audio device --type StreamConfig list" |
| )] |
| pub struct ListCommand {} |
| |
| #[derive(ArgsInfo, FromArgs, Debug, PartialEq)] |
| #[argh( |
| subcommand, |
| name = "info", |
| description = "Show information about a specific audio device.", |
| example = "ffx audio device --type StreamConfig --direction input info" |
| )] |
| pub struct InfoCommand {} |
| |
| #[derive(ArgsInfo, FromArgs, Debug, PartialEq)] |
| #[argh(subcommand, name = "play", description = "Send audio data directly to device ring buffer.")] |
| pub struct PlayCommand { |
| #[argh( |
| option, |
| description = "file in WAV format containing audio signal. \ |
| If not specified, ffx command will read from stdin." |
| )] |
| pub file: Option<String>, |
| |
| #[argh( |
| option, |
| description = "signal processing element ID, \ |
| for an Endpoint element of type RingBuffer" |
| )] |
| pub element_id: Option<fadevice::ElementId>, |
| } |
| |
| #[derive(ArgsInfo, FromArgs, Debug, PartialEq)] |
| #[argh(subcommand, name = "record", description = "Capture audio data directly from ring buffer.")] |
| pub struct RecordCommand { |
| #[argh( |
| option, |
| description = "duration of output signal. Examples: 5ms or 3s. \ |
| If not specified, press ENTER to stop recording.", |
| from_str_fn(parse_duration) |
| )] |
| pub duration: Option<std::time::Duration>, |
| |
| #[argh(option, description = "output format (see 'ffx audio help' for more information).")] |
| pub format: Format, |
| |
| #[argh( |
| option, |
| description = "signal processing element ID, \ |
| for an Endpoint element of type RingBuffer" |
| )] |
| pub element_id: Option<fadevice::ElementId>, |
| } |
| |
| #[derive(ArgsInfo, FromArgs, Debug, PartialEq)] |
| #[argh( |
| subcommand, |
| name = "gain", |
| description = "Request to set the gain of the stream, in decibels." |
| )] |
| pub struct GainCommand { |
| #[argh(option, description = "gain, in decibels, to set the stream to.")] |
| pub gain: f32, |
| } |
| |
| #[derive(ArgsInfo, FromArgs, Debug, PartialEq)] |
| #[argh(subcommand, name = "mute", description = "Request to mute a stream.")] |
| pub struct MuteCommand {} |
| |
| #[derive(ArgsInfo, FromArgs, Debug, PartialEq)] |
| #[argh(subcommand, name = "unmute", description = "Request to unmute a stream.")] |
| pub struct UnmuteCommmand {} |
| |
| #[derive(ArgsInfo, FromArgs, Debug, PartialEq)] |
| #[argh( |
| subcommand, |
| name = "agc", |
| description = "Request to enable or disable automatic gain control for the stream." |
| )] |
| pub struct AgcCommand { |
| #[argh( |
| positional, |
| description = "enable or disable AGC. Accepted values: on, off", |
| from_str_fn(string_to_enable) |
| )] |
| pub enable: bool, |
| } |
| |
| fn string_to_enable(value: &str) -> Result<bool, String> { |
| if value == "on" { |
| Ok(true) |
| } else if value == "off" { |
| Ok(false) |
| } else { |
| Err(format!("Expected one of: on, off")) |
| } |
| } |
| |
| fn parse_duration(value: &str) -> Result<std::time::Duration, String> { |
| fuchsia_audio::parse_duration(value) |
| } |