ensure that client-side message buffer thread calls thread_init callback if/when...
[jack.git] / libjack / transclient.c
blobf449003d1251677fd21439e512c7bf0284634c95
1 /*
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
20 02111-1307, USA.
23 #include <config.h>
24 #include <errno.h>
25 #include <math.h>
26 #include <stdio.h>
27 #include <jack/atomicity.h>
28 #include <jack/internal.h>
29 #include "local.h"
32 /********************* Internal functions *********************/
34 /* generate a unique non-zero ID, different for each call */
35 jack_unique_t
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);
42 static inline void
43 jack_read_frame_time (const jack_client_t *client, jack_frame_timer_t *copy)
45 int tries = 0;
46 long timeout = 1000;
48 do {
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
57 sleeping.
60 if (tries > 10) {
61 usleep (20);
62 tries = 0;
64 /* debug code to avoid system hangs... */
65 if (--timeout == 0) {
66 jack_error ("hung in loop copying position A");
67 abort();
71 *copy = client->engine->frame_timer;
73 tries++;
75 } while (copy->guard1 != copy->guard2);
78 /* copy a JACK transport position structure (thread-safe) */
79 void
80 jack_transport_copy_position (jack_position_t *from, jack_position_t *to)
82 int tries = 0;
83 long timeout = 1000;
85 do {
86 /* throttle the busy wait if we don't get the answer
87 * very quickly. See comment above about this
88 * design.
90 if (tries > 10) {
91 usleep (20);
92 tries = 0;
94 /* debug code to avoid system hangs... */
95 if (--timeout == 0) {
96 jack_error ("hung in loop copying position B");
97 abort();
100 *to = *from;
101 tries++;
103 } while (to->unique_1 != to->unique_2);
106 static inline int
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);
121 return 0;
125 /******************** Callback invocations ********************/
127 void
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,
139 &ectl->current_time,
140 client->sync_arg)) {
142 if (control->sync_poll) {
143 control->sync_poll = 0;
144 ectl->sync_remain--;
147 control->sync_new = 0;
151 void
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;
165 new_pos = 1;
169 if ((ectl->transport_state == JackTransportRolling) ||
170 new_pos) {
172 client->timebase_cb (ectl->transport_state,
173 control->nframes,
174 &ectl->pending_time,
175 new_pos,
176 client->timebase_arg);
179 } else {
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 *************************/
191 jack_nframes_t
192 jack_get_current_transport_frame (const jack_client_t *client)
194 jack_position_t position;
195 float usecs;
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;
224 jack_nframes_t
225 jack_frames_since_cycle_start (const jack_client_t *client)
227 float usecs;
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);
235 jack_time_t
236 jack_get_time()
238 return jack_get_microseconds();
241 jack_nframes_t
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) {
250 #if 0
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),
254 time.frames +
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);
259 #endif
261 return time.frames +
262 (long) rint (((double) ((long long) (now - time.current_wakeup))/
263 ((long long) (time.next_wakeup - time.current_wakeup))) * ectl->buffer_size);
265 return 0;
268 jack_nframes_t
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);
275 jack_nframes_t
276 jack_last_frame_time (const jack_client_t *client)
278 return client->engine->frame_timer.frames;
281 jack_time_t
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) );
295 return 0;
298 jack_nframes_t
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.");
310 return -1;
312 client->srate_arg = arg;
313 client->srate = callback;
314 client->control->srate_cbset = (callback != NULL);
316 /* Now invoke it */
318 callback (client->engine->current_time.frame_rate, arg);
320 return 0;
323 int
324 jack_release_timebase (jack_client_t *client)
326 int rc;
327 jack_request_t req;
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);
334 if (rc == 0) {
335 client->timebase_cb = NULL;
336 client->timebase_arg = NULL;
337 ctl->timebase_cb_cbset = 0;
340 return rc;
343 int
344 jack_set_sync_callback (jack_client_t *client,
345 JackSyncCallback sync_callback, void *arg)
347 jack_client_control_t *ctl = client->control;
348 jack_request_t req;
349 int rc;
351 if (sync_callback)
352 req.type = SetSyncClient;
353 else
354 req.type = ResetSyncClient;
355 req.x.client_id = ctl->id;
357 rc = jack_client_deliver_request (client, &req);
358 if (rc == 0) {
359 client->sync_cb = sync_callback;
360 client->sync_arg = arg;
361 ctl->sync_cb_cbset = TRUE;
363 return rc;
366 int
367 jack_set_sync_timeout (jack_client_t *client, jack_time_t usecs)
369 jack_request_t req;
371 req.type = SetSyncTimeout;
372 req.x.timeout = usecs;
374 return jack_client_deliver_request (client, &req);
377 int
378 jack_set_timebase_callback (jack_client_t *client, int conditional,
379 JackTimebaseCallback timebase_cb, void *arg)
381 int rc;
382 jack_request_t req;
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);
390 if (rc == 0) {
391 client->timebase_arg = arg;
392 client->timebase_cb = timebase_cb;
393 ctl->timebase_cb_cbset = TRUE;
395 return rc;
399 jack_transport_locate (jack_client_t *client, jack_nframes_t frame)
401 jack_position_t pos;
403 pos.frame = frame;
404 pos.valid = 0;
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;
413 if (pos) {
414 /* the guarded copy makes this function work in any
415 * thread
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;
429 /* validate input */
430 if (tmp.valid & ~JACK_POSITION_MASK) /* unknown field present? */
431 return EINVAL;
433 return jack_transport_request_new_pos (client, &tmp);
436 void
437 jack_transport_start (jack_client_t *client)
439 client->engine->transport_cmd = TransportCommandStart;
442 void
443 jack_transport_stop (jack_client_t *client)
445 client->engine->transport_cmd = TransportCommandStop;
449 #ifdef OLD_TRANSPORT
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.");
457 return ENOSYS;
460 void
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;
467 if (first_time)
468 jack_error ("jack_get_transport_info() is deprecated.");
469 first_time = 0;
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;
496 void
497 jack_set_transport_info (jack_client_t *client,
498 jack_transport_info_t *info)
500 static int first_time = 1;
502 if (first_time)
503 jack_error ("jack_set_transport_info() no longer supported.");
504 first_time = 0;
507 #endif /* OLD_TRANSPORT */