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) | TO BE DECIDED (owner) | ||||
- whether to hide the transport.h structs from clients (kaiv) | |||||
- dropping the use of CAP_RESOURCE (nando) | - dropping the use of CAP_RESOURCE (nando) | ||||
- whether to default to triangular dithering the at 16bit (swh) | - whether to default to triangular dithering the at 16bit (swh) | ||||
TODO for 1.0 (owner) | 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 | - ensure that UST/MSC pairs work for transport API | ||||
- resolve helper thread design question? | - resolve helper thread design question? | ||||
@@ -61,5 +57,7 @@ CLOSED (date,who,comment) | |||||
- alsa-driver parameter parsing patch (2003/02/25, joq, done) | - alsa-driver parameter parsing patch (2003/02/25, joq, done) | ||||
- jack_deactive called unconditionally in jack_client_close (2003/02/05, kaiv, fixed) | - 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) | - 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 changes are made | ||||
dnl --- | dnl --- | ||||
JACK_MAJOR_VERSION=0 | JACK_MAJOR_VERSION=0 | ||||
JACK_MINOR_VERSION=75 | |||||
JACK_MICRO_VERSION=0 | |||||
JACK_MINOR_VERSION=76 | |||||
JACK_MICRO_VERSION=1 | |||||
dnl --- | dnl --- | ||||
@@ -25,7 +25,7 @@ dnl made to the way libjack communicates with jackd | |||||
dnl that would break applications linked with an older | dnl that would break applications linked with an older | ||||
dnl version of libjack. | dnl version of libjack. | ||||
dnl --- | dnl --- | ||||
JACK_PROTOCOL_VERSION=6 | |||||
JACK_PROTOCOL_VERSION=7 | |||||
dnl --- | dnl --- | ||||
dnl HOWTO: updating the libjack interface version | 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 number. | ||||
dnl --- | dnl --- | ||||
JACK_API_CURRENT=0 | JACK_API_CURRENT=0 | ||||
JACK_API_REVISION=19 | |||||
JACK_API_REVISION=20 | |||||
JACK_API_AGE=0 | JACK_API_AGE=0 | ||||
AC_SUBST(JACK_MAJOR_VERSION) | AC_SUBST(JACK_MAJOR_VERSION) | ||||
@@ -3,15 +3,19 @@ MAINTAINERCLEANFILES=Makefile.in | |||||
DOX=reference.doxygen | DOX=reference.doxygen | ||||
EXTRA_DIST= | EXTRA_DIST= | ||||
INSTIMAGES=reference/html/doxygen.png reference/html/fsm.png | |||||
DOC_STAMPS=html-build.stamp | DOC_STAMPS=html-build.stamp | ||||
DOC_DIR=$(HTML_DIR) | DOC_DIR=$(HTML_DIR) | ||||
all-local: doxygen-build.stamp | 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 ***' | @echo '*** Running doxygen ***' | ||||
doxygen $(DOX) | doxygen $(DOX) | ||||
touch doxygen-build.stamp | touch doxygen-build.stamp | ||||
@@ -29,7 +33,7 @@ install-data-local: | |||||
if test "$$installfiles" = 'reference/html/*.html'; \ | if test "$$installfiles" = 'reference/html/*.html'; \ | ||||
then echo '-- Nothing to install' ; \ | then echo '-- Nothing to install' ; \ | ||||
else \ | 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 ; \ | echo '-- Installing '$$i ; \ | ||||
$(INSTALL_DATA) $$i $(DESTDIR)$(DOC_DIR)/reference/html; \ | $(INSTALL_DATA) $$i $(DESTDIR)$(DOC_DIR)/reference/html; \ | ||||
done; \ | done; \ | ||||
@@ -41,7 +45,7 @@ uninstall-local: doxygen-build.stamp | |||||
if test "$$installfiles" = 'reference/html/*.html'; \ | if test "$$installfiles" = 'reference/html/*.html'; \ | ||||
then echo '-- Nothing to uninstall' ; \ | then echo '-- Nothing to uninstall' ; \ | ||||
else \ | 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 ; \ | echo '-- Unstalling '$$i ; \ | ||||
rm $(DESTDIR)$(DOC_DIR)/$$i; \ | rm $(DESTDIR)$(DOC_DIR)/$$i; \ | ||||
done; \ | 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 | # The PROJECT_NAME tag is a single word (or a sequence of words surrounded | ||||
# by quotes) that should identify the project. | # 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. | # The PROJECT_NUMBER tag can be used to enter a project or revision number. | ||||
# This could be handy for archiving the generated documentation or | # 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 | # directories like "/usr/src/myproject". Separate the files or directories | ||||
# with spaces. | # 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/types.h \ | ||||
@top_srcdir@/jack/transport.h | @top_srcdir@/jack/transport.h | ||||
@@ -362,7 +363,7 @@ EXAMPLE_RECURSIVE = NO | |||||
# directories that contain image that are included in the documentation (see | # directories that contain image that are included in the documentation (see | ||||
# the \image command). | # the \image command). | ||||
IMAGE_PATH = | |||||
IMAGE_PATH = . | |||||
# The INPUT_FILTER tag can be used to specify a program that doxygen should | # 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 | # 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 | # top of each HTML page. The value NO (the default) enables the index and | ||||
# the value YES disables it. | # 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]) | # 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. | # 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_last = poll_ret; | ||||
driver->poll_next = poll_ret + driver->period_usecs; | 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 | #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; | return 0; | ||||
} | } | ||||
@@ -7,86 +7,40 @@ | |||||
#include <jack/jack.h> | #include <jack/jack.h> | ||||
#include <jack/transport.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; | jack_client_t *client; | ||||
void | void | ||||
showtime () | 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 | else | ||||
printf ("BBT: [-]\n"); | 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 | void | ||||
jack_shutdown (void *arg) | jack_shutdown (void *arg) | ||||
{ | { | ||||
@@ -117,12 +71,6 @@ main (int argc, char *argv[]) | |||||
signal (SIGHUP, signal_handler); | signal (SIGHUP, signal_handler); | ||||
signal (SIGINT, 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 | /* tell the JACK server to call `jack_shutdown()' if | ||||
it ever shuts down, either entirely, or if it | it ever shuts down, either entirely, or if it | ||||
just decides to stop calling us. | just decides to stop calling us. | ||||
@@ -145,4 +93,3 @@ main (int argc, char *argv[]) | |||||
jack_client_close (client); | jack_client_close (client); | ||||
exit (0); | exit (0); | ||||
} | } | ||||
@@ -33,32 +33,61 @@ char *package; /* program name */ | |||||
int done = 0; | int done = 0; | ||||
jack_client_t *client; | 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) | void jack_shutdown(void *arg) | ||||
@@ -83,25 +112,19 @@ void com_exit(char *arg) | |||||
void com_help(char *); /* forward declaration */ | void com_help(char *); /* forward declaration */ | ||||
void com_loop(char *arg) | |||||
{ | |||||
tinfo.transport_state = JackTransportLooping; | |||||
} | |||||
void com_play(char *arg) | void com_play(char *arg) | ||||
{ | { | ||||
tinfo.transport_state = JackTransportRolling; | |||||
jack_transport_start(client); | |||||
} | } | ||||
void com_rewind(char *arg) | void com_rewind(char *arg) | ||||
{ | { | ||||
tinfo.transport_state = JackTransportStopped; | |||||
tinfo.frame = 0; | |||||
jack_transport_goto_frame(client, 0); | |||||
} | } | ||||
void com_stop(char *arg) | void com_stop(char *arg) | ||||
{ | { | ||||
tinfo.transport_state = JackTransportStopped; | |||||
jack_transport_stop(client); | |||||
} | } | ||||
@@ -120,7 +143,6 @@ typedef struct { | |||||
command_t commands[] = { | command_t commands[] = { | ||||
{ "exit", com_exit, "Exit transport program" }, | { "exit", com_exit, "Exit transport program" }, | ||||
{ "help", com_help, "Display help text" }, | { "help", com_help, "Display help text" }, | ||||
{ "loop", com_loop, "Start transport looping" }, | |||||
{ "play", com_play, "Start transport rolling" }, | { "play", com_play, "Start transport rolling" }, | ||||
{ "quit", com_exit, "Synonym for `exit'"}, | { "quit", com_exit, "Synonym for `exit'"}, | ||||
{ "rewind", com_rewind, "Reset transport position to beginning" }, | { "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[]) | int main(int argc, char *argv[]) | ||||
{ | { | ||||
/* basename $0 */ | /* basename $0 */ | ||||
@@ -343,17 +355,11 @@ int main(int argc, char *argv[]) | |||||
signal(SIGHUP, signal_handler); | signal(SIGHUP, signal_handler); | ||||
signal(SIGINT, 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, "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); | jack_on_shutdown(client, jack_shutdown, 0); | ||||
initialize_transport(); | |||||
if (jack_activate(client)) { | if (jack_activate(client)) { | ||||
fprintf(stderr, "cannot activate client"); | fprintf(stderr, "cannot activate client"); | ||||
return 1; | return 1; | ||||
@@ -36,7 +36,9 @@ struct _jack_engine { | |||||
int (*set_buffer_size)(struct _jack_engine *, jack_nframes_t frames); | int (*set_buffer_size)(struct _jack_engine *, jack_nframes_t frames); | ||||
int (*set_sample_rate)(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 */ | /* "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_set_asio_mode (jack_engine_t *, int yn); | ||||
void jack_dump_configuration(jack_engine_t *engine, int take_lock); | 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__ */ | #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 | Copyright (C) 2001-2003 Paul Davis | ||||
This program is free software; you can redistribute it and/or modify | This program is free software; you can redistribute it and/or modify | ||||
@@ -68,6 +73,12 @@ typedef struct { | |||||
size_t offset; | size_t offset; | ||||
} jack_port_buffer_info_t; | } jack_port_buffer_info_t; | ||||
typedef enum { | |||||
TransportCommandNone = 0, | |||||
TransportCommandPlay = 1, | |||||
TransportCommandStop = 2, | |||||
} transport_command_t; | |||||
typedef struct { | typedef struct { | ||||
volatile jack_time_t guard1; | volatile jack_time_t guard1; | ||||
volatile jack_nframes_t frames; | volatile jack_nframes_t frames; | ||||
@@ -82,10 +93,18 @@ typedef struct { | |||||
#define JACK_MAX_PORT_TYPES 4 | #define JACK_MAX_PORT_TYPES 4 | ||||
/* JACK engine shared memory data structure. */ | |||||
typedef struct { | 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; | jack_frame_timer_t frame_timer; | ||||
int internal; | int internal; | ||||
jack_nframes_t frames_at_cycle_start; | jack_nframes_t frames_at_cycle_start; | ||||
@@ -100,7 +119,7 @@ typedef struct { | |||||
jack_engine_t *engine; | jack_engine_t *engine; | ||||
unsigned long n_port_types; | unsigned long n_port_types; | ||||
jack_port_type_info_t port_types[JACK_MAX_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; | } jack_control_t; | ||||
@@ -147,17 +166,19 @@ typedef enum { | |||||
Finished | Finished | ||||
} jack_client_state_t; | } jack_client_state_t; | ||||
/* JACK client shared memory data structure. */ | |||||
typedef volatile struct { | 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 jack_client_state_t state; /* w: engine and client r: engine */ | ||||
volatile char name[JACK_CLIENT_NAME_SIZE+1]; | volatile char name[JACK_CLIENT_NAME_SIZE+1]; | ||||
volatile ClientType type; /* w: engine r: engine and client */ | volatile ClientType type; /* w: engine r: engine and client */ | ||||
volatile char active : 1; /* 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 dead : 1; /* r/w: engine */ | ||||
volatile char timed_out : 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 signalled_at; | ||||
volatile unsigned long long awake_at; | volatile unsigned long long awake_at; | ||||
volatile unsigned long long finished_at; | volatile unsigned long long finished_at; | ||||
@@ -176,16 +197,17 @@ typedef volatile struct { | |||||
void *graph_order_arg; | void *graph_order_arg; | ||||
JackXRunCallback xrun; | JackXRunCallback xrun; | ||||
void *xrun_arg; | 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*); | int (*deliver_request)(void*, jack_request_t*); | ||||
void *deliver_arg; | void *deliver_arg; | ||||
/* for engine use only */ | /* for engine use only */ | ||||
void *private_client; | void *private_client; | ||||
} jack_client_control_t; | } jack_client_control_t; | ||||
@@ -256,6 +278,8 @@ typedef enum { | |||||
SetClientCapabilities = 9, | SetClientCapabilities = 9, | ||||
GetPortConnections = 10, | GetPortConnections = 10, | ||||
GetPortNConnections = 11, | GetPortNConnections = 11, | ||||
ResetTimeBaseClient = 12, | |||||
SetSyncClient = 13, | |||||
} RequestType; | } RequestType; | ||||
struct _jack_request { | struct _jack_request { | ||||
@@ -278,6 +302,10 @@ struct _jack_request { | |||||
unsigned int nports; | unsigned int nports; | ||||
const char **ports; | const char **ports; | ||||
} port_connections; | } port_connections; | ||||
struct { | |||||
jack_client_id_t client_id; | |||||
int conditional; | |||||
} timebase; | |||||
jack_client_id_t client_id; | jack_client_id_t client_id; | ||||
jack_nframes_t nframes; | jack_nframes_t nframes; | ||||
} x; | } 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_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__ */ | #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); | 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 | * @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); | 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 *); | jack_nframes_t jack_frames_since_cycle_start (const jack_client_t *); | ||||
@@ -1,5 +1,6 @@ | |||||
/* | /* | ||||
Copyright (C) 2002 Paul Davis | Copyright (C) 2002 Paul Davis | ||||
Copyright (C) 2003 Jack O'Quin | |||||
This program is free software; you can redistribute it and/or modify | 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 | it under the terms of the GNU Lesser General Public License as published by | ||||
@@ -28,56 +29,339 @@ extern "C" { | |||||
#include <jack/types.h> | #include <jack/types.h> | ||||
/** | /** | ||||
* Possible transport states. | |||||
* Transport states. | |||||
*/ | */ | ||||
typedef enum { | 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; | } 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 { | 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; | } 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 { | typedef struct { | ||||
/* these two cannot be set from clients: the server sets them */ | /* 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_transport_state_t transport_state; | ||||
jack_nframes_t frame; | jack_nframes_t frame; | ||||
jack_nframes_t loop_start; | jack_nframes_t loop_start; | ||||
jack_nframes_t loop_end; | 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; | double bar_start_tick; | ||||
float beats_per_bar; | float beats_per_bar; | ||||
@@ -86,31 +370,37 @@ typedef struct { | |||||
double beats_per_minute; | double beats_per_minute; | ||||
} jack_transport_info_t; | } 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); | 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); | jack_transport_info_t *tinfo); | ||||
#ifdef __cplusplus | #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 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_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 void jack_sort_graph (jack_engine_t *engine); | ||||
static int jack_rechain_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); | 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 | static inline void | ||||
jack_engine_reset_rolling_usecs (jack_engine_t *engine) | 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 */ | return NULL; /* will stop the loop */ | ||||
} | } | ||||
//JOQ: can an internal client be slow sync? | |||||
//JOQ: can an internal client have a timebase master? | |||||
} else { | } else { | ||||
DEBUG ("internal client has no process() function"); | 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) { | if (client == engine->timebase_client) { | ||||
engine->timebase_client = 0; | engine->timebase_client = 0; | ||||
jack_transport_reset (engine); | |||||
jack_timebase_exit (engine); | |||||
} | } | ||||
for (portnode = client->ports; portnode; portnode = jack_slist_next (portnode)) { | 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; | 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 | static int | ||||
handle_client_socket_error (jack_engine_t *engine, int fd) | 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; | break; | ||||
case SetTimeBaseClient: | 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; | break; | ||||
#ifdef USE_CAPABILITIES | #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_sample_rate = jack_set_sample_rate; | ||||
engine->set_buffer_size = jack_set_buffer_size; | engine->set_buffer_size = jack_set_buffer_size; | ||||
engine->run_cycle = jack_run_cycle; | engine->run_cycle = jack_run_cycle; | ||||
engine->transport_cycle_start = jack_transport_cycle_start; | |||||
engine->client_timeout_msecs = client_timeout; | engine->client_timeout_msecs = client_timeout; | ||||
engine->next_client_id = 1; | 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; | engine->control->buffer_size = 0; | ||||
jack_set_sample_rate (engine, 0); | jack_set_sample_rate (engine, 0); | ||||
jack_transport_reset (engine); | |||||
jack_timebase_init (engine); | |||||
engine->control->internal = 0; | engine->control->internal = 0; | ||||
engine->control->has_capabilities = 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) { | if (client == engine->timebase_client) { | ||||
engine->timebase_client = 0; | engine->timebase_client = 0; | ||||
jack_transport_reset (engine); | |||||
jack_timebase_exit (engine); | |||||
} | } | ||||
jack_client_disconnect (engine, client); | jack_client_disconnect (engine, client); | ||||
@@ -4,60 +4,257 @@ | |||||
Copyright (C) 2001-2003 Paul Davis | Copyright (C) 2001-2003 Paul Davis | ||||
Copyright (C) 2003 Jack O'Quin | 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, | This program is distributed in the hope that it will be useful, | ||||
but WITHOUT ANY WARRANTY; without even the implied warranty of | 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 <config.h> | ||||
#include <errno.h> | |||||
#include <jack/internal.h> | #include <jack/internal.h> | ||||
#include <jack/engine.h> | #include <jack/engine.h> | ||||
#include "transengine.h" | #include "transengine.h" | ||||
/*********************** driver functions ***********************/ | |||||
int | int | ||||
jack_set_sample_rate (jack_engine_t *engine, jack_nframes_t nframes) | 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; | 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 | 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 | 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 | 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, | This program is distributed in the hope that it will be useful, | ||||
but WITHOUT ANY WARRANTY; without even the implied warranty of | 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 | You should have received a copy of the GNU General Public License | ||||
along with this program; if not, write to the Free Software | along with this program; if not, write to the Free Software | ||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | 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_cycle_end (jack_engine_t *engine); | ||||
void jack_transport_reset (jack_engine_t *engine); |
@@ -39,7 +39,6 @@ | |||||
#include <stdio.h> | #include <stdio.h> | ||||
#include <stdint.h> | #include <stdint.h> | ||||
#include <regex.h> | #include <regex.h> | ||||
#include <math.h> | |||||
#include <config.h> | #include <config.h> | ||||
@@ -792,6 +791,9 @@ jack_client_thread (void *arg) | |||||
control->state = Running; | control->state = Running; | ||||
if (control->sync_cb) | |||||
jack_call_sync_client (client); | |||||
if (control->process) { | if (control->process) { | ||||
if (control->process (control->nframes, control->process_arg) == 0) { | if (control->process (control->nframes, control->process_arg) == 0) { | ||||
control->state = Finished; | control->state = Finished; | ||||
@@ -799,7 +801,10 @@ jack_client_thread (void *arg) | |||||
} else { | } else { | ||||
control->state = Finished; | control->state = Finished; | ||||
} | } | ||||
if (control->timebase_cb) | |||||
jack_call_timebase_master (client); | |||||
control->finished_at = jack_get_microseconds(); | control->finished_at = jack_get_microseconds(); | ||||
#ifdef WITH_TIMESTAMPS | #ifdef WITH_TIMESTAMPS | ||||
@@ -1279,12 +1284,6 @@ unsigned long jack_get_buffer_size (jack_client_t *client) | |||||
return client->engine->buffer_size; | return client->engine->buffer_size; | ||||
} | } | ||||
unsigned long jack_get_sample_rate (jack_client_t *client) | |||||
{ | |||||
return client->engine->current_time.frame_rate; | |||||
} | |||||
int | int | ||||
jack_connect (jack_client_t *client, const char *source_port, const char *destination_port) | 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; | 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 | int | ||||
jack_set_port_registration_callback(jack_client_t *client, JackPortRegistrationCallback callback, void *arg) | 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; | 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 | float | ||||
jack_cpu_load (jack_client_t *client) | jack_cpu_load (jack_client_t *client) | ||||
{ | { | ||||
@@ -1,6 +1,7 @@ | |||||
#ifndef __jack_libjack_local_h__ | #ifndef __jack_libjack_local_h__ | ||||
#define __jack_libjack_local_h__ | #define __jack_libjack_local_h__ | ||||
/* Client data structure, in the client's address space. */ | |||||
struct _jack_client { | struct _jack_client { | ||||
jack_control_t *engine; | jack_control_t *engine; | ||||
@@ -17,6 +18,7 @@ struct _jack_client { | |||||
void *on_shutdown_arg; | void *on_shutdown_arg; | ||||
char thread_ok : 1; | char thread_ok : 1; | ||||
char first_active : 1; | char first_active : 1; | ||||
char new_timebase : 1; | |||||
pthread_t thread_id; | pthread_t thread_id; | ||||
#if defined(__APPLE__) && defined(__POWERPC__) | #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 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 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__ */ | #endif /* __jack_libjack_local_h__ */ |
@@ -4,38 +4,308 @@ | |||||
Copyright (C) 2001-2003 Paul Davis | Copyright (C) 2001-2003 Paul Davis | ||||
Copyright (C) 2003 Jack O'Quin | 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, | This program is distributed in the hope that it will be useful, | ||||
but WITHOUT ANY WARRANTY; without even the implied warranty of | 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 <config.h> | ||||
#include <errno.h> | |||||
#include <math.h> | |||||
#include <jack/internal.h> | #include <jack/internal.h> | ||||
#include "local.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 transport position structure (thread-safe) */ | |||||
void | |||||
jack_transport_copy_position (jack_position_t *from, jack_position_t *to) | |||||
{ | |||||
int tries = 0; | |||||
long timeout = 1000000; | |||||
do { | |||||
/* throttle the busy wait if we don't get the answer | |||||
* very quickly. */ | |||||
if (tries > 10) { | |||||
usleep (20); | |||||
tries = 0; | |||||
/* debug code to avoid system hangs... */ | |||||
if (--timeout == 0) { | |||||
jack_error("infinte loop copying position"); | |||||
abort(); | |||||
} | |||||
} | |||||
*to = *from; | |||||
tries++; | |||||
} while (to->usecs != to->guard_usecs); | |||||
} | |||||
static inline int | |||||
jack_transport_request_new_pos (jack_client_t *client, jack_position_t *pos) | |||||
{ | |||||
jack_control_t *eng = client->engine; | |||||
//JOQ: check validity of input | |||||
/* carefully copy requested postion into shared memory */ | |||||
pos->guard_usecs = pos->usecs = jack_get_microseconds(); | |||||
jack_transport_copy_position (pos, &eng->request_time); | |||||
return 0; | |||||
} | |||||
void | |||||
jack_call_sync_client (jack_client_t *client) | |||||
{ | |||||
jack_client_control_t *control = client->control; | |||||
jack_control_t *eng = client->engine; | |||||
if (eng->new_pos || !control->sync_ready) { | |||||
if (control->sync_cb (eng->transport_state, | |||||
&eng->current_time, | |||||
control->sync_arg)) { | |||||
control->sync_ready = 1; | |||||
eng->sync_cycle--; | |||||
} | |||||
} | |||||
} | |||||
void | |||||
jack_call_timebase_master (jack_client_t *client) | |||||
{ | |||||
jack_client_control_t *control = client->control; | |||||
jack_control_t *eng = client->engine; | |||||
int new_pos; | |||||
if (client->new_timebase) { /* first time after being set? */ | |||||
client->new_timebase = 0; | |||||
new_pos = 1; | |||||
} else | |||||
new_pos = eng->new_pos; | |||||
if ((eng->transport_state == JackTransportRolling) || new_pos) { | |||||
control->timebase_cb (eng->transport_state, | |||||
control->nframes, | |||||
&eng->pending_time, | |||||
new_pos, | |||||
control->timebase_arg); | |||||
} | |||||
} | |||||
/************************* API functions *************************/ | |||||
jack_nframes_t | |||||
jack_frames_since_cycle_start (const jack_client_t *client) | |||||
{ | |||||
float usecs; | |||||
jack_control_t *eng = client->engine; | |||||
usecs = jack_get_microseconds() - eng->current_time.usecs; | |||||
return (jack_nframes_t) floor ((((float) eng->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_control_t *eng = client->engine; | |||||
jack_read_frame_time (client, ¤t); | |||||
usecs = jack_get_microseconds() - current.stamp; | |||||
elapsed = (jack_nframes_t) | |||||
floor ((((float) eng->current_time.frame_rate) | |||||
/ 1000000.0f) * usecs); | |||||
return current.frames + elapsed; | |||||
} | |||||
unsigned long | |||||
jack_get_sample_rate (jack_client_t *client) | |||||
{ | |||||
return client->engine->current_time.frame_rate; | |||||
} | |||||
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_release_timebase (jack_client_t *client) | |||||
{ | |||||
int rc; | |||||
jack_request_t req; | |||||
jack_client_control_t *ctl = client->control; | |||||
req.type = ResetTimeBaseClient; | |||||
req.x.client_id = ctl->id; | |||||
rc = jack_client_deliver_request (client, &req); | |||||
if (rc == 0) { | |||||
ctl->timebase_cb = NULL; | |||||
ctl->timebase_arg = NULL; | |||||
} | |||||
return rc; | |||||
} | |||||
int | |||||
jack_set_sync_callback (jack_client_t *client, | |||||
JackSyncCallback sync_callback, void *arg) | |||||
{ | |||||
jack_client_control_t *ctl = client->control; | |||||
jack_request_t req; | |||||
int rc; | |||||
req.type = SetSyncClient; | |||||
req.x.client_id = ctl->id; | |||||
rc = jack_client_deliver_request (client, &req); | |||||
if (rc == 0) { | |||||
ctl->sync_cb = sync_callback; | |||||
ctl->sync_arg = arg; | |||||
} | |||||
return rc; | |||||
} | |||||
int | |||||
jack_set_sync_timeout (jack_client_t *client, jack_nframes_t timeout) | |||||
{ | |||||
return ENOSYS; /* this is a stub */ | |||||
} | |||||
int | |||||
jack_set_timebase_callback (jack_client_t *client, int conditional, | |||||
JackTimebaseCallback timebase_cb, void *arg) | |||||
{ | |||||
int rc; | |||||
jack_request_t req; | |||||
jack_client_control_t *ctl = client->control; | |||||
req.type = SetTimeBaseClient; | |||||
req.x.timebase.client_id = ctl->id; | |||||
req.x.timebase.conditional = conditional; | |||||
rc = jack_client_deliver_request (client, &req); | |||||
if (rc == 0) { | |||||
client->new_timebase = 1; | |||||
ctl->timebase_arg = arg; | |||||
ctl->timebase_cb = timebase_cb; | |||||
} | |||||
return rc; | |||||
} | |||||
int | |||||
jack_transport_goto_frame (jack_client_t *client, jack_nframes_t frame) | |||||
{ | |||||
jack_position_t pos; | |||||
pos.frame = frame; | |||||
pos.valid = 0; | |||||
return jack_transport_request_new_pos (client, &pos); | |||||
} | |||||
jack_transport_state_t | |||||
jack_transport_query (jack_client_t *client, jack_position_t *pos) | |||||
{ | |||||
jack_control_t *eng = client->engine; | |||||
/* the guarded copy makes this function work in any thread */ | |||||
jack_transport_copy_position (&eng->current_time, pos); | |||||
return eng->transport_state; | |||||
} | |||||
int | |||||
jack_transport_reposition (jack_client_t *client, jack_position_t *pos) | |||||
{ | |||||
/* copy the input, so we don't modify the input argument */ | |||||
jack_position_t tmp = *pos; | |||||
return jack_transport_request_new_pos (client, &tmp); | |||||
} | |||||
void | |||||
jack_transport_start (jack_client_t *client) | |||||
{ | |||||
client->engine->transport_cmd = TransportCommandPlay; | |||||
} | |||||
void | |||||
jack_transport_stop (jack_client_t *client) | |||||
{ | |||||
client->engine->transport_cmd = TransportCommandStop; | |||||
} | |||||
#ifdef OLD_TRANSPORT | #ifdef OLD_TRANSPORT | ||||
/* * * API functions for compatibility with old transport interface * * */ | |||||
/************* Compatibility with old transport API. *************/ | |||||
int | int | ||||
jack_engine_takeover_timebase (jack_client_t *client) | jack_engine_takeover_timebase (jack_client_t *client) | ||||
{ | { | ||||
jack_request_t req; | jack_request_t req; | ||||
req.type = SetTimeBaseClient; | req.type = SetTimeBaseClient; | ||||
req.x.client_id = client->control->id; | |||||
req.x.timebase.client_id = client->control->id; | |||||
req.x.timebase.conditional = 0; | |||||
return jack_client_deliver_request (client, &req); | return jack_client_deliver_request (client, &req); | ||||
} | } | ||||
@@ -44,14 +314,63 @@ void | |||||
jack_get_transport_info (jack_client_t *client, | jack_get_transport_info (jack_client_t *client, | ||||
jack_transport_info_t *info) | jack_transport_info_t *info) | ||||
{ | { | ||||
*info = client->engine->current_time; | |||||
jack_control_t *eng = client->engine; | |||||
info->usecs = eng->current_time.usecs; | |||||
info->frame_rate = eng->current_time.frame_rate; | |||||
info->transport_state = eng->transport_state; | |||||
info->frame = eng->current_time.frame; | |||||
info->valid = (eng->current_time.valid | | |||||
JackTransportState | JackTransportPosition); | |||||
if (info->valid & JackPositionBBT) { | |||||
info->bar = eng->current_time.bar; | |||||
info->beat = eng->current_time.beat; | |||||
info->tick = eng->current_time.tick; | |||||
info->bar_start_tick = eng->current_time.bar_start_tick; | |||||
info->beats_per_bar = eng->current_time.beats_per_bar; | |||||
info->beat_type = eng->current_time.beat_type; | |||||
info->ticks_per_beat = eng->current_time.ticks_per_beat; | |||||
info->beats_per_minute = eng->current_time.beats_per_minute; | |||||
} | |||||
} | } | ||||
void | void | ||||
jack_set_transport_info (jack_client_t *client, | jack_set_transport_info (jack_client_t *client, | ||||
jack_transport_info_t *info) | jack_transport_info_t *info) | ||||
{ | { | ||||
client->engine->pending_time = *info; | |||||
jack_control_t *eng = client->engine; | |||||
//JOQ: check that this is the timebase master? | |||||
//JOQ: check that this is the process thread? | |||||
/* is there a new state? */ | |||||
if ((info->valid & JackTransportState) && | |||||
(info->transport_state != eng->transport_state)) { | |||||
if (info->transport_state == JackTransportStopped) | |||||
eng->transport_cmd = TransportCommandStop; | |||||
else if (info->transport_state == JackTransportRolling) | |||||
eng->transport_cmd = TransportCommandPlay; | |||||
/* silently ignore anything else */ | |||||
} | |||||
if (info->valid & JackTransportPosition) | |||||
eng->pending_time.frame = info->frame; | |||||
else | |||||
eng->pending_time.frame = eng->current_time.frame; | |||||
eng->pending_time.valid = (info->valid & JackTransportBBT); | |||||
if (info->valid & JackTransportBBT) { | |||||
eng->pending_time.bar = info->bar; | |||||
eng->pending_time.beat = info->beat; | |||||
eng->pending_time.tick = info->tick; | |||||
eng->pending_time.bar_start_tick = info->bar_start_tick; | |||||
eng->pending_time.beats_per_bar = info->beats_per_bar; | |||||
eng->pending_time.beat_type = info->beat_type; | |||||
eng->pending_time.ticks_per_beat = info->ticks_per_beat; | |||||
eng->pending_time.beats_per_minute = info->beats_per_minute; | |||||
} | |||||
} | } | ||||
#endif /* OLD_TRANSPORT */ | #endif /* OLD_TRANSPORT */ |