diff --git a/configure.in b/configure.in index c5088c5..606a128 100644 --- a/configure.in +++ b/configure.in @@ -14,7 +14,7 @@ dnl changes are made dnl --- JACK_MAJOR_VERSION=0 JACK_MINOR_VERSION=76 -JACK_MICRO_VERSION=5 +JACK_MICRO_VERSION=6 dnl --- diff --git a/doc/transport.dox b/doc/transport.dox index 9306771..737fb31 100644 --- a/doc/transport.dox +++ b/doc/transport.dox @@ -145,7 +145,7 @@ to NULL. @code int jack_set_sync_timeout (jack_client_t *client, - jack_nframes_t timeout); + jack_time_t usecs); @endcode There must be a @a timeout to prevent unresponsive slow-sync clients diff --git a/jack/internal.h b/jack/internal.h index a40f899..fe45771 100644 --- a/jack/internal.h +++ b/jack/internal.h @@ -75,7 +75,7 @@ typedef struct { typedef enum { TransportCommandNone = 0, - TransportCommandPlay = 1, + TransportCommandStart = 1, TransportCommandStop = 2, } transport_command_t; @@ -102,9 +102,10 @@ typedef struct { jack_position_t pending_time; /* position for next cycle */ jack_position_t request_time; /* latest requested position */ int new_pos; /* new position this cycle */ - unsigned long sync_remain; /* remaining sync count */ - unsigned long sync_cycle; /* number ready this cycle */ - unsigned long sync_clients; /* number of slow-sync clients */ + unsigned long sync_clients; /* number of is_slowsync clients */ + unsigned long sync_remain; /* number of them with sync_poll */ + jack_time_t sync_timeout; + jack_time_t sync_time_left; jack_frame_timer_t frame_timer; int internal; jack_nframes_t frames_at_cycle_start; @@ -177,8 +178,10 @@ typedef volatile struct { volatile char active : 1; /* w: engine r: engine and client */ volatile char dead : 1; /* r/w: engine */ volatile char timed_out : 1; /* r/w: engine */ - volatile char sync_ready : 1; /* w: engine and client, r: engine */ volatile char is_timebase : 1; /* w: engine, r: engine and client */ + volatile char is_slowsync : 1; /* w: engine, r: engine and client */ + volatile char sync_poll : 1; /* w: engine and client, r: engine */ + volatile char sync_new : 1; /* w: engine and client, r: engine */ volatile pid_t pid; /* w: client r: engine; client pid */ volatile unsigned long long signalled_at; volatile unsigned long long awake_at; @@ -281,6 +284,8 @@ typedef enum { GetPortNConnections = 11, ResetTimeBaseClient = 12, SetSyncClient = 13, + ResetSyncClient = 14, + SetSyncTimeout = 15, } RequestType; struct _jack_request { @@ -309,6 +314,7 @@ struct _jack_request { } timebase; jack_client_id_t client_id; jack_nframes_t nframes; + jack_time_t timeout; } x; int status; }; diff --git a/jack/transport.h b/jack/transport.h index 9dec10c..7c67081 100644 --- a/jack/transport.h +++ b/jack/transport.h @@ -50,6 +50,7 @@ typedef enum { } jack_position_bits_t; +#define JACK_POSITION_MASK (JackPositionBBT) /**< all valid position bits */ #define EXTENDED_TIME_INFO /** @@ -105,17 +106,18 @@ typedef struct { int jack_release_timebase (jack_client_t *client); /** - * Prototype for the sync callback defined by slow-sync clients. - * Called just before process() in the same thread when some client - * has requested a new position. This realtime function must not - * wait. + * Prototype for the @a sync_callback defined by slow-sync clients. + * Called just before process() in the same thread on the first cycle + * after being registered, whenever some client requests a new + * position, or when the transport enters the ::JackTransportStarting + * state. This realtime function must not wait. * * The transport @a state will be: * - * - ::JackTransportStopped some client just requested a new position; - * - ::JackTransportStarting the transport is waiting to start; - * - ::JackTransportRolling the timeout has expired, and the position - * is now a moving target. + * - ::JackTransportStopped when a new position is requested; + * - ::JackTransportStarting when the transport is waiting to start; + * - ::JackTransportRolling when the timeout has expired, and the + * position is now a moving target. * * @param state current transport state. * @param pos new transport position. @@ -131,18 +133,11 @@ typedef int (*JackSyncCallback)(jack_transport_state_t state, * Register (or unregister) as a slow-sync client, one that cannot * respond immediately to transport position changes. * - * When there are slow-sync clients and the JACK transport starts - * playing in a new postion, it first enters the - * ::JackTransportStarting state. The @a sync_callback function for - * each slow-sync client will be invoked in the JACK process thread - * while the transport is starting. If it has not already done so, - * the client needs to initiate a seek to reach the starting position. - * The @a sync_callback returns false until the seek completes and the - * client is ready to play. When all slow-sync clients are ready, the - * state changes to ::JackTransportRolling. - * - * Clients that don't set a @a sync_callback are assumed to be ready - * immediately, any time the transport wants to start. + * The @a sync_callback will be invoked on the first process cycle + * after its registration is complete. After that, it runs according + * to the ::JackSyncCallback rules. Clients that don't set a @a + * sync_callback are assumed to be ready immediately any time the + * transport wants to start. * * @param client the JACK client structure. * @param sync_callback is a realtime function that returns true when @@ -162,24 +157,24 @@ int jack_set_sync_callback (jack_client_t *client, * This timeout prevents unresponsive slow-sync clients from * completely halting the transport mechanism. The default is two * seconds. When the timeout expires, the transport starts rolling, - * even if some slow-sync clients are still unready. The - * sync_callbacksof these clients continue being invoked, giving them + * even if some slow-sync clients are still unready. The @a + * sync_callbacks of these clients continue being invoked, giving them * a chance to catch up. * * @see jack_set_sync_callback * * @param client the JACK client structure. - * @param timeout is delay (in frames) before the timeout expires. + * @param timeout is delay (in microseconds) before the timeout expires. * * @return 0 on success, otherwise a non-zero error code. */ int jack_set_sync_timeout (jack_client_t *client, - jack_nframes_t timeout); + jack_time_t timeout); /** - * Prototype for the timebase master callback used to continuously - * update position information. Without extended information, there - * is no need for this function, JACK can count frames automatically. + * Prototype for the @a timebase_callback used to continuously update + * position information. Without extended information, there is no + * need for this function, JACK will count frames automatically. * * This function is called immediately after process() in the same * thread whenever the transport is rolling, or when any client has @@ -252,7 +247,7 @@ int jack_set_timebase_callback (jack_client_t *client, * @param client the JACK client structure. * @param frame frame number of new transport position. * - * @return 0 if valid request, otherwise a non-zero error code. + * @return 0 if valid request, non-zero otherwise. */ int jack_transport_locate (jack_client_t *client, jack_nframes_t frame); @@ -288,7 +283,7 @@ jack_transport_state_t jack_transport_query (jack_client_t *client, * @param client the JACK client structure. * @param pos requested new transport position. * - * @return 0 if valid request, otherwise a non-zero error code. + * @return 0 if valid request, EINVAL if position structure rejected. */ int jack_transport_reposition (jack_client_t *client, jack_position_t *pos); diff --git a/jackd/engine.c b/jackd/engine.c index 034c5f0..8bb9cea 100644 --- a/jackd/engine.c +++ b/jackd/engine.c @@ -1322,8 +1322,7 @@ jack_client_deactivate (jack_engine_t *engine, jack_client_id_t id) JSList *portnode; jack_port_internal_t *port; - if (client == engine->timebase_client) - jack_timebase_exit (engine); + jack_transport_client_exit (engine, client); for (portnode = client->ports; portnode; portnode = jack_slist_next (portnode)) { port = (jack_port_internal_t *) portnode->data; @@ -1446,7 +1445,20 @@ do_request (jack_engine_t *engine, jack_request_t *req, int *reply_fd) break; case SetSyncClient: - req->status = jack_set_sync_client (engine, req->x.client_id); + req->status = + jack_transport_client_set_sync (engine, + req->x.client_id); + break; + + case ResetSyncClient: + req->status = + jack_transport_client_reset_sync (engine, + req->x.client_id); + break; + + case SetSyncTimeout: + req->status = jack_transport_set_sync_timeout (engine, + req->x.timeout); break; #ifdef USE_CAPABILITIES @@ -1782,7 +1794,7 @@ jack_engine_new (int realtime, int rtpriority, int verbose, int client_timeout) engine->control->buffer_size = 0; jack_set_sample_rate (engine, 0); - jack_timebase_init (engine); + jack_transport_init (engine); engine->control->internal = 0; engine->control->has_capabilities = 0; @@ -2239,6 +2251,8 @@ jack_client_internal_new (jack_engine_t *engine, int fd, jack_client_connect_req client->control->port_register_arg = NULL; client->control->graph_order = NULL; client->control->graph_order_arg = NULL; + + jack_transport_client_new (client); #if defined(__APPLE__) && defined(__POWERPC__) /* specific ressources for server/client real-time thread communication */ @@ -2287,9 +2301,7 @@ jack_zombify_client (jack_engine_t *engine, jack_client_internal_t *client) client->control->dead = TRUE; - if (client == engine->timebase_client) - jack_timebase_exit (engine); - + jack_transport_client_exit (engine, client); jack_client_disconnect (engine, client); jack_client_do_deactivate (engine, client, FALSE); } diff --git a/jackd/transengine.c b/jackd/transengine.c index 4e55edd..c8b34bd 100644 --- a/jackd/transengine.c +++ b/jackd/transengine.c @@ -5,9 +5,9 @@ Copyright (C) 2003 Jack O'Quin This program is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public License - as published by the Free Software Foundation; either version 2.1 - of the License, or (at your option) any later version. + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -21,69 +21,123 @@ #include #include +#include #include #include #include "transengine.h" -/*********************** driver callbacks ***********************/ +/********************** internal functions **********************/ -int -jack_set_sample_rate (jack_engine_t *engine, jack_nframes_t nframes) +/* stop polling a specific slow-sync client + * + * precondition: caller holds the graph lock. */ +static inline void +jack_sync_poll_exit(jack_engine_t *engine, jack_client_internal_t *client) { - jack_control_t *ectl = engine->control; - - ectl->current_time.frame_rate = nframes; - ectl->pending_time.frame_rate = nframes; - return 0; + if (client->control->sync_poll) { + client->control->sync_poll = 0; + client->control->sync_new = 0; + engine->control->sync_remain--; + } + client->control->is_slowsync = 0; + engine->control->sync_clients--; } -void -jack_transport_cycle_start (jack_engine_t *engine, jack_time_t time) +/* stop polling all the slow-sync clients + * + * precondition: caller holds the graph lock. */ +static void +jack_sync_poll_stop(jack_engine_t *engine) { - engine->control->current_time.guard_usecs = - engine->control->current_time.usecs = time; -} + JSList *node; + long poll_count = 0; /* count sync_poll clients */ + for (node = engine->clients; node; node = jack_slist_next (node)) { + jack_client_internal_t *client = + (jack_client_internal_t *) node->data; + if (client->control->is_slowsync && + client->control->sync_poll) { + client->control->sync_poll = 0; + poll_count++; + } + } -/********************* RPC request handlers *********************/ + //JOQ: check invariant for debugging... + assert (poll_count == engine->control->sync_remain); + engine->control->sync_remain = 0; + engine->control->sync_time_left = 0; +} -/* for SetSyncClient */ -int -jack_set_sync_client (jack_engine_t *engine, jack_client_id_t client) +/* start polling all the slow-sync clients + * + * precondition: caller holds the graph lock. */ +static void +jack_sync_poll_start(jack_engine_t *engine) { - int ret; - jack_client_internal_t *clintl; + JSList *node; + long sync_count = 0; /* count slow-sync clients */ - jack_lock_graph (engine); + for (node = engine->clients; node; node = jack_slist_next (node)) { + jack_client_internal_t *client = + (jack_client_internal_t *) node->data; + if (client->control->is_slowsync) { + client->control->sync_poll = 1; + sync_count++; + } + } - clintl = jack_client_internal_by_id (engine, client); + //JOQ: check invariant for debugging... + assert (sync_count == engine->control->sync_clients); + engine->control->sync_remain = engine->control->sync_clients; + engine->control->sync_time_left = engine->control->sync_timeout; +} - if (clintl) { - clintl->control->sync_ready = 0; - engine->control->sync_clients++; - ret = 0; - } else - ret = EINVAL; +/* check for sync timeout */ +static inline int +jack_sync_timeout(jack_engine_t *engine) +{ + jack_control_t *ectl = engine->control; + jack_time_t buf_usecs = + ((ectl->buffer_size * (jack_time_t) 1000000000) / + ectl->current_time.frame_rate); + + /* compare carefully, jack_time_t is unsigned */ + if (ectl->sync_time_left <= buf_usecs) { + ectl->sync_time_left = 0; + return 1; /* timed out */ + } else { + ectl->sync_time_left -= buf_usecs; + return 0; /* continue */ + } +} - jack_unlock_graph (engine); +/**************** subroutines used by engine.c ****************/ - return ret; +/* driver callback */ +int +jack_set_sample_rate (jack_engine_t *engine, jack_nframes_t nframes) +{ + jack_control_t *ectl = engine->control; + + ectl->current_time.frame_rate = nframes; + ectl->pending_time.frame_rate = nframes; + return 0; } -/* for ResetTimeBaseClient */ +/* on ResetTimeBaseClient request */ int -jack_timebase_reset (jack_engine_t *engine, jack_client_id_t client) +jack_timebase_reset (jack_engine_t *engine, jack_client_id_t client_id) { int ret; - struct _jack_client_internal *clint; + struct _jack_client_internal *client; jack_control_t *ectl = engine->control; jack_lock_graph (engine); - clint = jack_client_internal_by_id (engine, client); - if (clint && (clint == engine->timebase_client)) { - clint->control->is_timebase = 0; + client = jack_client_internal_by_id (engine, client_id); + if (client && (client == engine->timebase_client)) { + client->control->is_timebase = 0; engine->timebase_client = NULL; ectl->pending_time.valid = 0; ret = 0; @@ -95,32 +149,32 @@ jack_timebase_reset (jack_engine_t *engine, jack_client_id_t client) return ret; } -/* for SetTimeBaseClient */ +/* on SetTimeBaseClient request */ int jack_timebase_set (jack_engine_t *engine, - jack_client_id_t client, int conditional) + jack_client_id_t client_id, int conditional) { int ret = 0; - struct _jack_client_internal *clint; + struct _jack_client_internal *client; jack_lock_graph (engine); - clint = jack_client_internal_by_id (engine, client); + client = jack_client_internal_by_id (engine, client_id); if (conditional && engine->timebase_client) { /* see if timebase master is someone else */ - if (clint && (clint != engine->timebase_client)) + if (client && (client != engine->timebase_client)) ret = EBUSY; } else { - if (clint) { + if (client) { if (engine->timebase_client) engine->timebase_client-> control->is_timebase = 0; - engine->timebase_client = clint; - clint->control->is_timebase = 1; + engine->timebase_client = client; + client->control->is_timebase = 1; } else ret = EINVAL; } @@ -130,68 +184,129 @@ jack_timebase_set (jack_engine_t *engine, return ret; } - -/******************** engine.c subroutines ********************/ - -/* start polling slow-sync clients */ +/* for engine initialization */ void -jack_start_sync_poll(jack_engine_t *engine) +jack_transport_init (jack_engine_t *engine) { - /* precondition: caller holds the graph lock. */ jack_control_t *ectl = engine->control; - JSList *node; - long sync_count = 0; /* number of slow-sync clients */ - for (node = engine->clients; node; node = jack_slist_next (node)) { - jack_client_internal_t *clintl = - (jack_client_internal_t *) node->data; - if (clintl->control->sync_cb) { - clintl->control->sync_ready = 0; - sync_count++; - } + engine->timebase_client = NULL; + ectl->transport_state = JackTransportStopped; + ectl->transport_cmd = TransportCommandNone; + 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->new_pos = 0; + ectl->sync_remain = 0; + ectl->sync_clients = 0; + ectl->sync_timeout = 2000000000; /* 2 second default */ + ectl->sync_time_left = 0; +} + +/* when any client exits the graph + * + * precondition: caller holds the graph lock */ +void +jack_transport_client_exit (jack_engine_t *engine, + jack_client_internal_t *client) +{ + if (client == engine->timebase_client) { + engine->timebase_client->control->is_timebase = 0; + engine->timebase_client = NULL; + engine->control->current_time.valid = 0; + engine->control->pending_time.valid = 0; } - ectl->sync_remain = ectl->sync_clients = sync_count; - ectl->sync_cycle = 0; + if (client->control->is_slowsync) + jack_sync_poll_exit(engine, client); } -/* when timebase master exits the graph */ -void -jack_timebase_exit (jack_engine_t *engine) +/* when a new client is being created */ +void +jack_transport_client_new (jack_client_internal_t *client) { - jack_control_t *ectl = engine->control; + client->control->is_timebase = 0; + client->control->is_slowsync = 0; + client->control->sync_poll = 0; + client->control->sync_new = 0; + client->control->sync_cb = NULL; + client->control->sync_arg = NULL; + client->control->timebase_cb = NULL; + client->control->timebase_arg = NULL; +} - engine->timebase_client->control->is_timebase = 0; - engine->timebase_client = NULL; - ectl->current_time.valid = 0; - ectl->pending_time.valid = 0; +/* on ResetSyncClient request */ +int +jack_transport_client_reset_sync (jack_engine_t *engine, + jack_client_id_t client_id) +{ + int ret; + jack_client_internal_t *client; + + jack_lock_graph (engine); + + client = jack_client_internal_by_id (engine, client_id); + + if (client && (client->control->is_slowsync)) { + jack_sync_poll_exit(engine, client); + ret = 0; + } else + ret = EINVAL; + + jack_unlock_graph (engine); + + return ret; } -/* engine initialization */ -void -jack_timebase_init (jack_engine_t *engine) +/* on SetSyncClient request */ +int +jack_transport_client_set_sync (jack_engine_t *engine, + jack_client_id_t client_id) { - jack_control_t *ectl = engine->control; + int ret; + jack_client_internal_t *client; - engine->timebase_client = NULL; - ectl->transport_state = JackTransportStopped; - ectl->transport_cmd = TransportCommandNone; - 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->new_pos = 0; - ectl->sync_remain = 0; - ectl->sync_cycle = 0; - ectl->sync_clients = 0; + // JOQ: I am assuming the process cycle is serialized with + // respect to this lock... + jack_lock_graph (engine); + + client = jack_client_internal_by_id (engine, client_id); + + if (client) { + if (!client->control->is_slowsync) { + + /* force poll of the new slow-sync client */ + client->control->is_slowsync = 1; + engine->control->sync_clients++; + + // JOQ: I don't like doing this here... + client->control->sync_poll = 1; + engine->control->sync_remain++; + engine->control->sync_time_left = + engine->control->sync_timeout; + if (engine->control->transport_state == + JackTransportRolling) + engine->control->transport_state = + JackTransportStarting; + } + client->control->sync_new = 1; + ret = 0; + } else + ret = EINVAL; + + jack_unlock_graph (engine); + + return ret; } -/* This runs at the end of every process cycle. It determines the - * transport parameters for the next cycle. +/* at the end of every process cycle + * + * Determines the transport parameters for the following cycle. + * precondition: caller holds the graph lock. */ void jack_transport_cycle_end (jack_engine_t *engine) { - /* precondition: caller holds the graph lock. */ jack_control_t *ectl = engine->control; transport_command_t cmd; /* latest transport command */ @@ -211,6 +326,8 @@ jack_transport_cycle_end (jack_engine_t *engine) * replaced it anyway, we won't bother with . */ 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) { @@ -229,25 +346,27 @@ jack_transport_cycle_end (jack_engine_t *engine) ectl->pending_time.frame_rate = ectl->current_time.frame_rate; ectl->current_time = ectl->pending_time; - /* accumulate sync results from previous cycle */ - if (ectl->sync_remain) { - ectl->sync_remain -= ectl->sync_cycle; - if ((ectl->sync_remain == 0) && - (ectl->transport_state == JackTransportStarting)) + /* check sync results from previous cycle */ + if (ectl->transport_state == JackTransportStarting) { + if ((ectl->sync_remain == 0) || + (jack_sync_timeout(engine))) ectl->transport_state = JackTransportRolling; - ectl->sync_cycle = 0; + + + } /* state transition switch */ switch (ectl->transport_state) { case JackTransportStopped: - if (cmd == TransportCommandPlay) { + if (cmd == TransportCommandStart) { if (ectl->sync_clients) { ectl->transport_state = JackTransportStarting; - jack_start_sync_poll(engine); - } else + jack_sync_poll_start(engine); + } else { ectl->transport_state = JackTransportRolling; + } } break; @@ -255,14 +374,14 @@ jack_transport_cycle_end (jack_engine_t *engine) case JackTransportRolling: if (cmd == TransportCommandStop) { ectl->transport_state = JackTransportStopped; - ectl->sync_remain = 0; /* halt polling */ + jack_sync_poll_stop(engine); } else if (ectl->new_pos) { if (ectl->sync_clients) { ectl->transport_state = JackTransportStarting; - jack_start_sync_poll(engine); - } - else + jack_sync_poll_start(engine); + } else { ectl->transport_state = JackTransportRolling; + } } break; @@ -272,3 +391,20 @@ jack_transport_cycle_end (jack_engine_t *engine) } return; } + +/* driver callback at start of cycle */ +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; +} + +/* on SetSyncTimeout request */ +int +jack_transport_set_sync_timeout (jack_engine_t *engine, + jack_time_t usecs) +{ + engine->control->sync_timeout = usecs; + return 0; +} diff --git a/jackd/transengine.h b/jackd/transengine.h index 0a0928d..c23263d 100644 --- a/jackd/transengine.h +++ b/jackd/transengine.h @@ -19,11 +19,19 @@ */ int jack_set_sample_rate (jack_engine_t *engine, jack_nframes_t nframes); -int jack_set_sync_client (jack_engine_t *engine, jack_client_id_t client); -void jack_timebase_exit (jack_engine_t *engine); -void jack_timebase_init (jack_engine_t *engine); -int jack_timebase_reset (jack_engine_t *engine, jack_client_id_t client); +int jack_timebase_reset (jack_engine_t *engine, + jack_client_id_t client_id); int jack_timebase_set (jack_engine_t *engine, - jack_client_id_t client, int conditional); -void jack_transport_cycle_start(jack_engine_t *engine, jack_time_t time); + jack_client_id_t client_id, int conditional); +void jack_transport_init (jack_engine_t *engine); +void jack_transport_client_exit (jack_engine_t *engine, + jack_client_internal_t *client); +void jack_transport_client_new (jack_client_internal_t *client); +int jack_transport_client_reset_sync (jack_engine_t *engine, + jack_client_id_t client_id); +int jack_transport_client_set_sync (jack_engine_t *engine, + jack_client_id_t client_id); void jack_transport_cycle_end (jack_engine_t *engine); +void jack_transport_cycle_start(jack_engine_t *engine, jack_time_t time); +int jack_transport_set_sync_timeout (jack_engine_t *engine, + jack_time_t usecs); diff --git a/libjack/transclient.c b/libjack/transclient.c index faaeae9..23974ea 100644 --- a/libjack/transclient.c +++ b/libjack/transclient.c @@ -82,7 +82,11 @@ jack_transport_request_new_pos (jack_client_t *client, jack_position_t *pos) { jack_control_t *eng = client->engine; - //JOQ: check validity of input + // 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. /* carefully copy requested postion into shared memory */ pos->guard_usecs = pos->usecs = jack_get_microseconds(); @@ -100,15 +104,21 @@ jack_call_sync_client (jack_client_t *client) jack_client_control_t *control = client->control; jack_control_t *eng = client->engine; - if (eng->new_pos || !control->sync_ready) { + /* 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) && + control->is_slowsync) { if (control->sync_cb (eng->transport_state, &eng->current_time, control->sync_arg)) { - control->sync_ready = 1; - eng->sync_cycle++; + if (control->sync_poll) { + control->sync_poll = 0; + eng->sync_remain--; + } } + control->sync_new = 0; } } @@ -119,7 +129,8 @@ jack_call_timebase_master (jack_client_t *client) jack_control_t *eng = client->engine; int new_pos = eng->new_pos; - /* make sure we're still the master */ + /* Make sure we're still the master. Test is_timebase, which + * is set in a critical section; timebase_cb is not. */ if (control->is_timebase) { if (client->new_timebase) { /* first callback? */ @@ -229,7 +240,10 @@ jack_set_sync_callback (jack_client_t *client, jack_request_t req; int rc; - req.type = SetSyncClient; + if (sync_callback) + req.type = SetSyncClient; + else + req.type = ResetSyncClient; req.x.client_id = ctl->id; rc = jack_client_deliver_request (client, &req); @@ -241,9 +255,14 @@ jack_set_sync_callback (jack_client_t *client, } int -jack_set_sync_timeout (jack_client_t *client, jack_nframes_t timeout) +jack_set_sync_timeout (jack_client_t *client, jack_time_t usecs) { - return ENOSYS; /* this is a stub */ + jack_request_t req; + + req.type = SetSyncTimeout; + req.x.timeout = usecs; + + return jack_client_deliver_request (client, &req); } int @@ -290,16 +309,20 @@ jack_transport_query (jack_client_t *client, jack_position_t *pos) int jack_transport_reposition (jack_client_t *client, jack_position_t *pos) { - /* copy the input, so we don't modify the input argument */ + /* copy the input, to avoid modifying its contents */ jack_position_t tmp = *pos; + /* validate input */ + if (tmp.valid & ~JACK_POSITION_MASK) /* unknown field present? */ + return EINVAL; + return jack_transport_request_new_pos (client, &tmp); } void jack_transport_start (jack_client_t *client) { - client->engine->transport_cmd = TransportCommandPlay; + client->engine->transport_cmd = TransportCommandStart; } void @@ -344,7 +367,7 @@ jack_get_transport_info (jack_client_t *client, info->valid = (eng->current_time.valid | JackTransportState | JackTransportPosition); - if (info->valid & JackPositionBBT) { + if (info->valid & JackTransportBBT) { info->bar = eng->current_time.bar; info->beat = eng->current_time.beat; info->tick = eng->current_time.tick; @@ -366,11 +389,12 @@ jack_set_transport_info (jack_client_t *client, if (!client->control->is_timebase) { /* not timebase master? */ if (first_error) - jack_error ("Called jack_set_transport_info(), but not timebase master."); + jack_error ("Called jack_set_transport_info(), " + "but not timebase master."); first_error = 0; - /* JOQ: I would prefer to ignore this request, but if - * I do, it breaks ardour 0.9-beta2. So, let's allow + /* JOQ: I would prefer to ignore this request, but + * that would break ardour 0.9-beta2. So, let's allow * it for now. */ // return; } @@ -387,7 +411,7 @@ jack_set_transport_info (jack_client_t *client, if (info->transport_state == JackTransportStopped) eng->transport_cmd = TransportCommandStop; else if (info->transport_state == JackTransportRolling) - eng->transport_cmd = TransportCommandPlay; + eng->transport_cmd = TransportCommandStart; /* silently ignore anything else */ } @@ -396,7 +420,7 @@ jack_set_transport_info (jack_client_t *client, else eng->pending_time.frame = eng->current_time.frame; - eng->pending_time.valid = (info->valid & JackTransportBBT); + eng->pending_time.valid = (info->valid & JACK_POSITION_MASK); if (info->valid & JackTransportBBT) { eng->pending_time.bar = info->bar;