git-svn-id: svn+ssh://jackaudio.org/trunk/jack@444 0c269be4-1314-0410-8aa9-9f06e86f4224tags/0.109.0
| @@ -11,15 +11,11 @@ send a change request to Kai Vehmanen (mailto:k_at_eca.cx). | |||
| TO BE DECIDED (owner) | |||
| - whether to hide the transport.h structs from clients (kaiv) | |||
| - dropping the use of CAP_RESOURCE (nando) | |||
| - whether to default to triangular dithering the at 16bit (swh) | |||
| TODO for 1.0 (owner) | |||
| - finalize discussion on transport API, and implement | |||
| - how to support sync (client "synced" bit) | |||
| - should we separate transport+time? | |||
| - ensure that UST/MSC pairs work for transport API | |||
| - resolve helper thread design question? | |||
| @@ -61,5 +57,7 @@ CLOSED (date,who,comment) | |||
| - alsa-driver parameter parsing patch (2003/02/25, joq, done) | |||
| - jack_deactive called unconditionally in jack_client_close (2003/02/05, kaiv, fixed) | |||
| - call time client before all others (2003/01/28, kaiv, another solution) | |||
| - finalize discussion on transport API, and implement (2003/08/04, joq) | |||
| - whether to hide the transport.h structs from clients (2003/08/04, joq) | |||
| ----------------------------------------------------------------------- | |||
| @@ -13,8 +13,8 @@ dnl micro version = incremented when implementation-only | |||
| dnl changes are made | |||
| dnl --- | |||
| JACK_MAJOR_VERSION=0 | |||
| JACK_MINOR_VERSION=75 | |||
| JACK_MICRO_VERSION=0 | |||
| JACK_MINOR_VERSION=76 | |||
| JACK_MICRO_VERSION=1 | |||
| dnl --- | |||
| @@ -25,7 +25,7 @@ dnl made to the way libjack communicates with jackd | |||
| dnl that would break applications linked with an older | |||
| dnl version of libjack. | |||
| dnl --- | |||
| JACK_PROTOCOL_VERSION=6 | |||
| JACK_PROTOCOL_VERSION=7 | |||
| dnl --- | |||
| dnl HOWTO: updating the libjack interface version | |||
| @@ -42,7 +42,7 @@ dnl slacker than this, and closer to those for the JACK version | |||
| dnl number. | |||
| dnl --- | |||
| JACK_API_CURRENT=0 | |||
| JACK_API_REVISION=19 | |||
| JACK_API_REVISION=20 | |||
| JACK_API_AGE=0 | |||
| AC_SUBST(JACK_MAJOR_VERSION) | |||
| @@ -3,15 +3,19 @@ MAINTAINERCLEANFILES=Makefile.in | |||
| DOX=reference.doxygen | |||
| EXTRA_DIST= | |||
| INSTIMAGES=reference/html/doxygen.png reference/html/fsm.png | |||
| DOC_STAMPS=html-build.stamp | |||
| DOC_DIR=$(HTML_DIR) | |||
| all-local: doxygen-build.stamp | |||
| doxygen-build.stamp: $(DOX) | |||
| doxygen-build.stamp: $(DOX) mainpage.dox transport.dox fsm.png \ | |||
| ../jack/jack.h ../jack/types.h ../jack/transport.h | |||
| @echo '*** Running doxygen ***' | |||
| doxygen $(DOX) | |||
| touch doxygen-build.stamp | |||
| @@ -29,7 +33,7 @@ install-data-local: | |||
| if test "$$installfiles" = 'reference/html/*.html'; \ | |||
| then echo '-- Nothing to install' ; \ | |||
| else \ | |||
| for i in $$installfiles reference/html/doxygen.png reference/html/doxygen.css; do \ | |||
| for i in $$installfiles $(INSTIMAGES) reference/html/doxygen.css; do \ | |||
| echo '-- Installing '$$i ; \ | |||
| $(INSTALL_DATA) $$i $(DESTDIR)$(DOC_DIR)/reference/html; \ | |||
| done; \ | |||
| @@ -41,7 +45,7 @@ uninstall-local: doxygen-build.stamp | |||
| if test "$$installfiles" = 'reference/html/*.html'; \ | |||
| then echo '-- Nothing to uninstall' ; \ | |||
| else \ | |||
| for i in $$installfiles reference/html/doxygen.png reference/html/doxygen.css; do \ | |||
| for i in $$installfiles $(INSTIMAGES) reference/html/doxygen.css; do \ | |||
| echo '-- Unstalling '$$i ; \ | |||
| rm $(DESTDIR)$(DOC_DIR)/$$i; \ | |||
| done; \ | |||
| @@ -0,0 +1,54 @@ | |||
| /* | |||
| * This is the main page of the JACK reference manual, built using | |||
| * doxygen. | |||
| */ | |||
| /** | |||
| @mainpage JACK Audio Connection Kit | |||
| @section intro Introduction | |||
| JACK is a low-latency audio server, written primarily for the | |||
| GNU/Linux operating system. It can connect several client | |||
| applications to an audio device, and allow them to share audio with | |||
| each other. Clients can run as separate processes like normal | |||
| applications, or within the JACK server as "plugins". | |||
| JACK differs from other audio servers in being designed from the | |||
| ground up for professional audio work, focusing on two key areas: | |||
| synchronous execution of all clients, and low latency operation. | |||
| @see <http://jackit.sourceforge.net> | |||
| @section contents Contents | |||
| The JACK programming interfaces are described in several header files: | |||
| - <jack/jack.h> defines most of the interfaces used by JACK clients. | |||
| - <jack/transport.h> provides a simple transport control mechanism | |||
| for starting, stopping and repositioning clients. This is described | |||
| in the @ref transport-design document. | |||
| - <jack/types.h> defines most of the data types for JACK. | |||
| @section license License | |||
| Copyright (C) 2001-2003 by Paul Davis and others. | |||
| JACK is free software; you can redistribute it and/or modify it under | |||
| the terms of the GNU GPL and LGPL licenses as published by the Free | |||
| Software Foundation, <http://www.gnu.org>. Most of JACK uses the GPL, | |||
| as noted in the source file headers. But, the library interfaces are | |||
| licensed under the LGPL, allowing proprietary programs to link with | |||
| JACK and call its services. You should have received a copy of these | |||
| Licenses along with the program; if not, write to the Free Software | |||
| Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, | |||
| USA. | |||
| This program is distributed in the hope that it will be useful, but | |||
| WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
| General Public License for more details. | |||
| */ | |||
| @@ -17,7 +17,7 @@ | |||
| # The PROJECT_NAME tag is a single word (or a sequence of words surrounded | |||
| # by quotes) that should identify the project. | |||
| PROJECT_NAME = Jack | |||
| PROJECT_NAME = JACK | |||
| # The PROJECT_NUMBER tag can be used to enter a project or revision number. | |||
| # This could be handy for archiving the generated documentation or | |||
| @@ -301,7 +301,8 @@ WARN_LOGFILE = | |||
| # directories like "/usr/src/myproject". Separate the files or directories | |||
| # with spaces. | |||
| INPUT = @top_srcdir@/jack/jack.h \ | |||
| INPUT = mainpage.dox transport.dox \ | |||
| @top_srcdir@/jack/jack.h \ | |||
| @top_srcdir@/jack/types.h \ | |||
| @top_srcdir@/jack/transport.h | |||
| @@ -362,7 +363,7 @@ EXAMPLE_RECURSIVE = NO | |||
| # directories that contain image that are included in the documentation (see | |||
| # the \image command). | |||
| IMAGE_PATH = | |||
| IMAGE_PATH = . | |||
| # The INPUT_FILTER tag can be used to specify a program that doxygen should | |||
| # invoke to filter for each input file. Doxygen will invoke the filter program | |||
| @@ -502,7 +503,7 @@ TOC_EXPAND = NO | |||
| # top of each HTML page. The value NO (the default) enables the index and | |||
| # the value YES disables it. | |||
| DISABLE_INDEX = NO | |||
| DISABLE_INDEX = YES | |||
| # This tag can be used to set the number of enum values (range [1..20]) | |||
| # that doxygen will group on one line in the generated HTML documentation. | |||
| @@ -0,0 +1,244 @@ | |||
| /* | |||
| * This file documents the JACK transport design. It is part of the | |||
| * JACK reference manual, built using doxygen. | |||
| */ | |||
| /** | |||
| @page transport-design JACK Transport Design | |||
| The @ref index provides simple transport interfaces for starting, | |||
| stopping and repositioning a set of clients. This document describes | |||
| the overall design of these interfaces, their detailed specifications | |||
| are in <jack/transport.h> | |||
| - @ref requirements | |||
| - @ref overview | |||
| - @ref timebase | |||
| - @ref transportcontrol | |||
| - @ref transportclients | |||
| - @ref compatibility | |||
| - @ref issues | |||
| @subsection requirements Requirements | |||
| - We need sample-level accuracy for transport control. This implies | |||
| that the transport client logic has to be part of the realtime | |||
| process chain. | |||
| - We don't want to add another context switch. So, the transport | |||
| client logic has to run in the context of the client's process | |||
| thread. To avoid making an extra pass through the process graph, no | |||
| transport changes take effect until the following process cycle. | |||
| That way, the transport info is stable throughout each cycle. | |||
| - We want to allow multiple clients to change the transport state. | |||
| This is mostly a usability issue. Any client can start or stop | |||
| playback, or seek to a new location. The user need not switch | |||
| windows to accomplish these tasks. | |||
| - We want a way for clients with heavyweight state to sync up when | |||
| the user presses "play", before the transport starts rolling. | |||
| - We need to provide for ongoing binary compatibility as the | |||
| transport design evolves. | |||
| @subsection overview Overview | |||
| The former transport master role has been divided into two layers: | |||
| - @ref timebase - counting beats, frames, etc. on every cycle. | |||
| - @ref transportcontrol - start, stop and reposition the playback. | |||
| Existing clients continue to work in compatibility mode. | |||
| @subsection timebase Timebase Master | |||
| The timebase master continuously updates extended position | |||
| information, counting beats, SMPTE frames, etc. Without this extended | |||
| information, there is no need for this function. There is at most one | |||
| master active at a time. If no client is registered as timebase | |||
| master, the JACK engine will provide simple frame counting whenever | |||
| the transport is rolling. | |||
| The timebase master registers a callback that updates position | |||
| information while the transport is rolling. This function is called | |||
| immediately after the process callback in the same thread whenever the | |||
| transport is rolling, or when any client has set a new position in the | |||
| previous cycle. The first cycle after jack_set_timebase_callback() is | |||
| also treated as a new position. Its output affects all of the | |||
| following process cycle, unless a new position request arrives during | |||
| the current cycle. | |||
| @code | |||
| typedef int (*JackTimebaseCallback)(jack_transport_state_t state, | |||
| jack_nframes_t nframes, | |||
| jack_position_t *pos, | |||
| int new_pos, | |||
| void *arg); | |||
| @endcode | |||
| When a new client takes over, the former timebase callback is no | |||
| longer called. Taking over the timebase may be done conditionally, in | |||
| which case the takeover fails when there is a master already. The | |||
| existing master can release it voluntarily, if desired. | |||
| @code | |||
| int jack_set_timebase_callback (jack_client_t *client, | |||
| int conditional, | |||
| JackTimebaseCallback timebase_callback, | |||
| void *arg); | |||
| int jack_release_timebase(jack_client_t *client); | |||
| @endcode | |||
| If the timebase master releases the timebase or exits the JACK graph | |||
| for any reason, the JACK engine takes over at the start of the next | |||
| process cycle. The transport state does not change. If rolling, it | |||
| continues to play, with frame numbers as the only available position | |||
| information. | |||
| @subsection transportcontrol Transport Control | |||
| The JACK engine itself manages stopping and starting of the transport. | |||
| Any client can make transport control requests at any time. These | |||
| requests take effect no sooner than the next process cycle, sometimes | |||
| later. The transport state is always valid, initially it is | |||
| ::JackTransportStopped. | |||
| @code | |||
| void jack_transport_start (jack_client_t *client); | |||
| void jack_transport_stop (jack_client_t *client); | |||
| @endcode | |||
| The engine handles polling of slow-sync clients. When someone calls | |||
| jack_transport_start(), the engine resets the poll bits and changes to | |||
| a new state, ::JackTransportStarting. The @a sync_callback function | |||
| for each slow-sync client will be invoked in the JACK process thread | |||
| while the transport is starting. If it has not already done so, the | |||
| client needs to initiate a seek to reach the starting position. The | |||
| @a sync_callback returns false until the seek completes and the client | |||
| is ready to play. When all slow-sync clients are ready, the state | |||
| changes to ::JackTransportRolling. | |||
| @code | |||
| typedef int (*JackSyncCallback)(jack_transport_state_t state, | |||
| jack_position_t *pos, void *arg); | |||
| @endcode | |||
| This callback is a realtime function that runs in the JACK process | |||
| thread. | |||
| @code | |||
| int jack_set_sync_callback (jack_client_t *client, | |||
| JackSyncCallback sync_callback, void *arg); | |||
| @endcode | |||
| Clients that don't declare a @a sync_callback are assumed to be ready | |||
| immediately, any time the transport wants to start. If a client no | |||
| longer requires slow-sync processing, it can set its @a sync_callback | |||
| to NULL. | |||
| @code | |||
| int jack_set_sync_timeout (jack_client_t *client, | |||
| jack_nframes_t timeout); | |||
| @endcode | |||
| There must be a @a timeout to prevent unresponsive slow-sync clients | |||
| from completely halting the transport mechanism. Two seconds is the | |||
| default. When this @a timeout expires, the transport will start | |||
| rolling, even if some slow-sync clients are still unready. The @a | |||
| sync_callback for these clients continues being invoked, giving them | |||
| an opportunity to catch up. | |||
| @code | |||
| int jack_transport_reposition (jack_client_t *client, | |||
| jack_position_t *pos); | |||
| int jack_transport_goto_frame (jack_client_t *client, | |||
| jack_nframes_t frame); | |||
| @endcode | |||
| These request a new transport position. They can be called at any | |||
| time by any client. Even the timebase master must use them. If the | |||
| request is valid, it goes into effect on the next process cycle. If | |||
| there are slow-sync clients and the transport is already rolling, it | |||
| will enter the ::JackTransportStarting state and begin invoking their | |||
| @a sync_callback until they find the new position. | |||
| @image html fsm.png "Transport State Transition Diagram" | |||
| @image latex fsm.png "Transport State Transition Diagram" | |||
| @subsection transportclients Transport Clients | |||
| Transport clients were formerly known as "transport slaves". We want | |||
| to make it easy for almost every JACK client to be a transport client. | |||
| @code | |||
| jack_transport_state_t jack_transport_query (jack_client_t *client, | |||
| jack_position_t *pos); | |||
| @endcode | |||
| This function can be called from any thread. If called from the | |||
| process thread, @a pos corresponds to the first frame of the current | |||
| cycle and the state returned is valid for the entire cycle. | |||
| @subsection compatibility Compatibility | |||
| We want to support the existing functions in compatibility mode as | |||
| deprecated interfaces for a while. The main reasons are: | |||
| - facilitate testing with clients that already have transport | |||
| support | |||
| - provide a clean migration path, so application developers are | |||
| not discouraged from supporting the transport interface | |||
| - inter-operate with ardour 0.9 beta. | |||
| So, these interfaces continue to be supported (but deprecated): | |||
| @code | |||
| typedef struct jack_transport_info_t; | |||
| void jack_set_transport_info (jack_client_t *client, | |||
| jack_transport_info_t *tinfo); | |||
| void jack_get_transport_info (jack_client_t *client, | |||
| jack_transport_info_t *tinfo); | |||
| int jack_engine_takeover_timebase (jack_client_t *); | |||
| @endcode | |||
| For compatibility with future changes, it would be good to avoid | |||
| structures entirely. Nevertheless, the jack_position_t structure | |||
| provides a convenient way to collect timebase information in several | |||
| formats that clearly all refer to a single moment. To minimize future | |||
| binary compatibility problems, this structure has some padding at the | |||
| end, making it possible to extend it without necessarily breaking | |||
| compatibility. New fields can be allocated from the padding area, | |||
| with access controlled by newly defined valid bits, all of which are | |||
| currently forced to zero. That allows the structure size and offsets | |||
| to remain constant. | |||
| @subsection issues Issues Not Addressed | |||
| This design currently does not address several issues. This means they | |||
| will probably not be included in JACK release 1.0. | |||
| - variable speed -- does the frame_rate field in the transport | |||
| info structure have anything to do with this? | |||
| - reverse play | |||
| - SMPTE -- slave mode depends on variable speed and interacts | |||
| badly with slow-sync clients. These are deep issues. Until they | |||
| are addressed there's no reason to retain those two SMPTE fields in | |||
| the jack_position_t structure. | |||
| - looping -- unless we get feedback that this is really required | |||
| - internal timebase master or slow-sync clients | |||
| - probably a good idea | |||
| - haven't studied it | |||
| */ | |||
| @@ -911,7 +911,8 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float *delay | |||
| } | |||
| driver->poll_last = poll_ret; | |||
| driver->poll_next = poll_ret + driver->period_usecs; | |||
| driver->engine->control->current_time.usecs = poll_ret; | |||
| driver->engine->transport_cycle_start (driver->engine, | |||
| poll_ret); | |||
| } | |||
| #ifdef DEBUG_WAKEUP | |||
| @@ -163,7 +163,8 @@ portaudio_driver_read (portaudio_driver_t *driver, jack_nframes_t nframes) | |||
| } | |||
| driver->engine->control->current_time.usecs = jack_get_microseconds (); | |||
| driver->engine->transport_cycle_start (driver->engine, | |||
| jack_get_microseconds ()); | |||
| return 0; | |||
| } | |||
| @@ -7,86 +7,40 @@ | |||
| #include <jack/jack.h> | |||
| #include <jack/transport.h> | |||
| typedef struct { | |||
| volatile jack_nframes_t guard1; | |||
| volatile jack_transport_info_t info; | |||
| volatile jack_nframes_t guard2; | |||
| } guarded_transport_info_t; | |||
| guarded_transport_info_t now; | |||
| jack_client_t *client; | |||
| void | |||
| showtime () | |||
| { | |||
| guarded_transport_info_t current; | |||
| int tries = 0; | |||
| /* Since "now" is updated from the process() thread every | |||
| * buffer period, we must copy it carefully to avoid getting | |||
| * an incoherent hash of multiple versions. */ | |||
| do { | |||
| /* Throttle the busy wait if we don't get the a clean | |||
| * copy very quickly. */ | |||
| if (tries > 10) { | |||
| usleep (20); | |||
| tries = 0; | |||
| } | |||
| current = now; | |||
| tries++; | |||
| } while (current.guard1 != current.guard2); | |||
| if (current.info.valid & JackTransportPosition) | |||
| printf ("frame: %7lu\t", current.info.frame); | |||
| else | |||
| printf ("frame: [-]\t"); | |||
| if (current.info.valid & JackTransportState) { | |||
| switch (current.info.transport_state) { | |||
| case JackTransportStopped: | |||
| printf ("state: Stopped\t"); | |||
| break; | |||
| case JackTransportRolling: | |||
| printf ("state: Rolling\t"); | |||
| break; | |||
| case JackTransportLooping: | |||
| printf ("state: Looping\t"); | |||
| break; | |||
| default: | |||
| printf ("state: [unknown]\t"); | |||
| } | |||
| jack_position_t current; | |||
| jack_transport_state_t transport_state; | |||
| transport_state = jack_transport_query (client, ¤t); | |||
| printf ("frame: %7lu\t", current.frame); | |||
| switch (transport_state) { | |||
| case JackTransportStopped: | |||
| printf ("state: Stopped\t"); | |||
| break; | |||
| case JackTransportRolling: | |||
| printf ("state: Rolling\t"); | |||
| break; | |||
| case JackTransportStarting: | |||
| printf ("state: Starting\t"); | |||
| break; | |||
| default: | |||
| printf ("state: [unknown]\t"); | |||
| } | |||
| else | |||
| printf ("state: [-]\t"); | |||
| if (current.info.valid & JackTransportLoop) | |||
| printf ("loop: %lu-%lu\t", current.info.loop_start, | |||
| current.info.loop_end); | |||
| else | |||
| printf ("loop: [-]\t"); | |||
| if (current.info.valid & JackTransportBBT) | |||
| printf ("BBT: %3d|%d|%04d\n", current.info.bar, | |||
| current.info.beat, current.info.tick); | |||
| if (current.valid & JackPositionBBT) | |||
| printf ("BBT: %3d|%d|%04d\n", | |||
| current.bar, current.beat, current.tick); | |||
| else | |||
| printf ("BBT: [-]\n"); | |||
| } | |||
| int | |||
| process (jack_nframes_t nframes, void *arg) | |||
| { | |||
| /* The guard flags contain a running counter of sufficiently | |||
| * high resolution, that showtime() can detect whether the | |||
| * last update is complete. */ | |||
| now.guard1 = jack_frame_time(client); | |||
| jack_get_transport_info (client, (jack_transport_info_t *) &now.info); | |||
| now.guard2 = now.guard1; | |||
| return 0; | |||
| } | |||
| void | |||
| jack_shutdown (void *arg) | |||
| { | |||
| @@ -117,12 +71,6 @@ main (int argc, char *argv[]) | |||
| signal (SIGHUP, signal_handler); | |||
| signal (SIGINT, signal_handler); | |||
| /* tell the JACK server to call `process()' whenever | |||
| there is work to be done. | |||
| */ | |||
| jack_set_process_callback (client, process, 0); | |||
| /* tell the JACK server to call `jack_shutdown()' if | |||
| it ever shuts down, either entirely, or if it | |||
| just decides to stop calling us. | |||
| @@ -145,4 +93,3 @@ main (int argc, char *argv[]) | |||
| jack_client_close (client); | |||
| exit (0); | |||
| } | |||
| @@ -33,32 +33,61 @@ char *package; /* program name */ | |||
| int done = 0; | |||
| jack_client_t *client; | |||
| jack_transport_info_t tinfo; /* multi-threaded access */ | |||
| /* JACK process() handler. | |||
| /* JACK timebase callback. | |||
| * | |||
| * Runs in a separate realtime thread. Must not wait. | |||
| * Runs in the process realtime thread. Must not wait. | |||
| */ | |||
| int process(jack_nframes_t nframes, void *arg) | |||
| void timebase(jack_transport_state_t state, jack_nframes_t nframes, | |||
| jack_position_t *pos, int new_pos, void *arg) | |||
| { | |||
| jack_set_transport_info(client, &tinfo); | |||
| /* frame number for next cycle */ | |||
| if (tinfo.transport_state != JackTransportStopped) { | |||
| tinfo.frame += nframes; | |||
| /* When looping, adjust the frame number periodically. Make | |||
| * sure improper loop limits don't lock up the system in an | |||
| * infinite while(). */ | |||
| if ((tinfo.transport_state == JackTransportLooping) && | |||
| (tinfo.loop_end > tinfo.loop_start)) { | |||
| while (tinfo.frame >= tinfo.loop_end) | |||
| tinfo.frame -= (tinfo.loop_end - tinfo.loop_start); | |||
| } | |||
| if (state == JackTransportRolling) | |||
| pos->frame += nframes; | |||
| if ((pos->valid & JackPositionBBT) == 0) { | |||
| /* set "march time" parameters: 4/4, 120bpm */ | |||
| pos->valid |= JackPositionBBT; | |||
| pos->beats_per_bar = 4.0; | |||
| pos->beat_type = 0.25; | |||
| pos->ticks_per_beat = 1920.0; | |||
| pos->beats_per_minute = 120.0; | |||
| } | |||
| return 0; | |||
| if (new_pos) { | |||
| /* Compute BBT info from frame number. This is relatively | |||
| * simple here, but could become complex if there were tempo | |||
| * or time signature changes. */ | |||
| double min = (double) pos->frame / ((double) pos->frame_rate * 60.0); | |||
| long abs_tick = min * pos->beats_per_minute * pos->ticks_per_beat; | |||
| long abs_beat = abs_tick / pos->ticks_per_beat; | |||
| pos->bar = abs_beat / pos->beats_per_bar; | |||
| pos->beat = abs_beat - (pos->bar * pos->beats_per_bar) + 1; | |||
| pos->tick = abs_tick - (abs_beat * pos->ticks_per_beat); | |||
| pos->bar_start_tick = pos->bar * pos->beats_per_bar * | |||
| pos->ticks_per_beat; | |||
| pos->bar++; /* adjust start to bar 1 */ | |||
| } else { | |||
| /* Compute BBT info from previous period. */ | |||
| pos->tick += | |||
| nframes * pos->ticks_per_beat * pos->beats_per_minute | |||
| / (pos->frame_rate * 60); | |||
| while (pos->tick >= pos->ticks_per_beat) { | |||
| pos->tick -= pos->ticks_per_beat; | |||
| if (++pos->beat > pos->beats_per_bar) { | |||
| pos->beat = 1; | |||
| ++pos->bar; | |||
| pos->bar_start_tick += | |||
| pos->beats_per_bar * pos->ticks_per_beat; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| void jack_shutdown(void *arg) | |||
| @@ -83,25 +112,19 @@ void com_exit(char *arg) | |||
| void com_help(char *); /* forward declaration */ | |||
| void com_loop(char *arg) | |||
| { | |||
| tinfo.transport_state = JackTransportLooping; | |||
| } | |||
| void com_play(char *arg) | |||
| { | |||
| tinfo.transport_state = JackTransportRolling; | |||
| jack_transport_start(client); | |||
| } | |||
| void com_rewind(char *arg) | |||
| { | |||
| tinfo.transport_state = JackTransportStopped; | |||
| tinfo.frame = 0; | |||
| jack_transport_goto_frame(client, 0); | |||
| } | |||
| void com_stop(char *arg) | |||
| { | |||
| tinfo.transport_state = JackTransportStopped; | |||
| jack_transport_stop(client); | |||
| } | |||
| @@ -120,7 +143,6 @@ typedef struct { | |||
| command_t commands[] = { | |||
| { "exit", com_exit, "Exit transport program" }, | |||
| { "help", com_help, "Display help text" }, | |||
| { "loop", com_loop, "Start transport looping" }, | |||
| { "play", com_play, "Start transport rolling" }, | |||
| { "quit", com_exit, "Synonym for `exit'"}, | |||
| { "rewind", com_rewind, "Reset transport position to beginning" }, | |||
| @@ -313,16 +335,6 @@ void command_loop() | |||
| } | |||
| } | |||
| void initialize_transport() | |||
| { | |||
| /* must run before jack_activate */ | |||
| tinfo.loop_start = 0; /* default loop is one second */ | |||
| tinfo.loop_end = jack_get_sample_rate(client); | |||
| tinfo.valid = JackTransportState|JackTransportPosition|JackTransportLoop; | |||
| com_rewind(NULL); | |||
| } | |||
| int main(int argc, char *argv[]) | |||
| { | |||
| /* basename $0 */ | |||
| @@ -343,17 +355,11 @@ int main(int argc, char *argv[]) | |||
| signal(SIGHUP, signal_handler); | |||
| signal(SIGINT, signal_handler); | |||
| if (jack_engine_takeover_timebase(client) != 0) { | |||
| if (jack_set_timebase_callback(client, 1, timebase, NULL) != 0) | |||
| fprintf(stderr, "Unable to take over timebase.\n"); | |||
| fprintf(stderr, "Is another transport master already running?\n"); | |||
| return 1; | |||
| } | |||
| jack_set_process_callback(client, process, 0); | |||
| jack_on_shutdown(client, jack_shutdown, 0); | |||
| initialize_transport(); | |||
| if (jack_activate(client)) { | |||
| fprintf(stderr, "cannot activate client"); | |||
| return 1; | |||
| @@ -36,7 +36,9 @@ struct _jack_engine { | |||
| int (*set_buffer_size)(struct _jack_engine *, jack_nframes_t frames); | |||
| int (*set_sample_rate)(struct _jack_engine *, jack_nframes_t frames); | |||
| int (*run_cycle)(struct _jack_engine *, jack_nframes_t nframes, float delayed_usecs); | |||
| int (*run_cycle)(struct _jack_engine *, jack_nframes_t nframes, | |||
| float delayed_usecs); | |||
| void (*transport_cycle_start)(struct _jack_engine *, jack_time_t time); | |||
| /* "private" sections starts here */ | |||
| @@ -111,4 +113,24 @@ int jack_engine_load_driver (jack_engine_t *, int, char **); | |||
| void jack_set_asio_mode (jack_engine_t *, int yn); | |||
| void jack_dump_configuration(jack_engine_t *engine, int take_lock); | |||
| extern jack_client_internal_t * | |||
| jack_client_internal_by_id (jack_engine_t *engine, jack_client_id_t id); | |||
| static inline void jack_lock_graph (jack_engine_t* engine) { | |||
| DEBUG ("acquiring graph lock"); | |||
| pthread_mutex_lock (&engine->client_lock); | |||
| } | |||
| static inline int jack_try_lock_graph (jack_engine_t *engine) | |||
| { | |||
| DEBUG ("TRYING to acquiring graph lock"); | |||
| return pthread_mutex_trylock (&engine->client_lock); | |||
| } | |||
| static inline void jack_unlock_graph (jack_engine_t* engine) | |||
| { | |||
| DEBUG ("releasing graph lock"); | |||
| pthread_mutex_unlock (&engine->client_lock); | |||
| } | |||
| #endif /* __jack_engine_h__ */ | |||
| @@ -1,4 +1,9 @@ | |||
| /* | |||
| Internal shared data and functions. | |||
| If you edit this file, you should carefully consider changing the | |||
| JACK_PROTOCOL_VERSION in configure.in. | |||
| Copyright (C) 2001-2003 Paul Davis | |||
| This program is free software; you can redistribute it and/or modify | |||
| @@ -68,6 +73,12 @@ typedef struct { | |||
| size_t offset; | |||
| } jack_port_buffer_info_t; | |||
| typedef enum { | |||
| TransportCommandNone = 0, | |||
| TransportCommandPlay = 1, | |||
| TransportCommandStop = 2, | |||
| } transport_command_t; | |||
| typedef struct { | |||
| volatile jack_time_t guard1; | |||
| volatile jack_nframes_t frames; | |||
| @@ -82,10 +93,18 @@ typedef struct { | |||
| #define JACK_MAX_PORT_TYPES 4 | |||
| /* JACK engine shared memory data structure. */ | |||
| typedef struct { | |||
| jack_transport_info_t current_time; | |||
| jack_transport_info_t pending_time; | |||
| jack_transport_state_t transport_state; | |||
| volatile transport_command_t transport_cmd; | |||
| jack_position_t current_time; /* position for current cycle */ | |||
| jack_position_t pending_time; /* position for next cycle */ | |||
| jack_position_t request_time; /* latest requested position */ | |||
| int new_pos; /* new position this cycle */ | |||
| unsigned long sync_remain; /* remaining sync count */ | |||
| unsigned long sync_cycle; /* number ready this cycle */ | |||
| unsigned long sync_clients; /* number of slow-sync clients */ | |||
| jack_frame_timer_t frame_timer; | |||
| int internal; | |||
| jack_nframes_t frames_at_cycle_start; | |||
| @@ -100,7 +119,7 @@ typedef struct { | |||
| jack_engine_t *engine; | |||
| unsigned long n_port_types; | |||
| jack_port_type_info_t port_types[JACK_MAX_PORT_TYPES]; | |||
| jack_port_shared_t ports[0]; | |||
| jack_port_shared_t ports[0]; | |||
| } jack_control_t; | |||
| @@ -147,17 +166,19 @@ typedef enum { | |||
| Finished | |||
| } jack_client_state_t; | |||
| /* JACK client shared memory data structure. */ | |||
| typedef volatile struct { | |||
| volatile jack_client_id_t id; /* w: engine r: engine and client */ | |||
| volatile jack_nframes_t nframes; /* w: engine r: client */ | |||
| volatile jack_client_id_t id; /* w: engine r: engine and client */ | |||
| volatile jack_nframes_t nframes; /* w: engine r: client */ | |||
| volatile jack_client_state_t state; /* w: engine and client r: engine */ | |||
| volatile char name[JACK_CLIENT_NAME_SIZE+1]; | |||
| volatile ClientType type; /* w: engine r: engine and client */ | |||
| volatile char active : 1; /* w: engine r: engine and client */ | |||
| volatile char dead : 1; /* r/w: engine */ | |||
| volatile char timed_out : 1; /* r/w: engine */ | |||
| volatile pid_t pid; /* w: client r: engine; pid of client */ | |||
| volatile char sync_ready : 1; /* w: engine and client, r: engine */ | |||
| volatile pid_t pid; /* w: client r: engine; client pid */ | |||
| volatile unsigned long long signalled_at; | |||
| volatile unsigned long long awake_at; | |||
| volatile unsigned long long finished_at; | |||
| @@ -176,16 +197,17 @@ typedef volatile struct { | |||
| void *graph_order_arg; | |||
| JackXRunCallback xrun; | |||
| void *xrun_arg; | |||
| JackSyncCallback sync_cb; | |||
| void *sync_arg; | |||
| JackTimebaseCallback timebase_cb; | |||
| void *timebase_arg; | |||
| /* OOP clients: set by libjack | |||
| IP clients: set by engine | |||
| */ | |||
| /* external clients: set by libjack | |||
| * internal clients: set by engine */ | |||
| int (*deliver_request)(void*, jack_request_t*); | |||
| void *deliver_arg; | |||
| /* for engine use only */ | |||
| void *private_client; | |||
| } jack_client_control_t; | |||
| @@ -256,6 +278,8 @@ typedef enum { | |||
| SetClientCapabilities = 9, | |||
| GetPortConnections = 10, | |||
| GetPortNConnections = 11, | |||
| ResetTimeBaseClient = 12, | |||
| SetSyncClient = 13, | |||
| } RequestType; | |||
| struct _jack_request { | |||
| @@ -278,6 +302,10 @@ struct _jack_request { | |||
| unsigned int nports; | |||
| const char **ports; | |||
| } port_connections; | |||
| struct { | |||
| jack_client_id_t client_id; | |||
| int conditional; | |||
| } timebase; | |||
| jack_client_id_t client_id; | |||
| jack_nframes_t nframes; | |||
| } x; | |||
| @@ -332,5 +360,8 @@ extern jack_port_type_info_t jack_builtin_port_types[]; | |||
| extern void jack_client_invalidate_port_buffers (jack_client_t *client); | |||
| extern void jack_transport_copy_position (jack_position_t *from, | |||
| jack_position_t *to); | |||
| #endif /* __jack_internal_h__ */ | |||
| @@ -536,11 +536,14 @@ jack_port_t *jack_port_by_name (jack_client_t *, const char *portname); | |||
| jack_port_t *jack_port_by_id (const jack_client_t *client, jack_port_id_t id); | |||
| /** | |||
| * If a client is told (by the user) to become the timebase | |||
| * for the entire system, it calls this function. If it | |||
| * returns zero, then the client has the responsibility to | |||
| * call jack_set_transport_info()) at the end of its process() | |||
| * callback. | |||
| * If a client is told (by the user) to become the timebase for the | |||
| * entire system, it calls this function. If it returns zero, then the | |||
| * client has the responsibility to call jack_set_transport_info() at | |||
| * the end of its process callback. | |||
| * | |||
| * @deprecated This function is only for compatibility with earlier | |||
| * transport implementations. Instead, see <jack/transport.h> and use | |||
| * jack_set_timebase_callback(). | |||
| * | |||
| * @return 0 on success, otherwise a non-zero error code | |||
| */ | |||
| @@ -552,9 +555,8 @@ int jack_engine_takeover_timebase (jack_client_t *); | |||
| void jack_update_time (jack_client_t *, jack_nframes_t); | |||
| /** | |||
| * This estimates the time that has passed since the | |||
| * start jack server started calling the process() | |||
| * callbacks of all its clients. | |||
| * This estimates the time that has passed since the JACK server | |||
| * started calling the process callbacks of all its clients. | |||
| */ | |||
| jack_nframes_t jack_frames_since_cycle_start (const jack_client_t *); | |||
| @@ -1,5 +1,6 @@ | |||
| /* | |||
| Copyright (C) 2002 Paul Davis | |||
| Copyright (C) 2003 Jack O'Quin | |||
| This program is free software; you can redistribute it and/or modify | |||
| it under the terms of the GNU Lesser General Public License as published by | |||
| @@ -28,56 +29,339 @@ extern "C" { | |||
| #include <jack/types.h> | |||
| /** | |||
| * Possible transport states. | |||
| * Transport states. | |||
| */ | |||
| typedef enum { | |||
| JackTransportStopped, | |||
| JackTransportRolling, | |||
| JackTransportLooping | |||
| /* the order matters for binary compatibility */ | |||
| JackTransportStopped = 0, /**< Transport halted */ | |||
| JackTransportRolling = 1, /**< Transport playing */ | |||
| JackTransportLooping = 2, /**< For OLD_TRANSPORT, now ignored */ | |||
| JackTransportStarting = 3, /**< Waiting for sync ready */ | |||
| } jack_transport_state_t; | |||
| /** | |||
| * Bitfield of all possible transport info struct | |||
| * fields. | |||
| * Optional struct jack_position_t fields. | |||
| */ | |||
| typedef enum { | |||
| JackPositionBBT = 0x10, /**< Bar, Beat, Tick */ | |||
| } jack_position_bits_t; | |||
| #define EXTENDED_TIME_INFO | |||
| /** | |||
| * Struct for transport position information. | |||
| */ | |||
| typedef struct { | |||
| /* these two cannot be set from clients: the server sets them */ | |||
| jack_time_t usecs; /**< monotonic, free-rolling */ | |||
| jack_nframes_t frame_rate; /**< current frame rate (per second) */ | |||
| jack_nframes_t frame; /**< frame number, always present */ | |||
| jack_position_bits_t valid; /**< which other fields are valid */ | |||
| /* JackPositionBBT fields: */ | |||
| int bar; /**< current bar */ | |||
| int beat; /**< current beat-within-bar */ | |||
| int tick; /**< current tick-within-beat */ | |||
| double bar_start_tick; | |||
| float beats_per_bar; | |||
| float beat_type; | |||
| double ticks_per_beat; | |||
| double beats_per_minute; | |||
| /* For binary compatibility, new fields should be allocated from | |||
| * this padding area with new valid bits controlling access, so | |||
| * the existing structure size and offsets are preserved. */ | |||
| int padding[14]; | |||
| /* When (guard_usecs == usecs) the entire structure is consistent. | |||
| * This is set by server. */ | |||
| jack_time_t guard_usecs; /**< guard copy of usecs */ | |||
| } jack_position_t; | |||
| /** | |||
| * Called by the timebase master to release itself from that | |||
| * responsibility. | |||
| * | |||
| * If the timebase master releases the timebase or leaves the JACK | |||
| * graph for any reason, the JACK engine takes over at the start of | |||
| * the next process cycle. The transport state does not change. If | |||
| * rolling, it continues to play, with frame numbers as the only | |||
| * available position information. | |||
| * | |||
| * @see jack_set_timebase_callback | |||
| * | |||
| * @param client the JACK client structure. | |||
| * | |||
| * @return 0 on success, otherwise a non-zero error code. | |||
| */ | |||
| int jack_release_timebase (jack_client_t *client); | |||
| /** | |||
| * Prototype for the sync callback defined by slow-sync clients. | |||
| * Called just before process() in the same thread when some client | |||
| * has requested a new position. This realtime function must not | |||
| * wait. | |||
| * | |||
| * @see jack_transport_info_t | |||
| * The transport @a state will be: | |||
| * | |||
| * - ::JackTransportStopped some client just requested a new position; | |||
| * - ::JackTransportStarting the transport is waiting to start; | |||
| * - ::JackTransportRolling the timeout has expired, and the position | |||
| * is now a moving target. | |||
| * | |||
| * @param state current transport state. | |||
| * @param pos new transport position. | |||
| * @param arg the argument supplied by jack_set_sync_callback(). | |||
| * | |||
| * @return true (non-zero) when ready to roll. | |||
| */ | |||
| typedef int (*JackSyncCallback)(jack_transport_state_t state, | |||
| jack_position_t *pos, | |||
| void *arg); | |||
| /** | |||
| * Register (or unregister) as a slow-sync client, one that cannot | |||
| * respond immediately to transport position changes. | |||
| * | |||
| * When there are slow-sync clients and the JACK transport starts | |||
| * playing in a new postion, it first enters the | |||
| * ::JackTransportStarting state. The @a sync_callback function for | |||
| * each slow-sync client will be invoked in the JACK process thread | |||
| * while the transport is starting. If it has not already done so, | |||
| * the client needs to initiate a seek to reach the starting position. | |||
| * The @a sync_callback returns false until the seek completes and the | |||
| * client is ready to play. When all slow-sync clients are ready, the | |||
| * state changes to ::JackTransportRolling. | |||
| * | |||
| * Clients that don't set a @a sync_callback are assumed to be ready | |||
| * immediately, any time the transport wants to start. | |||
| * | |||
| * @param client the JACK client structure. | |||
| * @param sync_callback is a realtime function that returns true when | |||
| * the client is ready. Setting @a sync_callback to NULL declares that | |||
| * this client no longer requires slow-sync processing. | |||
| * @param arg an argument for the @a sync_callback function. | |||
| * | |||
| * @return 0 on success, otherwise a non-zero error code. | |||
| */ | |||
| int jack_set_sync_callback (jack_client_t *client, | |||
| JackSyncCallback sync_callback, | |||
| void *arg); | |||
| /** | |||
| * Set the timeout value for slow-sync clients. | |||
| * | |||
| * This timeout prevents unresponsive slow-sync clients from | |||
| * completely halting the transport mechanism. The default is two | |||
| * seconds. When the timeout expires, the transport starts rolling, | |||
| * even if some slow-sync clients are still unready. The | |||
| * sync_callbacksof these clients continue being invoked, giving them | |||
| * a chance to catch up. | |||
| * | |||
| * @see jack_set_sync_callback | |||
| * | |||
| * @param client the JACK client structure. | |||
| * @param timeout is delay (in frames) before the timeout expires. | |||
| * | |||
| * @return 0 on success, otherwise a non-zero error code. | |||
| */ | |||
| int jack_set_sync_timeout (jack_client_t *client, | |||
| jack_nframes_t timeout); | |||
| /** | |||
| * Prototype for the timebase master callback used to continuously | |||
| * update position information. Without extended information, there | |||
| * is no need for this function, JACK can count frames automatically. | |||
| * | |||
| * This function is called immediately after process() in the same | |||
| * thread whenever the transport is rolling, or when any client has | |||
| * set a new position in the previous cycle. The first cycle after | |||
| * jack_set_timebase_callback() is also treated as a new position. | |||
| * This realtime function must not wait. Its output affects all of | |||
| * the following process cycle, unless a new position request arrives | |||
| * during the current cycle. | |||
| * | |||
| * The timebase master should not use its @a pos argument to set a | |||
| * discontinuous position. Use jack_transport_reposition() or | |||
| * jack_transport_goto_frame(), instead. | |||
| * | |||
| * @param state current transport state. | |||
| * @param nframes number of frames in current period. | |||
| * @param pos pointer to a structure containing the current position. | |||
| * Store updated position information for the next cycle here. At a | |||
| * minimum, @a pos->frame must be incremented. | |||
| * @param new_pos true (non-zero) for a newly requested @a pos, or | |||
| * for the first cycle after jack_set_timebase_callback(). | |||
| * @param arg the argument supplied by jack_set_timebase_callback(). | |||
| */ | |||
| typedef void (*JackTimebaseCallback)(jack_transport_state_t state, | |||
| jack_nframes_t nframes, | |||
| jack_position_t *pos, | |||
| int new_pos, | |||
| void *arg); | |||
| /** | |||
| * Register as timebase master for the JACK subsystem. | |||
| * | |||
| * The timebase master is responsible for counting beats, frames, | |||
| * etc. on every process cycle. There is never more than one at a | |||
| * time. The timebase master registers a callback that updates | |||
| * position information while the transport is rolling. | |||
| * | |||
| * When a new client takes over, the former timebase callback is no | |||
| * longer called. Taking over the timebase may be done conditionally, | |||
| * so it fails if there is a master already. | |||
| * | |||
| * @param client the JACK client structure. | |||
| * @param conditional non-zero for a conditional request. | |||
| * @param timebase_callback is a realtime function that returns | |||
| * position information. | |||
| * @param arg an argument for the @a timebase_callback function. | |||
| * | |||
| * @return | |||
| * - 0 on success; | |||
| * - EBUSY if a conditional request fails because there is already a | |||
| * timebase master; | |||
| * - other non-zero error code. | |||
| */ | |||
| int jack_set_timebase_callback (jack_client_t *client, | |||
| int conditional, | |||
| JackTimebaseCallback timebase_callback, | |||
| void *arg); | |||
| /** | |||
| * Reposition transport to a new frame number. | |||
| * | |||
| * May be called at any time by any client. If valid, the new | |||
| * position takes effect in the next process cycle. The timebase | |||
| * master and any slow-sync clients are notified of the new position. | |||
| * If there are slow-sync clients and the transport is already | |||
| * rolling, it enters the ::JackTransportStarting state and begins | |||
| * invoking their sync_callbacks until they declare themselves ready. | |||
| * | |||
| * @see jack_transport_reposition, jack_set_sync_callback | |||
| * | |||
| * @param client the JACK client structure. | |||
| * @param frame frame number of new transport position. | |||
| * | |||
| * @return 0 if valid request, otherwise a non-zero error code. | |||
| */ | |||
| int jack_transport_goto_frame (jack_client_t *client, | |||
| jack_nframes_t frame); | |||
| /** | |||
| * Query the current transport state and position. | |||
| * | |||
| * This function can be called from any thread. If called from the | |||
| * process thread, @a pos corresponds to the first frame of the | |||
| * current cycle and the state returned is valid for the entire cycle. | |||
| * | |||
| * @param client the JACK client structure. | |||
| * @param pos current position structure, @a pos->valid describes | |||
| * which fields contain valid data. | |||
| * | |||
| * @return Current transport state. | |||
| */ | |||
| jack_transport_state_t jack_transport_query (jack_client_t *client, | |||
| jack_position_t *pos); | |||
| /** | |||
| * Request a new transport position. | |||
| * | |||
| * May be called at any time by any client. If valid, the new | |||
| * position takes effect in the next process cycle. The timebase | |||
| * master and any slow-sync clients are notified of the new position. | |||
| * If there are slow-sync clients and the transport is already | |||
| * rolling, it enters the ::JackTransportStarting state and begins | |||
| * invoking their sync_callbacks until they declare themselves ready. | |||
| * | |||
| * @see jack_transport_goto_frame, jack_set_sync_callback | |||
| * | |||
| * @param client the JACK client structure. | |||
| * @param pos requested new transport position. | |||
| * | |||
| * @return 0 if valid request, otherwise a non-zero error code. | |||
| */ | |||
| int jack_transport_reposition (jack_client_t *client, | |||
| jack_position_t *pos); | |||
| /** | |||
| * Start the JACK transport rolling. | |||
| * | |||
| * Any client can make this request at any time. It takes effect no | |||
| * sooner than the next process cycle, perhaps later if there are | |||
| * slow-sync clients. | |||
| * | |||
| * @see jack_set_sync_callback | |||
| * | |||
| * @param client the JACK client structure. | |||
| */ | |||
| void jack_transport_start (jack_client_t *client); | |||
| /** | |||
| * Stop the JACK transport. | |||
| * | |||
| * Any client can make this request at any time. It takes effect on | |||
| * the next process cycle. | |||
| * | |||
| * @param client the JACK client structure. | |||
| */ | |||
| void jack_transport_stop (jack_client_t *client); | |||
| /********************************************************************* | |||
| * The following interfaces are DEPRECATED. They are only provided | |||
| * for compatibility with the earlier JACK transport implementation. | |||
| *********************************************************************/ | |||
| /** | |||
| * Optional struct jack_transport_info_t fields. | |||
| * | |||
| * @see jack_position_bits_t. | |||
| */ | |||
| typedef enum { | |||
| JackTransportState = 0x1, | |||
| JackTransportPosition = 0x2, | |||
| JackTransportLoop = 0x4, | |||
| JackTransportSMPTE = 0x8, | |||
| JackTransportBBT = 0x10, | |||
| JackTransportState = 0x1, /**< Transport state */ | |||
| JackTransportPosition = 0x2, /**< Frame number */ | |||
| JackTransportLoop = 0x4, /**< Loop boundaries (ignored) */ | |||
| JackTransportSMPTE = 0x8, /**< SMPTE (ignored) */ | |||
| JackTransportBBT = 0x10, /**< Bar, Beat, Tick */ | |||
| } jack_transport_bits_t; | |||
| #define EXTENDED_TIME_INFO \ | |||
| /** | |||
| * Struct for transport status information. | |||
| * Deprecated struct for transport position information. | |||
| * | |||
| * @deprecated This is for compatibility with the earlier transport | |||
| * interface. Use the jack_position_t struct, instead. | |||
| */ | |||
| typedef struct { | |||
| /* these two cannot be set from clients: the server sets them */ | |||
| jack_nframes_t frame_rate; /**< current frame rate (per second) */ | |||
| jack_time_t usecs; /**< monotonic, free-rolling */ | |||
| jack_nframes_t frame_rate; /**< current frame rate (per second) */ | |||
| jack_time_t usecs; /**< monotonic, free-rolling */ | |||
| jack_transport_bits_t valid; /**< which fields are legal to read */ | |||
| jack_transport_bits_t valid; /**< which fields are legal to read */ | |||
| jack_transport_state_t transport_state; | |||
| jack_nframes_t frame; | |||
| jack_nframes_t loop_start; | |||
| jack_nframes_t loop_end; | |||
| long smpte_offset; /**< SMPTE offset (SMPTE frame when frame = 0) */ | |||
| float smpte_frame_rate; /**< 29.97, 30, 24 etc. */ | |||
| long smpte_offset; /**< SMPTE offset (from frame 0) */ | |||
| float smpte_frame_rate; /**< 29.97, 30, 24 etc. */ | |||
| int bar; /**< current bar */ | |||
| int beat; /**< current beat-within-bar */ | |||
| int tick; /**< current tick-within-beat */ | |||
| int bar; | |||
| int beat; | |||
| int tick; | |||
| double bar_start_tick; | |||
| float beats_per_bar; | |||
| @@ -86,31 +370,37 @@ typedef struct { | |||
| double beats_per_minute; | |||
| } jack_transport_info_t; | |||
| /** | |||
| * Sets the transport state for the next engine | |||
| * cycle. | |||
| * Gets the current transport info structure (deprecated). | |||
| * | |||
| * The 'valid' field of the tinfo struct should contain | |||
| * a bitmask of all transport info fields that are set | |||
| * in tinfo. | |||
| * @param client the JACK client structure. | |||
| * @param tinfo current transport info structure. The "valid" field | |||
| * describes which fields contain valid data. | |||
| * | |||
| * @pre Caller must be the current timebase master. Must be called | |||
| * from the process() thread. | |||
| * @deprecated This is for compatibility with the earlier transport | |||
| * interface. Use jack_transport_query(), instead. | |||
| * | |||
| * @pre Must be called from the process thread. | |||
| */ | |||
| void jack_set_transport_info (jack_client_t *client, | |||
| void jack_get_transport_info (jack_client_t *client, | |||
| jack_transport_info_t *tinfo); | |||
| /** | |||
| * Gets the current transport state. | |||
| * Set the transport info structure (deprecated). | |||
| * | |||
| * @param client the JACK client structure. | |||
| * @param tinfo used to return a new transport info structure used | |||
| * for the next process cycle. The "valid" field must say which | |||
| * fields contain valid data. | |||
| * | |||
| * On return, the 'valid' field of the tinfo struct will contain | |||
| * a bitmask of all transport info fields that are legal to | |||
| * use. | |||
| * @deprecated This is for compatibility with the earlier transport | |||
| * interface. Define a ::JackTimebaseCallback, instead. | |||
| * | |||
| * @pre Must be called from the process() thread. | |||
| * @pre Caller must be the current timebase master. Must be called | |||
| * from the process thread. | |||
| */ | |||
| void jack_get_transport_info (jack_client_t *client, | |||
| void jack_set_transport_info (jack_client_t *client, | |||
| jack_transport_info_t *tinfo); | |||
| #ifdef __cplusplus | |||
| @@ -85,7 +85,6 @@ static void jack_remove_client (jack_engine_t *engine, jack_client_internal_t *c | |||
| static void jack_client_delete (jack_engine_t *, jack_client_internal_t *); | |||
| static jack_client_internal_t *jack_client_internal_new (jack_engine_t *engine, int fd, jack_client_connect_request_t *); | |||
| static jack_client_internal_t *jack_client_internal_by_id (jack_engine_t *engine, jack_client_id_t id); | |||
| static void jack_sort_graph (jack_engine_t *engine); | |||
| static int jack_rechain_graph (jack_engine_t *engine); | |||
| @@ -128,23 +127,6 @@ jack_client_is_internal (jack_client_internal_t *client) | |||
| return (client->control->type == ClientInternal) || (client->control->type == ClientDriver); | |||
| } | |||
| static inline void jack_lock_graph (jack_engine_t* engine) { | |||
| DEBUG ("acquiring graph lock"); | |||
| pthread_mutex_lock (&engine->client_lock); | |||
| } | |||
| static inline int jack_try_lock_graph (jack_engine_t *engine) | |||
| { | |||
| DEBUG ("TRYING to acquiring graph lock"); | |||
| return pthread_mutex_trylock (&engine->client_lock); | |||
| } | |||
| static inline void jack_unlock_graph (jack_engine_t* engine) | |||
| { | |||
| DEBUG ("releasing graph lock"); | |||
| pthread_mutex_unlock (&engine->client_lock); | |||
| } | |||
| static inline void | |||
| jack_engine_reset_rolling_usecs (jack_engine_t *engine) | |||
| { | |||
| @@ -426,6 +408,9 @@ jack_process_internal(jack_engine_t *engine, JSList *node, jack_nframes_t nframe | |||
| return NULL; /* will stop the loop */ | |||
| } | |||
| //JOQ: can an internal client be slow sync? | |||
| //JOQ: can an internal client have a timebase master? | |||
| } else { | |||
| DEBUG ("internal client has no process() function"); | |||
| @@ -1338,7 +1323,7 @@ jack_client_deactivate (jack_engine_t *engine, jack_client_id_t id) | |||
| if (client == engine->timebase_client) { | |||
| engine->timebase_client = 0; | |||
| jack_transport_reset (engine); | |||
| jack_timebase_exit (engine); | |||
| } | |||
| for (portnode = client->ports; portnode; portnode = jack_slist_next (portnode)) { | |||
| @@ -1356,22 +1341,6 @@ jack_client_deactivate (jack_engine_t *engine, jack_client_id_t id) | |||
| return ret; | |||
| } | |||
| static int | |||
| jack_set_timebase (jack_engine_t *engine, jack_client_id_t client) | |||
| { | |||
| int ret = -1; | |||
| jack_lock_graph (engine); | |||
| if ((engine->timebase_client = jack_client_internal_by_id (engine, client)) != 0) { | |||
| ret = 0; | |||
| } | |||
| jack_unlock_graph (engine); | |||
| return ret; | |||
| } | |||
| static int | |||
| handle_client_socket_error (jack_engine_t *engine, int fd) | |||
| { | |||
| @@ -1468,7 +1437,17 @@ do_request (jack_engine_t *engine, jack_request_t *req, int *reply_fd) | |||
| break; | |||
| case SetTimeBaseClient: | |||
| req->status = jack_set_timebase (engine, req->x.client_id); | |||
| req->status = jack_timebase_set (engine, | |||
| req->x.timebase.client_id, | |||
| req->x.timebase.conditional); | |||
| break; | |||
| case ResetTimeBaseClient: | |||
| req->status = jack_timebase_reset (engine, req->x.client_id); | |||
| break; | |||
| case SetSyncClient: | |||
| req->status = jack_set_sync_client (engine, req->x.client_id); | |||
| break; | |||
| #ifdef USE_CAPABILITIES | |||
| @@ -1695,6 +1674,7 @@ jack_engine_new (int realtime, int rtpriority, int verbose, int client_timeout) | |||
| engine->set_sample_rate = jack_set_sample_rate; | |||
| engine->set_buffer_size = jack_set_buffer_size; | |||
| engine->run_cycle = jack_run_cycle; | |||
| engine->transport_cycle_start = jack_transport_cycle_start; | |||
| engine->client_timeout_msecs = client_timeout; | |||
| engine->next_client_id = 1; | |||
| @@ -1804,7 +1784,7 @@ jack_engine_new (int realtime, int rtpriority, int verbose, int client_timeout) | |||
| engine->control->buffer_size = 0; | |||
| jack_set_sample_rate (engine, 0); | |||
| jack_transport_reset (engine); | |||
| jack_timebase_init (engine); | |||
| engine->control->internal = 0; | |||
| engine->control->has_capabilities = 0; | |||
| @@ -2311,7 +2291,7 @@ jack_zombify_client (jack_engine_t *engine, jack_client_internal_t *client) | |||
| if (client == engine->timebase_client) { | |||
| engine->timebase_client = 0; | |||
| jack_transport_reset (engine); | |||
| jack_timebase_exit (engine); | |||
| } | |||
| jack_client_disconnect (engine, client); | |||
| @@ -4,60 +4,257 @@ | |||
| Copyright (C) 2001-2003 Paul Davis | |||
| Copyright (C) 2003 Jack O'Quin | |||
| This program is free software; you can redistribute it and/or modify | |||
| it under the terms of the GNU Lesser General Public License as published by | |||
| the Free Software Foundation; either version 2.1 of the License, or | |||
| (at your option) any later version. | |||
| This program is free software; you can redistribute it and/or | |||
| modify it under the terms of the GNU Lesser General Public License | |||
| as published by the Free Software Foundation; either version 2.1 | |||
| of the License, or (at your option) any later version. | |||
| This program is distributed in the hope that it will be useful, | |||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| GNU Lesser General Public License for more details. | |||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
| Lesser General Public License for more details. | |||
| You should have received a copy of the GNU Lesser General Public License | |||
| along with this program; if not, write to the Free Software | |||
| Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
| You should have received a copy of the GNU General Public License | |||
| along with this program; if not, write to the Free Software | |||
| Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |||
| */ | |||
| #include <config.h> | |||
| #include <errno.h> | |||
| #include <jack/internal.h> | |||
| #include <jack/engine.h> | |||
| #include "transengine.h" | |||
| /*********************** driver functions ***********************/ | |||
| int | |||
| jack_set_sample_rate (jack_engine_t *engine, jack_nframes_t nframes) | |||
| { | |||
| engine->control->current_time.frame_rate = nframes; | |||
| engine->control->pending_time.frame_rate = nframes; | |||
| jack_control_t *ectl = engine->control; | |||
| ectl->current_time.frame_rate = nframes; | |||
| ectl->pending_time.frame_rate = nframes; | |||
| return 0; | |||
| } | |||
| void | |||
| jack_transport_cycle_start (jack_engine_t *engine, jack_time_t time) | |||
| { | |||
| engine->control->current_time.guard_usecs = | |||
| engine->control->current_time.usecs = time; | |||
| } | |||
| /********************* RPC request handlers *********************/ | |||
| /* for SetSyncClient */ | |||
| int | |||
| jack_set_sync_client (jack_engine_t *engine, jack_client_id_t client) | |||
| { | |||
| int ret; | |||
| jack_client_internal_t *clintl; | |||
| jack_lock_graph (engine); | |||
| clintl = jack_client_internal_by_id (engine, client); | |||
| if (clintl) { | |||
| clintl->control->sync_ready = 0; | |||
| engine->control->sync_clients++; | |||
| ret = 0; | |||
| } else | |||
| ret = EINVAL; | |||
| jack_unlock_graph (engine); | |||
| return ret; | |||
| } | |||
| /* for ResetTimeBaseClient */ | |||
| int | |||
| jack_timebase_reset (jack_engine_t *engine, jack_client_id_t client) | |||
| { | |||
| int ret; | |||
| jack_lock_graph (engine); | |||
| if ((engine->timebase_client = | |||
| jack_client_internal_by_id (engine, client)) != 0) { | |||
| engine->timebase_client = 0; | |||
| engine->control->pending_time.valid = 0; | |||
| ret = 0; | |||
| } else | |||
| ret = EINVAL; | |||
| jack_unlock_graph (engine); | |||
| return ret; | |||
| } | |||
| /* for SetTimeBaseClient */ | |||
| int | |||
| jack_timebase_set (jack_engine_t *engine, | |||
| jack_client_id_t client, int conditional) | |||
| { | |||
| int ret; | |||
| jack_lock_graph (engine); | |||
| if (conditional && engine->timebase_client) | |||
| ret = EBUSY; | |||
| else if ((engine->timebase_client = | |||
| jack_client_internal_by_id (engine, client)) != 0) | |||
| ret = 0; | |||
| else | |||
| ret = -1; | |||
| jack_unlock_graph (engine); | |||
| return ret; | |||
| } | |||
| /******************** engine.c subroutines ********************/ | |||
| /* start polling slow-sync clients */ | |||
| void | |||
| jack_transport_cycle_end (jack_engine_t *engine) | |||
| jack_start_sync_poll(jack_engine_t *engine) | |||
| { | |||
| jack_control_t *ectl = engine->control; | |||
| JSList *node; | |||
| long sync_count = 0; /* number of slow-sync clients */ | |||
| jack_lock_graph (engine); | |||
| for (node = engine->clients; node; node = jack_slist_next (node)) { | |||
| jack_client_internal_t *clintl = | |||
| (jack_client_internal_t *) node->data; | |||
| if (clintl->control->sync_cb) { | |||
| clintl->control->sync_ready = 0; | |||
| sync_count++; | |||
| } | |||
| } | |||
| ectl->sync_remain = ectl->sync_clients = sync_count; | |||
| ectl->sync_cycle = 0; | |||
| jack_unlock_graph (engine); | |||
| } | |||
| /* when timebase master exits the graph */ | |||
| void | |||
| jack_timebase_exit (jack_engine_t *engine) | |||
| { | |||
| jack_control_t *ectl = engine->control; | |||
| ectl->current_time.valid = 0; | |||
| ectl->pending_time.valid = 0; | |||
| } | |||
| /* engine initialization */ | |||
| void | |||
| jack_timebase_init (jack_engine_t *engine) | |||
| { | |||
| jack_control_t *ctl = engine->control; | |||
| jack_control_t *ectl = engine->control; | |||
| /* maintain the current_time.usecs and frame_rate values, | |||
| since clients are not permitted to set them. | |||
| */ | |||
| ctl->pending_time.usecs = ctl->current_time.usecs; | |||
| ctl->pending_time.frame_rate = ctl->current_time.frame_rate; | |||
| ctl->current_time = ctl->pending_time; | |||
| ectl->transport_state = JackTransportStopped; | |||
| ectl->transport_cmd = TransportCommandNone; | |||
| memset (&ectl->current_time, 0, sizeof(ectl->current_time)); | |||
| memset (&ectl->pending_time, 0, sizeof(ectl->pending_time)); | |||
| memset (&ectl->request_time, 0, sizeof(ectl->request_time)); | |||
| ectl->new_pos = 0; | |||
| ectl->sync_remain = 0; | |||
| ectl->sync_cycle = 0; | |||
| ectl->sync_clients = 0; | |||
| } | |||
| /* This is the heart of the transport control. It runs at the end of | |||
| * every process cycle. | |||
| */ | |||
| void | |||
| jack_transport_reset (jack_engine_t *engine) | |||
| { | |||
| #ifdef OLD_TRANSPORT | |||
| jack_control_t *ctl = engine->control; | |||
| ctl->current_time.frame = 0; | |||
| ctl->pending_time.frame = 0; | |||
| ctl->current_time.transport_state = JackTransportStopped; | |||
| ctl->pending_time.transport_state = JackTransportStopped; | |||
| ctl->current_time.valid = | |||
| JackTransportState|JackTransportPosition; | |||
| ctl->pending_time.valid = | |||
| JackTransportState|JackTransportPosition; | |||
| #endif /* OLD_TRANSPORT */ | |||
| jack_transport_cycle_end (jack_engine_t *engine) | |||
| { | |||
| jack_control_t *ectl = engine->control; | |||
| transport_command_t cmd; /* latest transport command */ | |||
| jack_time_t repos; /* nonzero if reposition requested */ | |||
| /* update timebase, if needed */ | |||
| if ((engine->timebase_client == 0) && | |||
| (ectl->transport_state == JackTransportRolling)) { | |||
| ectl->pending_time.frame = | |||
| ectl->current_time.frame + ectl->buffer_size; | |||
| } | |||
| /* Handle latest asynchronous requests from the last cycle. | |||
| * | |||
| * This should ideally use an atomic swap, since commands can | |||
| * arrive at any time. There is a small timing window during | |||
| * which a request could be ignored inadvertently. Since | |||
| * another could have arrived in the previous moment and | |||
| * replaced it anyway, we won't bother with <asm/atomic.h>. | |||
| */ | |||
| cmd = ectl->transport_cmd; | |||
| ectl->transport_cmd = TransportCommandNone; | |||
| repos = ectl->request_time.usecs; | |||
| if (repos) { | |||
| /* request_time could change during this copy */ | |||
| jack_transport_copy_position(&ectl->request_time, | |||
| &ectl->pending_time); | |||
| ectl->request_time.usecs = 0; /* empty request buffer */ | |||
| ectl->new_pos = 1; | |||
| } else | |||
| ectl->new_pos = 0; | |||
| /* Promote pending_time to current_time. Maintain the usecs | |||
| * and frame_rate values, clients may not set them. */ | |||
| ectl->pending_time.guard_usecs = | |||
| ectl->pending_time.usecs = ectl->current_time.usecs; | |||
| ectl->pending_time.frame_rate = ectl->current_time.frame_rate; | |||
| ectl->current_time = ectl->pending_time; | |||
| /* accumulate sync results from previous cycle */ | |||
| if (ectl->sync_remain) { | |||
| ectl->sync_remain -= ectl->sync_cycle; | |||
| if ((ectl->sync_remain == 0) && | |||
| (ectl->transport_state == JackTransportStarting)) | |||
| ectl->transport_state = JackTransportRolling; | |||
| ectl->sync_cycle = 0; | |||
| } | |||
| /* state transition switch */ | |||
| switch (ectl->transport_state) { | |||
| case JackTransportStopped: | |||
| if (cmd == TransportCommandPlay) { | |||
| if (ectl->sync_clients) { | |||
| ectl->transport_state = JackTransportStarting; | |||
| jack_start_sync_poll(engine); | |||
| } else | |||
| ectl->transport_state = JackTransportRolling; | |||
| } | |||
| break; | |||
| case JackTransportStarting: | |||
| case JackTransportRolling: | |||
| if (cmd == TransportCommandStop) { | |||
| ectl->transport_state = JackTransportStopped; | |||
| ectl->sync_remain = 0; /* halt polling */ | |||
| } else if (repos) { | |||
| if (ectl->sync_clients) { | |||
| ectl->transport_state = JackTransportStarting; | |||
| jack_start_sync_poll(engine); | |||
| } | |||
| else | |||
| ectl->transport_state = JackTransportRolling; | |||
| } | |||
| break; | |||
| default: | |||
| jack_error ("invalid JACK transport state: %d", | |||
| ectl->transport_state); | |||
| } | |||
| return; | |||
| } | |||
| @@ -1,24 +1,29 @@ | |||
| /* | |||
| Internal interfaces to JACK transport engine. | |||
| Internal interfaces for JACK transport engine. | |||
| Copyright (C) 2003 Jack O'Quin | |||
| This program is free software; you can redistribute it and/or modify | |||
| it under the terms of the GNU General Public License as published by | |||
| the Free Software Foundation; either version 2 of the License, or | |||
| (at your option) any later version. | |||
| This program is free software; you can redistribute it and/or | |||
| modify it under the terms of the GNU General Public License as | |||
| published by the Free Software Foundation; either version 2 of the | |||
| License, or (at your option) any later version. | |||
| This program is distributed in the hope that it will be useful, | |||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| GNU General Public License for more details. | |||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
| General Public License for more details. | |||
| You should have received a copy of the GNU General Public License | |||
| along with this program; if not, write to the Free Software | |||
| Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |||
| */ | |||
| int jack_set_sample_rate (jack_engine_t *engine, | |||
| jack_nframes_t nframes); | |||
| int jack_set_sample_rate (jack_engine_t *engine, jack_nframes_t nframes); | |||
| int jack_set_sync_client (jack_engine_t *engine, jack_client_id_t client); | |||
| void jack_timebase_exit (jack_engine_t *engine); | |||
| void jack_timebase_init (jack_engine_t *engine); | |||
| int jack_timebase_reset (jack_engine_t *engine, jack_client_id_t client); | |||
| int jack_timebase_set (jack_engine_t *engine, | |||
| jack_client_id_t client, int conditional); | |||
| void jack_transport_cycle_start(jack_engine_t *engine, jack_time_t time); | |||
| void jack_transport_cycle_end (jack_engine_t *engine); | |||
| void jack_transport_reset (jack_engine_t *engine); | |||
| @@ -39,7 +39,6 @@ | |||
| #include <stdio.h> | |||
| #include <stdint.h> | |||
| #include <regex.h> | |||
| #include <math.h> | |||
| #include <config.h> | |||
| @@ -792,6 +791,9 @@ jack_client_thread (void *arg) | |||
| control->state = Running; | |||
| if (control->sync_cb) | |||
| jack_call_sync_client (client); | |||
| if (control->process) { | |||
| if (control->process (control->nframes, control->process_arg) == 0) { | |||
| control->state = Finished; | |||
| @@ -799,7 +801,10 @@ jack_client_thread (void *arg) | |||
| } else { | |||
| control->state = Finished; | |||
| } | |||
| if (control->timebase_cb) | |||
| jack_call_timebase_master (client); | |||
| control->finished_at = jack_get_microseconds(); | |||
| #ifdef WITH_TIMESTAMPS | |||
| @@ -1279,12 +1284,6 @@ unsigned long jack_get_buffer_size (jack_client_t *client) | |||
| return client->engine->buffer_size; | |||
| } | |||
| unsigned long jack_get_sample_rate (jack_client_t *client) | |||
| { | |||
| return client->engine->current_time.frame_rate; | |||
| } | |||
| int | |||
| jack_connect (jack_client_t *client, const char *source_port, const char *destination_port) | |||
| @@ -1384,23 +1383,6 @@ jack_set_buffer_size_callback (jack_client_t *client, JackBufferSizeCallback cal | |||
| return 0; | |||
| } | |||
| int | |||
| jack_set_sample_rate_callback (jack_client_t *client, JackSampleRateCallback callback, void *arg) | |||
| { | |||
| if (client->control->active) { | |||
| jack_error ("You cannot set callbacks on an active client."); | |||
| return -1; | |||
| } | |||
| client->control->srate_arg = arg; | |||
| client->control->srate = callback; | |||
| /* Now invoke it */ | |||
| callback (client->engine->current_time.frame_rate, arg); | |||
| return 0; | |||
| } | |||
| int | |||
| jack_set_port_registration_callback(jack_client_t *client, JackPortRegistrationCallback callback, void *arg) | |||
| @@ -1509,52 +1491,6 @@ jack_get_ports (jack_client_t *client, | |||
| return matching_ports; | |||
| } | |||
| static inline void | |||
| jack_read_frame_time (const jack_client_t *client, jack_frame_timer_t *copy) | |||
| { | |||
| int tries = 0; | |||
| do { | |||
| /* throttle the busy wait if we don't get | |||
| the answer very quickly. | |||
| */ | |||
| if (tries > 10) { | |||
| usleep (20); | |||
| tries = 0; | |||
| } | |||
| *copy = client->engine->frame_timer; | |||
| tries++; | |||
| } while (copy->guard1 != copy->guard2); | |||
| } | |||
| jack_nframes_t | |||
| jack_frames_since_cycle_start (const jack_client_t *client) | |||
| { | |||
| float usecs; | |||
| usecs = jack_get_microseconds() - client->engine->current_time.usecs; | |||
| return (jack_nframes_t) floor ((((float) client->engine->current_time.frame_rate) / 1000000.0f) * usecs); | |||
| } | |||
| jack_nframes_t | |||
| jack_frame_time (const jack_client_t *client) | |||
| { | |||
| jack_frame_timer_t current; | |||
| float usecs; | |||
| jack_nframes_t elapsed; | |||
| jack_read_frame_time (client, ¤t); | |||
| usecs = jack_get_microseconds() - current.stamp; | |||
| elapsed = (jack_nframes_t) floor ((((float) client->engine->current_time.frame_rate) / 1000000.0f) * usecs); | |||
| return current.frames + elapsed; | |||
| } | |||
| float | |||
| jack_cpu_load (jack_client_t *client) | |||
| { | |||
| @@ -1,6 +1,7 @@ | |||
| #ifndef __jack_libjack_local_h__ | |||
| #define __jack_libjack_local_h__ | |||
| /* Client data structure, in the client's address space. */ | |||
| struct _jack_client { | |||
| jack_control_t *engine; | |||
| @@ -17,6 +18,7 @@ struct _jack_client { | |||
| void *on_shutdown_arg; | |||
| char thread_ok : 1; | |||
| char first_active : 1; | |||
| char new_timebase : 1; | |||
| pthread_t thread_id; | |||
| #if defined(__APPLE__) && defined(__POWERPC__) | |||
| @@ -30,7 +32,10 @@ struct _jack_client { | |||
| extern int jack_client_deliver_request (const jack_client_t *client, jack_request_t *req); | |||
| extern jack_port_t *jack_port_new (const jack_client_t *client, jack_port_id_t port_id, jack_control_t *control); | |||
| extern void jack_call_sync_client (jack_client_t *client); | |||
| extern void jack_call_timebase_master (jack_client_t *client); | |||
| extern void* jack_zero_filled_buffer; | |||
| extern void *jack_zero_filled_buffer; | |||
| #endif /* __jack_libjack_local_h__ */ | |||
| @@ -4,38 +4,308 @@ | |||
| Copyright (C) 2001-2003 Paul Davis | |||
| Copyright (C) 2003 Jack O'Quin | |||
| This program is free software; you can redistribute it and/or modify | |||
| it under the terms of the GNU Lesser General Public License as published by | |||
| the Free Software Foundation; either version 2.1 of the License, or | |||
| (at your option) any later version. | |||
| This program is free software; you can redistribute it and/or | |||
| modify it under the terms of the GNU Lesser General Public License | |||
| as published by the Free Software Foundation; either version 2.1 | |||
| of the License, or (at your option) any later version. | |||
| This program is distributed in the hope that it will be useful, | |||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| GNU Lesser General Public License for more details. | |||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
| Lesser General Public License for more details. | |||
| You should have received a copy of the GNU Lesser General Public License | |||
| along with this program; if not, write to the Free Software | |||
| Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
| You should have received a copy of the GNU Lesser General Public | |||
| License along with this program; if not, write to the Free | |||
| Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA | |||
| 02111-1307, USA. | |||
| */ | |||
| #include <config.h> | |||
| #include <errno.h> | |||
| #include <math.h> | |||
| #include <jack/internal.h> | |||
| #include "local.h" | |||
| /********************* Internal functions *********************/ | |||
| static inline void | |||
| jack_read_frame_time (const jack_client_t *client, jack_frame_timer_t *copy) | |||
| { | |||
| int tries = 0; | |||
| do { | |||
| /* throttle the busy wait if we don't get | |||
| the answer very quickly. | |||
| */ | |||
| if (tries > 10) { | |||
| usleep (20); | |||
| tries = 0; | |||
| } | |||
| *copy = client->engine->frame_timer; | |||
| tries++; | |||
| } while (copy->guard1 != copy->guard2); | |||
| } | |||
| /* copy a JACK 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 | |||
| /* * * API functions for compatibility with old transport interface * * */ | |||
| /************* Compatibility with old transport API. *************/ | |||
| int | |||
| jack_engine_takeover_timebase (jack_client_t *client) | |||
| { | |||
| jack_request_t req; | |||
| 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); | |||
| } | |||
| @@ -44,14 +314,63 @@ void | |||
| jack_get_transport_info (jack_client_t *client, | |||
| 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 | |||
| jack_set_transport_info (jack_client_t *client, | |||
| 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 */ | |||