git-svn-id: svn+ssh://jackaudio.org/trunk/jack@453 0c269be4-1314-0410-8aa9-9f06e86f4224tags/0.109.0
| @@ -14,7 +14,7 @@ Paul Davis was the principal author of the JACK API and of the sample | |||
| implementation contained here. Andy Wingo provided many small patches | |||
| Fernando Pablo Lopez-Lezcano contributed the capabilities-based code. | |||
| Jeremy Hall, Steve Harris, and Martin Boer contributed sample clients | |||
| and utilities. Jack O'Quin contributed documentation and minor | |||
| development work. | |||
| and utilities. Jack O'Quin contributed new transport interfaces and | |||
| documentation. | |||
| Many others have contributed patches and/or test results. | |||
| @@ -18,6 +18,7 @@ TODO for 1.0 (owner) | |||
| - ensure that UST/MSC pairs work for transport API | |||
| - resolve helper thread design question? | |||
| - API to change buffer size (joq) | |||
| TODO post-1.0 | |||
| @@ -25,7 +26,6 @@ TODO post-1.0 | |||
| TODO general (owner) | |||
| - define transport info struct contents (paul) | |||
| - don't build static libraries of drivers and ip-clients (kaiv) | |||
| - add explanation of protocol versioning to README.developers (kaiv) | |||
| - add Paul's graph/subgraph explanation to the design doc (kaiv) | |||
| @@ -59,5 +59,6 @@ CLOSED (date,who,comment) | |||
| - call time client before all others (2003/01/28, kaiv, another solution) | |||
| - finalize discussion on transport API, and implement (2003/08/04, joq) | |||
| - whether to hide the transport.h structs from clients (2003/08/04, joq) | |||
| - define transport info struct contents (2003/08/13, joq) | |||
| ----------------------------------------------------------------------- | |||
| @@ -13,8 +13,8 @@ dnl micro version = incremented when implementation-only | |||
| dnl changes are made | |||
| dnl --- | |||
| JACK_MAJOR_VERSION=0 | |||
| JACK_MINOR_VERSION=76 | |||
| JACK_MICRO_VERSION=7 | |||
| JACK_MINOR_VERSION=77 | |||
| JACK_MICRO_VERSION=0 | |||
| dnl --- | |||
| @@ -25,7 +25,7 @@ dnl made to the way libjack communicates with jackd | |||
| dnl that would break applications linked with an older | |||
| dnl version of libjack. | |||
| dnl --- | |||
| JACK_PROTOCOL_VERSION=7 | |||
| JACK_PROTOCOL_VERSION=8 | |||
| dnl --- | |||
| dnl HOWTO: updating the libjack interface version | |||
| @@ -42,7 +42,7 @@ dnl slacker than this, and closer to those for the JACK version | |||
| dnl number. | |||
| dnl --- | |||
| JACK_API_CURRENT=0 | |||
| JACK_API_REVISION=20 | |||
| JACK_API_REVISION=21 | |||
| JACK_API_AGE=0 | |||
| AC_SUBST(JACK_MAJOR_VERSION) | |||
| @@ -34,7 +34,7 @@ bin_PROGRAMS = jack_load \ | |||
| jack_metro \ | |||
| jack_showtime \ | |||
| jack_lsp \ | |||
| jackrec \ | |||
| $(JACKREC) \ | |||
| $(JACK_TRANSPORT) | |||
| if HAVE_SNDFILE | |||
| @@ -24,6 +24,9 @@ | |||
| #include <jack/jack.h> | |||
| #include <jack/internal.h> | |||
| #define VERBOSE(engine,format,args...) \ | |||
| if ((engine)->verbose) fprintf (stderr, format, ## args) | |||
| struct _jack_driver; | |||
| struct _jack_client_internal; | |||
| struct _jack_port_internal; | |||
| @@ -98,9 +98,11 @@ typedef struct { | |||
| jack_transport_state_t transport_state; | |||
| volatile transport_command_t transport_cmd; | |||
| transport_command_t previous_cmd; /* previous transport_cmd */ | |||
| jack_position_t current_time; /* position for current cycle */ | |||
| jack_position_t pending_time; /* position for next cycle */ | |||
| jack_position_t request_time; /* latest requested position */ | |||
| jack_unique_t prev_request; /* previous request unique ID */ | |||
| int new_pos; /* new position this cycle */ | |||
| unsigned long sync_clients; /* number of is_slowsync clients */ | |||
| unsigned long sync_remain; /* number of them with sync_poll */ | |||
| @@ -41,6 +41,8 @@ typedef enum { | |||
| } jack_transport_state_t; | |||
| typedef unsigned long long jack_unique_t; /**< Unique ID (opaque) */ | |||
| /** | |||
| * Optional struct jack_position_t fields. | |||
| */ | |||
| @@ -58,7 +60,8 @@ typedef enum { | |||
| */ | |||
| typedef struct { | |||
| /* these two cannot be set from clients: the server sets them */ | |||
| /* these three cannot be set from clients: the server sets them */ | |||
| jack_unique_t unique_1; /**< unique ID */ | |||
| jack_time_t usecs; /**< monotonic, free-rolling */ | |||
| jack_nframes_t frame_rate; /**< current frame rate (per second) */ | |||
| @@ -81,9 +84,8 @@ typedef struct { | |||
| * the existing structure size and offsets are preserved. */ | |||
| int padding[14]; | |||
| /* When (guard_usecs == usecs) the entire structure is consistent. | |||
| * This is set by server. */ | |||
| jack_time_t guard_usecs; /**< guard copy of usecs */ | |||
| /* When (unique_1 == unique_2) the contents are consistent. */ | |||
| jack_unique_t unique_2; /**< unique ID */ | |||
| } jack_position_t; | |||
| @@ -22,6 +22,7 @@ | |||
| #include <config.h> | |||
| #include <errno.h> | |||
| #include <assert.h> | |||
| #include <stdio.h> | |||
| #include <jack/internal.h> | |||
| #include <jack/engine.h> | |||
| #include "transengine.h" | |||
| @@ -44,8 +45,11 @@ jack_sync_poll_new (jack_engine_t *engine, jack_client_internal_t *client) | |||
| } | |||
| // JOQ: I don't like doing this here... | |||
| if (engine->control->transport_state == JackTransportRolling) | |||
| if (engine->control->transport_state == JackTransportRolling) { | |||
| engine->control->transport_state = JackTransportStarting; | |||
| VERBOSE (engine, "force transport state to Starting\n"); | |||
| } | |||
| VERBOSE (engine, "polling sync client %lu\n", client->control->id); | |||
| } | |||
| /* stop polling a specific slow-sync client | |||
| @@ -58,6 +62,8 @@ jack_sync_poll_exit (jack_engine_t *engine, jack_client_internal_t *client) | |||
| client->control->sync_poll = 0; | |||
| client->control->sync_new = 0; | |||
| engine->control->sync_remain--; | |||
| VERBOSE (engine, "sync poll interrupted for client %lu\n", | |||
| client->control->id); | |||
| } | |||
| client->control->is_slowsync = 0; | |||
| engine->control->sync_clients--; | |||
| @@ -84,6 +90,9 @@ jack_sync_poll_stop (jack_engine_t *engine) | |||
| //JOQ: check invariant for debugging... | |||
| assert (poll_count == engine->control->sync_remain); | |||
| VERBOSE (engine, | |||
| "sync poll halted with %ld clients and %llu usecs remaining\n", | |||
| engine->control->sync_remain, engine->control->sync_time_left); | |||
| engine->control->sync_remain = 0; | |||
| engine->control->sync_time_left = 0; | |||
| } | |||
| @@ -110,6 +119,9 @@ jack_sync_poll_start (jack_engine_t *engine) | |||
| assert (sync_count == engine->control->sync_clients); | |||
| engine->control->sync_remain = engine->control->sync_clients; | |||
| engine->control->sync_time_left = engine->control->sync_timeout; | |||
| VERBOSE (engine, "transport Starting, sync poll of %ld clients " | |||
| "for %llu usecs\n", engine->control->sync_remain, | |||
| engine->control->sync_time_left); | |||
| } | |||
| /* check for sync timeout */ | |||
| @@ -124,12 +136,13 @@ jack_sync_timeout (jack_engine_t *engine) | |||
| /* compare carefully, jack_time_t is unsigned */ | |||
| if (ectl->sync_time_left > buf_usecs) { | |||
| ectl->sync_time_left -= buf_usecs; | |||
| return 0; /* continue */ | |||
| return FALSE; | |||
| } | |||
| /* timed out */ | |||
| VERBOSE (engine, "transport sync timeout\n"); | |||
| ectl->sync_time_left = 0; | |||
| return 1; | |||
| return TRUE; | |||
| } | |||
| /**************** subroutines used by engine.c ****************/ | |||
| @@ -212,10 +225,12 @@ jack_transport_init (jack_engine_t *engine) | |||
| engine->timebase_client = NULL; | |||
| ectl->transport_state = JackTransportStopped; | |||
| ectl->transport_cmd = TransportCommandNone; | |||
| ectl->transport_cmd = TransportCommandStop; | |||
| ectl->previous_cmd = TransportCommandStop; | |||
| memset (&ectl->current_time, 0, sizeof(ectl->current_time)); | |||
| memset (&ectl->pending_time, 0, sizeof(ectl->pending_time)); | |||
| memset (&ectl->request_time, 0, sizeof(ectl->request_time)); | |||
| ectl->prev_request = 0; | |||
| ectl->new_pos = 0; | |||
| ectl->sync_remain = 0; | |||
| ectl->sync_clients = 0; | |||
| @@ -235,6 +250,7 @@ jack_transport_client_exit (jack_engine_t *engine, | |||
| engine->timebase_client = NULL; | |||
| engine->control->current_time.valid = 0; | |||
| engine->control->pending_time.valid = 0; | |||
| VERBOSE (engine, "timebase master exit\n"); | |||
| } | |||
| if (client->control->is_slowsync) | |||
| @@ -327,43 +343,44 @@ jack_transport_cycle_end (jack_engine_t *engine) | |||
| ectl->current_time.frame + ectl->buffer_size; | |||
| } | |||
| /* Handle latest asynchronous requests from the last cycle. | |||
| * | |||
| * This should ideally use an atomic swap, since commands can | |||
| * arrive at any time. There is a small timing window during | |||
| * which a request could be ignored inadvertently. Since | |||
| * another could have arrived in the previous moment and | |||
| * replaced it anyway, we won't bother with <asm/atomic.h>. | |||
| */ | |||
| /* Handle any new transport command from the last cycle. */ | |||
| cmd = ectl->transport_cmd; | |||
| // JOQ: may be able to close the window by eliminating this | |||
| // store, but watch out below... | |||
| ectl->transport_cmd = TransportCommandNone; | |||
| if (ectl->request_time.usecs) { | |||
| /* request_time could change during this copy */ | |||
| if (cmd != ectl->previous_cmd) { | |||
| ectl->previous_cmd = cmd; | |||
| VERBOSE (engine, "transport command: %s\n", | |||
| (cmd == TransportCommandStart? "START": "STOP")); | |||
| } else | |||
| cmd = TransportCommandNone; | |||
| /* See if a position request arrived during the last cycle. | |||
| * The request_time could change during the guarded copy. If | |||
| * so, we'll handle it now, but mistake it for a new request | |||
| * in the following cycle. That may cause an extra sync poll | |||
| * cycle, but should work. */ | |||
| if (ectl->request_time.unique_1 != ectl->prev_request) { | |||
| ectl->prev_request = ectl->request_time.unique_1; | |||
| jack_transport_copy_position(&ectl->request_time, | |||
| &ectl->pending_time); | |||
| ectl->request_time.usecs = 0; /* empty request buffer */ | |||
| ectl->new_pos = 1; | |||
| VERBOSE (engine, "new transport postition: %lu, id=0x%llx\n", | |||
| ectl->pending_time.frame, ectl->pending_time.unique_1); | |||
| } else | |||
| ectl->new_pos = 0; | |||
| /* Promote pending_time to current_time. Maintain the usecs | |||
| * and frame_rate values, clients may not set them. */ | |||
| ectl->pending_time.guard_usecs = | |||
| ectl->pending_time.usecs = ectl->current_time.usecs; | |||
| ectl->pending_time.usecs = ectl->current_time.usecs; | |||
| ectl->pending_time.frame_rate = ectl->current_time.frame_rate; | |||
| ectl->current_time = ectl->pending_time; | |||
| /* check sync results from previous cycle */ | |||
| if (ectl->transport_state == JackTransportStarting) { | |||
| if ((ectl->sync_remain == 0) || | |||
| (jack_sync_timeout(engine))) | |||
| (jack_sync_timeout(engine))) { | |||
| ectl->transport_state = JackTransportRolling; | |||
| VERBOSE (engine, "transport Rolling, %lld usec" | |||
| " left for poll\n", ectl->sync_time_left); | |||
| } | |||
| } | |||
| /* state transition switch */ | |||
| @@ -376,21 +393,38 @@ jack_transport_cycle_end (jack_engine_t *engine) | |||
| jack_sync_poll_start(engine); | |||
| } else { | |||
| ectl->transport_state = JackTransportRolling; | |||
| VERBOSE (engine, "transport Rolling\n"); | |||
| } | |||
| } | |||
| break; | |||
| case JackTransportStarting: | |||
| case JackTransportRolling: | |||
| if (cmd == TransportCommandStop) { | |||
| ectl->transport_state = JackTransportStopped; | |||
| jack_sync_poll_stop(engine); | |||
| VERBOSE (engine, "transport Stopped\n"); | |||
| if (ectl->sync_remain) | |||
| jack_sync_poll_stop(engine); | |||
| } else if (ectl->new_pos) { | |||
| if (ectl->sync_clients) { | |||
| ectl->transport_state = JackTransportStarting; | |||
| jack_sync_poll_start(engine); | |||
| } else { | |||
| ectl->transport_state = JackTransportRolling; | |||
| VERBOSE (engine, "transport Rolling\n"); | |||
| } | |||
| } | |||
| break; | |||
| case JackTransportRolling: | |||
| if (cmd == TransportCommandStop) { | |||
| ectl->transport_state = JackTransportStopped; | |||
| VERBOSE (engine, "transport Stopped\n"); | |||
| if (ectl->sync_remain) | |||
| jack_sync_poll_stop(engine); | |||
| } else if (ectl->new_pos) { | |||
| if (ectl->sync_clients) { | |||
| ectl->transport_state = JackTransportStarting; | |||
| jack_sync_poll_start(engine); | |||
| } | |||
| } | |||
| break; | |||
| @@ -406,8 +440,7 @@ jack_transport_cycle_end (jack_engine_t *engine) | |||
| void | |||
| jack_transport_cycle_start (jack_engine_t *engine, jack_time_t time) | |||
| { | |||
| engine->control->current_time.guard_usecs = | |||
| engine->control->current_time.usecs = time; | |||
| engine->control->current_time.usecs = time; | |||
| } | |||
| /* on SetSyncTimeout request */ | |||
| @@ -416,5 +449,6 @@ jack_transport_set_sync_timeout (jack_engine_t *engine, | |||
| jack_time_t usecs) | |||
| { | |||
| engine->control->sync_timeout = usecs; | |||
| VERBOSE (engine, "new sync timeout: %llu usecs\n", usecs); | |||
| return 0; | |||
| } | |||
| @@ -23,12 +23,52 @@ | |||
| #include <config.h> | |||
| #include <errno.h> | |||
| #include <math.h> | |||
| #include <stdio.h> | |||
| #include <jack/internal.h> | |||
| #include "local.h" | |||
| /********************* Internal functions *********************/ | |||
| /* generate a unique non-zero ID, different for each call */ | |||
| jack_unique_t | |||
| jack_generate_unique_id (jack_control_t *ectl) | |||
| { | |||
| /* The jack_unique_t is an opaque type. Its structure is only | |||
| * known here. We use the least significant word of the CPU | |||
| * cycle counter. For SMP, I would like to include the | |||
| * current thread ID, since only one thread runs on a CPU at a | |||
| * time. | |||
| * | |||
| * But, pthread_self() is broken on my Debian GNU/Linux | |||
| * system, it always seems to return 16384. That's useless. | |||
| * So, I'm using the process ID instead. With Linux 2.4 and | |||
| * Linuxthreads there is an LWP for each thread so this works, | |||
| * but is not portable. :-( | |||
| */ | |||
| volatile union { | |||
| jack_unique_t unique; | |||
| struct { | |||
| pid_t pid; | |||
| unsigned long cycle; | |||
| } field; | |||
| } id; | |||
| id.field.cycle = (unsigned long) get_cycles(); | |||
| id.field.pid = getpid(); | |||
| // JOQ: Alternatively, we could keep a sequence number in | |||
| // shared memory, using <asm/atomic.h> to increment it. I | |||
| // really like the simplicity of that approach. But I hate | |||
| // forcing JACK to either depend on that pesky header file, or | |||
| // maintain its own like ardour does. | |||
| // fprintf (stderr, "unique ID 0x%llx, process %d, cycle 0x%lx\n", | |||
| // id.unique, id.field.pid, id.field.cycle); | |||
| return id.unique; | |||
| } | |||
| static inline void | |||
| jack_read_frame_time (const jack_client_t *client, jack_frame_timer_t *copy) | |||
| { | |||
| @@ -56,7 +96,7 @@ void | |||
| jack_transport_copy_position (jack_position_t *from, jack_position_t *to) | |||
| { | |||
| int tries = 0; | |||
| long timeout = 1000000; | |||
| long timeout = 1000; | |||
| do { | |||
| /* throttle the busy wait if we don't get the answer | |||
| @@ -67,30 +107,24 @@ jack_transport_copy_position (jack_position_t *from, jack_position_t *to) | |||
| /* debug code to avoid system hangs... */ | |||
| if (--timeout == 0) { | |||
| jack_error("infinte loop copying position"); | |||
| jack_error("hung in loop copying position"); | |||
| abort(); | |||
| } | |||
| } | |||
| *to = *from; | |||
| tries++; | |||
| } while (to->usecs != to->guard_usecs); | |||
| } while (to->unique_1 != to->unique_2); | |||
| } | |||
| static inline int | |||
| jack_transport_request_new_pos (jack_client_t *client, jack_position_t *pos) | |||
| { | |||
| jack_control_t *eng = client->engine; | |||
| // JOQ: I don't think using guard_usecs is good enough. On | |||
| // faster machines we could get another request in the same | |||
| // microsecond. Better to use something like get_cycles(). | |||
| // SMP further complicates the issue. It may require a CPU id | |||
| // in addition to the cycle counter for uniqueness. | |||
| jack_control_t *ectl = client->engine; | |||
| /* carefully copy requested postion into shared memory */ | |||
| pos->guard_usecs = pos->usecs = jack_get_microseconds(); | |||
| jack_transport_copy_position (pos, &eng->request_time); | |||
| pos->unique_1 = pos->unique_2 = jack_generate_unique_id(ectl); | |||
| jack_transport_copy_position (pos, &ectl->request_time); | |||
| return 0; | |||
| } | |||
| @@ -102,20 +136,20 @@ void | |||
| jack_call_sync_client (jack_client_t *client) | |||
| { | |||
| jack_client_control_t *control = client->control; | |||
| jack_control_t *eng = client->engine; | |||
| jack_control_t *ectl = client->engine; | |||
| /* Make sure we are still slow-sync; is_slowsync is set in a | |||
| * critical section; sync_cb is not. */ | |||
| if ((eng->new_pos || control->sync_poll || control->sync_new) && | |||
| if ((ectl->new_pos || control->sync_poll || control->sync_new) && | |||
| control->is_slowsync) { | |||
| if (control->sync_cb (eng->transport_state, | |||
| &eng->current_time, | |||
| if (control->sync_cb (ectl->transport_state, | |||
| &ectl->current_time, | |||
| control->sync_arg)) { | |||
| if (control->sync_poll) { | |||
| control->sync_poll = 0; | |||
| eng->sync_remain--; | |||
| ectl->sync_remain--; | |||
| } | |||
| } | |||
| control->sync_new = 0; | |||
| @@ -126,8 +160,8 @@ void | |||
| jack_call_timebase_master (jack_client_t *client) | |||
| { | |||
| jack_client_control_t *control = client->control; | |||
| jack_control_t *eng = client->engine; | |||
| int new_pos = eng->new_pos; | |||
| jack_control_t *ectl = client->engine; | |||
| int new_pos = ectl->new_pos; | |||
| /* Make sure we're still the master. Test is_timebase, which | |||
| * is set in a critical section; timebase_cb is not. */ | |||
| @@ -138,12 +172,12 @@ jack_call_timebase_master (jack_client_t *client) | |||
| new_pos = 1; | |||
| } | |||
| if ((eng->transport_state == JackTransportRolling) || | |||
| if ((ectl->transport_state == JackTransportRolling) || | |||
| new_pos) { | |||
| control->timebase_cb (eng->transport_state, | |||
| control->timebase_cb (ectl->transport_state, | |||
| control->nframes, | |||
| &eng->pending_time, | |||
| &ectl->pending_time, | |||
| new_pos, | |||
| control->timebase_arg); | |||
| } | |||
| @@ -164,10 +198,10 @@ jack_nframes_t | |||
| jack_frames_since_cycle_start (const jack_client_t *client) | |||
| { | |||
| float usecs; | |||
| jack_control_t *eng = client->engine; | |||
| jack_control_t *ectl = client->engine; | |||
| usecs = jack_get_microseconds() - eng->current_time.usecs; | |||
| return (jack_nframes_t) floor ((((float) eng->current_time.frame_rate) | |||
| usecs = jack_get_microseconds() - ectl->current_time.usecs; | |||
| return (jack_nframes_t) floor ((((float) ectl->current_time.frame_rate) | |||
| / 1000000.0f) * usecs); | |||
| } | |||
| @@ -177,13 +211,13 @@ jack_frame_time (const jack_client_t *client) | |||
| jack_frame_timer_t current; | |||
| float usecs; | |||
| jack_nframes_t elapsed; | |||
| jack_control_t *eng = client->engine; | |||
| jack_control_t *ectl = client->engine; | |||
| jack_read_frame_time (client, ¤t); | |||
| usecs = jack_get_microseconds() - current.stamp; | |||
| elapsed = (jack_nframes_t) | |||
| floor ((((float) eng->current_time.frame_rate) | |||
| floor ((((float) ectl->current_time.frame_rate) | |||
| / 1000000.0f) * usecs); | |||
| return current.frames + elapsed; | |||
| @@ -299,11 +333,11 @@ jack_transport_locate (jack_client_t *client, jack_nframes_t frame) | |||
| jack_transport_state_t | |||
| jack_transport_query (jack_client_t *client, jack_position_t *pos) | |||
| { | |||
| jack_control_t *eng = client->engine; | |||
| jack_control_t *ectl = client->engine; | |||
| /* the guarded copy makes this function work in any thread */ | |||
| jack_transport_copy_position (&eng->current_time, pos); | |||
| return eng->transport_state; | |||
| jack_transport_copy_position (&ectl->current_time, pos); | |||
| return ectl->transport_state; | |||
| } | |||
| int | |||
| @@ -352,30 +386,30 @@ void | |||
| jack_get_transport_info (jack_client_t *client, | |||
| jack_transport_info_t *info) | |||
| { | |||
| jack_control_t *eng = client->engine; | |||
| jack_control_t *ectl = client->engine; | |||
| /* check that this is the process thread */ | |||
| if (client->thread_id != pthread_self()) { | |||
| if (!pthread_equal(client->thread_id, pthread_self())) { | |||
| jack_error("Invalid thread for jack_get_transport_info()."); | |||
| abort(); /* kill this client */ | |||
| } | |||
| info->usecs = eng->current_time.usecs; | |||
| info->frame_rate = eng->current_time.frame_rate; | |||
| info->transport_state = eng->transport_state; | |||
| info->frame = eng->current_time.frame; | |||
| info->valid = (eng->current_time.valid | | |||
| info->usecs = ectl->current_time.usecs; | |||
| info->frame_rate = ectl->current_time.frame_rate; | |||
| info->transport_state = ectl->transport_state; | |||
| info->frame = ectl->current_time.frame; | |||
| info->valid = (ectl->current_time.valid | | |||
| JackTransportState | JackTransportPosition); | |||
| if (info->valid & JackTransportBBT) { | |||
| info->bar = eng->current_time.bar; | |||
| info->beat = eng->current_time.beat; | |||
| info->tick = eng->current_time.tick; | |||
| info->bar_start_tick = eng->current_time.bar_start_tick; | |||
| info->beats_per_bar = eng->current_time.beats_per_bar; | |||
| info->beat_type = eng->current_time.beat_type; | |||
| info->ticks_per_beat = eng->current_time.ticks_per_beat; | |||
| info->beats_per_minute = eng->current_time.beats_per_minute; | |||
| info->bar = ectl->current_time.bar; | |||
| info->beat = ectl->current_time.beat; | |||
| info->tick = ectl->current_time.tick; | |||
| info->bar_start_tick = ectl->current_time.bar_start_tick; | |||
| info->beats_per_bar = ectl->current_time.beats_per_bar; | |||
| info->beat_type = ectl->current_time.beat_type; | |||
| info->ticks_per_beat = ectl->current_time.ticks_per_beat; | |||
| info->beats_per_minute = ectl->current_time.beats_per_minute; | |||
| } | |||
| } | |||
| @@ -385,7 +419,7 @@ void | |||
| jack_set_transport_info (jack_client_t *client, | |||
| jack_transport_info_t *info) | |||
| { | |||
| jack_control_t *eng = client->engine; | |||
| jack_control_t *ectl = client->engine; | |||
| if (!client->control->is_timebase) { /* not timebase master? */ | |||
| if (first_error) | |||
| @@ -400,37 +434,37 @@ jack_set_transport_info (jack_client_t *client, | |||
| } | |||
| /* check that this is the process thread */ | |||
| if (client->thread_id != pthread_self()) { | |||
| if (!pthread_equal(client->thread_id, pthread_self())) { | |||
| jack_error ("Invalid thread for jack_set_transport_info()."); | |||
| abort(); /* kill this client */ | |||
| } | |||
| /* is there a new state? */ | |||
| if ((info->valid & JackTransportState) && | |||
| (info->transport_state != eng->transport_state)) { | |||
| (info->transport_state != ectl->transport_state)) { | |||
| if (info->transport_state == JackTransportStopped) | |||
| eng->transport_cmd = TransportCommandStop; | |||
| ectl->transport_cmd = TransportCommandStop; | |||
| else if (info->transport_state == JackTransportRolling) | |||
| eng->transport_cmd = TransportCommandStart; | |||
| ectl->transport_cmd = TransportCommandStart; | |||
| /* silently ignore anything else */ | |||
| } | |||
| if (info->valid & JackTransportPosition) | |||
| eng->pending_time.frame = info->frame; | |||
| ectl->pending_time.frame = info->frame; | |||
| else | |||
| eng->pending_time.frame = eng->current_time.frame; | |||
| ectl->pending_time.frame = ectl->current_time.frame; | |||
| eng->pending_time.valid = (info->valid & JACK_POSITION_MASK); | |||
| ectl->pending_time.valid = (info->valid & JACK_POSITION_MASK); | |||
| if (info->valid & JackTransportBBT) { | |||
| eng->pending_time.bar = info->bar; | |||
| eng->pending_time.beat = info->beat; | |||
| eng->pending_time.tick = info->tick; | |||
| eng->pending_time.bar_start_tick = info->bar_start_tick; | |||
| eng->pending_time.beats_per_bar = info->beats_per_bar; | |||
| eng->pending_time.beat_type = info->beat_type; | |||
| eng->pending_time.ticks_per_beat = info->ticks_per_beat; | |||
| eng->pending_time.beats_per_minute = info->beats_per_minute; | |||
| ectl->pending_time.bar = info->bar; | |||
| ectl->pending_time.beat = info->beat; | |||
| ectl->pending_time.tick = info->tick; | |||
| ectl->pending_time.bar_start_tick = info->bar_start_tick; | |||
| ectl->pending_time.beats_per_bar = info->beats_per_bar; | |||
| ectl->pending_time.beat_type = info->beat_type; | |||
| ectl->pending_time.ticks_per_beat = info->ticks_per_beat; | |||
| ectl->pending_time.beats_per_minute = info->beats_per_minute; | |||
| } | |||
| } | |||