librespot/playback/src/audio_backend/pulseaudio.rs

96 lines
2.8 KiB
Rust
Raw Normal View History

2016-03-20 19:16:32 +00:00
use super::{Open, Sink};
2021-01-07 06:42:38 +00:00
use crate::audio::AudioPacket;
use libpulse_binding::{self as pulse, stream::Direction};
use libpulse_simple_binding::Simple;
2018-02-26 01:50:41 +00:00
use std::io;
2016-03-20 19:16:32 +00:00
const APP_NAME: &str = "librespot";
const STREAM_NAME: &str = "Spotify endpoint";
pub struct PulseAudioSink {
s: Option<Simple>,
ss: pulse::sample::Spec,
device: Option<String>,
}
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");
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
};
debug_assert!(ss.is_valid());
2016-03-20 19:16:32 +00:00
2017-01-14 21:22:33 +00:00
PulseAudioSink {
s: None,
2017-01-14 21:22:33 +00:00
ss: ss,
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<()> {
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.
Direction::Playback, // Direction.
device, // Our device (sink) name.
STREAM_NAME, // Description 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,
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<()> {
self.s = None;
2016-03-20 19:16:32 +00:00
Ok(())
}
2021-01-07 06:42:38 +00:00
fn write(&mut self, packet: &AudioPacket) -> io::Result<()> {
if let Some(s) = &self.s {
// SAFETY: An i16 consists of two bytes, so that the given slice can be interpreted
// as a byte array of double length. Each byte pointer is validly aligned, and so
// is the newly created slice.
2021-01-07 06:42:38 +00:00
let d: &[u8] = unsafe {
std::slice::from_raw_parts(
packet.samples().as_ptr() as *const u8,
packet.samples().len() * 2,
)
};
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",
))
}
2016-03-20 19:16:32 +00:00
}
}