@@ -4,16 +4,18 @@ use crate::convert::Converter;
44use crate :: decoder:: AudioPacket ;
55use crate :: { NUM_CHANNELS , SAMPLE_RATE } ;
66use alsa:: device_name:: HintIter ;
7- use alsa:: pcm:: { Access , Format , HwParams , PCM } ;
7+ use alsa:: pcm:: { Access , Format , Frames , HwParams , PCM } ;
88use alsa:: { Direction , ValueOr } ;
99use std:: cmp:: min;
1010use std:: process:: exit;
11- use std:: time:: Duration ;
1211use thiserror:: Error ;
1312
14- // 0.5 sec buffer.
15- const PERIOD_TIME : Duration = Duration :: from_millis ( 100 ) ;
16- const BUFFER_TIME : Duration = Duration :: from_millis ( 500 ) ;
13+ const MAX_BUFFER : Frames = ( SAMPLE_RATE / 2 ) as Frames ;
14+ const MIN_BUFFER : Frames = ( SAMPLE_RATE / 10 ) as Frames ;
15+ const ZERO_FRAMES : Frames = 0 ;
16+
17+ const MAX_PERIOD_DIVISOR : Frames = 4 ;
18+ const MIN_PERIOD_DIVISOR : Frames = 10 ;
1719
1820#[ derive( Debug , Error ) ]
1921enum AlsaError {
@@ -195,28 +197,187 @@ fn open_device(dev_name: &str, format: AudioFormat) -> SinkResult<(PCM, usize)>
195197 e,
196198 } ) ?;
197199
198- hwp. set_buffer_time_near ( BUFFER_TIME . as_micros ( ) as u32 , ValueOr :: Nearest )
199- . map_err ( AlsaError :: HwParams ) ?;
200+ // Clone the hwp while it's in
201+ // a good working state so that
202+ // in the event of an error setting
203+ // the buffer and period sizes
204+ // we can use the good working clone
205+ // instead of the hwp that's in an
206+ // error state.
207+ let hwp_clone = hwp. clone ( ) ;
208+
209+ // At a sampling rate of 44100:
210+ // The largest buffer is 22050 Frames (500ms) with 5512 Frame periods (125ms).
211+ // The smallest buffer is 4410 Frames (100ms) with 441 Frame periods (10ms).
212+ // Actual values may vary.
213+ //
214+ // Larger buffer and period sizes are preferred as extremely small values
215+ // will cause high CPU useage.
216+ //
217+ // If no buffer or period size is in those ranges or an error happens
218+ // trying to set the buffer or period size use the device's defaults
219+ // which may not be ideal but are *hopefully* serviceable.
220+
221+ let buffer_size = {
222+ let max = match hwp. get_buffer_size_max ( ) {
223+ Err ( e) => {
224+ trace ! ( "Error getting the device's max Buffer size: {}" , e) ;
225+ ZERO_FRAMES
226+ }
227+ Ok ( s) => s,
228+ } ;
200229
201- hwp. set_period_time_near ( PERIOD_TIME . as_micros ( ) as u32 , ValueOr :: Nearest )
202- . map_err ( AlsaError :: HwParams ) ?;
230+ let min = match hwp. get_buffer_size_min ( ) {
231+ Err ( e) => {
232+ trace ! ( "Error getting the device's min Buffer size: {}" , e) ;
233+ ZERO_FRAMES
234+ }
235+ Ok ( s) => s,
236+ } ;
237+
238+ let buffer_size = if min < max {
239+ match ( MIN_BUFFER ..=MAX_BUFFER )
240+ . rev ( )
241+ . find ( |f| ( min..=max) . contains ( f) )
242+ {
243+ Some ( size) => {
244+ trace ! ( "Desired Frames per Buffer: {:?}" , size) ;
245+
246+ match hwp. set_buffer_size_near ( size) {
247+ Err ( e) => {
248+ trace ! ( "Error setting the device's Buffer size: {}" , e) ;
249+ ZERO_FRAMES
250+ }
251+ Ok ( s) => s,
252+ }
253+ }
254+ None => {
255+ trace ! ( "No Desired Buffer size in range reported by the device." ) ;
256+ ZERO_FRAMES
257+ }
258+ }
259+ } else {
260+ trace ! ( "The device's min reported Buffer size was greater than or equal to it's max reported Buffer size." ) ;
261+ ZERO_FRAMES
262+ } ;
263+
264+ if buffer_size == ZERO_FRAMES {
265+ trace ! (
266+ "Desired Buffer Frame range: {:?} - {:?}" ,
267+ MIN_BUFFER ,
268+ MAX_BUFFER
269+ ) ;
270+
271+ trace ! (
272+ "Actual Buffer Frame range as reported by the device: {:?} - {:?}" ,
273+ min,
274+ max
275+ ) ;
276+ }
203277
204- pcm. hw_params ( & hwp) . map_err ( AlsaError :: Pcm ) ?;
278+ buffer_size
279+ } ;
280+
281+ let period_size = {
282+ if buffer_size == ZERO_FRAMES {
283+ ZERO_FRAMES
284+ } else {
285+ let max = match hwp. get_period_size_max ( ) {
286+ Err ( e) => {
287+ trace ! ( "Error getting the device's max Period size: {}" , e) ;
288+ ZERO_FRAMES
289+ }
290+ Ok ( s) => s,
291+ } ;
205292
206- let swp = pcm. sw_params_current ( ) . map_err ( AlsaError :: Pcm ) ?;
293+ let min = match hwp. get_period_size_min ( ) {
294+ Err ( e) => {
295+ trace ! ( "Error getting the device's min Period size: {}" , e) ;
296+ ZERO_FRAMES
297+ }
298+ Ok ( s) => s,
299+ } ;
300+
301+ let max_period = buffer_size / MAX_PERIOD_DIVISOR ;
302+ let min_period = buffer_size / MIN_PERIOD_DIVISOR ;
303+
304+ let period_size = if min < max && min_period < max_period {
305+ match ( min_period..=max_period)
306+ . rev ( )
307+ . find ( |f| ( min..=max) . contains ( f) )
308+ {
309+ Some ( size) => {
310+ trace ! ( "Desired Frames per Period: {:?}" , size) ;
311+
312+ match hwp. set_period_size_near ( size, ValueOr :: Nearest ) {
313+ Err ( e) => {
314+ trace ! ( "Error setting the device's Period size: {}" , e) ;
315+ ZERO_FRAMES
316+ }
317+ Ok ( s) => s,
318+ }
319+ }
320+ None => {
321+ trace ! ( "No Desired Period size in range reported by the device." ) ;
322+ ZERO_FRAMES
323+ }
324+ }
325+ } else {
326+ trace ! ( "The device's min reported Period size was greater than or equal to it's max reported Period size," ) ;
327+ trace ! ( "or the desired min Period size was greater than or equal to the desired max Period size." ) ;
328+ ZERO_FRAMES
329+ } ;
330+
331+ if period_size == ZERO_FRAMES {
332+ trace ! ( "Buffer size: {:?}" , buffer_size) ;
333+
334+ trace ! (
335+ "Desired Period Frame range: {:?} (Buffer size / {:?}) - {:?} (Buffer size / {:?})" ,
336+ min_period,
337+ MIN_PERIOD_DIVISOR ,
338+ max_period,
339+ MAX_PERIOD_DIVISOR ,
340+ ) ;
341+
342+ trace ! (
343+ "Actual Period Frame range as reported by the device: {:?} - {:?}" ,
344+ min,
345+ max
346+ ) ;
347+ }
348+
349+ period_size
350+ }
351+ } ;
352+
353+ if buffer_size == ZERO_FRAMES || period_size == ZERO_FRAMES {
354+ trace ! (
355+ "Failed to set Buffer and/or Period size, falling back to the device's defaults."
356+ ) ;
357+
358+ trace ! ( "You may experience higher than normal CPU usage and/or audio issues." ) ;
359+
360+ pcm. hw_params ( & hwp_clone) . map_err ( AlsaError :: Pcm ) ?;
361+ } else {
362+ pcm. hw_params ( & hwp) . map_err ( AlsaError :: Pcm ) ?;
363+ }
364+
365+ let hwp = pcm. hw_params_current ( ) . map_err ( AlsaError :: Pcm ) ?;
207366
208367 // Don't assume we got what we wanted. Ask to make sure.
209368 let frames_per_period = hwp. get_period_size ( ) . map_err ( AlsaError :: HwParams ) ?;
210369
211370 let frames_per_buffer = hwp. get_buffer_size ( ) . map_err ( AlsaError :: HwParams ) ?;
212371
372+ let swp = pcm. sw_params_current ( ) . map_err ( AlsaError :: Pcm ) ?;
373+
213374 swp. set_start_threshold ( frames_per_buffer - frames_per_period)
214375 . map_err ( AlsaError :: SwParams ) ?;
215376
216377 pcm. sw_params ( & swp) . map_err ( AlsaError :: Pcm ) ?;
217378
218- trace ! ( "Frames per Buffer: {:?}" , frames_per_buffer) ;
219- trace ! ( "Frames per Period: {:?}" , frames_per_period) ;
379+ trace ! ( "Actual Frames per Buffer: {:?}" , frames_per_buffer) ;
380+ trace ! ( "Actual Frames per Period: {:?}" , frames_per_period) ;
220381
221382 // Let ALSA do the math for us.
222383 pcm. frames_to_bytes ( frames_per_period) as usize
0 commit comments