@@ -688,7 +688,7 @@ fn input_stream_worker(
688688 delay_frames,
689689 stream_type,
690690 } => {
691- assert_eq ! (
691+ debug_assert_eq ! (
692692 stream_type,
693693 StreamType :: Input ,
694694 "expected input stream, but polling descriptors indicated output" ,
@@ -746,7 +746,7 @@ fn output_stream_worker(
746746 delay_frames,
747747 stream_type,
748748 } => {
749- assert_eq ! (
749+ debug_assert_eq ! (
750750 stream_type,
751751 StreamType :: Output ,
752752 "expected output stream, but polling descriptors indicated input" ,
@@ -875,11 +875,17 @@ fn process_input(
875875 stream. channel . io_bytes ( ) . readi ( buffer) ?;
876876 let data = buffer. as_mut_ptr ( ) as * mut ( ) ;
877877 let data = unsafe { Data :: from_parts ( data, stream. period_samples , stream. sample_format ) } ;
878- let callback = stream_timestamp ( & status, stream. creation_instant ) ?;
878+ let callback = match stream. creation_instant {
879+ None => stream_timestamp_hardware ( & status) ?,
880+ Some ( creation) => stream_timestamp_fallback ( creation) ?,
881+ } ;
879882 let delay_duration = frames_to_duration ( delay_frames, stream. conf . sample_rate ) ;
880883 let capture = callback
881884 . sub ( delay_duration)
882- . expect ( "`capture` is earlier than representation supported by `StreamInstant`" ) ;
885+ . ok_or_else ( || BackendSpecificError {
886+ description : "`capture` is earlier than representation supported by `StreamInstant`"
887+ . to_string ( ) ,
888+ } ) ?;
883889 let timestamp = crate :: InputStreamTimestamp { callback, capture } ;
884890 let info = crate :: InputCallbackInfo { timestamp } ;
885891 data_callback ( & data, & info) ;
@@ -904,11 +910,17 @@ fn process_output(
904910 let data = buffer. as_mut_ptr ( ) as * mut ( ) ;
905911 let mut data =
906912 unsafe { Data :: from_parts ( data, stream. period_samples , stream. sample_format ) } ;
907- let callback = stream_timestamp ( & status, stream. creation_instant ) ?;
913+ let callback = match stream. creation_instant {
914+ None => stream_timestamp_hardware ( & status) ?,
915+ Some ( creation) => stream_timestamp_fallback ( creation) ?,
916+ } ;
908917 let delay_duration = frames_to_duration ( delay_frames, stream. conf . sample_rate ) ;
909918 let playback = callback
910919 . add ( delay_duration)
911- . expect ( "`playback` occurs beyond representation supported by `StreamInstant`" ) ;
920+ . ok_or_else ( || BackendSpecificError {
921+ description : "`playback` occurs beyond representation supported by `StreamInstant`"
922+ . to_string ( ) ,
923+ } ) ?;
912924 let timestamp = crate :: OutputStreamTimestamp { callback, playback } ;
913925 let info = crate :: OutputCallbackInfo { timestamp } ;
914926 data_callback ( & mut data, & info) ;
@@ -948,42 +960,43 @@ fn process_output(
948960 Ok ( ( ) )
949961}
950962
951- // Use the elapsed duration since the start of the stream .
963+ // Use hardware timestamps from ALSA .
952964//
953- // This ensures positive values that are compatible with our `StreamInstant` representation.
954- fn stream_timestamp (
965+ // This ensures accurate timestamps based on actual hardware timing.
966+ #[ inline]
967+ fn stream_timestamp_hardware (
955968 status : & alsa:: pcm:: Status ,
956- creation_instant : Option < std:: time:: Instant > ,
957969) -> Result < crate :: StreamInstant , BackendSpecificError > {
958- match creation_instant {
959- None => {
960- let trigger_ts = status. get_trigger_htstamp ( ) ;
961- let ts = status. get_htstamp ( ) ;
962- let nanos = timespec_diff_nanos ( ts, trigger_ts) ;
963- if nanos < 0 {
964- let description = format ! (
965- "get_htstamp `{}.{}` was earlier than get_trigger_htstamp `{}.{}`" ,
966- ts. tv_sec, ts. tv_nsec, trigger_ts. tv_sec, trigger_ts. tv_nsec
967- ) ;
968- return Err ( BackendSpecificError { description } ) ;
969- }
970- Ok ( crate :: StreamInstant :: from_nanos ( nanos) )
971- }
972- Some ( creation) => {
973- let now = std:: time:: Instant :: now ( ) ;
974- let duration = now. duration_since ( creation) ;
975- crate :: StreamInstant :: from_nanos_i128 ( duration. as_nanos ( ) as i128 ) . ok_or (
976- BackendSpecificError {
977- description : "stream duration has exceeded `StreamInstant` representation"
978- . to_string ( ) ,
979- } ,
980- )
981- }
970+ let trigger_ts = status. get_trigger_htstamp ( ) ;
971+ let ts = status. get_htstamp ( ) ;
972+ let nanos = timespec_diff_nanos ( ts, trigger_ts) ;
973+ if nanos < 0 {
974+ let description = format ! (
975+ "get_htstamp `{}.{}` was earlier than get_trigger_htstamp `{}.{}`" ,
976+ ts. tv_sec, ts. tv_nsec, trigger_ts. tv_sec, trigger_ts. tv_nsec
977+ ) ;
978+ return Err ( BackendSpecificError { description } ) ;
982979 }
980+ Ok ( crate :: StreamInstant :: from_nanos ( nanos) )
981+ }
982+
983+ // Use elapsed duration since stream creation as fallback when hardware timestamps are unavailable.
984+ //
985+ // This ensures positive values that are compatible with our `StreamInstant` representation.
986+ #[ inline]
987+ fn stream_timestamp_fallback (
988+ creation : std:: time:: Instant ,
989+ ) -> Result < crate :: StreamInstant , BackendSpecificError > {
990+ let now = std:: time:: Instant :: now ( ) ;
991+ let duration = now. duration_since ( creation) ;
992+ crate :: StreamInstant :: from_nanos_i128 ( duration. as_nanos ( ) as i128 ) . ok_or ( BackendSpecificError {
993+ description : "stream duration has exceeded `StreamInstant` representation" . to_string ( ) ,
994+ } )
983995}
984996
985997// Adapted from `timestamp2ns` here:
986998// https://fossies.org/linux/alsa-lib/test/audio_time.c
999+ #[ inline]
9871000fn timespec_to_nanos ( ts : libc:: timespec ) -> i64 {
9881001 let nanos = ts. tv_sec * 1_000_000_000 + ts. tv_nsec ;
9891002 #[ cfg( target_pointer_width = "64" ) ]
@@ -994,11 +1007,13 @@ fn timespec_to_nanos(ts: libc::timespec) -> i64 {
9941007
9951008// Adapted from `timediff` here:
9961009// https://fossies.org/linux/alsa-lib/test/audio_time.c
1010+ #[ inline]
9971011fn timespec_diff_nanos ( a : libc:: timespec , b : libc:: timespec ) -> i64 {
9981012 timespec_to_nanos ( a) - timespec_to_nanos ( b)
9991013}
10001014
10011015// Convert the given duration in frames at the given sample rate to a `std::time::Duration`.
1016+ #[ inline]
10021017fn frames_to_duration ( frames : usize , rate : crate :: SampleRate ) -> std:: time:: Duration {
10031018 let secsf = frames as f64 / rate. 0 as f64 ;
10041019 let secs = secsf as u64 ;
0 commit comments