git-svn-id: svn+ssh://jackaudio.org/trunk/jack@444 0c269be4-1314-0410-8aa9-9f06e86f4224tags/0.109.0
@@ -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) | |||
----------------------------------------------------------------------- |
@@ -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) | |||
@@ -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; \ | |||
@@ -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. | |||
*/ |
@@ -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. | |||
@@ -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 | |||
*/ |
@@ -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 | |||
@@ -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; | |||
} | |||
@@ -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, ¤t); | |||
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); | |||
} | |||
@@ -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; | |||
@@ -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__ */ |
@@ -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__ */ | |||
@@ -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 *); | |||
@@ -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 | |||
@@ -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); | |||
@@ -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; | |||
} |
@@ -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); |
@@ -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, ¤t); | |||
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) | |||
{ | |||
@@ -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__ */ |
@@ -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 |