2016-03-20 19:16:32 +00:00
|
|
|
use super::{Open, Sink};
|
2020-11-27 01:06:25 +00:00
|
|
|
use libpulse_binding as pulse;
|
|
|
|
use libpulse_simple_binding::Simple;
|
2018-02-26 01:50:41 +00:00
|
|
|
use std::io;
|
2016-03-20 19:16:32 +00:00
|
|
|
|
2020-11-27 01:06:25 +00:00
|
|
|
const APP_NAME: &str = "librespot";
|
|
|
|
const STREAM_NAME: &str = "Spotify endpoint";
|
2017-11-27 19:01:30 +00:00
|
|
|
|
2020-11-27 01:06:25 +00:00
|
|
|
pub struct PulseAudioSink {
|
|
|
|
s: Option<Simple>,
|
|
|
|
ss: pulse::sample::Spec,
|
|
|
|
device: Option<String>,
|
2017-11-27 19:01:30 +00:00
|
|
|
}
|
|
|
|
|
2016-03-20 19:16:32 +00:00
|
|
|
impl Open for PulseAudioSink {
|
2018-02-26 01:50:41 +00:00
|
|
|
fn open(device: Option<String>) -> PulseAudioSink {
|
2016-07-06 09:54:46 +00:00
|
|
|
debug!("Using PulseAudio sink");
|
|
|
|
|
2020-11-27 01:06:25 +00:00
|
|
|
let ss = pulse::sample::Spec {
|
|
|
|
format: pulse::sample::Format::S16le,
|
2016-03-20 19:16:32 +00:00
|
|
|
channels: 2, // stereo
|
2018-02-26 01:50:41 +00:00
|
|
|
rate: 44100,
|
2016-03-20 19:16:32 +00:00
|
|
|
};
|
2020-11-27 01:06:25 +00:00
|
|
|
debug_assert!(ss.is_valid());
|
2016-03-20 19:16:32 +00:00
|
|
|
|
2017-01-14 21:22:33 +00:00
|
|
|
PulseAudioSink {
|
2020-11-27 01:06:25 +00:00
|
|
|
s: None,
|
2017-01-14 21:22:33 +00:00
|
|
|
ss: ss,
|
2020-11-27 01:06:25 +00:00
|
|
|
device: device,
|
2017-01-14 21:22:33 +00:00
|
|
|
}
|
2016-03-20 19:16:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Sink for PulseAudioSink {
|
2016-08-01 19:20:17 +00:00
|
|
|
fn start(&mut self) -> io::Result<()> {
|
2020-11-27 01:06:25 +00:00
|
|
|
if self.s.is_some() {
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
|
|
|
|
let device = self.device.as_ref().map(|s| (*s).as_str());
|
|
|
|
let result = Simple::new(
|
|
|
|
None, // Use the default server.
|
|
|
|
APP_NAME, // Our application's name.
|
|
|
|
pulse::stream::Direction::Playback,
|
|
|
|
device,
|
|
|
|
STREAM_NAME, // desc of our stream.
|
|
|
|
&self.ss, // Our sample format.
|
|
|
|
None, // Use default channel map
|
|
|
|
None, // Use default buffering attributes.
|
|
|
|
);
|
|
|
|
match result {
|
|
|
|
Ok(s) => {
|
|
|
|
self.s = Some(s);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
Err(e) => Err(io::Error::new(
|
2018-02-26 01:50:41 +00:00
|
|
|
io::ErrorKind::ConnectionRefused,
|
2020-11-27 01:06:25 +00:00
|
|
|
e.to_string().unwrap(),
|
|
|
|
)),
|
2017-01-14 21:22:33 +00:00
|
|
|
}
|
2016-03-20 19:16:32 +00:00
|
|
|
}
|
|
|
|
|
2016-08-01 19:20:17 +00:00
|
|
|
fn stop(&mut self) -> io::Result<()> {
|
2020-11-27 01:06:25 +00:00
|
|
|
self.s = None;
|
2016-03-20 19:16:32 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2016-08-01 19:20:17 +00:00
|
|
|
fn write(&mut self, data: &[i16]) -> io::Result<()> {
|
2020-11-27 01:06:25 +00:00
|
|
|
if let Some(s) = &self.s {
|
|
|
|
let d: &[u8] = unsafe { std::mem::transmute(data) };
|
|
|
|
|
|
|
|
match s.write(d) {
|
|
|
|
Ok(_) => Ok(()),
|
|
|
|
Err(e) => Err(io::Error::new(
|
|
|
|
io::ErrorKind::BrokenPipe,
|
|
|
|
e.to_string().unwrap(),
|
|
|
|
)),
|
|
|
|
}
|
|
|
|
} else {
|
2018-02-26 01:50:41 +00:00
|
|
|
Err(io::Error::new(
|
|
|
|
io::ErrorKind::NotConnected,
|
|
|
|
"Not connected to pulseaudio",
|
|
|
|
))
|
2017-11-27 19:01:30 +00:00
|
|
|
}
|
2016-03-20 19:16:32 +00:00
|
|
|
}
|
|
|
|
}
|