extern crate dbus; #[macro_use] extern crate log; #[macro_use] extern crate failure; #[macro_use] extern crate clap; extern crate clap_log_flag; extern crate clap_verbosity_flag; #[macro_use] extern crate structopt; mod dbus_api; mod parsing; mod utils; use utils::*; use dbus_api::sink::OrgPulseAudioExtEqualizing1Equalizer; use failure::{Error, ResultExt}; use structopt::StructOpt; use std::fs::File; use std::io; #[derive(StructOpt, Debug)] #[structopt(raw(setting = "structopt::clap::AppSettings::ColoredHelp"))] /// Hello World! How are you doing? struct Cli { #[structopt(flatten)] verbose: clap_verbosity_flag::Verbosity, #[structopt(flatten)] log: clap_log_flag::Log, #[structopt(short = "s")] /// Use the given sink. /// /// By default it will use the last equalized sink it finds sink: Option, #[structopt(subcommand)] cmd: Command, } #[derive(StructOpt, Debug)] enum Command { #[structopt(name = "load",)] /// Load and switch to a given equalizer configuration Load(LoadCli), #[structopt(name = "reset")] /// Switch to a neutral equalizer Reset(ResetCli), } #[derive(StructOpt, Debug)] struct LoadCli { #[structopt(default_value = "-")] /// The file from which to load the equalizer configuration /// /// If "-" is given, read the configuration from the command-line. file: String, #[structopt( short = "f", raw( possible_values = "&EqualizerConfFormat::variants()", case_insensitive = "true" ), default_value = "EqualizerAPO" )] /// The file format of the equalizer configuration format: EqualizerConfFormat, } arg_enum! { #[derive(Debug)] enum EqualizerConfFormat { EqualizerAPO } } #[derive(StructOpt, Debug)] struct ResetCli {} #[derive(Fail, Debug)] #[fail(display = "No equalized sink found")] struct NoEqualizedSink; #[derive(Debug)] pub struct Filter { preamp: f64, frequencies: Vec, coefficients: Vec, } impl Filter { fn pad(self, filter_rate: u32) -> Self { Filter { preamp: self.preamp, frequencies: vec![0u32] .into_iter() .chain(self.frequencies.into_iter()) .chain(vec![filter_rate / 2u32]) .collect(), coefficients: vec![1f64] .into_iter() .chain(self.coefficients.into_iter()) .chain(vec![1f64]) .collect(), } } } fn main() { match start() { Ok(()) => (), Err(err) => { eprintln!("Error: {}", err); for cause in err.iter_causes() { eprintln!(" : {}", cause); } std::process::exit(-1); } } } fn start() -> Result<(), Error> { let args = Cli::from_args(); args.log.log_all(args.verbose.log_level())?; use Command::*; match args.cmd { Load(args) => load(args), Reset(args) => reset(args), } } fn reset(args: ResetCli) -> Result<(), Error> { let conn = connect().context( "Could not connect to PulseAudio's D-Bus socket. Have you loaded the 'module-dbus-protocol' module?" )?; let conn_sink = get_equalized_sink(&conn).context( "Could not find an equalized sink. Have you loaded the 'module-equalizer-sink' module?", )?; let filter_rate = conn_sink.get_filter_sample_rate()?; let filter = Filter { preamp: 1f64, frequencies: vec![], coefficients: vec![], }.pad(filter_rate); send_filter(&conn_sink, filter)?; Ok(()) } fn load(args: LoadCli) -> Result<(), Error> { let conn = connect().context( "Could not connect to PulseAudio's D-Bus socket. Have you loaded the 'module-dbus-protocol' module?" )?; let conn_sink = get_equalized_sink(&conn).context( "Could not find an equalized sink. Have you loaded the 'module-equalizer-sink' module?", )?; let filter = if args.file == "-" { let stdin = io::stdin(); let mut handle = stdin.lock(); read_filter(&mut handle)? } else { let mut file = File::open(args.file)?; read_filter(&mut file)? }; let filter_rate = conn_sink.get_filter_sample_rate()?; send_filter(&conn_sink, filter.pad(filter_rate))?; Ok(()) }