~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Linux Cross Reference
JACK/libjack/transclient.c


** 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, &current); 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

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.