Browse Source

[0.79.0] modified timebase interface

git-svn-id: svn+ssh://jackaudio.org/trunk/jack@463 0c269be4-1314-0410-8aa9-9f06e86f4224
tags/0.109.0
joq 22 years ago
parent
commit
baf0187264
7 changed files with 102 additions and 104 deletions
  1. +3
    -3
      configure.in
  2. +10
    -17
      doc/transport.dox
  3. +1
    -4
      example-clients/transport.c
  4. +4
    -2
      jack/internal.h
  5. +51
    -49
      jack/transport.h
  6. +30
    -26
      jackd/transengine.c
  7. +3
    -3
      libjack/transclient.c

+ 3
- 3
configure.in View File

@@ -13,8 +13,8 @@ dnl micro version = incremented when implementation-only
dnl changes are made
dnl ---
JACK_MAJOR_VERSION=0
JACK_MINOR_VERSION=78
JACK_MICRO_VERSION=4
JACK_MINOR_VERSION=79
JACK_MICRO_VERSION=0

dnl ---
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 version of libjack.
dnl ---
JACK_PROTOCOL_VERSION=8
JACK_PROTOCOL_VERSION=9

dnl ---
dnl HOWTO: updating the libjack interface version


+ 10
- 17
doc/transport.dox View File

@@ -59,20 +59,18 @@ But, old-style timebase masters will no longer control the transport.
@subsection timebase Timebase Master

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
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
information while the transport is rolling. This function is called
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
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
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
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"
@@ -238,12 +236,7 @@ to remain constant.
This design currently does not address several issues. This means they
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
- 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
*/

+ 1
- 4
example-clients/transport.c View File

@@ -54,9 +54,6 @@ void timebase(jack_transport_state_t state, jack_nframes_t nframes,
long abs_tick; /* ticks since frame 0 */
long abs_beat; /* beats since frame 0 */

if (state == JackTransportRolling)
pos->frame += nframes;

if (new_pos || time_reset) {

pos->valid |= JackPositionBBT;
@@ -85,7 +82,7 @@ void timebase(jack_transport_state_t state, jack_nframes_t nframes,

} else {

/* Compute BBT info from previous period. */
/* Compute BBT info based on previous period. */
pos->tick +=
nframes * pos->ticks_per_beat * pos->beats_per_minute
/ (pos->frame_rate * 60);


+ 4
- 2
jack/internal.h View File

@@ -103,8 +103,10 @@ typedef struct {
jack_position_t pending_time; /* position for next cycle */
jack_position_t request_time; /* latest requested position */
jack_unique_t prev_request; /* previous request unique ID */
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_remain; /* number of them with sync_poll */
jack_time_t sync_timeout;


+ 51
- 49
jack/transport.h View File

@@ -60,12 +60,12 @@ typedef enum {
*/
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_time_t usecs; /**< monotonic, free-rolling */
jack_nframes_t frame_rate; /**< current frame rate (per second) */

jack_nframes_t frame; /**< frame number, always present */

jack_position_bits_t valid; /**< which other fields are valid */

/* JackPositionBBT fields: */
@@ -125,7 +125,7 @@ int jack_release_timebase (jack_client_t *client);
* @param pos new transport position.
* @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,
jack_position_t *pos,
@@ -142,7 +142,7 @@ typedef int (*JackSyncCallback)(jack_transport_state_t state,
* transport wants to start.
*
* @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
* this client no longer requires slow-sync processing.
* @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);

/**
* 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
* 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 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().
*/
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.
*
* 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 conditional non-zero for a conditional request.
@@ -225,7 +228,7 @@ typedef void (*JackTimebaseCallback)(jack_transport_state_t state,
*
* @return
* - 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;
* - other non-zero error code.
*/
@@ -235,14 +238,13 @@ int jack_set_timebase_callback (jack_client_t *client,
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
*
@@ -257,14 +259,15 @@ int jack_transport_locate (jack_client_t *client,
/**
* 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 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.
*/
@@ -274,12 +277,11 @@ jack_transport_state_t jack_transport_query (jack_client_t *client,
/**
* 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
*
@@ -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
* 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
*
@@ -308,7 +310,7 @@ void jack_transport_start (jack_client_t *client);
* Stop the JACK transport.
*
* 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.
*/


+ 30
- 26
jackd/transengine.c View File

@@ -236,8 +236,10 @@ jack_transport_init (jack_engine_t *engine)
ectl->prev_request = 0;
ectl->seq_number = 1; /* can't start at 0 */
ectl->new_pos = 0;
ectl->sync_remain = 0;
ectl->pending_pos = 0;
ectl->pending_frame = 0;
ectl->sync_clients = 0;
ectl->sync_remain = 0;
ectl->sync_timeout = 2000000; /* 2 second default */
ectl->sync_time_left = 0;
}
@@ -328,9 +330,8 @@ jack_transport_client_set_sync (jack_engine_t *engine,
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.
*/
void
@@ -339,31 +340,13 @@ jack_transport_cycle_end (jack_engine_t *engine)
jack_control_t *ectl = engine->control;
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.frame_rate = ectl->current_time.frame_rate;
ectl->pending_time.frame = ectl->pending_frame;
ectl->current_time = ectl->pending_time;
ectl->new_pos = ectl->pending_pos;

/* check sync results from previous cycle */
if (ectl->transport_state == JackTransportStarting) {
@@ -435,7 +418,28 @@ jack_transport_cycle_end (jack_engine_t *engine)
jack_error ("invalid JACK transport state: %d",
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 */


+ 3
- 3
libjack/transclient.c View File

@@ -161,10 +161,10 @@ jack_call_timebase_master (jack_client_t *client)
{
jack_client_control_t *control = client->control;
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 (client->new_timebase) { /* first callback? */


Loading…
Cancel
Save