diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/cli.rs | 113 | ||||
-rw-r--r-- | src/main.rs | 49 | ||||
-rw-r--r-- | src/pa_effects.rs | 125 | ||||
-rw-r--r-- | src/pa_eq.rs | 110 | ||||
-rw-r--r-- | src/parsing/equalizer_apo.lalrpop | 8 | ||||
-rw-r--r-- | src/parsing/equalizer_apo.rs | 9 | ||||
-rw-r--r-- | src/utils.rs | 96 |
7 files changed, 358 insertions, 152 deletions
@@ -21,31 +21,16 @@ pub struct Cli { | |||
21 | 21 | ||
22 | #[derive(StructOpt, Debug)] | 22 | #[derive(StructOpt, Debug)] |
23 | pub enum Command { | 23 | pub enum Command { |
24 | #[structopt(name = "load",)] | 24 | #[structopt(name = "pa-eq")] |
25 | /// Load and switch to a given equalizer configuration | 25 | /// PulseAudio equalizer related commands |
26 | Load(LoadCli), | 26 | /// |
27 | #[structopt(name = "reset")] | 27 | /// Warning: the PulseAudio equalizer has been deprecated for a while, |
28 | /// Switch to a neutral equalizer | 28 | /// and is known to sometimes cause crashes, latency or audible |
29 | Reset(ResetCli), | 29 | /// artifacts |
30 | } | 30 | PaEq(pa_eq::Command), |
31 | 31 | #[structopt(name = "pa-effects")] | |
32 | #[derive(StructOpt, Debug)] | 32 | /// PulseEffects equalizer related commands |
33 | pub struct LoadCli { | 33 | PaEffects(pa_effects::Command), |
34 | #[structopt(default_value = "-")] | ||
35 | /// The file from which to load the equalizer configuration | ||
36 | /// | ||
37 | /// If "-" is given, read the configuration from the command-line. | ||
38 | pub file: String, | ||
39 | #[structopt( | ||
40 | short = "f", | ||
41 | raw( | ||
42 | possible_values = "&EqualizerConfFormat::variants()", | ||
43 | case_insensitive = "true" | ||
44 | ), | ||
45 | default_value = "EqualizerAPO" | ||
46 | )] | ||
47 | /// The file format of the equalizer configuration | ||
48 | pub format: EqualizerConfFormat, | ||
49 | } | 34 | } |
50 | 35 | ||
51 | arg_enum! { | 36 | arg_enum! { |
@@ -55,5 +40,79 @@ arg_enum! { | |||
55 | } | 40 | } |
56 | } | 41 | } |
57 | 42 | ||
58 | #[derive(StructOpt, Debug)] | 43 | pub mod pa_eq { |
59 | pub struct ResetCli {} | 44 | use super::EqualizerConfFormat; |
45 | |||
46 | #[derive(StructOpt, Debug)] | ||
47 | pub enum Command { | ||
48 | #[structopt(name = "load",)] | ||
49 | /// Load and switch to a given equalizer configuration | ||
50 | Load(LoadCli), | ||
51 | #[structopt(name = "reset")] | ||
52 | /// Switch to a neutral equalizer | ||
53 | Reset(ResetCli), | ||
54 | } | ||
55 | |||
56 | #[derive(StructOpt, Debug)] | ||
57 | pub struct LoadCli { | ||
58 | #[structopt(default_value = "-")] | ||
59 | /// The file from which to load the equalizer configuration | ||
60 | /// | ||
61 | /// If "-" is given, read the configuration from the command-line. | ||
62 | pub file: String, | ||
63 | #[structopt( | ||
64 | short = "f", | ||
65 | raw( | ||
66 | possible_values = "&EqualizerConfFormat::variants()", | ||
67 | case_insensitive = "true" | ||
68 | ), | ||
69 | default_value = "EqualizerAPO" | ||
70 | )] | ||
71 | /// The file format of the equalizer configuration | ||
72 | pub format: EqualizerConfFormat, | ||
73 | } | ||
74 | |||
75 | #[derive(StructOpt, Debug)] | ||
76 | pub struct ResetCli {} | ||
77 | |||
78 | } | ||
79 | |||
80 | pub mod pa_effects { | ||
81 | use super::EqualizerConfFormat; | ||
82 | |||
83 | #[derive(StructOpt, Debug)] | ||
84 | pub enum Command { | ||
85 | #[structopt(name = "export-preset",)] | ||
86 | /// Export a PulseEffects preset | ||
87 | ExportPreset(ExportPresetCli), | ||
88 | } | ||
89 | |||
90 | #[derive(StructOpt, Debug)] | ||
91 | pub struct ExportPresetCli { | ||
92 | #[structopt(default_value = "-")] | ||
93 | /// The file from which to load the equalizer configuration | ||
94 | /// | ||
95 | /// If "-" is given, read the configuration from the command-line. | ||
96 | pub file: String, | ||
97 | #[structopt( | ||
98 | short = "f", | ||
99 | raw( | ||
100 | possible_values = "&EqualizerConfFormat::variants()", | ||
101 | case_insensitive = "true" | ||
102 | ), | ||
103 | default_value = "EqualizerAPO" | ||
104 | )] | ||
105 | /// The file format of the equalizer configuration | ||
106 | pub format: EqualizerConfFormat, | ||
107 | #[structopt(short = "p")] | ||
108 | /// Use a given file as a base for PulseEffects preset instead of the | ||
109 | /// default one. | ||
110 | /// | ||
111 | /// If "-" is given, read the base preset from the command-line. | ||
112 | pub base_preset: Option<String>, | ||
113 | #[structopt(short = "o")] | ||
114 | /// Write the preset to the given file, instead of the standard output | ||
115 | pub output: Option<String>, | ||
116 | } | ||
117 | |||
118 | } | ||
diff --git a/src/main.rs b/src/main.rs index 85d2443..a23034e 100644 --- a/src/main.rs +++ b/src/main.rs | |||
@@ -14,21 +14,22 @@ extern crate structopt; | |||
14 | 14 | ||
15 | extern crate lalrpop_util; | 15 | extern crate lalrpop_util; |
16 | 16 | ||
17 | #[macro_use] | ||
18 | extern crate serde_json; | ||
19 | |||
17 | mod cli; | 20 | mod cli; |
18 | mod dbus_api; | 21 | mod dbus_api; |
19 | mod parsing; | 22 | mod parsing; |
20 | mod utils; | 23 | mod utils; |
21 | 24 | ||
22 | use utils::*; | 25 | mod pa_eq; |
23 | use dbus_api::sink::OrgPulseAudioExtEqualizing1Equalizer; | 26 | mod pa_effects; |
27 | |||
24 | use cli::*; | 28 | use cli::*; |
25 | 29 | ||
26 | use failure::Error; | 30 | use failure::Error; |
27 | use structopt::StructOpt; | 31 | use structopt::StructOpt; |
28 | 32 | ||
29 | use std::fs::File; | ||
30 | use std::io; | ||
31 | |||
32 | #[derive(Debug)] | 33 | #[derive(Debug)] |
33 | pub struct Filter { | 34 | pub struct Filter { |
34 | preamp: f64, | 35 | preamp: f64, |
@@ -74,41 +75,7 @@ fn start() -> Result<(), Error> { | |||
74 | use Command::*; | 75 | use Command::*; |
75 | 76 | ||
76 | match args.cmd { | 77 | match args.cmd { |
77 | Load(args) => load(args), | 78 | PaEq(args) => pa_eq::main(args), |
78 | Reset(args) => reset(args), | 79 | PaEffects(args) => pa_effects::main(args), |
79 | } | 80 | } |
80 | } | 81 | } |
81 | |||
82 | fn reset(args: ResetCli) -> Result<(), Error> { | ||
83 | let conn = connect()?; | ||
84 | let conn_sink = get_equalized_sink(&conn)?; | ||
85 | let filter_rate = conn_sink.get_filter_sample_rate()?; | ||
86 | let filter = Filter { | ||
87 | preamp: 1f64, | ||
88 | frequencies: vec![], | ||
89 | coefficients: vec![], | ||
90 | }.pad(filter_rate); | ||
91 | |||
92 | send_filter(&conn_sink, filter)?; | ||
93 | |||
94 | Ok(()) | ||
95 | } | ||
96 | |||
97 | fn load(args: LoadCli) -> Result<(), Error> { | ||
98 | let conn = connect()?; | ||
99 | let conn_sink = get_equalized_sink(&conn)?; | ||
100 | |||
101 | let filter = if args.file == "-" { | ||
102 | let stdin = io::stdin(); | ||
103 | let mut handle = stdin.lock(); | ||
104 | read_filter(&mut handle)? | ||
105 | } else { | ||
106 | let mut file = File::open(args.file)?; | ||
107 | read_filter(&mut file)? | ||
108 | }; | ||
109 | |||
110 | let filter_rate = conn_sink.get_filter_sample_rate()?; | ||
111 | send_filter(&conn_sink, filter.pad(filter_rate))?; | ||
112 | |||
113 | Ok(()) | ||
114 | } | ||
diff --git a/src/pa_effects.rs b/src/pa_effects.rs new file mode 100644 index 0000000..9ed0a1f --- /dev/null +++ b/src/pa_effects.rs | |||
@@ -0,0 +1,125 @@ | |||
1 | use cli::pa_effects::*; | ||
2 | use utils::*; | ||
3 | use Filter; | ||
4 | |||
5 | use failure::Error; | ||
6 | |||
7 | use serde_json; | ||
8 | |||
9 | const DEFAULT_PRESET: &'static str = include_str!("../res/default-pa-effects-preset.json"); | ||
10 | |||
11 | pub fn main(cmd: Command) -> Result<(), Error> { | ||
12 | use cli::pa_effects::Command::*; | ||
13 | |||
14 | match cmd { | ||
15 | ExportPreset(args) => export_preset(args), | ||
16 | } | ||
17 | } | ||
18 | |||
19 | fn export_preset(args: ExportPresetCli) -> Result<(), Error> { | ||
20 | debug!("Parsing base preset"); | ||
21 | let mut preset: serde_json::Value = match args.base_preset { | ||
22 | Some(file) => serde_json::from_str(&read_filearg_to_str(&file)?), | ||
23 | None => serde_json::from_str(&DEFAULT_PRESET), | ||
24 | }?; | ||
25 | |||
26 | let filter = read_filter_from_arg(&args.file)?; | ||
27 | |||
28 | preset["output"]["equalizer"] = filter_to_eq_preset(filter); | ||
29 | |||
30 | println!("{}", preset); | ||
31 | Ok(()) | ||
32 | } | ||
33 | |||
34 | fn filter_to_eq_preset(mut filter: Filter) -> serde_json::Value { | ||
35 | if filter.frequencies.len() > 30 { | ||
36 | info!("More than 30 frequencies specified, taking the approximative approach"); | ||
37 | filter = simplify_filter(filter); | ||
38 | } | ||
39 | |||
40 | let mut equalizer: serde_json::Value = json!({ | ||
41 | "state": "true", | ||
42 | "num-bands": filter.frequencies.len(), | ||
43 | "input-gain": 0, | ||
44 | "output-gain": 0, | ||
45 | }); | ||
46 | |||
47 | for (i, (frequency, coeff)) in filter | ||
48 | .frequencies | ||
49 | .into_iter() | ||
50 | .zip(filter.coefficients) | ||
51 | .enumerate() | ||
52 | { | ||
53 | equalizer[format!("band{}", i)] = json!({ | ||
54 | "gain": coeff, | ||
55 | "frequency": frequency, | ||
56 | "type": "peak", | ||
57 | }); | ||
58 | } | ||
59 | |||
60 | equalizer | ||
61 | } | ||
62 | |||
63 | fn simplify_filter(filter: Filter) -> Filter { | ||
64 | //let partition_size = (filter.frequencies.len() as f64 / 30f64).floor() as usize; | ||
65 | let mut partition_size = filter.frequencies.len() / 30; | ||
66 | let step_error = filter.frequencies.len() as f64 % 30f64; | ||
67 | if step_error != 0f64 { | ||
68 | info!("The approximation will be imperfect"); | ||
69 | partition_size += 1; | ||
70 | } | ||
71 | |||
72 | //let mut cumulative_error = 0f64; | ||
73 | |||
74 | let frequencies = filter.frequencies.chunks(partition_size).map(|vec| { | ||
75 | let sum: u32 = vec.iter().sum(); | ||
76 | sum / vec.len() as u32 | ||
77 | }).collect(); | ||
78 | |||
79 | let coefficients = filter.coefficients.chunks(partition_size).map(|vec| { | ||
80 | let sum: f64 = vec.iter().sum(); | ||
81 | sum / vec.len() as f64 | ||
82 | }).collect(); | ||
83 | |||
84 | Filter { | ||
85 | preamp: filter.preamp, | ||
86 | frequencies, coefficients | ||
87 | } | ||
88 | } | ||
89 | |||
90 | /* | ||
91 | trait MultiPartitionnable { | ||
92 | type Item; | ||
93 | |||
94 | fn multi_partition(self, size: usize, wanted_parts: usize) -> MultiPartition<Self> | ||
95 | where | ||
96 | Self: Iterator + Sized, | ||
97 | { | ||
98 | MultiPartition { | ||
99 | iter: self, | ||
100 | size, | ||
101 | wanted_parts, | ||
102 | cumulative_error: 0f64, | ||
103 | } | ||
104 | } | ||
105 | } | ||
106 | |||
107 | impl<I> MultiPartitionnable for Iterator<Item = I> { | ||
108 | type Item = I; | ||
109 | } | ||
110 | |||
111 | struct MultiPartition<I: Iterator> { | ||
112 | iter: I, | ||
113 | size: usize, | ||
114 | wanted_parts: usize, | ||
115 | cumulative_error: f64, | ||
116 | } | ||
117 | |||
118 | impl<I: Iterator> Iterator for MultiPartition<I> { | ||
119 | type Item = Vec<I::Item>; | ||
120 | |||
121 | fn next(&mut self) -> Option<Self::Item> { | ||
122 | |||
123 | } | ||
124 | } | ||
125 | */ | ||
diff --git a/src/pa_eq.rs b/src/pa_eq.rs new file mode 100644 index 0000000..4086c31 --- /dev/null +++ b/src/pa_eq.rs | |||
@@ -0,0 +1,110 @@ | |||
1 | use Filter; | ||
2 | use cli::pa_eq::*; | ||
3 | use utils::*; | ||
4 | |||
5 | use dbus_api::equalizing_manager::OrgPulseAudioExtEqualizing1Manager; | ||
6 | use dbus_api::server_lookup::OrgPulseAudioServerLookup1; | ||
7 | use dbus_api::sink::OrgPulseAudioExtEqualizing1Equalizer; | ||
8 | |||
9 | use dbus::{BusType, ConnPath, Connection}; | ||
10 | |||
11 | use failure::{Error, ResultExt}; | ||
12 | |||
13 | #[derive(Fail, Debug)] | ||
14 | #[fail(display = "No equalized sink found")] | ||
15 | struct NoEqualizedSink; | ||
16 | |||
17 | pub fn main(cmd: Command) -> Result<(), Error> { | ||
18 | use cli::pa_eq::Command::*; | ||
19 | |||
20 | warn!("The PulseAudio equalizer has been deprecated for a while, and is known to sometimes cause crashes, latency or audible artifacts"); | ||
21 | |||
22 | match cmd { | ||
23 | Load(args) => load(args), | ||
24 | Reset(args) => reset(args), | ||
25 | } | ||
26 | } | ||
27 | |||
28 | pub fn reset(_args: ResetCli) -> Result<(), Error> { | ||
29 | let conn = connect()?; | ||
30 | let conn_sink = get_equalized_sink(&conn)?; | ||
31 | let filter_rate = conn_sink.get_filter_sample_rate()?; | ||
32 | let filter = Filter { | ||
33 | preamp: 1f64, | ||
34 | frequencies: vec![], | ||
35 | coefficients: vec![], | ||
36 | }.pad(filter_rate); | ||
37 | |||
38 | send_filter(&conn_sink, filter)?; | ||
39 | |||
40 | Ok(()) | ||
41 | } | ||
42 | |||
43 | pub fn load(args: LoadCli) -> Result<(), Error> { | ||
44 | let conn = connect()?; | ||
45 | let conn_sink = get_equalized_sink(&conn)?; | ||
46 | |||
47 | let filter = read_filter_from_arg(&args.file)?; | ||
48 | |||
49 | let filter_rate = conn_sink.get_filter_sample_rate()?; | ||
50 | send_filter(&conn_sink, filter.pad(filter_rate))?; | ||
51 | |||
52 | Ok(()) | ||
53 | } | ||
54 | |||
55 | fn connect() -> Result<Connection, Error> { | ||
56 | Ok(connect_impl().context( | ||
57 | "Could not connect to PulseAudio's D-Bus socket. Have you loaded the 'module-dbus-protocol' module?" | ||
58 | )?) | ||
59 | } | ||
60 | |||
61 | fn connect_impl() -> Result<Connection, Error> { | ||
62 | let pulse_sock_path = get_pulse_dbus_sock()?; | ||
63 | info!("PulseAudio's D-Bus socket path is: {}", pulse_sock_path); | ||
64 | |||
65 | trace!("Connecting to PulseAudio's D-Bus socket"); | ||
66 | Ok(Connection::open_private(&pulse_sock_path)?) | ||
67 | } | ||
68 | |||
69 | fn get_equalized_sink<'a>(conn: &'a Connection) -> Result<ConnPath<'a, &'a Connection>, Error> { | ||
70 | Ok(get_equalized_sink_impl(conn).context( | ||
71 | "Could not find an equalized sink. Have you loaded the 'module-equalizer-sink' module?", | ||
72 | )?) | ||
73 | } | ||
74 | |||
75 | fn get_equalized_sink_impl<'a>( | ||
76 | conn: &'a Connection, | ||
77 | ) -> Result<ConnPath<'a, &'a Connection>, Error> { | ||
78 | let conn_manager = conn.with_path("org.PulseAudio.Core1", "/org/pulseaudio/equalizing1", 2000); | ||
79 | |||
80 | // TODO: make that a command-line option | ||
81 | trace!("Getting (one of) the equalized sink(s)"); | ||
82 | let mut sinks = conn_manager.get_equalized_sinks()?; | ||
83 | let sink_path = sinks.pop().ok_or(NoEqualizedSink {})?; | ||
84 | info!("Using equalized sink: {:?}", sink_path.as_cstr()); | ||
85 | |||
86 | trace!("Connecting to equalized sink"); | ||
87 | Ok(conn.with_path("org.PulseAudio.Core1", sink_path, 2000)) | ||
88 | } | ||
89 | |||
90 | fn send_filter(conn_sink: &ConnPath<&Connection>, filter: Filter) -> Result<(), Error> { | ||
91 | let channel = conn_sink.get_nchannels()?; | ||
92 | info!("Using channel: {}", channel); | ||
93 | trace!("Sending filter: {:?}", filter); | ||
94 | conn_sink.seed_filter( | ||
95 | channel, | ||
96 | filter.frequencies, | ||
97 | filter.coefficients.into_iter().map(decibel_to_ratio).collect(), | ||
98 | decibel_to_ratio(filter.preamp), | ||
99 | )?; | ||
100 | Ok(()) | ||
101 | } | ||
102 | |||
103 | fn get_pulse_dbus_sock() -> Result<String, Error> { | ||
104 | trace!("Connecting to the D-Bus' session bus"); | ||
105 | let conn = Connection::get_private(BusType::Session)?; | ||
106 | let conn = conn.with_path("org.PulseAudio1", "/org/pulseaudio/server_lookup1", 2000); | ||
107 | |||
108 | trace!("Checking PulseAudio's D-Bus socket path"); | ||
109 | Ok(conn.get_address()?) | ||
110 | } | ||
diff --git a/src/parsing/equalizer_apo.lalrpop b/src/parsing/equalizer_apo.lalrpop index 39dda67..752aee4 100644 --- a/src/parsing/equalizer_apo.lalrpop +++ b/src/parsing/equalizer_apo.lalrpop | |||
@@ -5,12 +5,8 @@ use std::str::FromStr; | |||
5 | grammar; | 5 | grammar; |
6 | 6 | ||
7 | pub Main: Filter = { | 7 | pub Main: Filter = { |
8 | <preamp: Preamp> <eq: Eq> => { | 8 | <preamp: Preamp> <eq: Eq> => Filter { preamp, frequencies: eq.0, coefficients: eq.1 } |
9 | let coefficients: Vec<_> = eq.1.iter().map(|decibel| 10f64.powf(decibel / 10f64).sqrt()).collect(); | 9 | |
10 | // TODO: add decibel_to_ratio conversion function | ||
11 | let preamp = 10f64.powf(preamp / 10f64); | ||
12 | Filter { preamp, frequencies: eq.0, coefficients } | ||
13 | } | ||
14 | } | 10 | } |
15 | 11 | ||
16 | Preamp: f64 = { | 12 | Preamp: f64 = { |
diff --git a/src/parsing/equalizer_apo.rs b/src/parsing/equalizer_apo.rs index a24cf99..b0aa8f8 100644 --- a/src/parsing/equalizer_apo.rs +++ b/src/parsing/equalizer_apo.rs | |||
@@ -1,5 +1,5 @@ | |||
1 | // auto-generated: "lalrpop 0.15.2" | 1 | // auto-generated: "lalrpop 0.15.2" |
2 | // sha256: a735e8f9bd5cf5f3aac915d12b24752d366481c3952258e22871eef395f44 | 2 | // sha256: 3981cad2c0ee5c1d80c3b7278bb1bc9926ee7b273c2b68423accdd84e43c494 |
3 | use ::Filter; | 3 | use ::Filter; |
4 | use std::str::FromStr; | 4 | use std::str::FromStr; |
5 | #[allow(unused_extern_crates)] | 5 | #[allow(unused_extern_crates)] |
@@ -879,12 +879,7 @@ fn __action1< | |||
879 | (_, eq, _): (usize, (Vec<u32>, Vec<f64>), usize), | 879 | (_, eq, _): (usize, (Vec<u32>, Vec<f64>), usize), |
880 | ) -> Filter | 880 | ) -> Filter |
881 | { | 881 | { |
882 | { | 882 | Filter { preamp, frequencies: eq.0, coefficients: eq.1 } |
883 | let coefficients: Vec<_> = eq.1.iter().map(|decibel| 10f64.powf(decibel / 10f64).sqrt()).collect(); | ||
884 | // TODO: add decibel_to_ratio conversion function | ||
885 | let preamp = 10f64.powf(preamp / 10f64); | ||
886 | Filter { preamp, frequencies: eq.0, coefficients } | ||
887 | } | ||
888 | } | 883 | } |
889 | 884 | ||
890 | #[allow(unused_variables)] | 885 | #[allow(unused_variables)] |
diff --git a/src/utils.rs b/src/utils.rs index de9ed3c..d75971a 100644 --- a/src/utils.rs +++ b/src/utils.rs | |||
@@ -1,23 +1,16 @@ | |||
1 | use {EqualizerConfFormat, Filter}; | 1 | use Filter; |
2 | 2 | ||
3 | use cli::EqualizerConfFormat; | ||
3 | use parsing::EqualizerApoParser; | 4 | use parsing::EqualizerApoParser; |
4 | 5 | ||
5 | use dbus_api::equalizing_manager::OrgPulseAudioExtEqualizing1Manager; | 6 | use failure::Error; |
6 | use dbus_api::server_lookup::OrgPulseAudioServerLookup1; | ||
7 | use dbus_api::sink::OrgPulseAudioExtEqualizing1Equalizer; | ||
8 | |||
9 | use dbus::{BusType, ConnPath, Connection}; | ||
10 | use failure::{Error, ResultExt}; | ||
11 | use lalrpop_util; | 7 | use lalrpop_util; |
12 | 8 | ||
13 | use std::fmt; | 9 | use std::fmt; |
10 | use std::fs::File; | ||
14 | use std::io; | 11 | use std::io; |
15 | 12 | ||
16 | #[derive(Fail, Debug)] | 13 | #[derive(Fail, Debug)] |
17 | #[fail(display = "No equalized sink found")] | ||
18 | struct NoEqualizedSink; | ||
19 | |||
20 | #[derive(Fail, Debug)] | ||
21 | #[fail( | 14 | #[fail( |
22 | display = "Could not parse using the {} format: {}", | 15 | display = "Could not parse using the {} format: {}", |
23 | format, | 16 | format, |
@@ -28,66 +21,32 @@ struct ParseError { | |||
28 | message: String, | 21 | message: String, |
29 | } | 22 | } |
30 | 23 | ||
31 | pub fn connect() -> Result<Connection, Error> { | 24 | pub fn read_filearg_to_str(file: &str) -> Result<String, Error> { |
32 | Ok(connect_impl().context( | 25 | use std::io::Read; |
33 | "Could not connect to PulseAudio's D-Bus socket. Have you loaded the 'module-dbus-protocol' module?" | ||
34 | )?) | ||
35 | } | ||
36 | |||
37 | fn connect_impl() -> Result<Connection, Error> { | ||
38 | let pulse_sock_path = get_pulse_dbus_sock()?; | ||
39 | info!("PulseAudio's D-Bus socket path is: {}", pulse_sock_path); | ||
40 | |||
41 | trace!("Connecting to PulseAudio's D-Bus socket"); | ||
42 | Ok(Connection::open_private(&pulse_sock_path)?) | ||
43 | } | ||
44 | |||
45 | pub fn get_equalized_sink<'a>(conn: &'a Connection) -> Result<ConnPath<'a, &'a Connection>, Error> { | ||
46 | Ok(get_equalized_sink_impl(conn).context( | ||
47 | "Could not find an equalized sink. Have you loaded the 'module-equalizer-sink' module?", | ||
48 | )?) | ||
49 | } | ||
50 | |||
51 | fn get_equalized_sink_impl<'a>( | ||
52 | conn: &'a Connection, | ||
53 | ) -> Result<ConnPath<'a, &'a Connection>, Error> { | ||
54 | let conn_manager = conn.with_path("org.PulseAudio.Core1", "/org/pulseaudio/equalizing1", 2000); | ||
55 | 26 | ||
56 | // TODO: make that a command-line option | 27 | let mut buffer = String::new(); |
57 | trace!("Getting (one of) the equalized sink(s)"); | 28 | if file == "-" { |
58 | let mut sinks = conn_manager.get_equalized_sinks()?; | 29 | info!("Reading file from the command line"); |
59 | let sink_path = sinks.pop().ok_or(NoEqualizedSink {})?; | 30 | let stdin = io::stdin(); |
60 | info!("Using equalized sink: {:?}", sink_path.as_cstr()); | 31 | let mut handle = stdin.lock(); |
61 | 32 | handle.read_to_string(&mut buffer)?; | |
62 | trace!("Connecting to equalized sink"); | 33 | } else { |
63 | Ok(conn.with_path("org.PulseAudio.Core1", sink_path, 2000)) | 34 | let mut file = File::open(file)?; |
35 | file.read_to_string(&mut buffer)?; | ||
36 | } | ||
37 | Ok(buffer) | ||
64 | } | 38 | } |
65 | 39 | ||
66 | pub fn send_filter(conn_sink: &ConnPath<&Connection>, filter: Filter) -> Result<(), Error> { | 40 | pub fn read_filter_from_arg(file: &str) -> Result<Filter, Error> { |
67 | let channel = conn_sink.get_nchannels()?; | 41 | debug!("Reading filter from '{}' in the EqualizerAPO format", file); |
68 | info!("Using channel: {}", channel); | 42 | let content = read_filearg_to_str(file)?; |
69 | trace!("Sending filter: {:?}", filter); | 43 | parse_filter(&content) |
70 | conn_sink.seed_filter( | ||
71 | channel, | ||
72 | filter.frequencies, | ||
73 | filter.coefficients, | ||
74 | filter.preamp, | ||
75 | )?; | ||
76 | Ok(()) | ||
77 | } | 44 | } |
78 | 45 | ||
79 | pub fn read_filter<T>(file: &mut T) -> Result<Filter, Error> | 46 | pub fn parse_filter(content: &str) -> Result<Filter, Error> { |
80 | where | ||
81 | T: io::Read, | ||
82 | { | ||
83 | let mut buffer = String::new(); | ||
84 | |||
85 | info!("Reading filter in GraphicEQ format from the command line"); | ||
86 | file.read_to_string(&mut buffer)?; | ||
87 | |||
88 | // TODO: lifetime issue when "throwing" parse error | 47 | // TODO: lifetime issue when "throwing" parse error |
89 | let filter = EqualizerApoParser::new() | 48 | let filter = EqualizerApoParser::new() |
90 | .parse(&buffer) | 49 | .parse(&content) |
91 | .map_err(|e| convert_parse_error(EqualizerConfFormat::EqualizerAPO, e))?; | 50 | .map_err(|e| convert_parse_error(EqualizerConfFormat::EqualizerAPO, e))?; |
92 | trace!("Parsed filter: {:?}", filter); | 51 | trace!("Parsed filter: {:?}", filter); |
93 | 52 | ||
@@ -109,13 +68,8 @@ where | |||
109 | } | 68 | } |
110 | } | 69 | } |
111 | 70 | ||
112 | fn get_pulse_dbus_sock() -> Result<String, Error> { | 71 | pub fn decibel_to_ratio(decibel: f64) -> f64 { |
113 | trace!("Connecting to the D-Bus' session bus"); | 72 | 10f64.powf(decibel / 10f64).sqrt() |
114 | let conn = Connection::get_private(BusType::Session)?; | ||
115 | let conn = conn.with_path("org.PulseAudio1", "/org/pulseaudio/server_lookup1", 2000); | ||
116 | |||
117 | trace!("Checking PulseAudio's D-Bus socket path"); | ||
118 | Ok(conn.get_address()?) | ||
119 | } | 73 | } |
120 | 74 | ||
121 | /* | 75 | /* |