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; | |||