| 
							- /*
 -     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 <config.h>
 - #include <errno.h>
 - #include <math.h>
 - #include <stdio.h>
 - #include <jack/atomicity.h>
 - #include <jack/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,
 - 					      control->nframes,
 - 					      &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);
 - }
 - 
 - 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 now)
 - {
 - 	jack_frame_timer_t time;
 - 	jack_control_t *ectl = client->engine;
 - 
 - 	jack_read_frame_time (client, &time);
 - 
 - 	if (time.initialized) {
 - #if 0
 - 		jack_info ("now = %Lu current wakeup = %Lu next = %Lu frames = %lu + %f => %lu FC = %f SOI = %f", 
 - 			 now, time.current_wakeup, time.next_wakeup, time.frames,
 - 			 (double) (now - time.current_wakeup)/ (time.next_wakeup - time.current_wakeup),
 - 			 time.frames + 
 - 			   (long) rint (((double) ((long long) now - time.current_wakeup)/ 
 - 					 (long long) (time.next_wakeup - time.current_wakeup)) * ectl->buffer_size),
 - 			   time.filter_coefficient,  
 - 			   time.second_order_integrator);
 - #endif
 - 
 - 		return time.frames + 
 - 			(long) rint (((double) ((long long) (now - time.current_wakeup))/ 
 - 						((long long) (time.next_wakeup - time.current_wakeup))) * 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) {
 - 		return time.current_wakeup +
 - 			(long) rint (((double) ((long long) (frames - time.frames)) *
 - 						((long long) (time.next_wakeup - time.current_wakeup)) / 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;
 - 
 - 	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;
 - 
 - 	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;
 - 
 - 	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;
 - 
 - 	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, 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 */
 
 
  |