@@ -3,82 +3,117 @@ use crate::config::AudioFormat;
33use crate :: convert:: Converter ;
44use crate :: decoder:: AudioPacket ;
55use crate :: { NUM_CHANNELS , SAMPLE_RATE } ;
6- use libpulse_binding:: { self as pulse, stream:: Direction } ;
6+ use libpulse_binding:: { self as pulse, error :: PAErr , stream:: Direction } ;
77use libpulse_simple_binding:: Simple ;
88use std:: io;
9+ use thiserror:: Error ;
910
1011const APP_NAME : & str = "librespot" ;
1112const STREAM_NAME : & str = "Spotify endpoint" ;
1213
14+ #[ derive( Debug , Error ) ]
15+ enum PulseError {
16+ #[ error( "Error starting PulseAudioSink, invalid PulseAudio sample spec" ) ]
17+ InvalidSampleSpec ,
18+ #[ error( "Error starting PulseAudioSink, could not connect to PulseAudio server, {0}" ) ]
19+ ConnectionRefused ( PAErr ) ,
20+ #[ error( "Error stopping PulseAudioSink, failed to drain PulseAudio server buffer, {0}" ) ]
21+ DrainFailure ( PAErr ) ,
22+ #[ error( "Error in PulseAudioSink, Not connected to PulseAudio server" ) ]
23+ NotConnected ,
24+ #[ error( "Error writing from PulseAudioSink to PulseAudio server, {0}" ) ]
25+ OnWrite ( PAErr ) ,
26+ }
27+
1328pub struct PulseAudioSink {
1429 s : Option < Simple > ,
15- ss : pulse:: sample:: Spec ,
1630 device : Option < String > ,
1731 format : AudioFormat ,
1832}
1933
2034impl Open for PulseAudioSink {
2135 fn open ( device : Option < String > , format : AudioFormat ) -> Self {
22- info ! ( "Using PulseAudio sink with format: {:?}" , format) ;
36+ let mut actual_format = format;
37+
38+ if actual_format == AudioFormat :: F64 {
39+ warn ! ( "PulseAudio currently does not support F64 output" ) ;
40+ actual_format = AudioFormat :: F32 ;
41+ }
42+
43+ info ! ( "Using PulseAudioSink with format: {:?}" , actual_format) ;
44+
45+ Self {
46+ s : None ,
47+ device,
48+ format : actual_format,
49+ }
50+ }
51+ }
52+
53+ impl Sink for PulseAudioSink {
54+ fn start ( & mut self ) -> io:: Result < ( ) > {
55+ if self . s . is_some ( ) {
56+ return Ok ( ( ) ) ;
57+ }
2358
2459 // PulseAudio calls S24 and S24_3 different from the rest of the world
25- let pulse_format = match format {
60+ let pulse_format = match self . format {
2661 AudioFormat :: F32 => pulse:: sample:: Format :: FLOAT32NE ,
2762 AudioFormat :: S32 => pulse:: sample:: Format :: S32NE ,
2863 AudioFormat :: S24 => pulse:: sample:: Format :: S24_32NE ,
2964 AudioFormat :: S24_3 => pulse:: sample:: Format :: S24NE ,
3065 AudioFormat :: S16 => pulse:: sample:: Format :: S16NE ,
31- _ => {
32- unimplemented ! ( "PulseAudio currently does not support {:?} output" , format)
33- }
66+ _ => unreachable ! ( ) ,
3467 } ;
3568
3669 let ss = pulse:: sample:: Spec {
3770 format : pulse_format,
3871 channels : NUM_CHANNELS ,
3972 rate : SAMPLE_RATE ,
4073 } ;
41- debug_assert ! ( ss. is_valid( ) ) ;
4274
43- Self {
44- s : None ,
45- ss,
46- device,
47- format,
48- }
49- }
50- }
51-
52- impl Sink for PulseAudioSink {
53- fn start ( & mut self ) -> io:: Result < ( ) > {
54- if self . s . is_some ( ) {
55- return Ok ( ( ) ) ;
75+ if !ss. is_valid ( ) {
76+ return Err ( io:: Error :: new (
77+ io:: ErrorKind :: Other ,
78+ PulseError :: InvalidSampleSpec ,
79+ ) ) ;
5680 }
5781
58- let device = self . device . as_deref ( ) ;
5982 let result = Simple :: new (
60- None , // Use the default server.
61- APP_NAME , // Our application's name.
62- Direction :: Playback , // Direction.
63- device, // Our device (sink) name.
64- STREAM_NAME , // Description of our stream.
65- & self . ss , // Our sample format.
66- None , // Use default channel map.
67- None , // Use default buffering attributes.
83+ None , // Use the default server.
84+ APP_NAME , // Our application's name.
85+ Direction :: Playback , // Direction.
86+ self . device . as_deref ( ) , // Our device (sink) name.
87+ STREAM_NAME , // Description of our stream.
88+ & ss, // Our sample format.
89+ None , // Use default channel map.
90+ None , // Use default buffering attributes.
6891 ) ;
92+
6993 match result {
7094 Ok ( s) => {
7195 self . s = Some ( s) ;
72- Ok ( ( ) )
7396 }
74- Err ( e) => Err ( io:: Error :: new (
75- io:: ErrorKind :: ConnectionRefused ,
76- e. to_string ( ) . unwrap ( ) ,
77- ) ) ,
97+ Err ( e) => {
98+ return Err ( io:: Error :: new (
99+ io:: ErrorKind :: ConnectionRefused ,
100+ PulseError :: ConnectionRefused ( e) ,
101+ ) ) ;
102+ }
78103 }
104+
105+ Ok ( ( ) )
79106 }
80107
81108 fn stop ( & mut self ) -> io:: Result < ( ) > {
109+ let s = self
110+ . s
111+ . as_mut ( )
112+ . ok_or_else ( || io:: Error :: new ( io:: ErrorKind :: NotConnected , PulseError :: NotConnected ) ) ?;
113+
114+ s. drain ( )
115+ . map_err ( |e| io:: Error :: new ( io:: ErrorKind :: Other , PulseError :: DrainFailure ( e) ) ) ?;
116+
82117 self . s = None ;
83118 Ok ( ( ) )
84119 }
@@ -88,20 +123,15 @@ impl Sink for PulseAudioSink {
88123
89124impl SinkAsBytes for PulseAudioSink {
90125 fn write_bytes ( & mut self , data : & [ u8 ] ) -> io:: Result < ( ) > {
91- if let Some ( s) = & self . s {
92- match s. write ( data) {
93- Ok ( _) => Ok ( ( ) ) ,
94- Err ( e) => Err ( io:: Error :: new (
95- io:: ErrorKind :: BrokenPipe ,
96- e. to_string ( ) . unwrap ( ) ,
97- ) ) ,
98- }
99- } else {
100- Err ( io:: Error :: new (
101- io:: ErrorKind :: NotConnected ,
102- "Not connected to PulseAudio" ,
103- ) )
104- }
126+ let s = self
127+ . s
128+ . as_mut ( )
129+ . ok_or_else ( || io:: Error :: new ( io:: ErrorKind :: NotConnected , PulseError :: NotConnected ) ) ?;
130+
131+ s. write ( data)
132+ . map_err ( |e| io:: Error :: new ( io:: ErrorKind :: Other , PulseError :: OnWrite ( e) ) ) ?;
133+
134+ Ok ( ( ) )
105135 }
106136}
107137
0 commit comments