/* JACK transport client interface -- runs in the client process. 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 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. 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 #include #include #include #include "atomicity.h" #include "internal.h" #include "local.h" /********************* Internal functions *********************/ /* generate a unique non-zero ID, different for each call */ jack_unique_t jack_generate_unique_id (jack_control_t *ectl) { /* The jack_unique_t is an opaque type. */ return exchange_and_add(&ectl->seq_number, 1); } static inline void jack_read_frame_time (const jack_client_t *client, jack_frame_timer_t *copy) { int tries = 0; long timeout = 1000; do { /* throttle the busy wait if we don't get the answer very quickly. XXX This is disgusting. on a UP system, it needs to sleep if the first try didn't work. on an SMP system, it should wait for half of the context switch time before sleeping. */ if (tries > 10) { usleep (20); tries = 0; /* debug code to avoid system hangs... */ if (--timeout == 0) { jack_error ("hung in loop copying position A"); abort(); } } *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 = 1000; do { /* throttle the busy wait if we don't get the answer * very quickly. See comment above about this * design. */ if (tries > 10) { usleep (20); tries = 0; /* debug code to avoid system hangs... */ if (--timeout == 0) { jack_error ("hung in loop copying position B"); abort(); } } *to = *from; tries++; } while (to->unique_1 != to->unique_2); } static inline int jack_transport_request_new_pos (jack_client_t *client, jack_position_t *pos) { jack_control_t *ectl = client->engine; /* distinguish this request from all others */ pos->unique_1 = pos->unique_2 = jack_generate_unique_id(ectl); /* clients may not set these fields */ pos->usecs = ectl->current_time.usecs; pos->frame_rate = ectl->current_time.frame_rate; /* carefully copy requested postion into shared memory */ jack_transport_copy_position (pos, &ectl->request_time); return 0; } /******************** Callback invocations ********************/ void jack_call_sync_client (jack_client_t *client) { jack_client_control_t *control = client->control; jack_control_t *ectl = client->engine; /* Make sure still active and slow-sync; active_slowsync is * set in a critical section; sync_cb is not. */ if ((ectl->new_pos || control->sync_poll || control->sync_new) && control->active_slowsync) { if (client->sync_cb (ectl->transport_state, &ectl->current_time, client->sync_arg)) { if (control->sync_poll) { control->sync_poll = 0; ectl->sync_remain--; } } control->sync_new = 0; } } void jack_call_timebase_master (jack_client_t *client) { jack_client_control_t *control = client->control; jack_control_t *ectl = client->engine; int new_pos = (int) ectl->pending_pos; /* Make sure this is still the master; is_timebase is set in a * critical section; timebase_cb is not. */ if (control->is_timebase) { if (control->timebase_new) { /* first callback? */ control->timebase_new = 0; new_pos = 1; } if ((ectl->transport_state == JackTransportRolling) || new_pos) { client->timebase_cb (ectl->transport_state, ectl->buffer_size, &ectl->pending_time, new_pos, client->timebase_arg); } } else { /* another master took over, so resign */ client->timebase_cb = NULL; client->timebase_arg = NULL; control->timebase_cb_cbset = FALSE; } } /************************* API functions *************************/ jack_nframes_t jack_get_current_transport_frame (const jack_client_t *client) { jack_position_t position; float usecs; jack_nframes_t elapsed; jack_transport_state_t tstate; /* get the current transport position information. this is thread-safe and atomic with respect to the structure contents. */ tstate = jack_transport_query (client, &position); if (tstate != JackTransportRolling) { return position.frame; } /* compute the elapsed usecs then audio frames since the transport info was last updated */ usecs = jack_get_microseconds() - position.usecs; elapsed = (jack_nframes_t) floor ((((float) position.frame_rate) / 1000000.0f) * usecs); /* return the estimated transport frame position */ return position.frame + elapsed; } jack_nframes_t jack_frames_since_cycle_start (const jack_client_t *client) { float usecs; jack_control_t *ectl = client->engine; usecs = jack_get_microseconds() - ectl->current_time.usecs; return (jack_nframes_t) floor ((((float) ectl->current_time.frame_rate) / 1000000.0f) * usecs); } int jack_get_cycle_times (const jack_client_t *client, jack_nframes_t *current_frames, jack_time_t *current_usecs, jack_time_t *next_usecs, float *period_usecs) { jack_frame_timer_t time; jack_read_frame_time (client, &time); if (time.initialized) { *current_frames = time.frames; *current_usecs = time.current_wakeup; *next_usecs = time.next_wakeup; *period_usecs = time.period_usecs; return 0; } return 1; } jack_time_t jack_get_time() { return jack_get_microseconds(); } jack_nframes_t jack_time_to_frames(const jack_client_t *client, jack_time_t usecs) { jack_frame_timer_t time; jack_control_t *ectl = client->engine; jack_read_frame_time (client, &time); if (time.initialized) { /* Make sure we have signed differences. It would make a lot of sense to use the standard signed intNN_t types everywhere instead of e.g. jack_nframes_t and jack_time_t. This would at least ensure that the types used below are the correct ones. There is no way to get a type that would be 'a signed version of jack_time_t' for example - the types below are inherently fragile and there is no automatic way to check they are the correct ones. The only way is to check manually against jack/types.h. FA - 16/02/2012 */ int64_t du = usecs - time.current_wakeup; int64_t dp = time.next_wakeup - time.current_wakeup; return time.frames + (int32_t) rint ((double) du / (double) dp * ectl->buffer_size); } return 0; } jack_nframes_t jack_frame_time (const jack_client_t *client) { jack_time_t now = jack_get_microseconds(); return jack_time_to_frames(client, now); } jack_nframes_t jack_last_frame_time (const jack_client_t *client) { return client->engine->frame_timer.frames; } jack_time_t jack_frames_to_time(const jack_client_t *client, jack_nframes_t frames) { jack_frame_timer_t time; jack_control_t *ectl = client->engine; jack_read_frame_time (client, &time); if (time.initialized) { /* Make sure we have signed differences. It would make a lot of sense to use the standard signed intNN_t types everywhere instead of e.g. jack_nframes_t and jack_time_t. This would at least ensure that the types used below are the correct ones. There is no way to get a type that would be 'a signed version of jack_time_t' for example - the types below are inherently fragile and there is no automatic way to check they are the correct ones. The only way is to check manually against jack/types.h. FA - 16/02/2012 */ int32_t df = frames - time.frames; int64_t dp = time.next_wakeup - time.current_wakeup; return time.current_wakeup + (int64_t) rint ((double) df * (double) dp / ectl->buffer_size); } return 0; } jack_nframes_t 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->srate_arg = arg; client->srate = callback; client->control->srate_cbset = (callback != NULL); /* 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; VALGRIND_MEMSET (&req, 0, sizeof (req)); req.type = ResetTimeBaseClient; req.x.client_id = ctl->id; rc = jack_client_deliver_request (client, &req); if (rc == 0) { client->timebase_cb = NULL; client->timebase_arg = NULL; ctl->timebase_cb_cbset = 0; } 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; VALGRIND_MEMSET (&req, 0, sizeof (req)); if (sync_callback) req.type = SetSyncClient; else req.type = ResetSyncClient; req.x.client_id = ctl->id; rc = jack_client_deliver_request (client, &req); if (rc == 0) { client->sync_cb = sync_callback; client->sync_arg = arg; ctl->sync_cb_cbset = TRUE; } return rc; } int jack_set_sync_timeout (jack_client_t *client, jack_time_t usecs) { jack_request_t req; VALGRIND_MEMSET (&req, 0, sizeof (req)); req.type = SetSyncTimeout; req.x.timeout = usecs; return jack_client_deliver_request (client, &req); } 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; VALGRIND_MEMSET (&req, 0, sizeof (req)); 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->timebase_arg = arg; client->timebase_cb = timebase_cb; ctl->timebase_cb_cbset = TRUE; } return rc; } int jack_transport_locate (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 (const jack_client_t *client, jack_position_t *pos) { jack_control_t *ectl = client->engine; if (pos) { /* the guarded copy makes this function work in any * thread */ jack_transport_copy_position (&ectl->current_time, pos); } return ectl->transport_state; } int jack_transport_reposition (jack_client_t *client, const jack_position_t *pos) { /* copy the input, to avoid modifying its contents */ jack_position_t tmp = *pos; /* validate input */ if (tmp.valid & ~JACK_POSITION_MASK) /* unknown field present? */ return EINVAL; return jack_transport_request_new_pos (client, &tmp); } void jack_transport_start (jack_client_t *client) { client->engine->transport_cmd = TransportCommandStart; } void jack_transport_stop (jack_client_t *client) { client->engine->transport_cmd = TransportCommandStop; } #ifdef OLD_TRANSPORT /************* Compatibility with old transport API. *************/ int jack_engine_takeover_timebase (jack_client_t *client) { jack_error ("jack_engine_takeover_timebase() is no longer supported."); return ENOSYS; } void jack_get_transport_info (jack_client_t *client, jack_transport_info_t *info) { jack_control_t *ectl = client->engine; static int first_time = 1; if (first_time) jack_error ("jack_get_transport_info() is deprecated."); first_time = 0; /* check that this is the process thread */ if (!pthread_equal(client->thread_id, pthread_self())) { jack_error("Invalid thread for jack_get_transport_info()."); abort(); /* kill this client */ } info->usecs = ectl->current_time.usecs; info->frame_rate = ectl->current_time.frame_rate; info->transport_state = ectl->transport_state; info->frame = ectl->current_time.frame; info->valid = (ectl->current_time.valid | JackTransportState | JackTransportPosition); if (info->valid & JackTransportBBT) { info->bar = ectl->current_time.bar; info->beat = ectl->current_time.beat; info->tick = ectl->current_time.tick; info->bar_start_tick = ectl->current_time.bar_start_tick; info->beats_per_bar = ectl->current_time.beats_per_bar; info->beat_type = ectl->current_time.beat_type; info->ticks_per_beat = ectl->current_time.ticks_per_beat; info->beats_per_minute = ectl->current_time.beats_per_minute; } } void jack_set_transport_info (jack_client_t *client, jack_transport_info_t *info) { static int first_time = 1; if (first_time) jack_error ("jack_set_transport_info() no longer supported."); first_time = 0; } #endif /* OLD_TRANSPORT */