@@ -13,6 +13,7 @@ use crate::{
13
13
use std:: cell:: Cell ;
14
14
use std:: cmp;
15
15
use std:: convert:: TryInto ;
16
+ use std:: ops:: RangeInclusive ;
16
17
use std:: sync:: { Arc , Mutex } ;
17
18
use std:: thread:: { self , JoinHandle } ;
18
19
use std:: time:: Duration ;
@@ -25,6 +26,9 @@ pub type SupportedOutputConfigs = VecIntoIter<SupportedStreamConfigRange>;
25
26
26
27
mod enumerate;
27
28
29
+ const VALID_BUFFER_SIZE : RangeInclusive < alsa:: pcm:: Frames > =
30
+ 1 ..=FrameCount :: MAX as alsa:: pcm:: Frames ;
31
+
28
32
/// The default linux, dragonfly, freebsd and netbsd host type.
29
33
#[ derive( Debug ) ]
30
34
pub struct Host ;
@@ -413,12 +417,10 @@ impl Device {
413
417
} )
414
418
. collect :: < Vec < _ > > ( ) ;
415
419
416
- let min_buffer_size = hw_params. get_buffer_size_min ( ) ?;
417
- let max_buffer_size = hw_params. get_buffer_size_max ( ) ?;
418
-
420
+ let ( min_buffer_size, max_buffer_size) = hw_params_buffer_size_min_max ( & hw_params) ;
419
421
let buffer_size_range = SupportedBufferSize :: Range {
420
- min : min_buffer_size as u32 ,
421
- max : max_buffer_size as u32 ,
422
+ min : min_buffer_size,
423
+ max : max_buffer_size,
422
424
} ;
423
425
424
426
let mut output = Vec :: with_capacity (
@@ -1040,6 +1042,35 @@ impl StreamTrait for Stream {
1040
1042
}
1041
1043
}
1042
1044
1045
+ // Overly safe clamp because alsa Frames are i64
1046
+ fn clamp_frame_count ( buffer_size : alsa:: pcm:: Frames ) -> FrameCount {
1047
+ buffer_size. clamp ( 1 , FrameCount :: MAX as _ ) as _
1048
+ }
1049
+
1050
+ fn hw_params_buffer_size_min_max ( hw_params : & alsa:: pcm:: HwParams ) -> ( FrameCount , FrameCount ) {
1051
+ let min_buf = hw_params
1052
+ . get_buffer_size_min ( )
1053
+ . map ( clamp_frame_count)
1054
+ . unwrap_or ( 1 ) ;
1055
+ let max_buf = hw_params
1056
+ . get_buffer_size_max ( )
1057
+ . map ( clamp_frame_count)
1058
+ . unwrap_or ( FrameCount :: MAX ) ;
1059
+ ( min_buf, max_buf)
1060
+ }
1061
+
1062
+ fn hw_params_period_size_min_max ( hw_params : & alsa:: pcm:: HwParams ) -> ( FrameCount , FrameCount ) {
1063
+ let min_buf = hw_params
1064
+ . get_period_size_min ( )
1065
+ . map ( clamp_frame_count)
1066
+ . unwrap_or ( 1 ) ;
1067
+ let max_buf = hw_params
1068
+ . get_period_size_max ( )
1069
+ . map ( clamp_frame_count)
1070
+ . unwrap_or ( FrameCount :: MAX ) ;
1071
+ ( min_buf, max_buf)
1072
+ }
1073
+
1043
1074
fn set_hw_params_from_format (
1044
1075
pcm_handle : & alsa:: pcm:: PCM ,
1045
1076
config : & StreamConfig ,
@@ -1116,32 +1147,43 @@ fn set_hw_params_from_format(
1116
1147
fn set_hw_params_periods ( hw_params : & alsa:: pcm:: HwParams , buffer_size : BufferSize ) -> bool {
1117
1148
const FALLBACK_PERIOD_TIME : u32 = 25_000 ;
1118
1149
1119
- // When the API is made available, this could rely on snd_pcm_hw_params_get_periods_min and
1120
- // snd_pcm_hw_params_get_periods_max
1150
+ // TODO: When the API is made available, this could rely on snd_pcm_hw_params_get_periods_min
1151
+ // and snd_pcm_hw_params_get_periods_max
1121
1152
const PERIOD_COUNT : u32 = 2 ;
1122
1153
1123
1154
/// Returns true if the buffer size was reasonably set.
1124
- fn set_hw_params_buffer_size ( hw_params : & alsa:: pcm:: HwParams , buffer_size : FrameCount ) -> bool {
1125
- // Desired period size
1126
- let period_size = ( buffer_size / PERIOD_COUNT ) as alsa:: pcm:: Frames ;
1155
+ fn set_hw_params_buffer_size (
1156
+ hw_params : & alsa:: pcm:: HwParams ,
1157
+ mut buffer_size : FrameCount ,
1158
+ ) -> bool {
1159
+ buffer_size = {
1160
+ let ( min_buffer_size, max_buffer_size) = hw_params_buffer_size_min_max ( hw_params) ;
1161
+ buffer_size. clamp ( min_buffer_size, max_buffer_size)
1162
+ } ;
1127
1163
1128
- if hw_params
1129
- . set_period_size_near ( period_size, alsa:: ValueOr :: Greater )
1130
- . is_err ( )
1131
- {
1132
- return false ;
1133
- }
1164
+ // Desired period size
1165
+ let period_size = {
1166
+ let ( min_period_size, max_period_size) = hw_params_period_size_min_max ( hw_params) ;
1167
+ ( buffer_size / PERIOD_COUNT ) . clamp ( min_period_size, max_period_size)
1168
+ } ;
1134
1169
1135
1170
// Actual period size
1136
- let period_size = if let Ok ( period_size) = hw_params . get_period_size ( ) {
1137
- period_size
1138
- } else {
1171
+ let Ok ( period_size) =
1172
+ hw_params . set_period_size_near ( period_size as _ , alsa :: ValueOr :: Greater )
1173
+ else {
1139
1174
return false ;
1140
1175
} ;
1141
1176
1142
- hw_params
1143
- . set_buffer_size_near ( period_size * PERIOD_COUNT as alsa:: pcm:: Frames )
1144
- . is_ok ( )
1177
+ if let Ok ( buffer_size) =
1178
+ hw_params. set_buffer_size_near ( period_size * PERIOD_COUNT as alsa:: pcm:: Frames )
1179
+ {
1180
+ // Double-check the set size is within the CPAL range
1181
+ if VALID_BUFFER_SIZE . contains ( & buffer_size) {
1182
+ return true ;
1183
+ }
1184
+ }
1185
+
1186
+ false
1145
1187
}
1146
1188
1147
1189
if let BufferSize :: Fixed ( val) = buffer_size {
@@ -1166,12 +1208,22 @@ fn set_hw_params_periods(hw_params: &alsa::pcm::HwParams, buffer_size: BufferSiz
1166
1208
// We should not fail if the driver is unhappy here.
1167
1209
// `default` pcm sometimes fails here, but there no reason to as we attempt to provide a size or
1168
1210
// minimum number of periods.
1211
+ if let Ok ( buffer_size) =
1212
+ hw_params. set_buffer_size_near ( period_size * PERIOD_COUNT as alsa:: pcm:: Frames )
1213
+ {
1214
+ // Double-check the set size is within the CPAL range
1215
+ if VALID_BUFFER_SIZE . contains ( & buffer_size) {
1216
+ return true ;
1217
+ }
1218
+ }
1219
+
1220
+ hw_params. set_buffer_size_min ( 1 ) . unwrap_or_default ( ) ;
1221
+ hw_params
1222
+ . set_buffer_size_max ( FrameCount :: MAX as _ )
1223
+ . unwrap_or_default ( ) ;
1169
1224
hw_params
1170
- . set_buffer_size ( period_size * PERIOD_COUNT as alsa:: pcm :: Frames )
1225
+ . set_periods ( PERIOD_COUNT , alsa:: ValueOr :: Nearest )
1171
1226
. is_ok ( )
1172
- || hw_params
1173
- . set_periods ( PERIOD_COUNT , alsa:: ValueOr :: Greater )
1174
- . is_ok ( )
1175
1227
}
1176
1228
1177
1229
fn set_sw_params_from_format (
0 commit comments