git-svn-id: svn+ssh://jackaudio.org/trunk/jack@451 0c269be4-1314-0410-8aa9-9f06e86f4224tags/0.109.0
| @@ -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 --- | |||
| @@ -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 | |||
| @@ -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; | |||
| }; | |||
| @@ -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); | |||
| @@ -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); | |||
| } | |||
| @@ -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 <config.h> | |||
| #include <errno.h> | |||
| #include <assert.h> | |||
| #include <jack/internal.h> | |||
| #include <jack/engine.h> | |||
| #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 <asm/atomic.h>. | |||
| */ | |||
| 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; | |||
| } | |||
| @@ -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); | |||
| @@ -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; | |||