** Warning: Cannot open xref database.
1 /*
2 JACK transport client interface -- runs in the client process.
3
4 Copyright (C) 2001-2003 Paul Davis
5 Copyright (C) 2003 Jack O'Quin
6
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.
11
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.
16
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.
21 */
22
23 #include <config.h>
24 #include <errno.h>
25 #include <math.h>
26 #include <stdio.h>
27 #include <jack/internal.h>
28 #include "local.h"
29
30
31 /********************* Internal functions *********************/
32
33 /* generate a unique non-zero ID, different for each call */
34 jack_unique_t
35 jack_generate_unique_id (jack_control_t *ectl)
36 {
37 /* The jack_unique_t is an opaque type. Its structure is only
38 * known here. We use the least significant word of the CPU
39 * cycle counter. For SMP, I would like to include the
40 * current thread ID, since only one thread runs on a CPU at a
41 * time.
42 *
43 * But, pthread_self() is broken on my Debian GNU/Linux
44 * system, it always seems to return 16384. That's useless.
45 * So, I'm using the process ID instead. With Linux 2.4 and
46 * Linuxthreads there is an LWP for each thread so this works,
47 * but is not portable. :-(
48 */
49 volatile union {
50 jack_unique_t unique;
51 struct {
52 pid_t pid;
53 unsigned long cycle;
54 } field;
55 } id;
56
57 id.field.cycle = (unsigned long) get_cycles();
58 id.field.pid = getpid();
59
60 // JOQ: Alternatively, we could keep a sequence number in
61 // shared memory, using <asm/atomic.h> to increment it. I
62 // really like the simplicity of that approach. But I hate
63 // forcing JACK to either depend on that pesky header file, or
64 // maintain its own like ardour does.
65
66 // fprintf (stderr, "unique ID 0x%" PRIx64
67 // ", process %d, cycle 0x%" PRIx32 "\n",
68 // id.unique, id.field.pid, id.field.cycle);
69
70 return id.unique;
71 }
72
73 static inline void
74 jack_read_frame_time (const jack_client_t *client, jack_frame_timer_t *copy)
75 {
76 int tries = 0;
77
78 do {
79 /* throttle the busy wait if we don't get
80 the answer very quickly.
81 */
82
83 if (tries > 10) {
84 usleep (20);
85 tries = 0;
86 }
87
88 *copy = client->engine->frame_timer;
89
90 tries++;
91
92 } while (copy->guard1 != copy->guard2);
93 }
94
95 /* copy a JACK transport position structure (thread-safe) */
96 void
97 jack_transport_copy_position (jack_position_t *from, jack_position_t *to)
98 {
99 int tries = 0;
100 long timeout = 1000;
101
102 do {
103 /* throttle the busy wait if we don't get the answer
104 * very quickly. */
105 if (tries > 10) {
106 usleep (20);
107 tries = 0;
108
109 /* debug code to avoid system hangs... */
110 if (--timeout == 0) {
111 jack_error("hung in loop copying position");
112 abort();
113 }
114 }
115 *to = *from;
116 tries++;
117
118 } while (to->unique_1 != to->unique_2);
119 }
120
121 static inline int
122 jack_transport_request_new_pos (jack_client_t *client, jack_position_t *pos)
123 {
124 jack_control_t *ectl = client->engine;
125
126 /* distinguish this request from all others */
127 pos->unique_1 = pos->unique_2 = jack_generate_unique_id(ectl);
128
129 /* clients may not set these fields */
130 pos->usecs = ectl->current_time.usecs;
131 pos->frame_rate = ectl->current_time.frame_rate;
132
133 /* carefully copy requested postion into shared memory */
134 jack_transport_copy_position (pos, &ectl->request_time);
135
136 return 0;
137 }
138
139
140 /******************** Callback invocations ********************/
141
142 void
143 jack_call_sync_client (jack_client_t *client)
144 {
145 jack_client_control_t *control = client->control;
146 jack_control_t *ectl = client->engine;
147
148 /* Make sure still active and slow-sync; active_slowsync is
149 * set in a critical section; sync_cb is not. */
150 if ((ectl->new_pos || control->sync_poll || control->sync_new) &&
151 control->active_slowsync) {
152
153 if (control->sync_cb (ectl->transport_state,
154 &ectl->current_time,
155 control->sync_arg)) {
156
157 if (control->sync_poll) {
158 control->sync_poll = 0;
159 ectl->sync_remain--;
160 }
161 }
162 control->sync_new = 0;
163 }
164 }
165
166 void
167 jack_call_timebase_master (jack_client_t *client)
168 {
169 jack_client_control_t *control = client->control;
170 jack_control_t *ectl = client->engine;
171 int new_pos = (int) ectl->pending_pos;
172
173
174 /* Make sure this is still the master; is_timebase is set in a
175 * critical section; timebase_cb is not. */
176 if (control->is_timebase) {
177
178 if (control->timebase_new) { /* first callback? */
179 control->timebase_new = 0;
180 new_pos = 1;
181 }
182
183
184 if ((ectl->transport_state == JackTransportRolling) ||
185 new_pos) {
186
187 control->timebase_cb (ectl->transport_state,
188 control->nframes,
189 &ectl->pending_time,
190 new_pos,
191 control->timebase_arg);
192 }
193
194 } else {
195
196 /* another master took over, so resign */
197 control->timebase_cb = NULL;
198 control->timebase_arg = NULL;
199 }
200 }
201
202
203 /************************* API functions *************************/
204
205 jack_nframes_t
206 jack_get_current_transport_frame (const jack_client_t *client)
207 {
208 jack_position_t position;
209 float usecs;
210 jack_nframes_t elapsed;
211 jack_transport_state_t tstate;
212
213 /* get the current transport position information.
214 this is thread-safe and atomic with respect
215 to the structure contents.
216 */
217
218 tstate = jack_transport_query (client, &position);
219
220 if (tstate != JackTransportRolling) {
221 return position.frame;
222 }
223
224 /* compute the elapsed usecs then audio frames since
225 the transport info was last updated
226 */
227
228 usecs = jack_get_microseconds() - position.usecs;
229 elapsed = (jack_nframes_t) floor ((((float) position.frame_rate) / 1000000.0f) * usecs);
230
231 /* return the estimated transport frame position
232 */
233
234 return position.frame + elapsed;
235 }
236
237 jack_nframes_t
238 jack_frames_since_cycle_start (const jack_client_t *client)
239 {
240 float usecs;
241 jack_control_t *ectl = client->engine;
242
243 usecs = jack_get_microseconds() - ectl->current_time.usecs;
244 return (jack_nframes_t) floor ((((float) ectl->current_time.frame_rate)
245 / 1000000.0f) * usecs);
246 }
247
248 jack_nframes_t
249 jack_frame_time (const jack_client_t *client)
250 {
251 jack_frame_timer_t current;
252 float usecs;
253 jack_nframes_t elapsed;
254 jack_control_t *ectl = client->engine;
255
256 jack_read_frame_time (client, ¤t);
257
258 usecs = jack_get_microseconds() - current.stamp;
259 elapsed = (jack_nframes_t)
260 floor ((((float) ectl->current_time.frame_rate)
261 / 1000000.0f) * usecs);
262
263 return current.frames + elapsed;
264 }
265
266 jack_nframes_t
267 jack_get_sample_rate (jack_client_t *client)
268 {
269 return client->engine->current_time.frame_rate;
270 }
271
272 int
273 jack_set_sample_rate_callback (jack_client_t *client,
274 JackSampleRateCallback callback, void *arg)
275 {
276 if (client->control->active) {
277 jack_error ("You cannot set callbacks on an active client.");
278 return -1;
279 }
280 client->control->srate_arg = arg;
281 client->control->srate = callback;
282
283 /* Now invoke it */
284
285 callback (client->engine->current_time.frame_rate, arg);
286
287 return 0;
288 }
289
290 int
291 jack_release_timebase (jack_client_t *client)
292 {
293 int rc;
294 jack_request_t req;
295 jack_client_control_t *ctl = client->control;
296
297 req.type = ResetTimeBaseClient;
298 req.x.client_id = ctl->id;
299
300 rc = jack_client_deliver_request (client, &req);
301 if (rc == 0) {
302 ctl->timebase_cb = NULL;
303 ctl->timebase_arg = NULL;
304 }
305 return rc;
306 }
307
308 int
309 jack_set_sync_callback (jack_client_t *client,
310 JackSyncCallback sync_callback, void *arg)
311 {
312 jack_client_control_t *ctl = client->control;
313 jack_request_t req;
314 int rc;
315
316 if (sync_callback)
317 req.type = SetSyncClient;
318 else
319 req.type = ResetSyncClient;
320 req.x.client_id = ctl->id;
321
322 rc = jack_client_deliver_request (client, &req);
323 if (rc == 0) {
324 ctl->sync_cb = sync_callback;
325 ctl->sync_arg = arg;
326 }
327 return rc;
328 }
329
330 int
331 jack_set_sync_timeout (jack_client_t *client, jack_time_t usecs)
332 {
333 jack_request_t req;
334
335 req.type = SetSyncTimeout;
336 req.x.timeout = usecs;
337
338 return jack_client_deliver_request (client, &req);
339 }
340
341 int
342 jack_set_timebase_callback (jack_client_t *client, int conditional,
343 JackTimebaseCallback timebase_cb, void *arg)
344 {
345 int rc;
346 jack_request_t req;
347 jack_client_control_t *ctl = client->control;
348
349 req.type = SetTimeBaseClient;
350 req.x.timebase.client_id = ctl->id;
351 req.x.timebase.conditional = conditional;
352
353 rc = jack_client_deliver_request (client, &req);
354 if (rc == 0) {
355 ctl->timebase_arg = arg;
356 ctl->timebase_cb = timebase_cb;
357 }
358 return rc;
359 }
360
361 int
362 jack_transport_locate (jack_client_t *client, jack_nframes_t frame)
363 {
364 jack_position_t pos;
365
366 pos.frame = frame;
367 pos.valid = 0;
368 return jack_transport_request_new_pos (client, &pos);
369 }
370
371 jack_transport_state_t
372 jack_transport_query (const jack_client_t *client, jack_position_t *pos)
373 {
374 jack_control_t *ectl = client->engine;
375
376 if (pos) {
377 /* the guarded copy makes this function work in any
378 * thread
379 */
380 jack_transport_copy_position (&ectl->current_time, pos);
381 }
382
383 return ectl->transport_state;
384 }
385
386 int
387 jack_transport_reposition (jack_client_t *client, jack_position_t *pos)
388 {
389 /* copy the input, to avoid modifying its contents */
390 jack_position_t tmp = *pos;
391
392 /* validate input */
393 if (tmp.valid & ~JACK_POSITION_MASK) /* unknown field present? */
394 return EINVAL;
395
396 return jack_transport_request_new_pos (client, &tmp);
397 }
398
399 void
400 jack_transport_start (jack_client_t *client)
401 {
402 client->engine->transport_cmd = TransportCommandStart;
403 }
404
405 void
406 jack_transport_stop (jack_client_t *client)
407 {
408 client->engine->transport_cmd = TransportCommandStop;
409 }
410
411
412 #ifdef OLD_TRANSPORT
413
414 /************* Compatibility with old transport API. *************/
415
416 #define OLD_TIMEBASE_BROKEN
417
418 int
419 jack_engine_takeover_timebase (jack_client_t *client)
420 {
421 #ifdef OLD_TIMEBASE_BROKEN
422 return ENOSYS;
423 #else
424 jack_request_t req;
425
426 req.type = SetTimeBaseClient;
427 req.x.timebase.client_id = client->control->id;
428 req.x.timebase.conditional = 0;
429
430 return jack_client_deliver_request (client, &req);
431 #endif /* OLD_TIMEBASE_BROKEN */
432 }
433
434 void
435 jack_get_transport_info (jack_client_t *client,
436 jack_transport_info_t *info)
437 {
438 jack_control_t *ectl = client->engine;
439
440 /* check that this is the process thread */
441 if (!pthread_equal(client->thread_id, pthread_self())) {
442 jack_error("Invalid thread for jack_get_transport_info().");
443 abort(); /* kill this client */
444 }
445
446 info->usecs = ectl->current_time.usecs;
447 info->frame_rate = ectl->current_time.frame_rate;
448 info->transport_state = ectl->transport_state;
449 info->frame = ectl->current_time.frame;
450 info->valid = (ectl->current_time.valid |
451 JackTransportState | JackTransportPosition);
452
453 if (info->valid & JackTransportBBT) {
454 info->bar = ectl->current_time.bar;
455 info->beat = ectl->current_time.beat;
456 info->tick = ectl->current_time.tick;
457 info->bar_start_tick = ectl->current_time.bar_start_tick;
458 info->beats_per_bar = ectl->current_time.beats_per_bar;
459 info->beat_type = ectl->current_time.beat_type;
460 info->ticks_per_beat = ectl->current_time.ticks_per_beat;
461 info->beats_per_minute = ectl->current_time.beats_per_minute;
462 }
463 }
464
465 void
466 jack_set_transport_info (jack_client_t *client,
467 jack_transport_info_t *info)
468 {
469 static int first_error = 1;
470
471 #ifdef OLD_TIMEBASE_BROKEN
472
473 if (first_error)
474 jack_error ("jack_set_transport_info() no longer supported.");
475 first_error = 0;
476
477 #else
478
479 jack_control_t *ectl = client->engine;
480
481 if (!client->control->is_timebase) { /* not timebase master? */
482 if (first_error)
483 jack_error ("Called jack_set_transport_info(), "
484 "but not timebase master.");
485 first_error = 0;
486
487 /* JOQ: I would prefer to ignore this request, but
488 * that would break ardour 0.9-beta2. So, let's allow
489 * it for now. */
490 // return;
491 }
492
493 /* check that this is the process thread */
494 if (!pthread_equal(client->thread_id, pthread_self())) {
495 jack_error ("Invalid thread for jack_set_transport_info().");
496 abort(); /* kill this client */
497 }
498
499 /* is there a new state? */
500 if ((info->valid & JackTransportState) &&
501 (info->transport_state != ectl->transport_state)) {
502 if (info->transport_state == JackTransportStopped)
503 ectl->transport_cmd = TransportCommandStop;
504 else if ((info->transport_state == JackTransportRolling) &&
505 (ectl->transport_state != JackTransportStarting))
506 ectl->transport_cmd = TransportCommandStart;
507 /* silently ignore anything else */
508 }
509
510 if (info->valid & JackTransportPosition)
511 ectl->pending_time.frame = info->frame;
512 else
513 ectl->pending_time.frame = ectl->current_time.frame;
514
515 ectl->pending_time.valid = (info->valid & JACK_POSITION_MASK);
516
517 if (info->valid & JackTransportBBT) {
518 ectl->pending_time.bar = info->bar;
519 ectl->pending_time.beat = info->beat;
520 ectl->pending_time.tick = info->tick;
521 ectl->pending_time.bar_start_tick = info->bar_start_tick;
522 ectl->pending_time.beats_per_bar = info->beats_per_bar;
523 ectl->pending_time.beat_type = info->beat_type;
524 ectl->pending_time.ticks_per_beat = info->ticks_per_beat;
525 ectl->pending_time.beats_per_minute = info->beats_per_minute;
526 }
527 #endif /* OLD_TIMEBASE_BROKEN */
528 }
529
530 #endif /* OLD_TRANSPORT */
531
This page was automatically generated by the
LXR engine.
Visit the LXR main site for more
information.