1- use super :: { Open , Sink , SinkAsBytes } ;
1+ use super :: { Open , Sink , SinkAsBytes , SinkError , SinkResult } ;
22use crate :: config:: AudioFormat ;
33use crate :: convert:: Converter ;
44use crate :: decoder:: AudioPacket ;
@@ -7,7 +7,6 @@ use alsa::device_name::HintIter;
77use alsa:: pcm:: { Access , Format , HwParams , PCM } ;
88use alsa:: { Direction , ValueOr } ;
99use std:: cmp:: min;
10- use std:: io;
1110use std:: process:: exit;
1211use std:: time:: Duration ;
1312use thiserror:: Error ;
@@ -18,34 +17,67 @@ const BUFFER_TIME: Duration = Duration::from_millis(500);
1817
1918#[ derive( Debug , Error ) ]
2019enum AlsaError {
21- #[ error( "AlsaSink, device {device} may be invalid or busy, {err}" ) ]
22- PcmSetUp { device : String , err : alsa:: Error } ,
23- #[ error( "AlsaSink, device {device} unsupported access type RWInterleaved, {err}" ) ]
24- UnsupportedAccessType { device : String , err : alsa:: Error } ,
25- #[ error( "AlsaSink, device {device} unsupported format {format:?}, {err}" ) ]
20+ #[ error( "<AlsaSink> Device {device} Unsupported Format {alsa_format:?} ({format:?}), {e}" ) ]
2621 UnsupportedFormat {
2722 device : String ,
23+ alsa_format : Format ,
2824 format : AudioFormat ,
29- err : alsa:: Error ,
25+ e : alsa:: Error ,
3026 } ,
31- #[ error( "AlsaSink, device {device} unsupported sample rate {samplerate}, {err}" ) ]
32- UnsupportedSampleRate {
33- device : String ,
34- samplerate : u32 ,
35- err : alsa:: Error ,
36- } ,
37- #[ error( "AlsaSink, device {device} unsupported channel count {channel_count}, {err}" ) ]
27+
28+ #[ error( "<AlsaSink> Device {device} Unsupported Channel Count {channel_count}, {e}" ) ]
3829 UnsupportedChannelCount {
3930 device : String ,
4031 channel_count : u8 ,
41- err : alsa:: Error ,
32+ e : alsa:: Error ,
33+ } ,
34+
35+ #[ error( "<AlsaSink> Device {device} Unsupported Sample Rate {samplerate}, {e}" ) ]
36+ UnsupportedSampleRate {
37+ device : String ,
38+ samplerate : u32 ,
39+ e : alsa:: Error ,
4240 } ,
43- #[ error( "AlsaSink Hardware Parameters Error, {0}" ) ]
41+
42+ #[ error( "<AlsaSink> Device {device} Unsupported Access Type RWInterleaved, {e}" ) ]
43+ UnsupportedAccessType { device : String , e : alsa:: Error } ,
44+
45+ #[ error( "<AlsaSink> Device {device} May be Invalid, Busy, or Already in Use, {e}" ) ]
46+ PcmSetUp { device : String , e : alsa:: Error } ,
47+
48+ #[ error( "<AlsaSink> Failed to Drain PCM Buffer, {0}" ) ]
49+ DrainFailure ( alsa:: Error ) ,
50+
51+ #[ error( "<AlsaSink> {0}" ) ]
52+ OnWrite ( alsa:: Error ) ,
53+
54+ #[ error( "<AlsaSink> Hardware, {0}" ) ]
4455 HwParams ( alsa:: Error ) ,
45- #[ error( "AlsaSink Software Parameters Error, {0}" ) ]
56+
57+ #[ error( "<AlsaSink> Software, {0}" ) ]
4658 SwParams ( alsa:: Error ) ,
47- #[ error( "AlsaSink PCM Error, {0}" ) ]
59+
60+ #[ error( "<AlsaSink> PCM, {0}" ) ]
4861 Pcm ( alsa:: Error ) ,
62+
63+ #[ error( "<AlsaSink> Could Not Parse Ouput Name(s) and/or Description(s)" ) ]
64+ Parsing ,
65+
66+ #[ error( "<AlsaSink>" ) ]
67+ NotConnected ,
68+ }
69+
70+ impl From < AlsaError > for SinkError {
71+ fn from ( e : AlsaError ) -> SinkError {
72+ use AlsaError :: * ;
73+ let es = e. to_string ( ) ;
74+ match e {
75+ DrainFailure ( _) | OnWrite ( _) => SinkError :: OnWrite ( es) ,
76+ PcmSetUp { .. } => SinkError :: ConnectionRefused ( es) ,
77+ NotConnected => SinkError :: NotConnected ( es) ,
78+ _ => SinkError :: InvalidParams ( es) ,
79+ }
80+ }
4981}
5082
5183pub struct AlsaSink {
@@ -55,25 +87,19 @@ pub struct AlsaSink {
5587 period_buffer : Vec < u8 > ,
5688}
5789
58- fn list_outputs ( ) -> io :: Result < ( ) > {
90+ fn list_outputs ( ) -> SinkResult < ( ) > {
5991 println ! ( "Listing available Alsa outputs:" ) ;
6092 for t in & [ "pcm" , "ctl" , "hwdep" ] {
6193 println ! ( "{} devices:" , t) ;
62- let i = match HintIter :: new_str ( None , t) {
63- Ok ( i) => i,
64- Err ( e) => {
65- return Err ( io:: Error :: new ( io:: ErrorKind :: Other , e) ) ;
66- }
67- } ;
94+
95+ let i = HintIter :: new_str ( None , & t) . map_err ( |_| AlsaError :: Parsing ) ?;
96+
6897 for a in i {
6998 if let Some ( Direction :: Playback ) = a. direction {
7099 // mimic aplay -L
71- let name = a
72- . name
73- . ok_or_else ( || io:: Error :: new ( io:: ErrorKind :: Other , "Could not parse name" ) ) ?;
74- let desc = a
75- . desc
76- . ok_or_else ( || io:: Error :: new ( io:: ErrorKind :: Other , "Could not parse desc" ) ) ?;
100+ let name = a. name . ok_or ( AlsaError :: Parsing ) ?;
101+ let desc = a. desc . ok_or ( AlsaError :: Parsing ) ?;
102+
77103 println ! ( "{}\n \t {}\n " , name, desc. replace( "\n " , "\n \t " ) ) ;
78104 }
79105 }
@@ -82,10 +108,10 @@ fn list_outputs() -> io::Result<()> {
82108 Ok ( ( ) )
83109}
84110
85- fn open_device ( dev_name : & str , format : AudioFormat ) -> Result < ( PCM , usize ) , AlsaError > {
111+ fn open_device ( dev_name : & str , format : AudioFormat ) -> SinkResult < ( PCM , usize ) > {
86112 let pcm = PCM :: new ( dev_name, Direction :: Playback , false ) . map_err ( |e| AlsaError :: PcmSetUp {
87113 device : dev_name. to_string ( ) ,
88- err : e,
114+ e,
89115 } ) ?;
90116
91117 let alsa_format = match format {
@@ -103,32 +129,34 @@ fn open_device(dev_name: &str, format: AudioFormat) -> Result<(PCM, usize), Alsa
103129
104130 let bytes_per_period = {
105131 let hwp = HwParams :: any ( & pcm) . map_err ( AlsaError :: HwParams ) ?;
132+
106133 hwp. set_access ( Access :: RWInterleaved )
107134 . map_err ( |e| AlsaError :: UnsupportedAccessType {
108135 device : dev_name. to_string ( ) ,
109- err : e,
136+ e,
110137 } ) ?;
111138
112139 hwp. set_format ( alsa_format)
113140 . map_err ( |e| AlsaError :: UnsupportedFormat {
114141 device : dev_name. to_string ( ) ,
142+ alsa_format,
115143 format,
116- err : e,
144+ e,
117145 } ) ?;
118146
119147 hwp. set_rate ( SAMPLE_RATE , ValueOr :: Nearest ) . map_err ( |e| {
120148 AlsaError :: UnsupportedSampleRate {
121149 device : dev_name. to_string ( ) ,
122150 samplerate : SAMPLE_RATE ,
123- err : e,
151+ e,
124152 }
125153 } ) ?;
126154
127155 hwp. set_channels ( NUM_CHANNELS as u32 )
128156 . map_err ( |e| AlsaError :: UnsupportedChannelCount {
129157 device : dev_name. to_string ( ) ,
130158 channel_count : NUM_CHANNELS ,
131- err : e,
159+ e,
132160 } ) ?;
133161
134162 hwp. set_buffer_time_near ( BUFFER_TIME . as_micros ( ) as u32 , ValueOr :: Nearest )
@@ -141,8 +169,7 @@ fn open_device(dev_name: &str, format: AudioFormat) -> Result<(PCM, usize), Alsa
141169
142170 let swp = pcm. sw_params_current ( ) . map_err ( AlsaError :: Pcm ) ?;
143171
144- // Don't assume we got what we wanted.
145- // Ask to make sure.
172+ // Don't assume we got what we wanted. Ask to make sure.
146173 let frames_per_period = hwp. get_period_size ( ) . map_err ( AlsaError :: HwParams ) ?;
147174
148175 let frames_per_buffer = hwp. get_buffer_size ( ) . map_err ( AlsaError :: HwParams ) ?;
@@ -171,8 +198,8 @@ impl Open for AlsaSink {
171198 Ok ( _) => {
172199 exit ( 0 ) ;
173200 }
174- Err ( err ) => {
175- error ! ( "Error listing Alsa outputs, {}" , err ) ;
201+ Err ( e ) => {
202+ error ! ( "{}" , e ) ;
176203 exit ( 1 ) ;
177204 }
178205 } ,
@@ -193,53 +220,40 @@ impl Open for AlsaSink {
193220}
194221
195222impl Sink for AlsaSink {
196- fn start ( & mut self ) -> io :: Result < ( ) > {
223+ fn start ( & mut self ) -> SinkResult < ( ) > {
197224 if self . pcm . is_none ( ) {
198- match open_device ( & self . device , self . format ) {
199- Ok ( ( pcm, bytes_per_period) ) => {
200- self . pcm = Some ( pcm) ;
201- // If the capacity is greater than we want shrink it
202- // to it's current len (which should be zero) before
203- // setting the capacity with reserve_exact.
204- if self . period_buffer . capacity ( ) > bytes_per_period {
205- self . period_buffer . shrink_to_fit ( ) ;
206- }
207- // This does nothing if the capacity is already sufficient.
208- // Len should always be zero, but for the sake of being thorough...
209- self . period_buffer
210- . reserve_exact ( bytes_per_period - self . period_buffer . len ( ) ) ;
211-
212- // Should always match the "Period Buffer size in bytes: " trace! message.
213- trace ! (
214- "Period Buffer capacity: {:?}" ,
215- self . period_buffer. capacity( )
216- ) ;
217- }
218- Err ( e) => {
219- return Err ( io:: Error :: new ( io:: ErrorKind :: Other , e) ) ;
220- }
225+ let ( pcm, bytes_per_period) = open_device ( & self . device , self . format ) ?;
226+ self . pcm = Some ( pcm) ;
227+
228+ let current_capacity = self . period_buffer . capacity ( ) ;
229+
230+ if current_capacity > bytes_per_period {
231+ self . period_buffer . truncate ( bytes_per_period) ;
232+ self . period_buffer . shrink_to_fit ( ) ;
233+ } else if current_capacity < bytes_per_period {
234+ let extra = bytes_per_period - self . period_buffer . len ( ) ;
235+ self . period_buffer . reserve_exact ( extra) ;
221236 }
237+
238+ // Should always match the "Period Buffer size in bytes: " trace! message.
239+ trace ! (
240+ "Period Buffer capacity: {:?}" ,
241+ self . period_buffer. capacity( )
242+ ) ;
222243 }
223244
224245 Ok ( ( ) )
225246 }
226247
227- fn stop ( & mut self ) -> io :: Result < ( ) > {
248+ fn stop ( & mut self ) -> SinkResult < ( ) > {
228249 // Zero fill the remainder of the period buffer and
229250 // write any leftover data before draining the actual PCM buffer.
230251 self . period_buffer . resize ( self . period_buffer . capacity ( ) , 0 ) ;
231252 self . write_buf ( ) ?;
232253
233- let pcm = self . pcm . as_mut ( ) . ok_or_else ( || {
234- io:: Error :: new ( io:: ErrorKind :: Other , "Error stopping AlsaSink, PCM is None" )
235- } ) ?;
254+ let pcm = self . pcm . as_mut ( ) . ok_or ( AlsaError :: NotConnected ) ?;
236255
237- pcm. drain ( ) . map_err ( |e| {
238- io:: Error :: new (
239- io:: ErrorKind :: Other ,
240- format ! ( "Error stopping AlsaSink {}" , e) ,
241- )
242- } ) ?;
256+ pcm. drain ( ) . map_err ( AlsaError :: DrainFailure ) ?;
243257
244258 self . pcm = None ;
245259 Ok ( ( ) )
@@ -249,23 +263,28 @@ impl Sink for AlsaSink {
249263}
250264
251265impl SinkAsBytes for AlsaSink {
252- fn write_bytes ( & mut self , data : & [ u8 ] ) -> io :: Result < ( ) > {
266+ fn write_bytes ( & mut self , data : & [ u8 ] ) -> SinkResult < ( ) > {
253267 let mut start_index = 0 ;
254268 let data_len = data. len ( ) ;
255269 let capacity = self . period_buffer . capacity ( ) ;
270+
256271 loop {
257272 let data_left = data_len - start_index;
258273 let space_left = capacity - self . period_buffer . len ( ) ;
259274 let data_to_buffer = min ( data_left, space_left) ;
260275 let end_index = start_index + data_to_buffer;
276+
261277 self . period_buffer
262278 . extend_from_slice ( & data[ start_index..end_index] ) ;
279+
263280 if self . period_buffer . len ( ) == capacity {
264281 self . write_buf ( ) ?;
265282 }
283+
266284 if end_index == data_len {
267285 break Ok ( ( ) ) ;
268286 }
287+
269288 start_index = end_index;
270289 }
271290 }
@@ -274,30 +293,18 @@ impl SinkAsBytes for AlsaSink {
274293impl AlsaSink {
275294 pub const NAME : & ' static str = "alsa" ;
276295
277- fn write_buf ( & mut self ) -> io:: Result < ( ) > {
278- let pcm = self . pcm . as_mut ( ) . ok_or_else ( || {
279- io:: Error :: new (
280- io:: ErrorKind :: Other ,
281- "Error writing from AlsaSink buffer to PCM, PCM is None" ,
282- )
283- } ) ?;
284- let io = pcm. io_bytes ( ) ;
285- if let Err ( err) = io. writei ( & self . period_buffer ) {
296+ fn write_buf ( & mut self ) -> SinkResult < ( ) > {
297+ let pcm = self . pcm . as_mut ( ) . ok_or ( AlsaError :: NotConnected ) ?;
298+
299+ if let Err ( e) = pcm. io_bytes ( ) . writei ( & self . period_buffer ) {
286300 // Capture and log the original error as a warning, and then try to recover.
287301 // If recovery fails then forward that error back to player.
288302 warn ! (
289- "Error writing from AlsaSink buffer to PCM, trying to recover {}" ,
290- err
303+ "Error writing from AlsaSink buffer to PCM, trying to recover, {}" ,
304+ e
291305 ) ;
292- pcm. try_recover ( err, false ) . map_err ( |e| {
293- io:: Error :: new (
294- io:: ErrorKind :: Other ,
295- format ! (
296- "Error writing from AlsaSink buffer to PCM, recovery failed {}" ,
297- e
298- ) ,
299- )
300- } ) ?
306+
307+ pcm. try_recover ( e, false ) . map_err ( AlsaError :: OnWrite ) ?
301308 }
302309
303310 self . period_buffer . clear ( ) ;
0 commit comments