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",
252 now
, time
.current_wakeup
, time
.next_wakeup
, time
.frames
,
253 (double) (now
- time
.current_wakeup
)/
254 (time
.next_wakeup
- time
.current_wakeup
),
256 (long) rint (((double) (now
- time
.current_wakeup
)/
257 (time
.next_wakeup
- time
.current_wakeup
)) * ectl
->buffer_size
));
261 (long) rint (((double) ((long long) (now
- time
.current_wakeup
))/
262 ((long long) (time
.next_wakeup
- time
.current_wakeup
))) * ectl
->buffer_size
);
268 jack_frame_time (const jack_client_t
*client
)
270 jack_time_t now
= jack_get_microseconds();
271 return jack_time_to_frames(client
, now
);
275 jack_last_frame_time (const jack_client_t
*client
)
277 jack_frame_timer_t current
;
278 jack_read_frame_time (client
, ¤t
);
279 return current
.frames
;
283 jack_frames_to_time(const jack_client_t
*client
, jack_nframes_t frames
)
285 jack_frame_timer_t time
;
286 jack_control_t
*ectl
= client
->engine
;
288 jack_read_frame_time (client
, &time
);
290 if (time
.initialized
) {
291 return time
.current_wakeup
+
292 (long) rint (((double) ((long long) (frames
- time
.frames
)) *
293 ((long long) (time
.next_wakeup
- time
.current_wakeup
)) / ectl
->buffer_size
) );
300 jack_get_sample_rate (jack_client_t
*client
)
302 return client
->engine
->current_time
.frame_rate
;
306 jack_set_sample_rate_callback (jack_client_t
*client
,
307 JackSampleRateCallback callback
, void *arg
)
309 if (client
->control
->active
) {
310 jack_error ("You cannot set callbacks on an active client.");
313 client
->srate_arg
= arg
;
314 client
->srate
= callback
;
315 client
->control
->srate_cbset
= (callback
!= NULL
);
319 callback (client
->engine
->current_time
.frame_rate
, arg
);
325 jack_release_timebase (jack_client_t
*client
)
329 jack_client_control_t
*ctl
= client
->control
;
331 req
.type
= ResetTimeBaseClient
;
332 req
.x
.client_id
= ctl
->id
;
334 rc
= jack_client_deliver_request (client
, &req
);
336 client
->timebase_cb
= NULL
;
337 client
->timebase_arg
= NULL
;
338 ctl
->timebase_cb_cbset
= 0;
345 jack_set_sync_callback (jack_client_t
*client
,
346 JackSyncCallback sync_callback
, void *arg
)
348 jack_client_control_t
*ctl
= client
->control
;
353 req
.type
= SetSyncClient
;
355 req
.type
= ResetSyncClient
;
356 req
.x
.client_id
= ctl
->id
;
358 rc
= jack_client_deliver_request (client
, &req
);
360 client
->sync_cb
= sync_callback
;
361 client
->sync_arg
= arg
;
362 ctl
->sync_cb_cbset
= TRUE
;
368 jack_set_sync_timeout (jack_client_t
*client
, jack_time_t usecs
)
372 req
.type
= SetSyncTimeout
;
373 req
.x
.timeout
= usecs
;
375 return jack_client_deliver_request (client
, &req
);
379 jack_set_timebase_callback (jack_client_t
*client
, int conditional
,
380 JackTimebaseCallback timebase_cb
, void *arg
)
384 jack_client_control_t
*ctl
= client
->control
;
386 req
.type
= SetTimeBaseClient
;
387 req
.x
.timebase
.client_id
= ctl
->id
;
388 req
.x
.timebase
.conditional
= conditional
;
390 rc
= jack_client_deliver_request (client
, &req
);
392 client
->timebase_arg
= arg
;
393 client
->timebase_cb
= timebase_cb
;
394 ctl
->timebase_cb_cbset
= TRUE
;
400 jack_transport_locate (jack_client_t
*client
, jack_nframes_t frame
)
406 return jack_transport_request_new_pos (client
, &pos
);
409 jack_transport_state_t
410 jack_transport_query (const jack_client_t
*client
, jack_position_t
*pos
)
412 jack_control_t
*ectl
= client
->engine
;
415 /* the guarded copy makes this function work in any
418 jack_transport_copy_position (&ectl
->current_time
, pos
);
421 return ectl
->transport_state
;
425 jack_transport_reposition (jack_client_t
*client
, jack_position_t
*pos
)
427 /* copy the input, to avoid modifying its contents */
428 jack_position_t tmp
= *pos
;
431 if (tmp
.valid
& ~JACK_POSITION_MASK
) /* unknown field present? */
434 return jack_transport_request_new_pos (client
, &tmp
);
438 jack_transport_start (jack_client_t
*client
)
440 client
->engine
->transport_cmd
= TransportCommandStart
;
444 jack_transport_stop (jack_client_t
*client
)
446 client
->engine
->transport_cmd
= TransportCommandStop
;
452 /************* Compatibility with old transport API. *************/
455 jack_engine_takeover_timebase (jack_client_t
*client
)
457 jack_error ("jack_engine_takeover_timebase() is no longer supported.");
462 jack_get_transport_info (jack_client_t
*client
,
463 jack_transport_info_t
*info
)
465 jack_control_t
*ectl
= client
->engine
;
466 static int first_time
= 1;
469 jack_error ("jack_get_transport_info() is deprecated.");
472 /* check that this is the process thread */
473 if (!pthread_equal(client
->thread_id
, pthread_self())) {
474 jack_error("Invalid thread for jack_get_transport_info().");
475 abort(); /* kill this client */
478 info
->usecs
= ectl
->current_time
.usecs
;
479 info
->frame_rate
= ectl
->current_time
.frame_rate
;
480 info
->transport_state
= ectl
->transport_state
;
481 info
->frame
= ectl
->current_time
.frame
;
482 info
->valid
= (ectl
->current_time
.valid
|
483 JackTransportState
| JackTransportPosition
);
485 if (info
->valid
& JackTransportBBT
) {
486 info
->bar
= ectl
->current_time
.bar
;
487 info
->beat
= ectl
->current_time
.beat
;
488 info
->tick
= ectl
->current_time
.tick
;
489 info
->bar_start_tick
= ectl
->current_time
.bar_start_tick
;
490 info
->beats_per_bar
= ectl
->current_time
.beats_per_bar
;
491 info
->beat_type
= ectl
->current_time
.beat_type
;
492 info
->ticks_per_beat
= ectl
->current_time
.ticks_per_beat
;
493 info
->beats_per_minute
= ectl
->current_time
.beats_per_minute
;
498 jack_set_transport_info (jack_client_t
*client
,
499 jack_transport_info_t
*info
)
501 static int first_time
= 1;
504 jack_error ("jack_set_transport_info() no longer supported.");
508 #endif /* OLD_TRANSPORT */