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