git-svn-id: svn+ssh://jackaudio.org/trunk/jack@463 0c269be4-1314-0410-8aa9-9f06e86f4224tags/0.109.0
@@ -13,8 +13,8 @@ dnl micro version = incremented when implementation-only | |||||
dnl changes are made | dnl changes are made | ||||
dnl --- | dnl --- | ||||
JACK_MAJOR_VERSION=0 | JACK_MAJOR_VERSION=0 | ||||
JACK_MINOR_VERSION=78 | |||||
JACK_MICRO_VERSION=4 | |||||
JACK_MINOR_VERSION=79 | |||||
JACK_MICRO_VERSION=0 | |||||
dnl --- | dnl --- | ||||
dnl HOWTO: updating the jack protocal version | dnl HOWTO: updating the jack protocal version | ||||
@@ -24,7 +24,7 @@ dnl made to the way libjack communicates with jackd | |||||
dnl that would break applications linked with an older | dnl that would break applications linked with an older | ||||
dnl version of libjack. | dnl version of libjack. | ||||
dnl --- | dnl --- | ||||
JACK_PROTOCOL_VERSION=8 | |||||
JACK_PROTOCOL_VERSION=9 | |||||
dnl --- | dnl --- | ||||
dnl HOWTO: updating the libjack interface version | dnl HOWTO: updating the libjack interface version | ||||
@@ -59,20 +59,18 @@ But, old-style timebase masters will no longer control the transport. | |||||
@subsection timebase Timebase Master | @subsection timebase Timebase Master | ||||
The timebase master continuously updates extended position | The timebase master continuously updates extended position | ||||
information, counting beats, SMPTE frames, etc. Without this extended | |||||
information, counting beats, timecode, etc. Without this extended | |||||
information, there is no need for this function. There is at most one | information, there is no need for this function. There is at most one | ||||
master active at a time. If no client is registered as timebase | master active at a time. If no client is registered as timebase | ||||
master, the JACK engine will provide simple frame counting whenever | |||||
the transport is rolling. | |||||
master, frame numbers will be the only position information available. | |||||
The timebase master registers a callback that updates position | The timebase master registers a callback that updates position | ||||
information while the transport is rolling. This function is called | information while the transport is rolling. This function is called | ||||
immediately after the process callback in the same thread whenever the | immediately after the process callback in the same thread whenever the | ||||
transport is rolling, or when any client has set a new position in the | transport is rolling, or when any client has set a new position in the | ||||
previous cycle. The first cycle after jack_set_timebase_callback() is | previous cycle. The first cycle after jack_set_timebase_callback() is | ||||
also treated as a new position. Its output affects all of the | |||||
following process cycle, unless a new position request arrives during | |||||
the current cycle. | |||||
also treated as a new position. Its output affects the following | |||||
process cycle. | |||||
@code | @code | ||||
typedef int (*JackTimebaseCallback)(jack_transport_state_t state, | typedef int (*JackTimebaseCallback)(jack_transport_state_t state, | ||||
@@ -165,10 +163,10 @@ an opportunity to catch up. | |||||
These request a new transport position. They can be called at any | These request a new transport position. They can be called at any | ||||
time by any client. Even the timebase master must use them. If the | time by any client. Even the timebase master must use them. If the | ||||
request is valid, it goes into effect on the next process cycle. If | |||||
there are slow-sync clients and the transport is already rolling, it | |||||
will enter the ::JackTransportStarting state and begin invoking their | |||||
@a sync_callback until they find the new position. | |||||
request is valid, it goes into effect in two process cycles. If there | |||||
are slow-sync clients and the transport is already rolling, it will | |||||
enter the ::JackTransportStarting state and begin invoking their @a | |||||
sync_callbacks until ready. | |||||
@image html fsm.png "Transport State Transition Diagram" | @image html fsm.png "Transport State Transition Diagram" | ||||
@@ -238,12 +236,7 @@ to remain constant. | |||||
This design currently does not address several issues. This means they | This design currently does not address several issues. This means they | ||||
will probably not be included in JACK release 1.0. | will probably not be included in JACK release 1.0. | ||||
- variable speed -- does the frame_rate field in the transport | |||||
info structure have anything to do with this? | |||||
- variable speed | |||||
- reverse play | - reverse play | ||||
- SMPTE -- slave mode depends on variable speed and interacts | |||||
badly with slow-sync clients. These are deep issues. Until they | |||||
are addressed there's no reason to retain those two SMPTE fields in | |||||
the jack_position_t structure. | |||||
- looping -- unless we get feedback that this is really required | |||||
- looping | |||||
*/ | */ |
@@ -54,9 +54,6 @@ void timebase(jack_transport_state_t state, jack_nframes_t nframes, | |||||
long abs_tick; /* ticks since frame 0 */ | long abs_tick; /* ticks since frame 0 */ | ||||
long abs_beat; /* beats since frame 0 */ | long abs_beat; /* beats since frame 0 */ | ||||
if (state == JackTransportRolling) | |||||
pos->frame += nframes; | |||||
if (new_pos || time_reset) { | if (new_pos || time_reset) { | ||||
pos->valid |= JackPositionBBT; | pos->valid |= JackPositionBBT; | ||||
@@ -85,7 +82,7 @@ void timebase(jack_transport_state_t state, jack_nframes_t nframes, | |||||
} else { | } else { | ||||
/* Compute BBT info from previous period. */ | |||||
/* Compute BBT info based on previous period. */ | |||||
pos->tick += | pos->tick += | ||||
nframes * pos->ticks_per_beat * pos->beats_per_minute | nframes * pos->ticks_per_beat * pos->beats_per_minute | ||||
/ (pos->frame_rate * 60); | / (pos->frame_rate * 60); | ||||
@@ -103,8 +103,10 @@ typedef struct { | |||||
jack_position_t pending_time; /* position for next cycle */ | jack_position_t pending_time; /* position for next cycle */ | ||||
jack_position_t request_time; /* latest requested position */ | jack_position_t request_time; /* latest requested position */ | ||||
jack_unique_t prev_request; /* previous request unique ID */ | jack_unique_t prev_request; /* previous request unique ID */ | ||||
volatile int seq_number; /* unique ID sequence number */ | |||||
int new_pos; /* new position this cycle */ | |||||
volatile long seq_number; /* unique ID sequence number */ | |||||
char new_pos; /* new position this cycle */ | |||||
char pending_pos; /* new position request pending */ | |||||
jack_nframes_t pending_frame; /* pending frame number */ | |||||
unsigned long sync_clients; /* number of is_slowsync clients */ | unsigned long sync_clients; /* number of is_slowsync clients */ | ||||
unsigned long sync_remain; /* number of them with sync_poll */ | unsigned long sync_remain; /* number of them with sync_poll */ | ||||
jack_time_t sync_timeout; | jack_time_t sync_timeout; | ||||
@@ -60,12 +60,12 @@ typedef enum { | |||||
*/ | */ | ||||
typedef struct { | typedef struct { | ||||
/* these three cannot be set from clients: the server sets them */ | |||||
/* these four cannot be set from clients: the server sets them */ | |||||
jack_unique_t unique_1; /**< unique ID */ | jack_unique_t unique_1; /**< unique ID */ | ||||
jack_time_t usecs; /**< monotonic, free-rolling */ | jack_time_t usecs; /**< monotonic, free-rolling */ | ||||
jack_nframes_t frame_rate; /**< current frame rate (per second) */ | jack_nframes_t frame_rate; /**< current frame rate (per second) */ | ||||
jack_nframes_t frame; /**< frame number, always present */ | jack_nframes_t frame; /**< frame number, always present */ | ||||
jack_position_bits_t valid; /**< which other fields are valid */ | jack_position_bits_t valid; /**< which other fields are valid */ | ||||
/* JackPositionBBT fields: */ | /* JackPositionBBT fields: */ | ||||
@@ -125,7 +125,7 @@ int jack_release_timebase (jack_client_t *client); | |||||
* @param pos new transport position. | * @param pos new transport position. | ||||
* @param arg the argument supplied by jack_set_sync_callback(). | * @param arg the argument supplied by jack_set_sync_callback(). | ||||
* | * | ||||
* @return true (non-zero) when ready to roll. | |||||
* @return TRUE (non-zero) when ready to roll. | |||||
*/ | */ | ||||
typedef int (*JackSyncCallback)(jack_transport_state_t state, | typedef int (*JackSyncCallback)(jack_transport_state_t state, | ||||
jack_position_t *pos, | jack_position_t *pos, | ||||
@@ -142,7 +142,7 @@ typedef int (*JackSyncCallback)(jack_transport_state_t state, | |||||
* transport wants to start. | * transport wants to start. | ||||
* | * | ||||
* @param client the JACK client structure. | * @param client the JACK client structure. | ||||
* @param sync_callback is a realtime function that returns true when | |||||
* @param sync_callback is a realtime function that returns TRUE when | |||||
* the client is ready. Setting @a sync_callback to NULL declares that | * the client is ready. Setting @a sync_callback to NULL declares that | ||||
* this client no longer requires slow-sync processing. | * this client no longer requires slow-sync processing. | ||||
* @param arg an argument for the @a sync_callback function. | * @param arg an argument for the @a sync_callback function. | ||||
@@ -174,29 +174,31 @@ int jack_set_sync_timeout (jack_client_t *client, | |||||
jack_time_t timeout); | jack_time_t timeout); | ||||
/** | /** | ||||
* 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. | |||||
* Prototype for the @a timebase_callback used to provide extended | |||||
* position information. | |||||
* | * | ||||
* This function is called immediately after process() in the same | * This function is called immediately after process() in the same | ||||
* thread whenever the transport is rolling, or when any client has | * thread whenever the transport is rolling, or when any client has | ||||
* set a new position in the previous cycle. The first cycle after | |||||
* jack_set_timebase_callback() is also treated as a new position. | |||||
* This realtime function must not wait. Its output affects all of | |||||
* the following process cycle, unless a new position request arrives | |||||
* during the current cycle. | |||||
* requested a new position in the previous cycle. The first cycle | |||||
* after jack_set_timebase_callback() is also treated as a new | |||||
* position. This realtime function must not wait. Its output | |||||
* affects all of the following process cycle. | |||||
* | * | ||||
* The timebase master should not use its @a pos argument to set a | |||||
* discontinuous position. Use jack_transport_reposition() or | |||||
* jack_transport_locate(), instead. | |||||
* The timebase master may not use its @a pos argument to set @a | |||||
* pos->frame. To change position, use jack_transport_reposition() or | |||||
* jack_transport_locate(). These functions are realtime-safe, the @a | |||||
* timebase_callback can call them directly. | |||||
* | * | ||||
* @param state current transport state. | * @param state current transport state. | ||||
* @param nframes number of frames in current period. | * @param nframes number of frames in current period. | ||||
* @param pos pointer to a structure containing the current position. | |||||
* Store updated position information for the next cycle here. At a | |||||
* minimum, @a pos->frame must be incremented. | |||||
* @param new_pos true (non-zero) for a newly requested @a pos, or | |||||
* for the first cycle after jack_set_timebase_callback(). | |||||
* @param pos address of the position structure for the next cycle; @a | |||||
* pos->frame will be its frame number. If @a new_pos is FALSE, this | |||||
* structure contains extended position information from the current | |||||
* cycle. If TRUE, it contains whatever was set by the requester. | |||||
* The @a timebase_callback's task is to update the extended | |||||
* information here. | |||||
* @param new_pos TRUE (non-zero) for a newly requested @a pos, or for | |||||
* the first cycle after the @a timebase_callback is defined. | |||||
* @param arg the argument supplied by jack_set_timebase_callback(). | * @param arg the argument supplied by jack_set_timebase_callback(). | ||||
*/ | */ | ||||
typedef void (*JackTimebaseCallback)(jack_transport_state_t state, | typedef void (*JackTimebaseCallback)(jack_transport_state_t state, | ||||
@@ -208,14 +210,15 @@ typedef void (*JackTimebaseCallback)(jack_transport_state_t state, | |||||
/** | /** | ||||
* Register as timebase master for the JACK subsystem. | * Register as timebase master for the JACK subsystem. | ||||
* | * | ||||
* The timebase master is responsible for counting beats, frames, | |||||
* etc. on every process cycle. There is never more than one at a | |||||
* time. The timebase master registers a callback that updates | |||||
* position information while the transport is rolling. | |||||
* The timebase master registers a callback that updates extended | |||||
* position information such as beats or timecode whenever necessary. | |||||
* Without this extended information, there is no need for this | |||||
* function. | |||||
* | * | ||||
* When a new client takes over, the former timebase callback is no | |||||
* longer called. Taking over the timebase may be done conditionally, | |||||
* so it fails if there is a master already. | |||||
* There is never more than one master at a time. When a new client | |||||
* takes over, the former @a timebase_callback is no longer called. | |||||
* Taking over the timebase may be done conditionally, so it fails if | |||||
* there was a master already. | |||||
* | * | ||||
* @param client the JACK client structure. | * @param client the JACK client structure. | ||||
* @param conditional non-zero for a conditional request. | * @param conditional non-zero for a conditional request. | ||||
@@ -225,7 +228,7 @@ typedef void (*JackTimebaseCallback)(jack_transport_state_t state, | |||||
* | * | ||||
* @return | * @return | ||||
* - 0 on success; | * - 0 on success; | ||||
* - EBUSY if a conditional request fails because there is already a | |||||
* - EBUSY if a conditional request fails because there was already a | |||||
* timebase master; | * timebase master; | ||||
* - other non-zero error code. | * - other non-zero error code. | ||||
*/ | */ | ||||
@@ -235,14 +238,13 @@ int jack_set_timebase_callback (jack_client_t *client, | |||||
void *arg); | void *arg); | ||||
/** | /** | ||||
* Reposition transport to a new frame number. | |||||
* Reposition the transport to a new frame number. | |||||
* | * | ||||
* May be called at any time by any client. If valid, the new | |||||
* position takes effect in the next process cycle. The timebase | |||||
* master and any slow-sync clients are notified of the new position. | |||||
* If there are slow-sync clients and the transport is already | |||||
* rolling, it enters the ::JackTransportStarting state and begins | |||||
* invoking their sync_callbacks until they declare themselves ready. | |||||
* May be called at any time by any client. The new position takes | |||||
* effect in two process cycles. If there are slow-sync clients and | |||||
* the transport is already rolling, it will enter the | |||||
* ::JackTransportStarting state and begin invoking their @a | |||||
* sync_callbacks until ready. This function is realtime-safe. | |||||
* | * | ||||
* @see jack_transport_reposition, jack_set_sync_callback | * @see jack_transport_reposition, jack_set_sync_callback | ||||
* | * | ||||
@@ -257,14 +259,15 @@ int jack_transport_locate (jack_client_t *client, | |||||
/** | /** | ||||
* Query the current transport state and position. | * Query the current transport state and position. | ||||
* | * | ||||
* This function can be called from any thread. If called from the | |||||
* process thread, @a pos corresponds to the first frame of the | |||||
* current cycle and the state returned is valid for the entire cycle. | |||||
* This function is realtime-safe, and can be called from any thread. | |||||
* If called from the process thread, @a pos corresponds to the first | |||||
* frame of the current cycle and the state returned is valid for the | |||||
* entire cycle. | |||||
* | * | ||||
* @param client the JACK client structure. | * @param client the JACK client structure. | ||||
* @param pos NULL; or pointer to structure for returning current | |||||
* transport position, @a pos->valid will describe which fields | |||||
* contain valid data. | |||||
* @param pos pointer to structure for returning current transport | |||||
* position; @a pos->valid will show which fields contain valid data. | |||||
* If @a pos is NULL, do not return position information. | |||||
* | * | ||||
* @return Current transport state. | * @return Current transport state. | ||||
*/ | */ | ||||
@@ -274,12 +277,11 @@ jack_transport_state_t jack_transport_query (jack_client_t *client, | |||||
/** | /** | ||||
* Request a new transport position. | * Request a new transport position. | ||||
* | * | ||||
* May be called at any time by any client. If valid, the new | |||||
* position takes effect in the next process cycle. The timebase | |||||
* master and any slow-sync clients are notified of the new position. | |||||
* If there are slow-sync clients and the transport is already | |||||
* rolling, it enters the ::JackTransportStarting state and begins | |||||
* invoking their sync_callbacks until they declare themselves ready. | |||||
* May be called at any time by any client. The new position takes | |||||
* effect in two process cycles. If there are slow-sync clients and | |||||
* the transport is already rolling, it will enter the | |||||
* ::JackTransportStarting state and begin invoking their @a | |||||
* sync_callbacks until ready. This function is realtime-safe. | |||||
* | * | ||||
* @see jack_transport_locate, jack_set_sync_callback | * @see jack_transport_locate, jack_set_sync_callback | ||||
* | * | ||||
@@ -296,7 +298,7 @@ int jack_transport_reposition (jack_client_t *client, | |||||
* | * | ||||
* Any client can make this request at any time. It takes effect no | * Any client can make this request at any time. It takes effect no | ||||
* sooner than the next process cycle, perhaps later if there are | * sooner than the next process cycle, perhaps later if there are | ||||
* slow-sync clients. | |||||
* slow-sync clients. This function is realtime-safe. | |||||
* | * | ||||
* @see jack_set_sync_callback | * @see jack_set_sync_callback | ||||
* | * | ||||
@@ -308,7 +310,7 @@ void jack_transport_start (jack_client_t *client); | |||||
* Stop the JACK transport. | * Stop the JACK transport. | ||||
* | * | ||||
* Any client can make this request at any time. It takes effect on | * Any client can make this request at any time. It takes effect on | ||||
* the next process cycle. | |||||
* the next process cycle. This function is realtime-safe. | |||||
* | * | ||||
* @param client the JACK client structure. | * @param client the JACK client structure. | ||||
*/ | */ | ||||
@@ -236,8 +236,10 @@ jack_transport_init (jack_engine_t *engine) | |||||
ectl->prev_request = 0; | ectl->prev_request = 0; | ||||
ectl->seq_number = 1; /* can't start at 0 */ | ectl->seq_number = 1; /* can't start at 0 */ | ||||
ectl->new_pos = 0; | ectl->new_pos = 0; | ||||
ectl->sync_remain = 0; | |||||
ectl->pending_pos = 0; | |||||
ectl->pending_frame = 0; | |||||
ectl->sync_clients = 0; | ectl->sync_clients = 0; | ||||
ectl->sync_remain = 0; | |||||
ectl->sync_timeout = 2000000; /* 2 second default */ | ectl->sync_timeout = 2000000; /* 2 second default */ | ||||
ectl->sync_time_left = 0; | ectl->sync_time_left = 0; | ||||
} | } | ||||
@@ -328,9 +330,8 @@ jack_transport_client_set_sync (jack_engine_t *engine, | |||||
return ret; | return ret; | ||||
} | } | ||||
/* at the end of every process cycle | |||||
/* at process cycle end, set transport parameters for the next cycle | |||||
* | * | ||||
* Determines the transport parameters for the following cycle. | |||||
* precondition: caller holds the graph lock. | * precondition: caller holds the graph lock. | ||||
*/ | */ | ||||
void | void | ||||
@@ -339,31 +340,13 @@ jack_transport_cycle_end (jack_engine_t *engine) | |||||
jack_control_t *ectl = engine->control; | jack_control_t *ectl = engine->control; | ||||
transport_command_t cmd; /* latest transport command */ | transport_command_t cmd; /* latest transport command */ | ||||
/* update timebase, if needed */ | |||||
if ((engine->timebase_client == NULL) && | |||||
(ectl->transport_state == JackTransportRolling)) { | |||||
ectl->pending_time.frame = | |||||
ectl->current_time.frame + ectl->buffer_size; | |||||
} | |||||
/* See if an asynchronous position request arrived during the | |||||
* last cycle. The request_time could change during the | |||||
* guarded copy. If so, we'll use that new request. */ | |||||
if (ectl->request_time.unique_1 != ectl->prev_request) { | |||||
jack_transport_copy_position(&ectl->request_time, | |||||
&ectl->pending_time); | |||||
VERBOSE (engine, "new transport position: %lu, id=0x%llx\n", | |||||
ectl->pending_time.frame, ectl->pending_time.unique_1); | |||||
ectl->prev_request = ectl->pending_time.unique_1; | |||||
ectl->new_pos = 1; | |||||
} else | |||||
ectl->new_pos = 0; | |||||
/* Promote pending_time to current_time. Maintain the usecs | |||||
* and frame_rate values, clients may not set them. */ | |||||
/* Promote pending_time to current_time. Maintain the usecs, | |||||
* frame_rate and frame values, clients may not set them. */ | |||||
ectl->pending_time.usecs = ectl->current_time.usecs; | ectl->pending_time.usecs = ectl->current_time.usecs; | ||||
ectl->pending_time.frame_rate = ectl->current_time.frame_rate; | ectl->pending_time.frame_rate = ectl->current_time.frame_rate; | ||||
ectl->pending_time.frame = ectl->pending_frame; | |||||
ectl->current_time = ectl->pending_time; | ectl->current_time = ectl->pending_time; | ||||
ectl->new_pos = ectl->pending_pos; | |||||
/* check sync results from previous cycle */ | /* check sync results from previous cycle */ | ||||
if (ectl->transport_state == JackTransportStarting) { | if (ectl->transport_state == JackTransportStarting) { | ||||
@@ -435,7 +418,28 @@ jack_transport_cycle_end (jack_engine_t *engine) | |||||
jack_error ("invalid JACK transport state: %d", | jack_error ("invalid JACK transport state: %d", | ||||
ectl->transport_state); | ectl->transport_state); | ||||
} | } | ||||
return; | |||||
/* update timebase, if needed */ | |||||
if (ectl->transport_state == JackTransportRolling) { | |||||
ectl->pending_time.frame = | |||||
ectl->current_time.frame + ectl->buffer_size; | |||||
} | |||||
/* See if an asynchronous position request arrived during the | |||||
* last cycle. The request_time could change during the | |||||
* guarded copy. If so, we use the newest request. */ | |||||
ectl->pending_pos = 0; | |||||
if (ectl->request_time.unique_1 != ectl->prev_request) { | |||||
jack_transport_copy_position(&ectl->request_time, | |||||
&ectl->pending_time); | |||||
VERBOSE (engine, "new transport position: %lu, id=0x%llx\n", | |||||
ectl->pending_time.frame, ectl->pending_time.unique_1); | |||||
ectl->prev_request = ectl->pending_time.unique_1; | |||||
ectl->pending_pos = 1; | |||||
} | |||||
/* clients can't set pending frame number, so save it here */ | |||||
ectl->pending_frame = ectl->pending_time.frame; | |||||
} | } | ||||
/* driver callback at start of cycle */ | /* driver callback at start of cycle */ | ||||
@@ -161,10 +161,10 @@ jack_call_timebase_master (jack_client_t *client) | |||||
{ | { | ||||
jack_client_control_t *control = client->control; | jack_client_control_t *control = client->control; | ||||
jack_control_t *ectl = client->engine; | jack_control_t *ectl = client->engine; | ||||
int new_pos = ectl->new_pos; | |||||
int new_pos = (int) ectl->pending_pos; | |||||
/* Make sure we're still the master. Test is_timebase, which | |||||
* is set in a critical section; timebase_cb is not. */ | |||||
/* Make sure this is still the master; is_timebase is set in a | |||||
* critical section; timebase_cb is not. */ | |||||
if (control->is_timebase) { | if (control->is_timebase) { | ||||
if (client->new_timebase) { /* first callback? */ | if (client->new_timebase) { /* first callback? */ | ||||