summaryrefslogtreecommitdiffstats
path: root/src/utils.rs
blob: de9ed3c83d4d9c0abf1e08ef5d9fc531bd4d803a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
use {EqualizerConfFormat, Filter};

use parsing::EqualizerApoParser;

use dbus_api::equalizing_manager::OrgPulseAudioExtEqualizing1Manager;
use dbus_api::server_lookup::OrgPulseAudioServerLookup1;
use dbus_api::sink::OrgPulseAudioExtEqualizing1Equalizer;

use dbus::{BusType, ConnPath, Connection};
use failure::{Error, ResultExt};
use lalrpop_util;

use std::fmt;
use std::io;

#[derive(Fail, Debug)]
#[fail(display = "No equalized sink found")]
struct NoEqualizedSink;

#[derive(Fail, Debug)]
#[fail(
    display = "Could not parse using the {} format: {}",
    format,
    message
)]
struct ParseError {
    format: EqualizerConfFormat,
    message: String,
}

pub fn connect() -> Result<Connection, Error> {
    Ok(connect_impl().context(
        "Could not connect to PulseAudio's D-Bus socket. Have you loaded the 'module-dbus-protocol' module?"
    )?)
}

fn connect_impl() -> Result<Connection, Error> {
    let pulse_sock_path = get_pulse_dbus_sock()?;
    info!("PulseAudio's D-Bus socket path is: {}", pulse_sock_path);

    trace!("Connecting to PulseAudio's D-Bus socket");
    Ok(Connection::open_private(&pulse_sock_path)?)
}

pub fn get_equalized_sink<'a>(conn: &'a Connection) -> Result<ConnPath<'a, &'a Connection>, Error> {
    Ok(get_equalized_sink_impl(conn).context(
        "Could not find an equalized sink. Have you loaded the 'module-equalizer-sink' module?",
    )?)
}

fn get_equalized_sink_impl<'a>(
    conn: &'a Connection,
) -> Result<ConnPath<'a, &'a Connection>, Error> {
    let conn_manager = conn.with_path("org.PulseAudio.Core1", "/org/pulseaudio/equalizing1", 2000);

    // TODO: make that a command-line option
    trace!("Getting (one of) the equalized sink(s)");
    let mut sinks = conn_manager.get_equalized_sinks()?;
    let sink_path = sinks.pop().ok_or(NoEqualizedSink {})?;
    info!("Using equalized sink: {:?}", sink_path.as_cstr());

    trace!("Connecting to equalized sink");
    Ok(conn.with_path("org.PulseAudio.Core1", sink_path, 2000))
}

pub fn send_filter(conn_sink: &ConnPath<&Connection>, filter: Filter) -> Result<(), Error> {
    let channel = conn_sink.get_nchannels()?;
    info!("Using channel: {}", channel);
    trace!("Sending filter: {:?}", filter);
    conn_sink.seed_filter(
        channel,
        filter.frequencies,
        filter.coefficients,
        filter.preamp,
    )?;
    Ok(())
}

pub fn read_filter<T>(file: &mut T) -> Result<Filter, Error>
where
    T: io::Read,
{
    let mut buffer = String::new();

    info!("Reading filter in GraphicEQ format from the command line");
    file.read_to_string(&mut buffer)?;

    // TODO: lifetime issue when "throwing" parse error
    let filter = EqualizerApoParser::new()
        .parse(&buffer)
        .map_err(|e| convert_parse_error(EqualizerConfFormat::EqualizerAPO, e))?;
    trace!("Parsed filter: {:?}", filter);

    Ok(filter)
}

fn convert_parse_error<L, T, E>(
    format: EqualizerConfFormat,
    error: lalrpop_util::ParseError<L, T, E>,
) -> ParseError
where
    L: fmt::Display,
    T: fmt::Display,
    E: fmt::Display,
{
    ParseError {
        format,
        message: format!("{}", error),
    }
}

fn get_pulse_dbus_sock() -> Result<String, Error> {
    trace!("Connecting to the D-Bus' session bus");
    let conn = Connection::get_private(BusType::Session)?;
    let conn = conn.with_path("org.PulseAudio1", "/org/pulseaudio/server_lookup1", 2000);

    trace!("Checking PulseAudio's D-Bus socket path");
    Ok(conn.get_address()?)
}

/*
fn introspect(conn: &dbus::ConnPath<&Connection>) {
    let mut thing = conn
        .method_call_with_args(
            &"org.freedesktop.DBus.Introspectable".into(),
            &"Introspect".into(),
            |_| {},
        ).unwrap();
    thing.as_result().unwrap();

    println!("{}", thing.iter_init().read::<String>().unwrap());
}
*/