1+ #include <pthread.h>
12#include <stdatomic.h>
3+ #include <stdbool.h>
4+ #include <stdint.h>
25#include <stdio.h>
36
47#include <jack/jack.h>
@@ -10,6 +13,14 @@ double jack_sample_rate;
1013
1114_Atomic uint32_t xrun_count = 0 ;
1215
16+ // maintain a 64-bit frame counter from jack's 32-bit frame time.
17+ // extends wraparound at 48khz from ~25 hours to millions of years.
18+ static uint64_t g_last_total_frames = 0ULL ;
19+
20+ // protects access to g_last_total_frames.
21+ // a mutex is safe here as time is not read from the audio thread.
22+ static pthread_mutex_t g_time_lock = PTHREAD_MUTEX_INITIALIZER ;
23+
1324static int xrun_callback (void * arg ) {
1425 (void )arg ;
1526 atomic_fetch_add (& xrun_count , 1 );
@@ -50,8 +61,51 @@ uint32_t jack_client_get_xrun_count() {
5061}
5162
5263double jack_client_get_current_time () {
53- return (double )jack_frame_time (jack_client ) / jack_sample_rate ;
64+ jack_nframes_t cur_low = jack_frame_time (jack_client );
65+
66+ const unsigned low_bits = (unsigned )(sizeof (jack_nframes_t ) * 8u );
67+ const uint64_t low_mask = (low_bits == 64u ) ? UINT64_MAX : ((1ULL << low_bits ) - 1ULL );
68+
69+ pthread_mutex_lock (& g_time_lock );
70+
71+ uint64_t prev = g_last_total_frames ;
72+ uint64_t prev_low = prev & low_mask ;
73+ uint64_t prev_high = prev & ~low_mask ;
74+
75+ uint64_t cur_low_u64 = (uint64_t )cur_low ;
76+ uint64_t candidate = prev_high | cur_low_u64 ;
77+
78+ // detect wrap and carry to high bits.
79+ // a wrap is a large decrease (prev in upper half, cur in lower).
80+ if (low_bits < 64u && cur_low_u64 < prev_low ) {
81+ const uint64_t half = (1ULL << (low_bits - 1 ));
82+ if (prev_low >= half && cur_low_u64 < half ) {
83+ candidate += (1ULL << low_bits );
84+ } else {
85+ // ignore small decreases (out-of-order reads)
86+ candidate = prev ;
87+ }
88+ }
89+
90+ // never move backwards
91+ if (candidate < prev ) {
92+ candidate = prev ;
93+ }
94+
95+ g_last_total_frames = candidate ;
96+
97+ pthread_mutex_unlock (& g_time_lock );
98+
99+ return (double )candidate / jack_sample_rate ;
100+ }
101+
102+ #ifdef NORNS_TEST
103+ void jack_client_test_reset_time_state (void ) {
104+ pthread_mutex_lock (& g_time_lock );
105+ g_last_total_frames = 0ULL ;
106+ pthread_mutex_unlock (& g_time_lock );
54107}
108+ #endif
55109
56110const char * * jack_client_get_input_ports () {
57111 return jack_get_ports (jack_client , NULL , NULL , JackPortIsInput );
0 commit comments