11use std:: sync:: {
22 Arc , Mutex ,
3- atomic:: { AtomicBool , AtomicU8 , Ordering } ,
3+ atomic:: { AtomicBool , AtomicU8 , AtomicU64 , Ordering } ,
44} ;
55
66use super :: JACK_SAMPLE_FORMAT ;
77use crate :: host:: try_emit_error;
88use crate :: {
99 CallbackInfo , ChannelCount , Data , DuplexCallbackInfo , Error , ErrorKind , FrameCount , ResultExt ,
1010 Sample , SampleRate , StreamInstant , StreamTimestamp ,
11- host:: { ErrorCallbackArc , emit_error, frames_to_duration} ,
11+ host:: { ErrorCallbackArc , emit_error, frames_to_duration, wait_for_drain } ,
1212 traits:: StreamTrait ,
1313} ;
1414
@@ -40,6 +40,8 @@ pub struct Stream {
4040 // Port names are stored in order to connect them to other ports in jack automatically
4141 input_port_names : Box < [ String ] > ,
4242 output_port_names : Box < [ String ] > ,
43+ // Written by the process thread every cycle. Stays zero on capture: no drain.
44+ playback_delay_nanos : Arc < AtomicU64 > ,
4345}
4446
4547impl Stream {
@@ -191,6 +193,7 @@ where
191193 let playback_state = Arc :: new ( AtomicU8 :: new ( StreamState :: Starting as u8 ) ) ;
192194 let pending_xrun = Arc :: new ( AtomicBool :: new ( false ) ) ;
193195 let error_callback_ptr: ErrorCallbackArc = Arc :: new ( Mutex :: new ( error_callback) ) ;
196+ let playback_delay_nanos = Arc :: new ( AtomicU64 :: new ( 0 ) ) ;
194197
195198 let process_handler = LocalProcessHandler :: new (
196199 out_ports,
@@ -201,6 +204,7 @@ where
201204 playback_state. clone ( ) ,
202205 pending_xrun. clone ( ) ,
203206 error_callback_ptr. clone ( ) ,
207+ playback_delay_nanos. clone ( ) ,
204208 ) ;
205209
206210 let notification_handler = JackNotificationHandler :: new (
@@ -220,6 +224,7 @@ where
220224 async_client,
221225 input_port_names : input_port_names. into_boxed_slice ( ) ,
222226 output_port_names : output_port_names. into_boxed_slice ( ) ,
227+ playback_delay_nanos,
223228 } )
224229}
225230
@@ -252,24 +257,11 @@ impl StreamTrait for Stream {
252257 }
253258
254259 fn stop ( & self , timeout : Option < std:: time:: Duration > ) -> Result < ( ) , Error > {
255- StreamState :: Paused . store ( & self . playback_state , Ordering :: Relaxed ) ;
256-
257- let is_output = !self . output_port_names . is_empty ( ) ;
258- if is_output && timeout != Some ( std:: time:: Duration :: ZERO ) {
259- let client = self . async_client . as_client ( ) ;
260- let ports: Vec < _ > = self
261- . output_port_names
262- . iter ( )
263- . filter_map ( |name| client. port_by_name ( name) )
264- . collect ( ) ;
265- let latency_frames = hardware_latency_frames ( & ports, jack:: LatencyType :: Playback )
266- . unwrap_or ( client. buffer_size ( ) as FrameCount ) ;
267- let buffered = frames_to_duration ( latency_frames, client. sample_rate ( ) as SampleRate ) ;
268- let wait = timeout. map_or ( buffered, |t| buffered. min ( t) ) ;
269- if !wait. is_zero ( ) {
270- std:: thread:: sleep ( wait) ;
271- }
272- }
260+ self . pause ( ) ?;
261+ wait_for_drain (
262+ std:: time:: Duration :: from_nanos ( self . playback_delay_nanos . load ( Ordering :: Relaxed ) ) ,
263+ timeout,
264+ ) ;
273265 Ok ( ( ) )
274266 }
275267
@@ -307,6 +299,7 @@ struct LocalProcessHandler {
307299 playback_state : Arc < AtomicU8 > ,
308300 pending_xrun : Arc < AtomicBool > ,
309301 error_callback : ErrorCallbackArc ,
302+ playback_delay_nanos : Arc < AtomicU64 > ,
310303 oversized_reported : bool ,
311304 #[ cfg( feature = "realtime" ) ]
312305 rt_checked : bool ,
@@ -323,6 +316,7 @@ impl LocalProcessHandler {
323316 playback_state : Arc < AtomicU8 > ,
324317 pending_xrun : Arc < AtomicBool > ,
325318 error_callback : ErrorCallbackArc ,
319+ playback_delay_nanos : Arc < AtomicU64 > ,
326320 ) -> Self {
327321 let temp_input_buffer = vec ! [ f32 :: EQUILIBRIUM ; in_ports. len( ) * buffer_size] ;
328322 let temp_output_buffer = vec ! [ f32 :: EQUILIBRIUM ; out_ports. len( ) * buffer_size] ;
@@ -338,6 +332,7 @@ impl LocalProcessHandler {
338332 playback_state,
339333 pending_xrun,
340334 error_callback,
335+ playback_delay_nanos,
341336 oversized_reported : false ,
342337 #[ cfg( feature = "realtime" ) ]
343338 rt_checked : false ,
@@ -542,13 +537,15 @@ impl jack::ProcessHandler for LocalProcessHandler {
542537
543538 let capture =
544539 capture_instant ( & self . in_ports , start_cycle_instant, self . sample_rate ) ;
545- let playback = playback_instant (
546- & self . out_ports ,
547- start_cycle_instant,
548- next_usecs_opt,
549- current_frame_count as FrameCount ,
550- self . sample_rate ,
551- ) ;
540+ let playback = start_cycle_instant
541+ + publish_playback_delay (
542+ & self . out_ports ,
543+ start_cycle_instant,
544+ next_usecs_opt,
545+ current_frame_count as FrameCount ,
546+ self . sample_rate ,
547+ & self . playback_delay_nanos ,
548+ ) ;
552549 let info = DuplexCallbackInfo :: new (
553550 CallbackInfo {
554551 timestamp : StreamTimestamp {
@@ -596,13 +593,15 @@ impl jack::ProcessHandler for LocalProcessHandler {
596593 ) ;
597594 let timestamp = StreamTimestamp {
598595 callback : start_callback_instant,
599- device : playback_instant (
600- & self . out_ports ,
601- start_cycle_instant,
602- next_usecs_opt,
603- current_frame_count as FrameCount ,
604- self . sample_rate ,
605- ) ,
596+ device : start_cycle_instant
597+ + publish_playback_delay (
598+ & self . out_ports ,
599+ start_cycle_instant,
600+ next_usecs_opt,
601+ current_frame_count as FrameCount ,
602+ self . sample_rate ,
603+ & self . playback_delay_nanos ,
604+ ) ,
606605 } ;
607606 let info = CallbackInfo { timestamp, xrun } ;
608607 output_callback ( & mut data, & info) ;
@@ -674,27 +673,39 @@ fn capture_instant(
674673 . unwrap_or ( StreamInstant :: ZERO )
675674}
676675
677- /// When the first frame written this cycle reaches the DAC, derived from JACK's port-to-hardware
678- /// playback latency, or the cycle's hardware deadline if JACK reports no latency.
676+ /// How long from the cycle start until the first frame written this cycle reaches the DAC, derived
677+ /// from JACK's port-to-hardware playback latency, or the cycle's hardware deadline if JACK reports
678+ /// no latency.
679+ ///
680+ /// Also publishes it to `published` for `stop()`, which cannot resolve the `next_usecs` fallback
681+ /// off the process thread and must drain by exactly what the timestamps promised.
679682#[ inline]
680- fn playback_instant (
683+ fn publish_playback_delay (
681684 out_ports : & [ jack:: Port < jack:: AudioOut > ] ,
682685 start_cycle_instant : StreamInstant ,
683686 next_usecs_opt : Option < u64 > ,
684687 current_frame_count : FrameCount ,
685688 sample_rate : SampleRate ,
686- ) -> StreamInstant {
687- match hardware_latency_frames ( out_ports, jack:: LatencyType :: Playback ) {
689+ published : & AtomicU64 ,
690+ ) -> std:: time:: Duration {
691+ let delay = match hardware_latency_frames ( out_ports, jack:: LatencyType :: Playback ) {
688692 // Prefer JACK's port-to-hardware latency, measured from the cycle start.
689- Some ( frames) => start_cycle_instant + frames_to_duration ( frames, sample_rate) ,
693+ Some ( frames) => frames_to_duration ( frames, sample_rate) ,
690694 // When no latency is reported, fall back to next_usecs, the hardware deadline for this
691695 // cycle.
692696 None => match next_usecs_opt {
693- Some ( next_usecs) => micros_to_stream_instant ( next_usecs) ,
697+ Some ( next_usecs) => micros_to_stream_instant ( next_usecs)
698+ . checked_duration_since ( start_cycle_instant)
699+ . unwrap_or_default ( ) ,
694700 // Fallback to one buffer ahead if that is unavailable too.
695- None => start_cycle_instant + frames_to_duration ( current_frame_count, sample_rate) ,
701+ None => frames_to_duration ( current_frame_count, sample_rate) ,
696702 } ,
697- }
703+ } ;
704+ published. store (
705+ u64:: try_from ( delay. as_nanos ( ) ) . unwrap_or ( u64:: MAX ) ,
706+ Ordering :: Relaxed ,
707+ ) ;
708+ delay
698709}
699710
700711/// Receives notifications from the JACK server on JACK's notification thread (single-threaded).
0 commit comments