| 
							- /*
 -  *  transport.c -- JACK transport master example client.
 -  *
 -  *  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 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.
 -  *
 -  *  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 <stdio.h>
 - #include <errno.h>
 - #include <unistd.h>
 - #include <signal.h>
 - #include <stdlib.h>
 - #include <string.h>
 - #include <readline/readline.h>
 - #include <readline/history.h>
 - #include <jack/jack.h>
 - #include <jack/transport.h>
 - 
 - 
 - char *package;				/* program name */
 - int done = 0;
 - jack_client_t *client;
 - 
 - /* Time and tempo variables.  These are global to the entire,
 -  * transport timeline.  There is no attempt to keep a true tempo map.
 -  * The default time signature is: "march time", 4/4, 120bpm
 -  */
 - float time_beats_per_bar = 4.0;
 - float time_beat_type = 4.0;
 - double time_ticks_per_beat = 1920.0;
 - double time_beats_per_minute = 120.0;
 - volatile int time_reset = 1;		/* true when time values change */
 - 
 - /* JACK timebase callback.
 -  *
 -  * Runs in the process thread.  Realtime, must not wait.
 -  */
 - void timebase(jack_transport_state_t state, jack_nframes_t nframes, 
 - 	      jack_position_t *pos, int new_pos, void *arg)
 - {
 - 	double min;			/* minutes since frame 0 */
 - 	long abs_tick;			/* ticks since frame 0 */
 - 	long abs_beat;			/* beats since frame 0 */
 - 
 - 	if (new_pos || time_reset) {
 - 
 - 		pos->valid = JackPositionBBT;
 - 		pos->beats_per_bar = time_beats_per_bar;
 - 		pos->beat_type = time_beat_type;
 - 		pos->ticks_per_beat = time_ticks_per_beat;
 - 		pos->beats_per_minute = time_beats_per_minute;
 - 
 - 		time_reset = 0;		/* time change complete */
 - 
 - 		/* Compute BBT info from frame number.  This is relatively
 - 		 * simple here, but would become complex if we supported tempo
 - 		 * or time signature changes at specific locations in the
 - 		 * transport timeline. */
 - 
 - 		min = pos->frame / ((double) pos->frame_rate * 60.0);
 - 		abs_tick = min * pos->beats_per_minute * pos->ticks_per_beat;
 - 		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 */
 - 
 - #if 0
 - 		/* some debug code... */
 - 		fprintf(stderr, "\nnew position: %" PRIu32 "\tBBT: %3"
 - 			PRIi32 "|%" PRIi32 "|%04" PRIi32 "\n",
 - 			pos->frame, pos->bar, pos->beat, pos->tick);
 - #endif
 - 
 - 	} else {
 - 
 - 		/* Compute BBT info based on 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)
 - {
 - #if defined(RL_READLINE_VERSION) && RL_READLINE_VERSION >= 0x0400
 - 	rl_cleanup_after_signal();
 - #endif
 - 	fprintf(stderr, "JACK shut down, exiting ...\n");
 - 	exit(1);
 - }
 - 
 - void signal_handler(int sig)
 - {
 - 	jack_client_close(client);
 - 	fprintf(stderr, "signal received, exiting ...\n");
 - 	exit(0);
 - }
 - 
 - 
 - /* Command functions: see commands[] table following. */
 - 
 - void com_activate(char *arg)
 - {
 - 	if (jack_activate(client)) {
 - 		fprintf(stderr, "cannot activate client");
 - 	}
 - }
 - 
 - void com_deactivate(char *arg)
 - {
 - 	if (jack_deactivate(client)) {
 - 		fprintf(stderr, "cannot deactivate client");
 - 	}
 - }
 - 
 - void com_exit(char *arg)
 - {
 - 	done = 1;
 - }
 - 
 - void com_help(char *);			/* forward declaration */
 - 
 - void com_locate(char *arg)
 - {
 - 	jack_nframes_t frame = 0;
 - 
 - 	if (*arg != '\0')
 - 		frame = atoi(arg);
 - 
 - 	jack_transport_locate(client, frame);
 - }
 - 
 - void com_master(char *arg)
 - {
 - 	int cond = (*arg != '\0');
 - 	if (jack_set_timebase_callback(client, cond, timebase, NULL) != 0)
 - 		fprintf(stderr, "Unable to take over timebase.\n");
 - }
 - 
 - void com_play(char *arg)
 - {
 - 	jack_transport_start(client);
 - }
 - 
 - void com_release(char *arg)
 - {
 - 	jack_release_timebase(client);
 - }
 - 
 - void com_stop(char *arg)
 - {
 - 	jack_transport_stop(client);
 - }
 - 
 - /* Change the tempo for the entire timeline, not just from the current
 -  * location. */
 - void com_tempo(char *arg)
 - {
 - 	float tempo = 120.0;
 - 
 - 	if (*arg != '\0')
 - 		tempo = atof(arg);
 - 
 - 	time_beats_per_minute = tempo;
 - 	time_reset = 1;
 - }
 - 
 - /* Set sync timeout in seconds. */
 - void com_timeout(char *arg)
 - {
 - 	double timeout = 2.0;
 - 
 - 	if (*arg != '\0')
 - 		timeout = atof(arg);
 - 
 - 	jack_set_sync_timeout(client, (jack_time_t) (timeout*1000000));
 - }
 - 
 - 
 - /* Command parsing based on GNU readline info examples. */
 - 
 - typedef void cmd_function_t(char *);	/* command function type */
 - 
 - /* Transport command table. */
 - typedef struct {
 - 	char *name;			/* user printable name */
 - 	cmd_function_t *func;		/* function to call */
 - 	char *doc;			/* documentation  */
 - } command_t;
 - 
 - /* command table must be in alphabetical order */
 - command_t commands[] = {
 - 	{"activate",	com_activate,	"Call jack_activate()"},
 - 	{"exit",	com_exit,	"Exit transport program"},
 - 	{"deactivate",	com_deactivate,	"Call jack_deactivate()"},
 - 	{"help",	com_help,	"Display help text [<command>]"},
 - 	{"locate",	com_locate,	"Locate to frame <position>"},
 - 	{"master",	com_master,	"Become timebase master "
 - 					"[<conditionally>]"},
 - 	{"play",	com_play,	"Start transport rolling"},
 - 	{"quit",	com_exit,	"Synonym for `exit'"},
 - 	{"release",	com_release,	"Release timebase"},
 - 	{"stop",	com_stop,	"Stop transport"},
 - 	{"tempo",	com_tempo,	"Set beat tempo <beats_per_min>"},
 - 	{"timeout",	com_timeout,	"Set sync timeout in <seconds>"},
 - 	{"?",		com_help,	"Synonym for `help'" },
 - 	{(char *)NULL, (cmd_function_t *)NULL, (char *)NULL }
 - };
 -      
 - command_t *find_command(char *name)
 - {
 - 	register int i;
 - 	size_t namelen;
 - 
 - 	if ((name == NULL) || (*name == '\0'))
 - 		return ((command_t *)NULL);
 - 
 - 	namelen = strlen(name);
 - 	for (i = 0; commands[i].name; i++)
 - 		if (strncmp(name, commands[i].name, namelen) == 0) {
 - 
 - 			/* make sure the match is unique */
 - 			if ((commands[i+1].name) &&
 - 			    (strncmp(name, commands[i+1].name, namelen) == 0))
 - 				return ((command_t *)NULL);
 - 			else
 - 				return (&commands[i]);
 - 		}
 -      
 - 	return ((command_t *)NULL);
 - }
 - 
 - void com_help(char *arg)
 - {
 - 	register int i;
 - 	command_t *cmd;
 - 
 - 	if (!*arg) {
 - 		/* print help for all commands */
 - 		for (i = 0; commands[i].name; i++) {
 - 			printf("%s\t\t%s.\n", commands[i].name,
 - 			       commands[i].doc);
 - 		}
 - 
 - 	} else if ((cmd = find_command(arg))) {
 - 		printf("%s\t\t%s.\n", cmd->name, cmd->doc);
 - 
 - 	} else {
 - 		int printed = 0;
 - 
 - 		printf("No `%s' command.  Valid command names are:\n", arg);
 - 
 - 		for (i = 0; commands[i].name; i++) {
 - 			/* Print in six columns. */
 - 			if (printed == 6) {
 - 				printed = 0;
 - 				printf ("\n");
 - 			}
 - 
 - 			printf ("%s\t", commands[i].name);
 - 			printed++;
 - 		}
 - 
 - 		printf("\n\nTry `help [command]\' for more information.\n");
 - 	}
 - }
 - 
 - void execute_command(char *line)
 - {
 - 	register int i;
 - 	command_t *command;
 - 	char *word;
 -      
 - 	/* Isolate the command word. */
 - 	i = 0;
 - 	while (line[i] && whitespace(line[i]))
 - 		i++;
 - 	word = line + i;
 -      
 - 	while (line[i] && !whitespace(line[i]))
 - 		i++;
 -      
 - 	if (line[i])
 - 		line[i++] = '\0';
 -      
 - 	command = find_command(word);
 -      
 - 	if (!command) {
 - 		fprintf(stderr, "%s: No such command.  There is `help\'.\n",
 - 			word);
 - 		return;
 - 	}
 -      
 - 	/* Get argument to command, if any. */
 - 	while (whitespace(line[i]))
 - 		i++;
 -      
 - 	word = line + i;
 -      
 - 	/* invoke the command function. */
 - 	(*command->func)(word);
 - }
 - 
 - 
 - /* Strip whitespace from the start and end of string. */
 - char *stripwhite(char *string)
 - {
 - 	register char *s, *t;
 - 
 - 	s = string;
 - 	while (whitespace(*s))
 - 		s++;
 - 
 - 	if (*s == '\0')
 - 		return s;
 -      
 - 	t = s + strlen (s) - 1;
 - 	while (t > s && whitespace(*t))
 - 		t--;
 - 	*++t = '\0';
 -      
 - 	return s;
 - }
 -      
 - char *dupstr(char *s)
 - {
 - 	char *r = malloc(strlen(s) + 1);
 - 	strcpy(r, s);
 - 	return r;
 - }
 -      
 - /* Readline generator function for command completion. */
 - char *command_generator (const char *text, int state)
 - {
 - 	static int list_index, len;
 - 	char *name;
 -      
 - 	/* If this is a new word to complete, initialize now.  This
 - 	   includes saving the length of TEXT for efficiency, and
 - 	   initializing the index variable to 0. */
 - 	if (!state) {
 - 		list_index = 0;
 - 		len = strlen (text);
 - 	}
 -      
 - 	/* Return the next name which partially matches from the
 - 	   command list. */
 - 	while ((name = commands[list_index].name)) {
 - 		list_index++;
 -      
 - 		if (strncmp(name, text, len) == 0)
 - 			return dupstr(name);
 - 	}
 -      
 - 	return (char *) NULL;		/* No names matched. */
 - }
 - 
 - void command_loop()
 - {
 - 	char *line, *cmd;
 - 	char prompt[32];
 - 
 - 	snprintf(prompt, sizeof(prompt), "%s> ", package);
 - 
 - 	/* Allow conditional parsing of the ~/.inputrc file. */
 - 	rl_readline_name = package;
 -      
 - 	/* Define a custom completion function. */
 - 	rl_completion_entry_function = command_generator;
 - 
 - 	/* Read and execute commands until the user quits. */
 - 	while (!done) {
 - 
 - 		line = readline(prompt);
 -      
 - 		if (line == NULL) {	/* EOF? */
 - 			printf("\n");	/* close out prompt */
 - 			done = 1;
 - 			break;
 - 		}
 -      
 - 		/* Remove leading and trailing whitespace from the line. */
 - 		cmd = stripwhite(line);
 - 
 - 		/* If anything left, add to history and execute it. */
 - 		if (*cmd)
 - 		{
 - 			add_history(cmd);
 - 			execute_command(cmd);
 - 		}
 -      
 - 		free(line);		/* realine() called malloc() */
 - 	}
 - }
 - 
 - int main(int argc, char *argv[])
 - {
 - 	jack_status_t status;
 - 
 - 	/* basename $0 */
 - 	package = strrchr(argv[0], '/');
 - 	if (package == 0)
 - 		package = argv[0];
 - 	else
 - 		package++;
 - 
 - 	/* open a connection to the JACK server */
 - 	client = jack_client_open (package, JackNullOption, &status);
 - 	if (client == NULL) {
 - 		fprintf (stderr, "jack_client_open() failed, "
 - 			 "status = 0x%2.0x\n", status);
 - 		return 1;
 - 	}
 - 
 - 	signal(SIGQUIT, signal_handler);
 - 	signal(SIGTERM, signal_handler);
 - 	signal(SIGHUP, signal_handler);
 - 	signal(SIGINT, signal_handler);
 - 
 - 	jack_on_shutdown(client, jack_shutdown, 0);
 - 
 - 	if (jack_activate(client)) {
 - 		fprintf(stderr, "cannot activate client");
 - 		return 1;
 - 	}
 - 
 - 	/* execute commands until done */
 - 	command_loop();
 - 
 - 	jack_client_close(client);
 - 	exit(0);
 - }
 
 
  |