Browse Source

[0.77.0] new transport BETA test

git-svn-id: svn+ssh://jackaudio.org/trunk/jack@453 0c269be4-1314-0410-8aa9-9f06e86f4224
tags/0.109.0
joq 22 years ago
parent
commit
94214de5b6
9 changed files with 178 additions and 102 deletions
  1. +2
    -2
      AUTHORS
  2. +2
    -1
      TODO
  3. +4
    -4
      configure.in
  4. +1
    -1
      example-clients/Makefile.am
  5. +3
    -0
      jack/engine.h
  6. +2
    -0
      jack/internal.h
  7. +6
    -4
      jack/transport.h
  8. +63
    -29
      jackd/transengine.c
  9. +95
    -61
      libjack/transclient.c

+ 2
- 2
AUTHORS View File

@@ -14,7 +14,7 @@ Paul Davis was the principal author of the JACK API and of the sample
implementation contained here. Andy Wingo provided many small patches
Fernando Pablo Lopez-Lezcano contributed the capabilities-based code.
Jeremy Hall, Steve Harris, and Martin Boer contributed sample clients
and utilities. Jack O'Quin contributed documentation and minor
development work.
and utilities. Jack O'Quin contributed new transport interfaces and
documentation.

Many others have contributed patches and/or test results.

+ 2
- 1
TODO View File

@@ -18,6 +18,7 @@ TODO for 1.0 (owner)

- ensure that UST/MSC pairs work for transport API
- resolve helper thread design question?
- API to change buffer size (joq)

TODO post-1.0

@@ -25,7 +26,6 @@ TODO post-1.0

TODO general (owner)

- define transport info struct contents (paul)
- don't build static libraries of drivers and ip-clients (kaiv)
- add explanation of protocol versioning to README.developers (kaiv)
- add Paul's graph/subgraph explanation to the design doc (kaiv)
@@ -59,5 +59,6 @@ CLOSED (date,who,comment)
- call time client before all others (2003/01/28, kaiv, another solution)
- finalize discussion on transport API, and implement (2003/08/04, joq)
- whether to hide the transport.h structs from clients (2003/08/04, joq)
- define transport info struct contents (2003/08/13, joq)

-----------------------------------------------------------------------

+ 4
- 4
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=76
JACK_MICRO_VERSION=7
JACK_MINOR_VERSION=77
JACK_MICRO_VERSION=0


dnl ---
@@ -25,7 +25,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=7
JACK_PROTOCOL_VERSION=8

dnl ---
dnl HOWTO: updating the libjack interface version
@@ -42,7 +42,7 @@ dnl slacker than this, and closer to those for the JACK version
dnl number.
dnl ---
JACK_API_CURRENT=0
JACK_API_REVISION=20
JACK_API_REVISION=21
JACK_API_AGE=0

AC_SUBST(JACK_MAJOR_VERSION)


+ 1
- 1
example-clients/Makefile.am View File

@@ -34,7 +34,7 @@ bin_PROGRAMS = jack_load \
jack_metro \
jack_showtime \
jack_lsp \
jackrec \
$(JACKREC) \
$(JACK_TRANSPORT)

if HAVE_SNDFILE


+ 3
- 0
jack/engine.h View File

@@ -24,6 +24,9 @@
#include <jack/jack.h>
#include <jack/internal.h>

#define VERBOSE(engine,format,args...) \
if ((engine)->verbose) fprintf (stderr, format, ## args)

struct _jack_driver;
struct _jack_client_internal;
struct _jack_port_internal;


+ 2
- 0
jack/internal.h View File

@@ -98,9 +98,11 @@ typedef struct {

jack_transport_state_t transport_state;
volatile transport_command_t transport_cmd;
transport_command_t previous_cmd; /* previous transport_cmd */
jack_position_t current_time; /* position for current cycle */
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 */
int new_pos; /* new position this cycle */
unsigned long sync_clients; /* number of is_slowsync clients */
unsigned long sync_remain; /* number of them with sync_poll */


+ 6
- 4
jack/transport.h View File

@@ -41,6 +41,8 @@ typedef enum {

} jack_transport_state_t;

typedef unsigned long long jack_unique_t; /**< Unique ID (opaque) */

/**
* Optional struct jack_position_t fields.
*/
@@ -58,7 +60,8 @@ typedef enum {
*/
typedef struct {
/* these two cannot be set from clients: the server sets them */
/* these three 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) */

@@ -81,9 +84,8 @@ typedef struct {
* the existing structure size and offsets are preserved. */
int padding[14];

/* When (guard_usecs == usecs) the entire structure is consistent.
* This is set by server. */
jack_time_t guard_usecs; /**< guard copy of usecs */
/* When (unique_1 == unique_2) the contents are consistent. */
jack_unique_t unique_2; /**< unique ID */

} jack_position_t;



+ 63
- 29
jackd/transengine.c View File

@@ -22,6 +22,7 @@
#include <config.h>
#include <errno.h>
#include <assert.h>
#include <stdio.h>
#include <jack/internal.h>
#include <jack/engine.h>
#include "transengine.h"
@@ -44,8 +45,11 @@ jack_sync_poll_new (jack_engine_t *engine, jack_client_internal_t *client)
}

// JOQ: I don't like doing this here...
if (engine->control->transport_state == JackTransportRolling)
if (engine->control->transport_state == JackTransportRolling) {
engine->control->transport_state = JackTransportStarting;
VERBOSE (engine, "force transport state to Starting\n");
}
VERBOSE (engine, "polling sync client %lu\n", client->control->id);
}

/* stop polling a specific slow-sync client
@@ -58,6 +62,8 @@ jack_sync_poll_exit (jack_engine_t *engine, jack_client_internal_t *client)
client->control->sync_poll = 0;
client->control->sync_new = 0;
engine->control->sync_remain--;
VERBOSE (engine, "sync poll interrupted for client %lu\n",
client->control->id);
}
client->control->is_slowsync = 0;
engine->control->sync_clients--;
@@ -84,6 +90,9 @@ jack_sync_poll_stop (jack_engine_t *engine)

//JOQ: check invariant for debugging...
assert (poll_count == engine->control->sync_remain);
VERBOSE (engine,
"sync poll halted with %ld clients and %llu usecs remaining\n",
engine->control->sync_remain, engine->control->sync_time_left);
engine->control->sync_remain = 0;
engine->control->sync_time_left = 0;
}
@@ -110,6 +119,9 @@ jack_sync_poll_start (jack_engine_t *engine)
assert (sync_count == engine->control->sync_clients);
engine->control->sync_remain = engine->control->sync_clients;
engine->control->sync_time_left = engine->control->sync_timeout;
VERBOSE (engine, "transport Starting, sync poll of %ld clients "
"for %llu usecs\n", engine->control->sync_remain,
engine->control->sync_time_left);
}

/* check for sync timeout */
@@ -124,12 +136,13 @@ jack_sync_timeout (jack_engine_t *engine)
/* compare carefully, jack_time_t is unsigned */
if (ectl->sync_time_left > buf_usecs) {
ectl->sync_time_left -= buf_usecs;
return 0; /* continue */
return FALSE;
}

/* timed out */
VERBOSE (engine, "transport sync timeout\n");
ectl->sync_time_left = 0;
return 1;
return TRUE;
}

/**************** subroutines used by engine.c ****************/
@@ -212,10 +225,12 @@ jack_transport_init (jack_engine_t *engine)

engine->timebase_client = NULL;
ectl->transport_state = JackTransportStopped;
ectl->transport_cmd = TransportCommandNone;
ectl->transport_cmd = TransportCommandStop;
ectl->previous_cmd = TransportCommandStop;
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->prev_request = 0;
ectl->new_pos = 0;
ectl->sync_remain = 0;
ectl->sync_clients = 0;
@@ -235,6 +250,7 @@ jack_transport_client_exit (jack_engine_t *engine,
engine->timebase_client = NULL;
engine->control->current_time.valid = 0;
engine->control->pending_time.valid = 0;
VERBOSE (engine, "timebase master exit\n");
}

if (client->control->is_slowsync)
@@ -327,43 +343,44 @@ jack_transport_cycle_end (jack_engine_t *engine)
ectl->current_time.frame + ectl->buffer_size;
}

/* Handle latest asynchronous requests from the last cycle.
*
* This should ideally use an atomic swap, since commands can
* arrive at any time. There is a small timing window during
* which a request could be ignored inadvertently. Since
* another could have arrived in the previous moment and
* replaced it anyway, we won't bother with <asm/atomic.h>.
*/
/* Handle any new transport command from the last cycle. */
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) {
/* request_time could change during this copy */
if (cmd != ectl->previous_cmd) {
ectl->previous_cmd = cmd;
VERBOSE (engine, "transport command: %s\n",
(cmd == TransportCommandStart? "START": "STOP"));
} else
cmd = TransportCommandNone;

/* See if a position request arrived during the last cycle.
* The request_time could change during the guarded copy. If
* so, we'll handle it now, but mistake it for a new request
* in the following cycle. That may cause an extra sync poll
* cycle, but should work. */
if (ectl->request_time.unique_1 != ectl->prev_request) {
ectl->prev_request = ectl->request_time.unique_1;
jack_transport_copy_position(&ectl->request_time,
&ectl->pending_time);
ectl->request_time.usecs = 0; /* empty request buffer */
ectl->new_pos = 1;
VERBOSE (engine, "new transport postition: %lu, id=0x%llx\n",
ectl->pending_time.frame, ectl->pending_time.unique_1);
} else
ectl->new_pos = 0;

/* Promote pending_time to current_time. Maintain the usecs
* and frame_rate values, clients may not set them. */
ectl->pending_time.guard_usecs =
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->current_time = ectl->pending_time;

/* check sync results from previous cycle */
if (ectl->transport_state == JackTransportStarting) {
if ((ectl->sync_remain == 0) ||
(jack_sync_timeout(engine)))
(jack_sync_timeout(engine))) {
ectl->transport_state = JackTransportRolling;
VERBOSE (engine, "transport Rolling, %lld usec"
" left for poll\n", ectl->sync_time_left);
}
}

/* state transition switch */
@@ -376,21 +393,38 @@ jack_transport_cycle_end (jack_engine_t *engine)
jack_sync_poll_start(engine);
} else {
ectl->transport_state = JackTransportRolling;
VERBOSE (engine, "transport Rolling\n");
}
}
break;

case JackTransportStarting:
case JackTransportRolling:
if (cmd == TransportCommandStop) {
ectl->transport_state = JackTransportStopped;
jack_sync_poll_stop(engine);
VERBOSE (engine, "transport Stopped\n");
if (ectl->sync_remain)
jack_sync_poll_stop(engine);
} else if (ectl->new_pos) {
if (ectl->sync_clients) {
ectl->transport_state = JackTransportStarting;
jack_sync_poll_start(engine);
} else {
ectl->transport_state = JackTransportRolling;
VERBOSE (engine, "transport Rolling\n");
}
}
break;

case JackTransportRolling:
if (cmd == TransportCommandStop) {
ectl->transport_state = JackTransportStopped;
VERBOSE (engine, "transport Stopped\n");
if (ectl->sync_remain)
jack_sync_poll_stop(engine);
} else if (ectl->new_pos) {
if (ectl->sync_clients) {
ectl->transport_state = JackTransportStarting;
jack_sync_poll_start(engine);
}
}
break;
@@ -406,8 +440,7 @@ jack_transport_cycle_end (jack_engine_t *engine)
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;
engine->control->current_time.usecs = time;
}

/* on SetSyncTimeout request */
@@ -416,5 +449,6 @@ jack_transport_set_sync_timeout (jack_engine_t *engine,
jack_time_t usecs)
{
engine->control->sync_timeout = usecs;
VERBOSE (engine, "new sync timeout: %llu usecs\n", usecs);
return 0;
}

+ 95
- 61
libjack/transclient.c View File

@@ -23,12 +23,52 @@
#include <config.h>
#include <errno.h>
#include <math.h>
#include <stdio.h>
#include <jack/internal.h>
#include "local.h"


/********************* Internal functions *********************/

/* generate a unique non-zero ID, different for each call */
jack_unique_t
jack_generate_unique_id (jack_control_t *ectl)
{
/* The jack_unique_t is an opaque type. Its structure is only
* known here. We use the least significant word of the CPU
* cycle counter. For SMP, I would like to include the
* current thread ID, since only one thread runs on a CPU at a
* time.
*
* But, pthread_self() is broken on my Debian GNU/Linux
* system, it always seems to return 16384. That's useless.
* So, I'm using the process ID instead. With Linux 2.4 and
* Linuxthreads there is an LWP for each thread so this works,
* but is not portable. :-(
*/
volatile union {
jack_unique_t unique;
struct {
pid_t pid;
unsigned long cycle;
} field;
} id;

id.field.cycle = (unsigned long) get_cycles();
id.field.pid = getpid();

// JOQ: Alternatively, we could keep a sequence number in
// shared memory, using <asm/atomic.h> to increment it. I
// really like the simplicity of that approach. But I hate
// forcing JACK to either depend on that pesky header file, or
// maintain its own like ardour does.

// fprintf (stderr, "unique ID 0x%llx, process %d, cycle 0x%lx\n",
// id.unique, id.field.pid, id.field.cycle);

return id.unique;
}

static inline void
jack_read_frame_time (const jack_client_t *client, jack_frame_timer_t *copy)
{
@@ -56,7 +96,7 @@ void
jack_transport_copy_position (jack_position_t *from, jack_position_t *to)
{
int tries = 0;
long timeout = 1000000;
long timeout = 1000;

do {
/* throttle the busy wait if we don't get the answer
@@ -67,30 +107,24 @@ jack_transport_copy_position (jack_position_t *from, jack_position_t *to)

/* debug code to avoid system hangs... */
if (--timeout == 0) {
jack_error("infinte loop copying position");
jack_error("hung in loop copying position");
abort();
}
}
*to = *from;
tries++;

} while (to->usecs != to->guard_usecs);
} while (to->unique_1 != to->unique_2);
}

static inline int
jack_transport_request_new_pos (jack_client_t *client, jack_position_t *pos)
{
jack_control_t *eng = client->engine;

// 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.
jack_control_t *ectl = client->engine;

/* carefully copy requested postion into shared memory */
pos->guard_usecs = pos->usecs = jack_get_microseconds();
jack_transport_copy_position (pos, &eng->request_time);
pos->unique_1 = pos->unique_2 = jack_generate_unique_id(ectl);
jack_transport_copy_position (pos, &ectl->request_time);
return 0;
}
@@ -102,20 +136,20 @@ void
jack_call_sync_client (jack_client_t *client)
{
jack_client_control_t *control = client->control;
jack_control_t *eng = client->engine;
jack_control_t *ectl = client->engine;

/* 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) &&
if ((ectl->new_pos || control->sync_poll || control->sync_new) &&
control->is_slowsync) {

if (control->sync_cb (eng->transport_state,
&eng->current_time,
if (control->sync_cb (ectl->transport_state,
&ectl->current_time,
control->sync_arg)) {

if (control->sync_poll) {
control->sync_poll = 0;
eng->sync_remain--;
ectl->sync_remain--;
}
}
control->sync_new = 0;
@@ -126,8 +160,8 @@ void
jack_call_timebase_master (jack_client_t *client)
{
jack_client_control_t *control = client->control;
jack_control_t *eng = client->engine;
int new_pos = eng->new_pos;
jack_control_t *ectl = client->engine;
int new_pos = ectl->new_pos;

/* Make sure we're still the master. Test is_timebase, which
* is set in a critical section; timebase_cb is not. */
@@ -138,12 +172,12 @@ jack_call_timebase_master (jack_client_t *client)
new_pos = 1;
}

if ((eng->transport_state == JackTransportRolling) ||
if ((ectl->transport_state == JackTransportRolling) ||
new_pos) {

control->timebase_cb (eng->transport_state,
control->timebase_cb (ectl->transport_state,
control->nframes,
&eng->pending_time,
&ectl->pending_time,
new_pos,
control->timebase_arg);
}
@@ -164,10 +198,10 @@ jack_nframes_t
jack_frames_since_cycle_start (const jack_client_t *client)
{
float usecs;
jack_control_t *eng = client->engine;
jack_control_t *ectl = client->engine;

usecs = jack_get_microseconds() - eng->current_time.usecs;
return (jack_nframes_t) floor ((((float) eng->current_time.frame_rate)
usecs = jack_get_microseconds() - ectl->current_time.usecs;
return (jack_nframes_t) floor ((((float) ectl->current_time.frame_rate)
/ 1000000.0f) * usecs);
}

@@ -177,13 +211,13 @@ jack_frame_time (const jack_client_t *client)
jack_frame_timer_t current;
float usecs;
jack_nframes_t elapsed;
jack_control_t *eng = client->engine;
jack_control_t *ectl = client->engine;

jack_read_frame_time (client, &current);
usecs = jack_get_microseconds() - current.stamp;
elapsed = (jack_nframes_t)
floor ((((float) eng->current_time.frame_rate)
floor ((((float) ectl->current_time.frame_rate)
/ 1000000.0f) * usecs);
return current.frames + elapsed;
@@ -299,11 +333,11 @@ jack_transport_locate (jack_client_t *client, jack_nframes_t frame)
jack_transport_state_t
jack_transport_query (jack_client_t *client, jack_position_t *pos)
{
jack_control_t *eng = client->engine;
jack_control_t *ectl = client->engine;

/* the guarded copy makes this function work in any thread */
jack_transport_copy_position (&eng->current_time, pos);
return eng->transport_state;
jack_transport_copy_position (&ectl->current_time, pos);
return ectl->transport_state;
}

int
@@ -352,30 +386,30 @@ void
jack_get_transport_info (jack_client_t *client,
jack_transport_info_t *info)
{
jack_control_t *eng = client->engine;
jack_control_t *ectl = client->engine;

/* check that this is the process thread */
if (client->thread_id != pthread_self()) {
if (!pthread_equal(client->thread_id, pthread_self())) {
jack_error("Invalid thread for jack_get_transport_info().");
abort(); /* kill this client */
}

info->usecs = eng->current_time.usecs;
info->frame_rate = eng->current_time.frame_rate;
info->transport_state = eng->transport_state;
info->frame = eng->current_time.frame;
info->valid = (eng->current_time.valid |
info->usecs = ectl->current_time.usecs;
info->frame_rate = ectl->current_time.frame_rate;
info->transport_state = ectl->transport_state;
info->frame = ectl->current_time.frame;
info->valid = (ectl->current_time.valid |
JackTransportState | JackTransportPosition);

if (info->valid & JackTransportBBT) {
info->bar = eng->current_time.bar;
info->beat = eng->current_time.beat;
info->tick = eng->current_time.tick;
info->bar_start_tick = eng->current_time.bar_start_tick;
info->beats_per_bar = eng->current_time.beats_per_bar;
info->beat_type = eng->current_time.beat_type;
info->ticks_per_beat = eng->current_time.ticks_per_beat;
info->beats_per_minute = eng->current_time.beats_per_minute;
info->bar = ectl->current_time.bar;
info->beat = ectl->current_time.beat;
info->tick = ectl->current_time.tick;
info->bar_start_tick = ectl->current_time.bar_start_tick;
info->beats_per_bar = ectl->current_time.beats_per_bar;
info->beat_type = ectl->current_time.beat_type;
info->ticks_per_beat = ectl->current_time.ticks_per_beat;
info->beats_per_minute = ectl->current_time.beats_per_minute;
}
}

@@ -385,7 +419,7 @@ void
jack_set_transport_info (jack_client_t *client,
jack_transport_info_t *info)
{
jack_control_t *eng = client->engine;
jack_control_t *ectl = client->engine;

if (!client->control->is_timebase) { /* not timebase master? */
if (first_error)
@@ -400,37 +434,37 @@ jack_set_transport_info (jack_client_t *client,
}

/* check that this is the process thread */
if (client->thread_id != pthread_self()) {
if (!pthread_equal(client->thread_id, pthread_self())) {
jack_error ("Invalid thread for jack_set_transport_info().");
abort(); /* kill this client */
}

/* is there a new state? */
if ((info->valid & JackTransportState) &&
(info->transport_state != eng->transport_state)) {
(info->transport_state != ectl->transport_state)) {
if (info->transport_state == JackTransportStopped)
eng->transport_cmd = TransportCommandStop;
ectl->transport_cmd = TransportCommandStop;
else if (info->transport_state == JackTransportRolling)
eng->transport_cmd = TransportCommandStart;
ectl->transport_cmd = TransportCommandStart;
/* silently ignore anything else */
}

if (info->valid & JackTransportPosition)
eng->pending_time.frame = info->frame;
ectl->pending_time.frame = info->frame;
else
eng->pending_time.frame = eng->current_time.frame;
ectl->pending_time.frame = ectl->current_time.frame;

eng->pending_time.valid = (info->valid & JACK_POSITION_MASK);
ectl->pending_time.valid = (info->valid & JACK_POSITION_MASK);

if (info->valid & JackTransportBBT) {
eng->pending_time.bar = info->bar;
eng->pending_time.beat = info->beat;
eng->pending_time.tick = info->tick;
eng->pending_time.bar_start_tick = info->bar_start_tick;
eng->pending_time.beats_per_bar = info->beats_per_bar;
eng->pending_time.beat_type = info->beat_type;
eng->pending_time.ticks_per_beat = info->ticks_per_beat;
eng->pending_time.beats_per_minute = info->beats_per_minute;
ectl->pending_time.bar = info->bar;
ectl->pending_time.beat = info->beat;
ectl->pending_time.tick = info->tick;
ectl->pending_time.bar_start_tick = info->bar_start_tick;
ectl->pending_time.beats_per_bar = info->beats_per_bar;
ectl->pending_time.beat_type = info->beat_type;
ectl->pending_time.ticks_per_beat = info->ticks_per_beat;
ectl->pending_time.beats_per_minute = info->beats_per_minute;
}
}



Loading…
Cancel
Save