Browse Source

[0.76.1] new transport system

git-svn-id: svn+ssh://jackaudio.org/trunk/jack@444 0c269be4-1314-0410-8aa9-9f06e86f4224
tags/0.109.0
joq 21 years ago
parent
commit
fcdb114b0a
21 changed files with 1408 additions and 365 deletions
  1. +2
    -4
      TODO
  2. +4
    -4
      configure.in
  3. +7
    -3
      doc/Makefile.am
  4. BIN
      doc/fsm.png
  5. +54
    -0
      doc/mainpage.dox
  6. +5
    -4
      doc/reference.doxygen.in
  7. +244
    -0
      doc/transport.dox
  8. +2
    -1
      drivers/alsa/alsa_driver.c
  9. +2
    -1
      drivers/portaudio/portaudio_driver.c
  10. +22
    -75
      example-clients/showtime.c
  11. +53
    -47
      example-clients/transport.c
  12. +23
    -1
      jack/engine.h
  13. +42
    -11
      jack/internal.h
  14. +10
    -8
      jack/jack.h
  15. +329
    -39
      jack/transport.h
  16. +18
    -38
      jackd/engine.c
  17. +230
    -33
      jackd/transengine.c
  18. +15
    -10
      jackd/transengine.h
  19. +7
    -71
      libjack/client.c
  20. +6
    -1
      libjack/local.h
  21. +333
    -14
      libjack/transclient.c

+ 2
- 4
TODO View File

@@ -11,15 +11,11 @@ send a change request to Kai Vehmanen (mailto:k_at_eca.cx).

TO BE DECIDED (owner)

- whether to hide the transport.h structs from clients (kaiv)
- dropping the use of CAP_RESOURCE (nando)
- whether to default to triangular dithering the at 16bit (swh)

TODO for 1.0 (owner)

- finalize discussion on transport API, and implement
- how to support sync (client "synced" bit)
- should we separate transport+time?
- ensure that UST/MSC pairs work for transport API
- resolve helper thread design question?

@@ -61,5 +57,7 @@ CLOSED (date,who,comment)
- alsa-driver parameter parsing patch (2003/02/25, joq, done)
- jack_deactive called unconditionally in jack_client_close (2003/02/05, kaiv, fixed)
- 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)

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

+ 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=75
JACK_MICRO_VERSION=0
JACK_MINOR_VERSION=76
JACK_MICRO_VERSION=1


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=6
JACK_PROTOCOL_VERSION=7

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=19
JACK_API_REVISION=20
JACK_API_AGE=0

AC_SUBST(JACK_MAJOR_VERSION)


+ 7
- 3
doc/Makefile.am View File

@@ -3,15 +3,19 @@ MAINTAINERCLEANFILES=Makefile.in

DOX=reference.doxygen


EXTRA_DIST=

INSTIMAGES=reference/html/doxygen.png reference/html/fsm.png

DOC_STAMPS=html-build.stamp

DOC_DIR=$(HTML_DIR)

all-local: doxygen-build.stamp

doxygen-build.stamp: $(DOX)
doxygen-build.stamp: $(DOX) mainpage.dox transport.dox fsm.png \
../jack/jack.h ../jack/types.h ../jack/transport.h
@echo '*** Running doxygen ***'
doxygen $(DOX)
touch doxygen-build.stamp
@@ -29,7 +33,7 @@ install-data-local:
if test "$$installfiles" = 'reference/html/*.html'; \
then echo '-- Nothing to install' ; \
else \
for i in $$installfiles reference/html/doxygen.png reference/html/doxygen.css; do \
for i in $$installfiles $(INSTIMAGES) reference/html/doxygen.css; do \
echo '-- Installing '$$i ; \
$(INSTALL_DATA) $$i $(DESTDIR)$(DOC_DIR)/reference/html; \
done; \
@@ -41,7 +45,7 @@ uninstall-local: doxygen-build.stamp
if test "$$installfiles" = 'reference/html/*.html'; \
then echo '-- Nothing to uninstall' ; \
else \
for i in $$installfiles reference/html/doxygen.png reference/html/doxygen.css; do \
for i in $$installfiles $(INSTIMAGES) reference/html/doxygen.css; do \
echo '-- Unstalling '$$i ; \
rm $(DESTDIR)$(DOC_DIR)/$$i; \
done; \


BIN
doc/fsm.png View File

Before After
Width: 470  |  Height: 321  |  Size: 4.0KB

+ 54
- 0
doc/mainpage.dox View File

@@ -0,0 +1,54 @@
/*
* This is the main page of the JACK reference manual, built using
* doxygen.
*/

/**
@mainpage JACK Audio Connection Kit

@section intro Introduction

JACK is a low-latency audio server, written primarily for the
GNU/Linux operating system. It can connect several client
applications to an audio device, and allow them to share audio with
each other. Clients can run as separate processes like normal
applications, or within the JACK server as "plugins".

JACK differs from other audio servers in being designed from the
ground up for professional audio work, focusing on two key areas:
synchronous execution of all clients, and low latency operation.

@see <http://jackit.sourceforge.net>


@section contents Contents

The JACK programming interfaces are described in several header files:

- <jack/jack.h> defines most of the interfaces used by JACK clients.
- <jack/transport.h> provides a simple transport control mechanism
for starting, stopping and repositioning clients. This is described
in the @ref transport-design document.
- <jack/types.h> defines most of the data types for JACK.


@section license License

Copyright (C) 2001-2003 by Paul Davis and others.

JACK is free software; you can redistribute it and/or modify it under
the terms of the GNU GPL and LGPL licenses as published by the Free
Software Foundation, <http://www.gnu.org>. Most of JACK uses the GPL,
as noted in the source file headers. But, the library interfaces are
licensed under the LGPL, allowing proprietary programs to link with
JACK and call its services. You should have received a copy of these
Licenses along with the program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
USA.

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.

*/

+ 5
- 4
doc/reference.doxygen.in View File

@@ -17,7 +17,7 @@
# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
# by quotes) that should identify the project.

PROJECT_NAME = Jack
PROJECT_NAME = JACK

# The PROJECT_NUMBER tag can be used to enter a project or revision number.
# This could be handy for archiving the generated documentation or
@@ -301,7 +301,8 @@ WARN_LOGFILE =
# directories like "/usr/src/myproject". Separate the files or directories
# with spaces.

INPUT = @top_srcdir@/jack/jack.h \
INPUT = mainpage.dox transport.dox \
@top_srcdir@/jack/jack.h \
@top_srcdir@/jack/types.h \
@top_srcdir@/jack/transport.h

@@ -362,7 +363,7 @@ EXAMPLE_RECURSIVE = NO
# directories that contain image that are included in the documentation (see
# the \image command).

IMAGE_PATH =
IMAGE_PATH = .

# The INPUT_FILTER tag can be used to specify a program that doxygen should
# invoke to filter for each input file. Doxygen will invoke the filter program
@@ -502,7 +503,7 @@ TOC_EXPAND = NO
# top of each HTML page. The value NO (the default) enables the index and
# the value YES disables it.

DISABLE_INDEX = NO
DISABLE_INDEX = YES

# This tag can be used to set the number of enum values (range [1..20])
# that doxygen will group on one line in the generated HTML documentation.


+ 244
- 0
doc/transport.dox View File

@@ -0,0 +1,244 @@
/*
* This file documents the JACK transport design. It is part of the
* JACK reference manual, built using doxygen.
*/

/**

@page transport-design JACK Transport Design

The @ref index provides simple transport interfaces for starting,
stopping and repositioning a set of clients. This document describes
the overall design of these interfaces, their detailed specifications
are in <jack/transport.h>

- @ref requirements
- @ref overview
- @ref timebase
- @ref transportcontrol
- @ref transportclients
- @ref compatibility
- @ref issues


@subsection requirements Requirements

- We need sample-level accuracy for transport control. This implies
that the transport client logic has to be part of the realtime
process chain.

- We don't want to add another context switch. So, the transport
client logic has to run in the context of the client's process
thread. To avoid making an extra pass through the process graph, no
transport changes take effect until the following process cycle.
That way, the transport info is stable throughout each cycle.

- We want to allow multiple clients to change the transport state.
This is mostly a usability issue. Any client can start or stop
playback, or seek to a new location. The user need not switch
windows to accomplish these tasks.

- We want a way for clients with heavyweight state to sync up when
the user presses "play", before the transport starts rolling.

- We need to provide for ongoing binary compatibility as the
transport design evolves.


@subsection overview Overview

The former transport master role has been divided into two layers:

- @ref timebase - counting beats, frames, etc. on every cycle.
- @ref transportcontrol - start, stop and reposition the playback.

Existing clients continue to work in compatibility mode.


@subsection timebase Timebase Master

The timebase master continuously updates extended position
information, counting beats, SMPTE frames, 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.

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.

@code
typedef int (*JackTimebaseCallback)(jack_transport_state_t state,
jack_nframes_t nframes,
jack_position_t *pos,
int new_pos,
void *arg);
@endcode

When a new client takes over, the former timebase callback is no
longer called. Taking over the timebase may be done conditionally, in
which case the takeover fails when there is a master already. The
existing master can release it voluntarily, if desired.

@code
int jack_set_timebase_callback (jack_client_t *client,
int conditional,
JackTimebaseCallback timebase_callback,
void *arg);

int jack_release_timebase(jack_client_t *client);
@endcode

If the timebase master releases the timebase or exits the JACK graph
for any reason, the JACK engine takes over at the start of the next
process cycle. The transport state does not change. If rolling, it
continues to play, with frame numbers as the only available position
information.


@subsection transportcontrol Transport Control

The JACK engine itself manages stopping and starting of the transport.
Any client can make transport control requests at any time. These
requests take effect no sooner than the next process cycle, sometimes
later. The transport state is always valid, initially it is
::JackTransportStopped.

@code
void jack_transport_start (jack_client_t *client);
void jack_transport_stop (jack_client_t *client);
@endcode

The engine handles polling of slow-sync clients. When someone calls
jack_transport_start(), the engine resets the poll bits and changes to
a new state, ::JackTransportStarting. 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.

@code
typedef int (*JackSyncCallback)(jack_transport_state_t state,
jack_position_t *pos, void *arg);
@endcode

This callback is a realtime function that runs in the JACK process
thread.

@code
int jack_set_sync_callback (jack_client_t *client,
JackSyncCallback sync_callback, void *arg);
@endcode

Clients that don't declare a @a sync_callback are assumed to be ready
immediately, any time the transport wants to start. If a client no
longer requires slow-sync processing, it can set its @a sync_callback
to NULL.

@code
int jack_set_sync_timeout (jack_client_t *client,
jack_nframes_t timeout);
@endcode

There must be a @a timeout to prevent unresponsive slow-sync clients
from completely halting the transport mechanism. Two seconds is the
default. When this @a timeout expires, the transport will start
rolling, even if some slow-sync clients are still unready. The @a
sync_callback for these clients continues being invoked, giving them
an opportunity to catch up.

@code
int jack_transport_reposition (jack_client_t *client,
jack_position_t *pos);
int jack_transport_goto_frame (jack_client_t *client,
jack_nframes_t frame);
@endcode

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.


@image html fsm.png "Transport State Transition Diagram"
@image latex fsm.png "Transport State Transition Diagram"


@subsection transportclients Transport Clients

Transport clients were formerly known as "transport slaves". We want
to make it easy for almost every JACK client to be a transport client.

@code
jack_transport_state_t jack_transport_query (jack_client_t *client,
jack_position_t *pos);
@endcode

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.


@subsection compatibility Compatibility

We want to support the existing functions in compatibility mode as
deprecated interfaces for a while. The main reasons are:

- facilitate testing with clients that already have transport
support
- provide a clean migration path, so application developers are
not discouraged from supporting the transport interface
- inter-operate with ardour 0.9 beta.

So, these interfaces continue to be supported (but deprecated):

@code
typedef struct jack_transport_info_t;

void jack_set_transport_info (jack_client_t *client,
jack_transport_info_t *tinfo);
void jack_get_transport_info (jack_client_t *client,
jack_transport_info_t *tinfo);
int jack_engine_takeover_timebase (jack_client_t *);
@endcode


For compatibility with future changes, it would be good to avoid
structures entirely. Nevertheless, the jack_position_t structure
provides a convenient way to collect timebase information in several
formats that clearly all refer to a single moment. To minimize future
binary compatibility problems, this structure has some padding at the
end, making it possible to extend it without necessarily breaking
compatibility. New fields can be allocated from the padding area,
with access controlled by newly defined valid bits, all of which are
currently forced to zero. That allows the structure size and offsets
to remain constant.


@subsection issues Issues Not Addressed

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?
- 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
- internal timebase master or slow-sync clients
- probably a good idea
- haven't studied it
*/

+ 2
- 1
drivers/alsa/alsa_driver.c View File

@@ -911,7 +911,8 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float *delay
}
driver->poll_last = poll_ret;
driver->poll_next = poll_ret + driver->period_usecs;
driver->engine->control->current_time.usecs = poll_ret;
driver->engine->transport_cycle_start (driver->engine,
poll_ret);
}

#ifdef DEBUG_WAKEUP


+ 2
- 1
drivers/portaudio/portaudio_driver.c View File

@@ -163,7 +163,8 @@ portaudio_driver_read (portaudio_driver_t *driver, jack_nframes_t nframes)
}
driver->engine->control->current_time.usecs = jack_get_microseconds ();
driver->engine->transport_cycle_start (driver->engine,
jack_get_microseconds ());
return 0;
}



+ 22
- 75
example-clients/showtime.c View File

@@ -7,86 +7,40 @@
#include <jack/jack.h>
#include <jack/transport.h>

typedef struct {
volatile jack_nframes_t guard1;
volatile jack_transport_info_t info;
volatile jack_nframes_t guard2;
} guarded_transport_info_t;

guarded_transport_info_t now;
jack_client_t *client;


void
showtime ()
{
guarded_transport_info_t current;
int tries = 0;

/* Since "now" is updated from the process() thread every
* buffer period, we must copy it carefully to avoid getting
* an incoherent hash of multiple versions. */
do {
/* Throttle the busy wait if we don't get the a clean
* copy very quickly. */
if (tries > 10) {
usleep (20);
tries = 0;
}
current = now;
tries++;

} while (current.guard1 != current.guard2);

if (current.info.valid & JackTransportPosition)
printf ("frame: %7lu\t", current.info.frame);
else
printf ("frame: [-]\t");

if (current.info.valid & JackTransportState) {
switch (current.info.transport_state) {
case JackTransportStopped:
printf ("state: Stopped\t");
break;
case JackTransportRolling:
printf ("state: Rolling\t");
break;
case JackTransportLooping:
printf ("state: Looping\t");
break;
default:
printf ("state: [unknown]\t");
}
jack_position_t current;
jack_transport_state_t transport_state;

transport_state = jack_transport_query (client, &current);

printf ("frame: %7lu\t", current.frame);

switch (transport_state) {
case JackTransportStopped:
printf ("state: Stopped\t");
break;
case JackTransportRolling:
printf ("state: Rolling\t");
break;
case JackTransportStarting:
printf ("state: Starting\t");
break;
default:
printf ("state: [unknown]\t");
}
else
printf ("state: [-]\t");

if (current.info.valid & JackTransportLoop)
printf ("loop: %lu-%lu\t", current.info.loop_start,
current.info.loop_end);
else
printf ("loop: [-]\t");

if (current.info.valid & JackTransportBBT)
printf ("BBT: %3d|%d|%04d\n", current.info.bar,
current.info.beat, current.info.tick);
if (current.valid & JackPositionBBT)
printf ("BBT: %3d|%d|%04d\n",
current.bar, current.beat, current.tick);
else
printf ("BBT: [-]\n");
}

int
process (jack_nframes_t nframes, void *arg)
{
/* The guard flags contain a running counter of sufficiently
* high resolution, that showtime() can detect whether the
* last update is complete. */
now.guard1 = jack_frame_time(client);
jack_get_transport_info (client, (jack_transport_info_t *) &now.info);
now.guard2 = now.guard1;

return 0;
}

void
jack_shutdown (void *arg)
{
@@ -117,12 +71,6 @@ main (int argc, char *argv[])
signal (SIGHUP, signal_handler);
signal (SIGINT, signal_handler);

/* tell the JACK server to call `process()' whenever
there is work to be done.
*/

jack_set_process_callback (client, process, 0);

/* tell the JACK server to call `jack_shutdown()' if
it ever shuts down, either entirely, or if it
just decides to stop calling us.
@@ -145,4 +93,3 @@ main (int argc, char *argv[])
jack_client_close (client);
exit (0);
}


+ 53
- 47
example-clients/transport.c View File

@@ -33,32 +33,61 @@ char *package; /* program name */
int done = 0;

jack_client_t *client;
jack_transport_info_t tinfo; /* multi-threaded access */


/* JACK process() handler.
/* JACK timebase callback.
*
* Runs in a separate realtime thread. Must not wait.
* Runs in the process realtime thread. Must not wait.
*/
int process(jack_nframes_t nframes, void *arg)
void timebase(jack_transport_state_t state, jack_nframes_t nframes,
jack_position_t *pos, int new_pos, void *arg)
{
jack_set_transport_info(client, &tinfo);

/* frame number for next cycle */
if (tinfo.transport_state != JackTransportStopped) {
tinfo.frame += nframes;

/* When looping, adjust the frame number periodically. Make
* sure improper loop limits don't lock up the system in an
* infinite while(). */
if ((tinfo.transport_state == JackTransportLooping) &&
(tinfo.loop_end > tinfo.loop_start)) {
while (tinfo.frame >= tinfo.loop_end)
tinfo.frame -= (tinfo.loop_end - tinfo.loop_start);
}
if (state == JackTransportRolling)
pos->frame += nframes;

if ((pos->valid & JackPositionBBT) == 0) {

/* set "march time" parameters: 4/4, 120bpm */
pos->valid |= JackPositionBBT;
pos->beats_per_bar = 4.0;
pos->beat_type = 0.25;
pos->ticks_per_beat = 1920.0;
pos->beats_per_minute = 120.0;
}

return 0;
if (new_pos) {

/* Compute BBT info from frame number. This is relatively
* simple here, but could become complex if there were tempo
* or time signature changes. */

double min = (double) pos->frame / ((double) pos->frame_rate * 60.0);
long abs_tick = min * pos->beats_per_minute * pos->ticks_per_beat;
long abs_beat = abs_tick / pos->ticks_per_beat;

pos->bar = abs_beat / pos->beats_per_bar;
pos->beat = abs_beat - (pos->bar * pos->beats_per_bar) + 1;
pos->tick = abs_tick - (abs_beat * pos->ticks_per_beat);
pos->bar_start_tick = pos->bar * pos->beats_per_bar *
pos->ticks_per_beat;
pos->bar++; /* adjust start to bar 1 */

} else {

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

while (pos->tick >= pos->ticks_per_beat) {
pos->tick -= pos->ticks_per_beat;
if (++pos->beat > pos->beats_per_bar) {
pos->beat = 1;
++pos->bar;
pos->bar_start_tick +=
pos->beats_per_bar * pos->ticks_per_beat;
}
}
}
}

void jack_shutdown(void *arg)
@@ -83,25 +112,19 @@ void com_exit(char *arg)

void com_help(char *); /* forward declaration */

void com_loop(char *arg)
{
tinfo.transport_state = JackTransportLooping;
}

void com_play(char *arg)
{
tinfo.transport_state = JackTransportRolling;
jack_transport_start(client);
}

void com_rewind(char *arg)
{
tinfo.transport_state = JackTransportStopped;
tinfo.frame = 0;
jack_transport_goto_frame(client, 0);
}

void com_stop(char *arg)
{
tinfo.transport_state = JackTransportStopped;
jack_transport_stop(client);
}


@@ -120,7 +143,6 @@ typedef struct {
command_t commands[] = {
{ "exit", com_exit, "Exit transport program" },
{ "help", com_help, "Display help text" },
{ "loop", com_loop, "Start transport looping" },
{ "play", com_play, "Start transport rolling" },
{ "quit", com_exit, "Synonym for `exit'"},
{ "rewind", com_rewind, "Reset transport position to beginning" },
@@ -313,16 +335,6 @@ void command_loop()
}
}

void initialize_transport()
{
/* must run before jack_activate */
tinfo.loop_start = 0; /* default loop is one second */
tinfo.loop_end = jack_get_sample_rate(client);
tinfo.valid = JackTransportState|JackTransportPosition|JackTransportLoop;
com_rewind(NULL);
}


int main(int argc, char *argv[])
{
/* basename $0 */
@@ -343,17 +355,11 @@ int main(int argc, char *argv[])
signal(SIGHUP, signal_handler);
signal(SIGINT, signal_handler);

if (jack_engine_takeover_timebase(client) != 0) {
if (jack_set_timebase_callback(client, 1, timebase, NULL) != 0)
fprintf(stderr, "Unable to take over timebase.\n");
fprintf(stderr, "Is another transport master already running?\n");
return 1;
}

jack_set_process_callback(client, process, 0);
jack_on_shutdown(client, jack_shutdown, 0);

initialize_transport();

if (jack_activate(client)) {
fprintf(stderr, "cannot activate client");
return 1;


+ 23
- 1
jack/engine.h View File

@@ -36,7 +36,9 @@ struct _jack_engine {

int (*set_buffer_size)(struct _jack_engine *, jack_nframes_t frames);
int (*set_sample_rate)(struct _jack_engine *, jack_nframes_t frames);
int (*run_cycle)(struct _jack_engine *, jack_nframes_t nframes, float delayed_usecs);
int (*run_cycle)(struct _jack_engine *, jack_nframes_t nframes,
float delayed_usecs);
void (*transport_cycle_start)(struct _jack_engine *, jack_time_t time);

/* "private" sections starts here */

@@ -111,4 +113,24 @@ int jack_engine_load_driver (jack_engine_t *, int, char **);
void jack_set_asio_mode (jack_engine_t *, int yn);
void jack_dump_configuration(jack_engine_t *engine, int take_lock);

extern jack_client_internal_t *
jack_client_internal_by_id (jack_engine_t *engine, jack_client_id_t id);

static inline void jack_lock_graph (jack_engine_t* engine) {
DEBUG ("acquiring graph lock");
pthread_mutex_lock (&engine->client_lock);
}

static inline int jack_try_lock_graph (jack_engine_t *engine)
{
DEBUG ("TRYING to acquiring graph lock");
return pthread_mutex_trylock (&engine->client_lock);
}

static inline void jack_unlock_graph (jack_engine_t* engine)
{
DEBUG ("releasing graph lock");
pthread_mutex_unlock (&engine->client_lock);
}

#endif /* __jack_engine_h__ */

+ 42
- 11
jack/internal.h View File

@@ -1,4 +1,9 @@
/*
Internal shared data and functions.

If you edit this file, you should carefully consider changing the
JACK_PROTOCOL_VERSION in configure.in.

Copyright (C) 2001-2003 Paul Davis
This program is free software; you can redistribute it and/or modify
@@ -68,6 +73,12 @@ typedef struct {
size_t offset;
} jack_port_buffer_info_t;

typedef enum {
TransportCommandNone = 0,
TransportCommandPlay = 1,
TransportCommandStop = 2,
} transport_command_t;

typedef struct {
volatile jack_time_t guard1;
volatile jack_nframes_t frames;
@@ -82,10 +93,18 @@ typedef struct {

#define JACK_MAX_PORT_TYPES 4

/* JACK engine shared memory data structure. */
typedef struct {

jack_transport_info_t current_time;
jack_transport_info_t pending_time;
jack_transport_state_t transport_state;
volatile transport_command_t 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 */
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 */
jack_frame_timer_t frame_timer;
int internal;
jack_nframes_t frames_at_cycle_start;
@@ -100,7 +119,7 @@ typedef struct {
jack_engine_t *engine;
unsigned long n_port_types;
jack_port_type_info_t port_types[JACK_MAX_PORT_TYPES];
jack_port_shared_t ports[0];
jack_port_shared_t ports[0];

} jack_control_t;

@@ -147,17 +166,19 @@ typedef enum {
Finished
} jack_client_state_t;

/* JACK client shared memory data structure. */
typedef volatile struct {

volatile jack_client_id_t id; /* w: engine r: engine and client */
volatile jack_nframes_t nframes; /* w: engine r: client */
volatile jack_client_id_t id; /* w: engine r: engine and client */
volatile jack_nframes_t nframes; /* w: engine r: client */
volatile jack_client_state_t state; /* w: engine and client r: engine */
volatile char name[JACK_CLIENT_NAME_SIZE+1];
volatile ClientType type; /* w: engine r: engine and client */
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 pid_t pid; /* w: client r: engine; pid of client */
volatile char sync_ready : 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;
volatile unsigned long long finished_at;
@@ -176,16 +197,17 @@ typedef volatile struct {
void *graph_order_arg;
JackXRunCallback xrun;
void *xrun_arg;
JackSyncCallback sync_cb;
void *sync_arg;
JackTimebaseCallback timebase_cb;
void *timebase_arg;

/* OOP clients: set by libjack
IP clients: set by engine
*/
/* external clients: set by libjack
* internal clients: set by engine */
int (*deliver_request)(void*, jack_request_t*);
void *deliver_arg;

/* for engine use only */

void *private_client;

} jack_client_control_t;
@@ -256,6 +278,8 @@ typedef enum {
SetClientCapabilities = 9,
GetPortConnections = 10,
GetPortNConnections = 11,
ResetTimeBaseClient = 12,
SetSyncClient = 13,
} RequestType;

struct _jack_request {
@@ -278,6 +302,10 @@ struct _jack_request {
unsigned int nports;
const char **ports;
} port_connections;
struct {
jack_client_id_t client_id;
int conditional;
} timebase;
jack_client_id_t client_id;
jack_nframes_t nframes;
} x;
@@ -332,5 +360,8 @@ extern jack_port_type_info_t jack_builtin_port_types[];

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

#endif /* __jack_internal_h__ */


+ 10
- 8
jack/jack.h View File

@@ -536,11 +536,14 @@ jack_port_t *jack_port_by_name (jack_client_t *, const char *portname);
jack_port_t *jack_port_by_id (const jack_client_t *client, jack_port_id_t id);

/**
* If a client is told (by the user) to become the timebase
* for the entire system, it calls this function. If it
* returns zero, then the client has the responsibility to
* call jack_set_transport_info()) at the end of its process()
* callback.
* If a client is told (by the user) to become the timebase for the
* entire system, it calls this function. If it returns zero, then the
* client has the responsibility to call jack_set_transport_info() at
* the end of its process callback.
*
* @deprecated This function is only for compatibility with earlier
* transport implementations. Instead, see <jack/transport.h> and use
* jack_set_timebase_callback().
*
* @return 0 on success, otherwise a non-zero error code
*/
@@ -552,9 +555,8 @@ int jack_engine_takeover_timebase (jack_client_t *);
void jack_update_time (jack_client_t *, jack_nframes_t);

/**
* This estimates the time that has passed since the
* start jack server started calling the process()
* callbacks of all its clients.
* This estimates the time that has passed since the JACK server
* started calling the process callbacks of all its clients.
*/
jack_nframes_t jack_frames_since_cycle_start (const jack_client_t *);



+ 329
- 39
jack/transport.h View File

@@ -1,5 +1,6 @@
/*
Copyright (C) 2002 Paul Davis
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
@@ -28,56 +29,339 @@ extern "C" {
#include <jack/types.h>

/**
* Possible transport states.
* Transport states.
*/
typedef enum {

JackTransportStopped,
JackTransportRolling,
JackTransportLooping
/* the order matters for binary compatibility */
JackTransportStopped = 0, /**< Transport halted */
JackTransportRolling = 1, /**< Transport playing */
JackTransportLooping = 2, /**< For OLD_TRANSPORT, now ignored */
JackTransportStarting = 3, /**< Waiting for sync ready */

} jack_transport_state_t;

/**
* Bitfield of all possible transport info struct
* fields.
* Optional struct jack_position_t fields.
*/
typedef enum {

JackPositionBBT = 0x10, /**< Bar, Beat, Tick */

} jack_position_bits_t;

#define EXTENDED_TIME_INFO

/**
* Struct for transport position information.
*/
typedef struct {
/* these two cannot be set from clients: the server sets them */
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: */
int bar; /**< current bar */
int beat; /**< current beat-within-bar */
int tick; /**< current tick-within-beat */
double bar_start_tick;

float beats_per_bar;
float beat_type;
double ticks_per_beat;
double beats_per_minute;

/* 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];

/* When (guard_usecs == usecs) the entire structure is consistent.
* This is set by server. */
jack_time_t guard_usecs; /**< guard copy of usecs */

} jack_position_t;

/**
* Called by the timebase master to release itself from that
* responsibility.
*
* If the timebase master releases the timebase or leaves the JACK
* graph for any reason, the JACK engine takes over at the start of
* the next process cycle. The transport state does not change. If
* rolling, it continues to play, with frame numbers as the only
* available position information.
*
* @see jack_set_timebase_callback
*
* @param client the JACK client structure.
*
* @return 0 on success, otherwise a non-zero error code.
*/
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.
*
* @see jack_transport_info_t
* 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.
*
* @param state current transport state.
* @param pos new transport position.
* @param arg the argument supplied by jack_set_sync_callback().
*
* @return true (non-zero) when ready to roll.
*/
typedef int (*JackSyncCallback)(jack_transport_state_t state,
jack_position_t *pos,
void *arg);

/**
* 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.
*
* @param client the JACK client structure.
* @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.
*
* @return 0 on success, otherwise a non-zero error code.
*/
int jack_set_sync_callback (jack_client_t *client,
JackSyncCallback sync_callback,
void *arg);

/**
* Set the timeout value for slow-sync clients.
*
* 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
* 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.
*
* @return 0 on success, otherwise a non-zero error code.
*/
int jack_set_sync_timeout (jack_client_t *client,
jack_nframes_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.
*
* 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.
*
* The timebase master should not use its @a pos argument to set a
* discontinuous position. Use jack_transport_reposition() or
* jack_transport_goto_frame(), instead.
*
* @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 arg the argument supplied by jack_set_timebase_callback().
*/
typedef void (*JackTimebaseCallback)(jack_transport_state_t state,
jack_nframes_t nframes,
jack_position_t *pos,
int new_pos,
void *arg);

/**
* 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.
*
* 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.
*
* @param client the JACK client structure.
* @param conditional non-zero for a conditional request.
* @param timebase_callback is a realtime function that returns
* position information.
* @param arg an argument for the @a timebase_callback function.
*
* @return
* - 0 on success;
* - EBUSY if a conditional request fails because there is already a
* timebase master;
* - other non-zero error code.
*/
int jack_set_timebase_callback (jack_client_t *client,
int conditional,
JackTimebaseCallback timebase_callback,
void *arg);

/**
* Reposition 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.
*
* @see jack_transport_reposition, jack_set_sync_callback
*
* @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.
*/
int jack_transport_goto_frame (jack_client_t *client,
jack_nframes_t frame);

/**
* 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.
*
* @param client the JACK client structure.
* @param pos current position structure, @a pos->valid describes
* which fields contain valid data.
*
* @return Current transport state.
*/
jack_transport_state_t jack_transport_query (jack_client_t *client,
jack_position_t *pos);

/**
* 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.
*
* @see jack_transport_goto_frame, jack_set_sync_callback
*
* @param client the JACK client structure.
* @param pos requested new transport position.
*
* @return 0 if valid request, otherwise a non-zero error code.
*/
int jack_transport_reposition (jack_client_t *client,
jack_position_t *pos);

/**
* Start the JACK transport rolling.
*
* 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.
*
* @see jack_set_sync_callback
*
* @param client the JACK client structure.
*/
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.
*
* @param client the JACK client structure.
*/
void jack_transport_stop (jack_client_t *client);


/*********************************************************************
* The following interfaces are DEPRECATED. They are only provided
* for compatibility with the earlier JACK transport implementation.
*********************************************************************/

/**
* Optional struct jack_transport_info_t fields.
*
* @see jack_position_bits_t.
*/
typedef enum {

JackTransportState = 0x1,
JackTransportPosition = 0x2,
JackTransportLoop = 0x4,
JackTransportSMPTE = 0x8,
JackTransportBBT = 0x10,
JackTransportState = 0x1, /**< Transport state */
JackTransportPosition = 0x2, /**< Frame number */
JackTransportLoop = 0x4, /**< Loop boundaries (ignored) */
JackTransportSMPTE = 0x8, /**< SMPTE (ignored) */
JackTransportBBT = 0x10, /**< Bar, Beat, Tick */

} jack_transport_bits_t;

#define EXTENDED_TIME_INFO \

/**
* Struct for transport status information.
* Deprecated struct for transport position information.
*
* @deprecated This is for compatibility with the earlier transport
* interface. Use the jack_position_t struct, instead.
*/
typedef struct {
/* these two cannot be set from clients: the server sets them */

jack_nframes_t frame_rate; /**< current frame rate (per second) */
jack_time_t usecs; /**< monotonic, free-rolling */
jack_nframes_t frame_rate; /**< current frame rate (per second) */
jack_time_t usecs; /**< monotonic, free-rolling */

jack_transport_bits_t valid; /**< which fields are legal to read */
jack_transport_bits_t valid; /**< which fields are legal to read */
jack_transport_state_t transport_state;
jack_nframes_t frame;
jack_nframes_t loop_start;
jack_nframes_t loop_end;

long smpte_offset; /**< SMPTE offset (SMPTE frame when frame = 0) */
float smpte_frame_rate; /**< 29.97, 30, 24 etc. */
long smpte_offset; /**< SMPTE offset (from frame 0) */
float smpte_frame_rate; /**< 29.97, 30, 24 etc. */

int bar; /**< current bar */
int beat; /**< current beat-within-bar */
int tick; /**< current tick-within-beat */
int bar;
int beat;
int tick;
double bar_start_tick;

float beats_per_bar;
@@ -86,31 +370,37 @@ typedef struct {
double beats_per_minute;

} jack_transport_info_t;
/**
* Sets the transport state for the next engine
* cycle.
* Gets the current transport info structure (deprecated).
*
* The 'valid' field of the tinfo struct should contain
* a bitmask of all transport info fields that are set
* in tinfo.
* @param client the JACK client structure.
* @param tinfo current transport info structure. The "valid" field
* describes which fields contain valid data.
*
* @pre Caller must be the current timebase master. Must be called
* from the process() thread.
* @deprecated This is for compatibility with the earlier transport
* interface. Use jack_transport_query(), instead.
*
* @pre Must be called from the process thread.
*/
void jack_set_transport_info (jack_client_t *client,
void jack_get_transport_info (jack_client_t *client,
jack_transport_info_t *tinfo);
/**
* Gets the current transport state.
* Set the transport info structure (deprecated).
*
* @param client the JACK client structure.
* @param tinfo used to return a new transport info structure used
* for the next process cycle. The "valid" field must say which
* fields contain valid data.
*
* On return, the 'valid' field of the tinfo struct will contain
* a bitmask of all transport info fields that are legal to
* use.
* @deprecated This is for compatibility with the earlier transport
* interface. Define a ::JackTimebaseCallback, instead.
*
* @pre Must be called from the process() thread.
* @pre Caller must be the current timebase master. Must be called
* from the process thread.
*/
void jack_get_transport_info (jack_client_t *client,
void jack_set_transport_info (jack_client_t *client,
jack_transport_info_t *tinfo);

#ifdef __cplusplus


+ 18
- 38
jackd/engine.c View File

@@ -85,7 +85,6 @@ static void jack_remove_client (jack_engine_t *engine, jack_client_internal_t *c
static void jack_client_delete (jack_engine_t *, jack_client_internal_t *);

static jack_client_internal_t *jack_client_internal_new (jack_engine_t *engine, int fd, jack_client_connect_request_t *);
static jack_client_internal_t *jack_client_internal_by_id (jack_engine_t *engine, jack_client_id_t id);

static void jack_sort_graph (jack_engine_t *engine);
static int jack_rechain_graph (jack_engine_t *engine);
@@ -128,23 +127,6 @@ jack_client_is_internal (jack_client_internal_t *client)
return (client->control->type == ClientInternal) || (client->control->type == ClientDriver);
}

static inline void jack_lock_graph (jack_engine_t* engine) {
DEBUG ("acquiring graph lock");
pthread_mutex_lock (&engine->client_lock);
}

static inline int jack_try_lock_graph (jack_engine_t *engine)
{
DEBUG ("TRYING to acquiring graph lock");
return pthread_mutex_trylock (&engine->client_lock);
}

static inline void jack_unlock_graph (jack_engine_t* engine)
{
DEBUG ("releasing graph lock");
pthread_mutex_unlock (&engine->client_lock);
}

static inline void
jack_engine_reset_rolling_usecs (jack_engine_t *engine)
{
@@ -426,6 +408,9 @@ jack_process_internal(jack_engine_t *engine, JSList *node, jack_nframes_t nframe
return NULL; /* will stop the loop */
}

//JOQ: can an internal client be slow sync?
//JOQ: can an internal client have a timebase master?

} else {
DEBUG ("internal client has no process() function");

@@ -1338,7 +1323,7 @@ jack_client_deactivate (jack_engine_t *engine, jack_client_id_t id)

if (client == engine->timebase_client) {
engine->timebase_client = 0;
jack_transport_reset (engine);
jack_timebase_exit (engine);
}
for (portnode = client->ports; portnode; portnode = jack_slist_next (portnode)) {
@@ -1356,22 +1341,6 @@ jack_client_deactivate (jack_engine_t *engine, jack_client_id_t id)
return ret;
}

static int
jack_set_timebase (jack_engine_t *engine, jack_client_id_t client)
{
int ret = -1;

jack_lock_graph (engine);

if ((engine->timebase_client = jack_client_internal_by_id (engine, client)) != 0) {
ret = 0;
}

jack_unlock_graph (engine);

return ret;
}

static int
handle_client_socket_error (jack_engine_t *engine, int fd)
{
@@ -1468,7 +1437,17 @@ do_request (jack_engine_t *engine, jack_request_t *req, int *reply_fd)
break;

case SetTimeBaseClient:
req->status = jack_set_timebase (engine, req->x.client_id);
req->status = jack_timebase_set (engine,
req->x.timebase.client_id,
req->x.timebase.conditional);
break;

case ResetTimeBaseClient:
req->status = jack_timebase_reset (engine, req->x.client_id);
break;

case SetSyncClient:
req->status = jack_set_sync_client (engine, req->x.client_id);
break;

#ifdef USE_CAPABILITIES
@@ -1695,6 +1674,7 @@ jack_engine_new (int realtime, int rtpriority, int verbose, int client_timeout)
engine->set_sample_rate = jack_set_sample_rate;
engine->set_buffer_size = jack_set_buffer_size;
engine->run_cycle = jack_run_cycle;
engine->transport_cycle_start = jack_transport_cycle_start;
engine->client_timeout_msecs = client_timeout;

engine->next_client_id = 1;
@@ -1804,7 +1784,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_transport_reset (engine);
jack_timebase_init (engine);
engine->control->internal = 0;

engine->control->has_capabilities = 0;
@@ -2311,7 +2291,7 @@ jack_zombify_client (jack_engine_t *engine, jack_client_internal_t *client)
if (client == engine->timebase_client) {
engine->timebase_client = 0;
jack_transport_reset (engine);
jack_timebase_exit (engine);
}

jack_client_disconnect (engine, client);


+ 230
- 33
jackd/transengine.c View File

@@ -4,60 +4,257 @@
Copyright (C) 2001-2003 Paul Davis
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.
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.
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 Lesser General Public License for more details.
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
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 <config.h>
#include <errno.h>
#include <jack/internal.h>
#include <jack/engine.h>
#include "transengine.h"


/*********************** driver functions ***********************/

int
jack_set_sample_rate (jack_engine_t *engine, jack_nframes_t nframes)
{
engine->control->current_time.frame_rate = nframes;
engine->control->pending_time.frame_rate = nframes;
jack_control_t *ectl = engine->control;

ectl->current_time.frame_rate = nframes;
ectl->pending_time.frame_rate = nframes;
return 0;
}

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


/********************* RPC request handlers *********************/

/* for SetSyncClient */
int
jack_set_sync_client (jack_engine_t *engine, jack_client_id_t client)
{
int ret;
jack_client_internal_t *clintl;

jack_lock_graph (engine);

clintl = jack_client_internal_by_id (engine, client);

if (clintl) {
clintl->control->sync_ready = 0;
engine->control->sync_clients++;
ret = 0;
} else
ret = EINVAL;

jack_unlock_graph (engine);

return ret;
}

/* for ResetTimeBaseClient */
int
jack_timebase_reset (jack_engine_t *engine, jack_client_id_t client)
{
int ret;

jack_lock_graph (engine);

if ((engine->timebase_client =
jack_client_internal_by_id (engine, client)) != 0) {

engine->timebase_client = 0;
engine->control->pending_time.valid = 0;
ret = 0;
} else
ret = EINVAL;

jack_unlock_graph (engine);

return ret;
}

/* for SetTimeBaseClient */
int
jack_timebase_set (jack_engine_t *engine,
jack_client_id_t client, int conditional)
{
int ret;

jack_lock_graph (engine);

if (conditional && engine->timebase_client)
ret = EBUSY;
else if ((engine->timebase_client =
jack_client_internal_by_id (engine, client)) != 0)
ret = 0;
else
ret = -1;

jack_unlock_graph (engine);

return ret;
}


/******************** engine.c subroutines ********************/

/* start polling slow-sync clients */
void
jack_transport_cycle_end (jack_engine_t *engine)
jack_start_sync_poll(jack_engine_t *engine)
{
jack_control_t *ectl = engine->control;
JSList *node;
long sync_count = 0; /* number of slow-sync clients */

jack_lock_graph (engine);

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

ectl->sync_remain = ectl->sync_clients = sync_count;
ectl->sync_cycle = 0;

jack_unlock_graph (engine);
}

/* when timebase master exits the graph */
void
jack_timebase_exit (jack_engine_t *engine)
{
jack_control_t *ectl = engine->control;

ectl->current_time.valid = 0;
ectl->pending_time.valid = 0;
}

/* engine initialization */
void
jack_timebase_init (jack_engine_t *engine)
{
jack_control_t *ctl = engine->control;
jack_control_t *ectl = engine->control;

/* maintain the current_time.usecs and frame_rate values,
since clients are not permitted to set them.
*/
ctl->pending_time.usecs = ctl->current_time.usecs;
ctl->pending_time.frame_rate = ctl->current_time.frame_rate;
ctl->current_time = ctl->pending_time;
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;
}

/* This is the heart of the transport control. It runs at the end of
* every process cycle.
*/
void
jack_transport_reset (jack_engine_t *engine)
{
#ifdef OLD_TRANSPORT
jack_control_t *ctl = engine->control;

ctl->current_time.frame = 0;
ctl->pending_time.frame = 0;
ctl->current_time.transport_state = JackTransportStopped;
ctl->pending_time.transport_state = JackTransportStopped;
ctl->current_time.valid =
JackTransportState|JackTransportPosition;
ctl->pending_time.valid =
JackTransportState|JackTransportPosition;
#endif /* OLD_TRANSPORT */
jack_transport_cycle_end (jack_engine_t *engine)
{
jack_control_t *ectl = engine->control;
transport_command_t cmd; /* latest transport command */
jack_time_t repos; /* nonzero if reposition requested */

/* update timebase, if needed */
if ((engine->timebase_client == 0) &&
(ectl->transport_state == JackTransportRolling)) {
ectl->pending_time.frame =
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>.
*/
cmd = ectl->transport_cmd;
ectl->transport_cmd = TransportCommandNone;

repos = ectl->request_time.usecs;
if (repos) {
/* request_time could change during this copy */
jack_transport_copy_position(&ectl->request_time,
&ectl->pending_time);
ectl->request_time.usecs = 0; /* empty request buffer */
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. */
ectl->pending_time.guard_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;

/* 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))
ectl->transport_state = JackTransportRolling;
ectl->sync_cycle = 0;
}

/* state transition switch */
switch (ectl->transport_state) {

case JackTransportStopped:
if (cmd == TransportCommandPlay) {
if (ectl->sync_clients) {
ectl->transport_state = JackTransportStarting;
jack_start_sync_poll(engine);
} else
ectl->transport_state = JackTransportRolling;
}
break;

case JackTransportStarting:
case JackTransportRolling:
if (cmd == TransportCommandStop) {
ectl->transport_state = JackTransportStopped;
ectl->sync_remain = 0; /* halt polling */
} else if (repos) {
if (ectl->sync_clients) {
ectl->transport_state = JackTransportStarting;
jack_start_sync_poll(engine);
}
else
ectl->transport_state = JackTransportRolling;
}
break;

default:
jack_error ("invalid JACK transport state: %d",
ectl->transport_state);
}
return;
}

+ 15
- 10
jackd/transengine.h View File

@@ -1,24 +1,29 @@
/*
Internal interfaces to JACK transport engine.
Internal interfaces for JACK transport engine.

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 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.
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.
*/

int jack_set_sample_rate (jack_engine_t *engine,
jack_nframes_t nframes);
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_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);
void jack_transport_cycle_end (jack_engine_t *engine);
void jack_transport_reset (jack_engine_t *engine);

+ 7
- 71
libjack/client.c View File

@@ -39,7 +39,6 @@
#include <stdio.h>
#include <stdint.h>
#include <regex.h>
#include <math.h>

#include <config.h>

@@ -792,6 +791,9 @@ jack_client_thread (void *arg)

control->state = Running;

if (control->sync_cb)
jack_call_sync_client (client);

if (control->process) {
if (control->process (control->nframes, control->process_arg) == 0) {
control->state = Finished;
@@ -799,7 +801,10 @@ jack_client_thread (void *arg)
} else {
control->state = Finished;
}

if (control->timebase_cb)
jack_call_timebase_master (client);

control->finished_at = jack_get_microseconds();

#ifdef WITH_TIMESTAMPS
@@ -1279,12 +1284,6 @@ unsigned long jack_get_buffer_size (jack_client_t *client)
return client->engine->buffer_size;
}

unsigned long jack_get_sample_rate (jack_client_t *client)

{
return client->engine->current_time.frame_rate;
}

int
jack_connect (jack_client_t *client, const char *source_port, const char *destination_port)

@@ -1384,23 +1383,6 @@ jack_set_buffer_size_callback (jack_client_t *client, JackBufferSizeCallback cal
return 0;
}

int
jack_set_sample_rate_callback (jack_client_t *client, JackSampleRateCallback callback, void *arg)
{
if (client->control->active) {
jack_error ("You cannot set callbacks on an active client.");
return -1;
}
client->control->srate_arg = arg;
client->control->srate = callback;

/* Now invoke it */

callback (client->engine->current_time.frame_rate, arg);

return 0;
}

int
jack_set_port_registration_callback(jack_client_t *client, JackPortRegistrationCallback callback, void *arg)

@@ -1509,52 +1491,6 @@ jack_get_ports (jack_client_t *client,
return matching_ports;
}

static inline void
jack_read_frame_time (const jack_client_t *client, jack_frame_timer_t *copy)
{
int tries = 0;

do {
/* throttle the busy wait if we don't get
the answer very quickly.
*/

if (tries > 10) {
usleep (20);
tries = 0;
}

*copy = client->engine->frame_timer;

tries++;

} while (copy->guard1 != copy->guard2);
}

jack_nframes_t
jack_frames_since_cycle_start (const jack_client_t *client)
{
float usecs;

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

jack_nframes_t
jack_frame_time (const jack_client_t *client)
{
jack_frame_timer_t current;
float usecs;
jack_nframes_t elapsed;

jack_read_frame_time (client, &current);
usecs = jack_get_microseconds() - current.stamp;
elapsed = (jack_nframes_t) floor ((((float) client->engine->current_time.frame_rate) / 1000000.0f) * usecs);
return current.frames + elapsed;
}

float
jack_cpu_load (jack_client_t *client)
{


+ 6
- 1
libjack/local.h View File

@@ -1,6 +1,7 @@
#ifndef __jack_libjack_local_h__
#define __jack_libjack_local_h__

/* Client data structure, in the client's address space. */
struct _jack_client {

jack_control_t *engine;
@@ -17,6 +18,7 @@ struct _jack_client {
void *on_shutdown_arg;
char thread_ok : 1;
char first_active : 1;
char new_timebase : 1;
pthread_t thread_id;
#if defined(__APPLE__) && defined(__POWERPC__)
@@ -30,7 +32,10 @@ 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;

extern void *jack_zero_filled_buffer;

#endif /* __jack_libjack_local_h__ */

+ 333
- 14
libjack/transclient.c View File

@@ -4,38 +4,308 @@
Copyright (C) 2001-2003 Paul Davis
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.
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.
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 Lesser General Public License for more details.
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
You should have received a copy of the GNU Lesser General Public
License along with this program; if not, write to the Free
Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA.
*/

#include <config.h>
#include <errno.h>
#include <math.h>
#include <jack/internal.h>
#include "local.h"


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

static inline void
jack_read_frame_time (const jack_client_t *client, jack_frame_timer_t *copy)
{
int tries = 0;

do {
/* throttle the busy wait if we don't get
the answer very quickly.
*/

if (tries > 10) {
usleep (20);
tries = 0;
}

*copy = client->engine->frame_timer;

tries++;

} while (copy->guard1 != copy->guard2);
}

/* copy a JACK transpor