From 056e6648e0cda5ae96daeb5a3cee6af4496a2ac4 Mon Sep 17 00:00:00 2001 From: joq Date: Mon, 25 Aug 2003 06:07:57 +0000 Subject: [PATCH] [0.79.1] timebase master fixes git-svn-id: svn+ssh://jackaudio.org/trunk/jack@464 0c269be4-1314-0410-8aa9-9f06e86f4224 --- configure.in | 2 +- example-clients/Makefile.am | 5 ++- example-clients/intime.c | 62 +++++++++++++++++++++++++++++++++++++ example-clients/showtime.c | 17 +++++----- example-clients/transport.c | 10 ++++-- jack/internal.h | 4 +++ jack/transport.h | 10 ++++-- jackd/engine.c | 42 ++++++++++++------------- jackd/transengine.c | 32 +++++++++++++------ libjack/local.h | 2 -- libjack/transclient.c | 8 ++++- 11 files changed, 146 insertions(+), 48 deletions(-) create mode 100644 example-clients/intime.c diff --git a/configure.in b/configure.in index 852f28b..c35caf7 100644 --- a/configure.in +++ b/configure.in @@ -14,7 +14,7 @@ dnl changes are made dnl --- JACK_MAJOR_VERSION=0 JACK_MINOR_VERSION=79 -JACK_MICRO_VERSION=0 +JACK_MICRO_VERSION=1 dnl --- dnl HOWTO: updating the jack protocal version diff --git a/example-clients/Makefile.am b/example-clients/Makefile.am index 36cc2fc..7f38cb7 100644 --- a/example-clients/Makefile.am +++ b/example-clients/Makefile.am @@ -108,9 +108,12 @@ jack_unload_LDADD = ../libjack/libjack.la ip_clientdir = $(ADDON_DIR) -ip_client_LTLIBRARIES = inprocess.la +ip_client_LTLIBRARIES = inprocess.la intime.la inprocess_la_LDFLAGS = -module -avoid-version inprocess_la_SOURCES = inprocess.c +intime_la_LDFLAGS = -module -avoid-version +intime_la_SOURCES = intime.c + dist-hook: dist-check-sndfile diff --git a/example-clients/intime.c b/example-clients/intime.c new file mode 100644 index 0000000..7201ca6 --- /dev/null +++ b/example-clients/intime.c @@ -0,0 +1,62 @@ +/* + * intime.c -- JACK internal timebase master example client. + * + * This demonstrates how to write an internal timebase master client. + * It fills in extended timecode fields using the trivial assumption + * that we are running at nominal speed, hence there is no drift. + * + * To run, first start `jackd', then `jack_load intime intime'. + * + * 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 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 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include + + +/* timebase callback + * + * Runs in the process thread. Realtime, must not wait. + */ +void timebase(jack_transport_state_t state, jack_nframes_t nframes, + jack_position_t *pos, int new_pos, void *arg) +{ + pos->valid = JackPositionTimecode; + pos->frame_time = pos->frame / (double) pos->frame_rate; + pos->period_duration = nframes / (double) pos->frame_rate; +} + +/* after internal client loaded */ +int +jack_initialize (jack_client_t *client, const char *data) +{ + if (jack_set_timebase_callback(client, 0, timebase, NULL) != 0) { + fprintf (stderr, "Unable to take over timebase.\n"); + return 1; + } + + fprintf (stderr, "Internal timebase master defined.\n"); + jack_activate (client); + return 0; +} + +/* before unloading */ +void +jack_finish (void) +{ + fprintf (stderr, "Internal timebase client exiting.\n"); +} diff --git a/example-clients/showtime.c b/example-clients/showtime.c index 405fe9d..2ce5b31 100644 --- a/example-clients/showtime.c +++ b/example-clients/showtime.c @@ -22,23 +22,26 @@ showtime () switch (transport_state) { case JackTransportStopped: - printf ("state: Stopped\t"); + printf ("state: Stopped"); break; case JackTransportRolling: - printf ("state: Rolling\t"); + printf ("state: Rolling"); break; case JackTransportStarting: - printf ("state: Starting\t"); + printf ("state: Starting"); break; default: - printf ("state: [unknown]\t"); + printf ("state: [unknown]"); } if (current.valid & JackPositionBBT) - printf ("BBT: %3d|%d|%04d\n", + printf ("\tBBT: %3d|%d|%04d", current.bar, current.beat, current.tick); - else - printf ("BBT: [-]\n"); + + if (current.valid & JackPositionTimecode) + printf ("\tTC: %.6f, %.6f", + current.frame_time, current.period_duration); + printf ("\n"); } void diff --git a/example-clients/transport.c b/example-clients/transport.c index 2d326c5..ad36a89 100644 --- a/example-clients/transport.c +++ b/example-clients/transport.c @@ -56,7 +56,7 @@ void timebase(jack_transport_state_t state, jack_nframes_t nframes, if (new_pos || time_reset) { - pos->valid |= JackPositionBBT; + pos->valid = JackPositionBBT; pos->beats_per_bar = time_beats_per_bar; pos->beat_type = time_beat_type; pos->ticks_per_beat = time_ticks_per_beat; @@ -69,7 +69,7 @@ void timebase(jack_transport_state_t state, jack_nframes_t nframes, * or time signature changes at specific locations in the * transport timeline. */ - min = (double) pos->frame / ((double) pos->frame_rate * 60.0); + min = pos->frame / ((double) pos->frame_rate * 60.0); abs_tick = min * pos->beats_per_minute * pos->ticks_per_beat; abs_beat = abs_tick / pos->ticks_per_beat; @@ -80,6 +80,12 @@ void timebase(jack_transport_state_t state, jack_nframes_t nframes, pos->ticks_per_beat; pos->bar++; /* adjust start to bar 1 */ +#if 0 + /* some debug code... */ + fprintf(stderr, "\nnew position: %lu\tBBT: %3d|%d|%04d\n", + pos->frame, pos->bar, pos->beat, pos->tick); +#endif + } else { /* Compute BBT info based on previous period. */ diff --git a/jack/internal.h b/jack/internal.h index ad8e1d8..de041a0 100644 --- a/jack/internal.h +++ b/jack/internal.h @@ -374,5 +374,9 @@ extern void jack_client_invalidate_port_buffers (jack_client_t *client); extern void jack_transport_copy_position (jack_position_t *from, jack_position_t *to); +extern void jack_call_sync_client (jack_client_t *client); + +extern void jack_call_timebase_master (jack_client_t *client); + #endif /* __jack_internal_h__ */ diff --git a/jack/transport.h b/jack/transport.h index 62fd0a1..9bb4b67 100644 --- a/jack/transport.h +++ b/jack/transport.h @@ -49,10 +49,12 @@ typedef unsigned long long jack_unique_t; /**< Unique ID (opaque) */ typedef enum { JackPositionBBT = 0x10, /**< Bar, Beat, Tick */ + JackPositionTimecode = 0x20, /**< External timecode */ } jack_position_bits_t; -#define JACK_POSITION_MASK (JackPositionBBT) /**< all valid position bits */ +/** all valid position bits */ +#define JACK_POSITION_MASK (JackPositionBBT|JackPositionTimecode) #define EXTENDED_TIME_INFO /** @@ -79,10 +81,14 @@ typedef struct { double ticks_per_beat; double beats_per_minute; + /* JackPositionTimecode fields: */ + double frame_time; /**< current time in seconds */ + double period_duration; /**< period duration (sec) */ + /* For binary compatibility, new fields should be allocated from * this padding area with new valid bits controlling access, so * the existing structure size and offsets are preserved. */ - int padding[14]; + int padding[10]; /* When (unique_1 == unique_2) the contents are consistent. */ jack_unique_t unique_2; /**< unique ID */ diff --git a/jackd/engine.c b/jackd/engine.c index 64f0850..00b78f4 100644 --- a/jackd/engine.c +++ b/jackd/engine.c @@ -390,34 +390,30 @@ jack_process_internal(jack_engine_t *engine, JSList *node, jack_nframes_t nframe /* internal client ("plugin") */ - if (ctl->process) { - - DEBUG ("calling process() on an internal client"); - - ctl->state = Running; - - /* XXX how to time out an internal client? */ - - engine->current_client = client; + DEBUG ("invoking an internal client's callbacks"); + ctl->state = Running; + engine->current_client = client; - if (ctl->process (nframes, ctl->process_arg) == 0) { - ctl->state = Finished; - } else { - jack_error ("internal client %s failed", client->control->name); - engine->process_errors++; - return NULL; /* will stop the loop */ - } + /* XXX how to time out an internal client? */ - //JOQ: can an internal client be slow sync? - //JOQ: can an internal client have a timebase master? + if (ctl->sync_cb) + jack_call_sync_client (ctl->private_client); - } else { - DEBUG ("internal client has no process() function"); + if (ctl->process) + if (ctl->process (nframes, ctl->process_arg)) { + jack_error ("internal client %s failed", ctl->name); + engine->process_errors++; + } - ctl->state = Finished; - } + if (ctl->timebase_cb) + jack_call_timebase_master (ctl->private_client); + + ctl->state = Finished; - return jack_slist_next (node); + if (engine->process_errors) + return NULL; /* will stop the loop */ + else + return jack_slist_next (node); } #if defined(__APPLE__) && defined(__POWERPC__) diff --git a/jackd/transengine.c b/jackd/transengine.c index 1f1fb64..c8559a1 100644 --- a/jackd/transengine.c +++ b/jackd/transengine.c @@ -176,6 +176,8 @@ jack_timebase_reset (jack_engine_t *engine, jack_client_id_t client_id) client->control->is_timebase = 0; engine->timebase_client = NULL; ectl->pending_time.valid = 0; + VERBOSE (engine, "%s resigned as timebase master\n", + client->control->name); ret = 0; } else ret = EINVAL; @@ -197,22 +199,34 @@ jack_timebase_set (jack_engine_t *engine, client = jack_client_internal_by_id (engine, client_id); + if (client == NULL) { + // JOQ: use PRIuLEAST32 here... + VERBOSE (engine, " %lu no longer exists!\n", client_id); + jack_unlock_graph (engine); + return EINVAL; + } + if (conditional && engine->timebase_client) { /* see if timebase master is someone else */ - if (client && (client != engine->timebase_client)) + if (client != engine->timebase_client) { + VERBOSE (engine, "conditional timebase for %s failed\n" + " %s is already the master\n", + client->control->name, + engine->timebase_client->control->name); ret = EBUSY; + } else + VERBOSE (engine, " %s was already timebase master:\n", + client->control->name); } else { - if (client) { - if (engine->timebase_client) - engine->timebase_client-> - control->is_timebase = 0; - engine->timebase_client = client; - client->control->is_timebase = 1; - } else - ret = EINVAL; + if (engine->timebase_client) + engine->timebase_client->control->is_timebase = 0; + engine->timebase_client = client; + client->control->is_timebase = 1; + VERBOSE (engine, "new timebase master: %s\n", + client->control->name); } jack_unlock_graph (engine); diff --git a/libjack/local.h b/libjack/local.h index e17e6ca..ae73e3e 100644 --- a/libjack/local.h +++ b/libjack/local.h @@ -32,8 +32,6 @@ struct _jack_client { extern int jack_client_deliver_request (const jack_client_t *client, jack_request_t *req); extern jack_port_t *jack_port_new (const jack_client_t *client, jack_port_id_t port_id, jack_control_t *control); -extern void jack_call_sync_client (jack_client_t *client); -extern void jack_call_timebase_master (jack_client_t *client); extern void *jack_zero_filled_buffer; diff --git a/libjack/transclient.c b/libjack/transclient.c index 6f786a0..14d5c51 100644 --- a/libjack/transclient.c +++ b/libjack/transclient.c @@ -122,8 +122,14 @@ jack_transport_request_new_pos (jack_client_t *client, jack_position_t *pos) { jack_control_t *ectl = client->engine; - /* carefully copy requested postion into shared memory */ + /* distinguish this request from all others */ pos->unique_1 = pos->unique_2 = jack_generate_unique_id(ectl); + + /* clients may not set these fields */ + pos->usecs = ectl->current_time.usecs; + pos->frame_rate = ectl->current_time.frame_rate; + + /* carefully copy requested postion into shared memory */ jack_transport_copy_position (pos, &ectl->request_time); return 0;