@@ -31,6 +31,9 @@ mod enumerate;
3131const VALID_BUFFER_SIZE : RangeInclusive < alsa:: pcm:: Frames > =
3232 1 ..=FrameCount :: MAX as alsa:: pcm:: Frames ;
3333
34+ const FALLBACK_PERIOD_TIME : u32 = 25_000 ;
35+ const PREFERRED_PERIOD_COUNT : u32 = 2 ;
36+
3437/// The default linux, dragonfly, freebsd and netbsd host type.
3538#[ derive( Debug ) ]
3639pub struct Host ;
@@ -1064,18 +1067,6 @@ fn hw_params_buffer_size_min_max(hw_params: &alsa::pcm::HwParams) -> (FrameCount
10641067 ( min_buf, max_buf)
10651068}
10661069
1067- fn hw_params_period_size_min_max ( hw_params : & alsa:: pcm:: HwParams ) -> ( FrameCount , FrameCount ) {
1068- let min_buf = hw_params
1069- . get_period_size_min ( )
1070- . map ( clamp_frame_count)
1071- . unwrap_or ( 1 ) ;
1072- let max_buf = hw_params
1073- . get_period_size_max ( )
1074- . map ( clamp_frame_count)
1075- . unwrap_or ( FrameCount :: MAX ) ;
1076- ( min_buf, max_buf)
1077- }
1078-
10791070fn set_hw_params_from_format (
10801071 pcm_handle : & alsa:: pcm:: PCM ,
10811072 config : & StreamConfig ,
@@ -1155,15 +1146,10 @@ fn set_hw_params_from_format(
11551146/// Returns true if the periods were reasonably set. A false result indicates the device default
11561147/// configuration is being used.
11571148fn set_hw_params_periods ( hw_params : & alsa:: pcm:: HwParams , buffer_size : BufferSize ) -> bool {
1158- const FALLBACK_PERIOD_TIME : u32 = 25_000 ;
1159-
11601149 // TODO: When the API is made available, this could rely on snd_pcm_hw_params_get_periods_min
11611150 // and snd_pcm_hw_params_get_periods_max
1162- const PERIOD_COUNT : u32 = 2 ;
1163-
1164- // Restrict the configuration space to contain only one periods count
11651151 hw_params
1166- . set_periods ( PERIOD_COUNT , alsa:: ValueOr :: Nearest )
1152+ . set_periods ( PREFERRED_PERIOD_COUNT , alsa:: ValueOr :: Greater )
11671153 . unwrap_or_default ( ) ;
11681154
11691155 let Some ( period_count) = hw_params
@@ -1191,27 +1177,58 @@ fn set_hw_params_periods(hw_params: &alsa::pcm::HwParams, buffer_size: BufferSiz
11911177 buffer_size. clamp ( min_buffer_size, max_buffer_size)
11921178 } ;
11931179
1194- // Desired period size
1195- let period_size = {
1196- let ( min_period_size, max_period_size) = hw_params_period_size_min_max ( hw_params) ;
1197- ( buffer_size / period_count) . clamp ( min_period_size, max_period_size)
1180+ // Use time-based period configuration for device compatibility. Some devices have better
1181+ // compatibility with set_period_time_near() than set_period_size_near(). Calculate a
1182+ // period time that works with the requested buffer.
1183+
1184+ // Calculate desired period time based on buffer size and period count.
1185+ // We can't yet get period_time constraints from alsa-rs, so we'll use period_size
1186+ // constraints and convert them to time constraints.
1187+ let ( min_period_size, max_period_size) = match (
1188+ hw_params. get_period_size_min ( ) ,
1189+ hw_params. get_period_size_max ( ) ,
1190+ ) {
1191+ ( Ok ( min) , Ok ( max) ) => ( min, max) ,
1192+ _ => {
1193+ // Fall back to a safe default if we can't get constraints
1194+ let Ok ( _) =
1195+ hw_params. set_period_time_near ( FALLBACK_PERIOD_TIME , alsa:: ValueOr :: Nearest )
1196+ else {
1197+ return false ;
1198+ } ;
1199+ let Ok ( actual_buffer_size) = hw_params. set_buffer_size_near ( buffer_size as _ )
1200+ else {
1201+ return false ;
1202+ } ;
1203+ return VALID_BUFFER_SIZE . contains ( & actual_buffer_size) ;
1204+ }
11981205 } ;
11991206
1200- // Actual period size
1201- let Ok ( period_size) =
1202- hw_params. set_period_size_near ( period_size as _ , alsa:: ValueOr :: Greater )
1203- else {
1204- return false ;
1205- } ;
1207+ // Calculate desired period size and convert to time
1208+ // period_time = (period_size_frames / sample_rate) * 1_000_000 microseconds
1209+ let desired_period_size_frames =
1210+ ( buffer_size / period_count) . clamp ( min_period_size as u32 , max_period_size as u32 ) ;
1211+
1212+ // Get actual sample rate that was set (don't assume 44100)
1213+ if let Ok ( sample_rate) = hw_params. get_rate ( ) {
1214+ let desired_period_time_us =
1215+ ( desired_period_size_frames as u64 * 1_000_000 ) / sample_rate as u64 ;
1216+
1217+ // Set the period time
1218+ let Ok ( _) = hw_params
1219+ . set_period_time_near ( desired_period_time_us as u32 , alsa:: ValueOr :: Nearest )
1220+ else {
1221+ return false ;
1222+ } ;
1223+ }
12061224
1207- let Ok ( buffer_size) =
1208- hw_params. set_buffer_size_near ( period_size * period_count as alsa:: pcm:: Frames )
1209- else {
1225+ // Now set the buffer size to the user's requested size
1226+ let Ok ( actual_buffer_size) = hw_params. set_buffer_size_near ( buffer_size as _ ) else {
12101227 return false ;
12111228 } ;
12121229
12131230 // Double-check the set size is within the CPAL range
1214- VALID_BUFFER_SIZE . contains ( & buffer_size )
1231+ VALID_BUFFER_SIZE . contains ( & actual_buffer_size )
12151232 }
12161233
12171234 if let BufferSize :: Fixed ( val) = buffer_size {
0 commit comments