2 JACK transport client interface -- runs in the client process.
4 Copyright (C) 2001-2003 Paul Davis
5 Copyright (C) 2003 Jack O'Quin
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Lesser General Public License
9 as published by the Free Software Foundation; either version 2.1
10 of the License, or (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public
18 License along with this program; if not, write to the Free
19 Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
27 #include <jack/atomicity.h>
28 #include <jack/internal.h>
32 /********************* Internal functions *********************/
34 /* generate a unique non-zero ID, different for each call */
36 jack_generate_unique_id (jack_control_t
*ectl
)
38 /* The jack_unique_t is an opaque type. */
39 return exchange_and_add(&ectl
->seq_number
, 1);
43 jack_read_frame_time (const jack_client_t
*client
, jack_frame_timer_t
*copy
)
49 /* throttle the busy wait if we don't get
50 the answer very quickly.
52 XXX This is disgusting. on a UP
53 system, it needs to sleep
54 if the first try didn't work. on an SMP
55 system, it should wait for half of
56 the context switch time before
64 /* debug code to avoid system hangs... */
66 jack_error ("hung in loop copying position A");
71 *copy
= client
->engine
->frame_timer
;
75 } while (copy
->guard1
!= copy
->guard2
);
78 /* copy a JACK transport position structure (thread-safe) */
80 jack_transport_copy_position (jack_position_t
*from
, jack_position_t
*to
)
86 /* throttle the busy wait if we don't get the answer
87 * very quickly. See comment above about this
94 /* debug code to avoid system hangs... */
96 jack_error ("hung in loop copying position B");
103 } while (to
->unique_1
!= to
->unique_2
);
107 jack_transport_request_new_pos (jack_client_t
*client
, jack_position_t
*pos
)
109 jack_control_t
*ectl
= client
->engine
;
111 /* distinguish this request from all others */
112 pos
->unique_1
= pos
->unique_2
= jack_generate_unique_id(ectl
);
114 /* clients may not set these fields */
115 pos
->usecs
= ectl
->current_time
.usecs
;
116 pos
->frame_rate
= ectl
->current_time
.frame_rate
;
118 /* carefully copy requested postion into shared memory */
119 jack_transport_copy_position (pos
, &ectl
->request_time
);
125 /******************** Callback invocations ********************/
128 jack_call_sync_client (jack_client_t
*client
)
130 jack_client_control_t
*control
= client
->control
;
131 jack_control_t
*ectl
= client
->engine
;
133 /* Make sure still active and slow-sync; active_slowsync is
134 * set in a critical section; sync_cb is not. */
135 if ((ectl
->new_pos
|| control
->sync_poll
|| control
->sync_new
) &&
136 control
->active_slowsync
) {
138 if (client
->sync_cb (ectl
->transport_state
,
142 if (control
->sync_poll
) {
143 control
->sync_poll
= 0;
147 control
->sync_new
= 0;
152 jack_call_timebase_master (jack_client_t
*client
)
154 jack_client_control_t
*control
= client
->control
;
155 jack_control_t
*ectl
= client
->engine
;
156 int new_pos
= (int) ectl
->pending_pos
;
159 /* Make sure this is still the master; is_timebase is set in a
160 * critical section; timebase_cb is not. */
161 if (control
->is_timebase
) {
163 if (control
->timebase_new
) { /* first callback? */
164 control
->timebase_new
= 0;
169 if ((ectl
->transport_state
== JackTransportRolling
) ||
172 client
->timebase_cb (ectl
->transport_state
,
176 client
->timebase_arg
);
181 /* another master took over, so resign */
182 client
->timebase_cb
= NULL
;
183 client
->timebase_arg
= NULL
;
184 control
->timebase_cb_cbset
= FALSE
;
189 /************************* API functions *************************/
192 jack_get_current_transport_frame (const jack_client_t
*client
)
194 jack_position_t position
;
196 jack_nframes_t elapsed
;
197 jack_transport_state_t tstate
;
199 /* get the current transport position information.
200 this is thread-safe and atomic with respect
201 to the structure contents.
204 tstate
= jack_transport_query (client
, &position
);
206 if (tstate
!= JackTransportRolling
) {
207 return position
.frame
;
210 /* compute the elapsed usecs then audio frames since
211 the transport info was last updated
214 usecs
= jack_get_microseconds() - position
.usecs
;
215 elapsed
= (jack_nframes_t
) floor ((((float) position
.frame_rate
)
216 / 1000000.0f
) * usecs
);
218 /* return the estimated transport frame position
221 return position
.frame
+ elapsed
;
225 jack_frames_since_cycle_start (const jack_client_t
*client
)
228 jack_control_t
*ectl
= client
->engine
;
230 usecs
= jack_get_microseconds() - ectl
->current_time
.usecs
;
231 return (jack_nframes_t
) floor ((((float) ectl
->current_time
.frame_rate
)
232 / 1000000.0f
) * usecs
);
238 return jack_get_microseconds();
242 jack_time_to_frames(const jack_client_t
*client
, jack_time_t now
)
244 jack_frame_timer_t time
;
245 jack_control_t
*ectl
= client
->engine
;
247 jack_read_frame_time (client
, &time
);
249 if (time
.initialized
) {
251 jack_info ("now = %Lu current wakeup = %Lu next = %Lu frames = %lu + %f => %lu FC = %f SOI = %f",
252 now
, time
.current_wakeup
, time
.next_wakeup
, time
.frames
,
253 (double) (now
- time
.current_wakeup
)/ (time
.next_wakeup
- time
.current_wakeup
),
255 (long) rint (((double) ((long long) now
- time
.current_wakeup
)/
256 (long long) (time
.next_wakeup
- time
.current_wakeup
)) * ectl
->buffer_size
),
257 time
.filter_coefficient
,
258 time
.second_order_integrator
);
262 (long) rint (((double) ((long long) (now
- time
.current_wakeup
))/
263 ((long long) (time
.next_wakeup
- time
.current_wakeup
))) * ectl
->buffer_size
);
269 jack_frame_time (const jack_client_t
*client
)
271 jack_time_t now
= jack_get_microseconds();
272 return jack_time_to_frames(client
, now
);
276 jack_last_frame_time (const jack_client_t
*client
)
278 return client
->engine
->frame_timer
.frames
;
282 jack_frames_to_time(const jack_client_t
*client
, jack_nframes_t frames
)
284 jack_frame_timer_t time
;
285 jack_control_t
*ectl
= client
->engine
;
287 jack_read_frame_time (client
, &time
);
289 if (time
.initialized
) {
290 return time
.current_wakeup
+
291 (long) rint (((double) ((long long) (frames
- time
.frames
)) *
292 ((long long) (time
.next_wakeup
- time
.current_wakeup
)) / ectl
->buffer_size
) );
299 jack_get_sample_rate (jack_client_t
*client
)
301 return client
->engine
->current_time
.frame_rate
;
305 jack_set_sample_rate_callback (jack_client_t
*client
,
306 JackSampleRateCallback callback
, void *arg
)
308 if (client
->control
->active
) {
309 jack_error ("You cannot set callbacks on an active client.");
312 client
->srate_arg
= arg
;
313 client
->srate
= callback
;
314 client
->control
->srate_cbset
= (callback
!= NULL
);
318 callback (client
->engine
->current_time
.frame_rate
, arg
);
324 jack_release_timebase (jack_client_t
*client
)
328 jack_client_control_t
*ctl
= client
->control
;
330 req
.type
= ResetTimeBaseClient
;
331 req
.x
.client_id
= ctl
->id
;
333 rc
= jack_client_deliver_request (client
, &req
);
335 client
->timebase_cb
= NULL
;
336 client
->timebase_arg
= NULL
;
337 ctl
->timebase_cb_cbset
= 0;
344 jack_set_sync_callback (jack_client_t
*client
,
345 JackSyncCallback sync_callback
, void *arg
)
347 jack_client_control_t
*ctl
= client
->control
;
352 req
.type
= SetSyncClient
;
354 req
.type
= ResetSyncClient
;
355 req
.x
.client_id
= ctl
->id
;
357 rc
= jack_client_deliver_request (client
, &req
);
359 client
->sync_cb
= sync_callback
;
360 client
->sync_arg
= arg
;
361 ctl
->sync_cb_cbset
= TRUE
;
367 jack_set_sync_timeout (jack_client_t
*client
, jack_time_t usecs
)
371 req
.type
= SetSyncTimeout
;
372 req
.x
.timeout
= usecs
;
374 return jack_client_deliver_request (client
, &req
);
378 jack_set_timebase_callback (jack_client_t
*client
, int conditional
,
379 JackTimebaseCallback timebase_cb
, void *arg
)
383 jack_client_control_t
*ctl
= client
->control
;
385 req
.type
= SetTimeBaseClient
;
386 req
.x
.timebase
.client_id
= ctl
->id
;
387 req
.x
.timebase
.conditional
= conditional
;
389 rc
= jack_client_deliver_request (client
, &req
);
391 client
->timebase_arg
= arg
;
392 client
->timebase_cb
= timebase_cb
;
393 ctl
->timebase_cb_cbset
= TRUE
;
399 jack_transport_locate (jack_client_t
*client
, jack_nframes_t frame
)
405 return jack_transport_request_new_pos (client
, &pos
);
408 jack_transport_state_t
409 jack_transport_query (const jack_client_t
*client
, jack_position_t
*pos
)
411 jack_control_t
*ectl
= client
->engine
;
414 /* the guarded copy makes this function work in any
417 jack_transport_copy_position (&ectl
->current_time
, pos
);
420 return ectl
->transport_state
;
424 jack_transport_reposition (jack_client_t
*client
, jack_position_t
*pos
)
426 /* copy the input, to avoid modifying its contents */
427 jack_position_t tmp
= *pos
;
430 if (tmp
.valid
& ~JACK_POSITION_MASK
) /* unknown field present? */
433 return jack_transport_request_new_pos (client
, &tmp
);
437 jack_transport_start (jack_client_t
*client
)
439 client
->engine
->transport_cmd
= TransportCommandStart
;
443 jack_transport_stop (jack_client_t
*client
)
445 client
->engine
->transport_cmd
= TransportCommandStop
;
451 /************* Compatibility with old transport API. *************/
454 jack_engine_takeover_timebase (jack_client_t
*client
)
456 jack_error ("jack_engine_takeover_timebase() is no longer supported.");
461 jack_get_transport_info (jack_client_t
*client
,
462 jack_transport_info_t
*info
)
464 jack_control_t
*ectl
= client
->engine
;
465 static int first_time
= 1;
468 jack_error ("jack_get_transport_info() is deprecated.");
471 /* check that this is the process thread */
472 if (!pthread_equal(client
->thread_id
, pthread_self())) {
473 jack_error("Invalid thread for jack_get_transport_info().");
474 abort(); /* kill this client */
477 info
->usecs
= ectl
->current_time
.usecs
;
478 info
->frame_rate
= ectl
->current_time
.frame_rate
;
479 info
->transport_state
= ectl
->transport_state
;
480 info
->frame
= ectl
->current_time
.frame
;
481 info
->valid
= (ectl
->current_time
.valid
|
482 JackTransportState
| JackTransportPosition
);
484 if (info
->valid
& JackTransportBBT
) {
485 info
->bar
= ectl
->current_time
.bar
;
486 info
->beat
= ectl
->current_time
.beat
;
487 info
->tick
= ectl
->current_time
.tick
;
488 info
->bar_start_tick
= ectl
->current_time
.bar_start_tick
;
489 info
->beats_per_bar
= ectl
->current_time
.beats_per_bar
;
490 info
->beat_type
= ectl
->current_time
.beat_type
;
491 info
->ticks_per_beat
= ectl
->current_time
.ticks_per_beat
;
492 info
->beats_per_minute
= ectl
->current_time
.beats_per_minute
;
497 jack_set_transport_info (jack_client_t
*client
,
498 jack_transport_info_t
*info
)
500 static int first_time
= 1;
503 jack_error ("jack_set_transport_info() no longer supported.");
507 #endif /* OLD_TRANSPORT */