Merge pull request #23 from jackaudio/device_reservation_fixes
[jack2.git] / example-clients / transport.c
bloba2ff0eef5c3f2b059db48afc4f84262f3765cb30
1 /*
2 * transport.c -- JACK transport master example client.
4 * Copyright (C) 2003 Jack O'Quin.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include <stdio.h>
22 #include <errno.h>
23 #include <unistd.h>
24 #include <signal.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <readline/readline.h>
28 #include <readline/history.h>
29 #include <jack/jack.h>
30 #include <jack/transport.h>
33 char *package; /* program name */
34 int done = 0;
35 jack_client_t *client;
37 /* Time and tempo variables. These are global to the entire,
38 * transport timeline. There is no attempt to keep a true tempo map.
39 * The default time signature is: "march time", 4/4, 120bpm
41 float time_beats_per_bar = 4.0;
42 float time_beat_type = 4.0;
43 double time_ticks_per_beat = 1920.0;
44 double time_beats_per_minute = 120.0;
45 volatile int time_reset = 1; /* true when time values change */
47 /* JACK timebase callback.
49 * Runs in the process thread. Realtime, must not wait.
51 static void timebase(jack_transport_state_t state, jack_nframes_t nframes,
52 jack_position_t *pos, int new_pos, void *arg)
54 double min; /* minutes since frame 0 */
55 long abs_tick; /* ticks since frame 0 */
56 long abs_beat; /* beats since frame 0 */
58 if (new_pos || time_reset) {
60 pos->valid = JackPositionBBT;
61 pos->beats_per_bar = time_beats_per_bar;
62 pos->beat_type = time_beat_type;
63 pos->ticks_per_beat = time_ticks_per_beat;
64 pos->beats_per_minute = time_beats_per_minute;
66 time_reset = 0; /* time change complete */
68 /* Compute BBT info from frame number. This is relatively
69 * simple here, but would become complex if we supported tempo
70 * or time signature changes at specific locations in the
71 * transport timeline. */
73 min = pos->frame / ((double) pos->frame_rate * 60.0);
74 abs_tick = min * pos->beats_per_minute * pos->ticks_per_beat;
75 abs_beat = abs_tick / pos->ticks_per_beat;
77 pos->bar = abs_beat / pos->beats_per_bar;
78 pos->beat = abs_beat - (pos->bar * pos->beats_per_bar) + 1;
79 pos->tick = abs_tick - (abs_beat * pos->ticks_per_beat);
80 pos->bar_start_tick = pos->bar * pos->beats_per_bar *
81 pos->ticks_per_beat;
82 pos->bar++; /* adjust start to bar 1 */
84 #if 0
85 /* some debug code... */
86 fprintf(stderr, "\nnew position: %" PRIu32 "\tBBT: %3"
87 PRIi32 "|%" PRIi32 "|%04" PRIi32 "\n",
88 pos->frame, pos->bar, pos->beat, pos->tick);
89 #endif
91 } else {
93 /* Compute BBT info based on previous period. */
94 pos->tick +=
95 nframes * pos->ticks_per_beat * pos->beats_per_minute
96 / (pos->frame_rate * 60);
98 while (pos->tick >= pos->ticks_per_beat) {
99 pos->tick -= pos->ticks_per_beat;
100 if (++pos->beat > pos->beats_per_bar) {
101 pos->beat = 1;
102 ++pos->bar;
103 pos->bar_start_tick +=
104 pos->beats_per_bar
105 * pos->ticks_per_beat;
111 static void jack_shutdown(void *arg)
113 #if defined(RL_READLINE_VERSION) && RL_READLINE_VERSION >= 0x0400
114 rl_cleanup_after_signal();
115 #endif
116 fprintf(stderr, "JACK shut down, exiting ...\n");
117 exit(1);
120 static void signal_handler(int sig)
122 jack_client_close(client);
123 fprintf(stderr, "signal received, exiting ...\n");
124 exit(0);
127 /* Command functions: see commands[] table following. */
129 static void com_activate(char *arg)
131 if (jack_activate(client)) {
132 fprintf(stderr, "cannot activate client");
136 static void com_deactivate(char *arg)
138 if (jack_deactivate(client)) {
139 fprintf(stderr, "cannot deactivate client");
143 static void com_exit(char *arg)
145 done = 1;
148 static void com_help(char *); /* forward declaration */
150 static void com_locate(char *arg)
152 jack_nframes_t frame = 0;
154 if (*arg != '\0')
155 frame = atoi(arg);
157 jack_transport_locate(client, frame);
160 static void com_master(char *arg)
162 int cond = (*arg != '\0');
163 if (jack_set_timebase_callback(client, cond, timebase, NULL) != 0)
164 fprintf(stderr, "Unable to take over timebase.\n");
167 static void com_play(char *arg)
169 jack_transport_start(client);
172 static void com_release(char *arg)
174 jack_release_timebase(client);
177 static void com_stop(char *arg)
179 jack_transport_stop(client);
182 /* Change the tempo for the entire timeline, not just from the current
183 * location. */
184 static void com_tempo(char *arg)
186 float tempo = 120.0;
188 if (*arg != '\0')
189 tempo = atof(arg);
191 time_beats_per_minute = tempo;
192 time_reset = 1;
195 /* Set sync timeout in seconds. */
196 static void com_timeout(char *arg)
198 double timeout = 2.0;
200 if (*arg != '\0')
201 timeout = atof(arg);
203 jack_set_sync_timeout(client, (jack_time_t) (timeout*1000000));
207 /* Command parsing based on GNU readline info examples. */
209 typedef void cmd_function_t(char *); /* command function type */
211 /* Transport command table. */
212 typedef struct {
213 char *name; /* user printable name */
214 cmd_function_t *func; /* function to call */
215 char *doc; /* documentation */
216 } command_t;
218 /* command table must be in alphabetical order */
219 command_t commands[] = {
220 {"activate", com_activate, "Call jack_activate()"},
221 {"exit", com_exit, "Exit transport program"},
222 {"deactivate", com_deactivate, "Call jack_deactivate()"},
223 {"help", com_help, "Display help text [<command>]"},
224 {"locate", com_locate, "Locate to frame <position>"},
225 {"master", com_master, "Become timebase master "
226 "[<conditionally>]"},
227 {"play", com_play, "Start transport rolling"},
228 {"quit", com_exit, "Synonym for `exit'"},
229 {"release", com_release, "Release timebase"},
230 {"stop", com_stop, "Stop transport"},
231 {"tempo", com_tempo, "Set beat tempo <beats_per_min>"},
232 {"timeout", com_timeout, "Set sync timeout in <seconds>"},
233 {"?", com_help, "Synonym for `help'" },
234 {(char *)NULL, (cmd_function_t *)NULL, (char *)NULL }
237 static command_t *find_command(char *name)
239 register int i;
240 size_t namelen;
242 if ((name == NULL) || (*name == '\0'))
243 return ((command_t *)NULL);
245 namelen = strlen(name);
246 for (i = 0; commands[i].name; i++)
247 if (strncmp(name, commands[i].name, namelen) == 0) {
249 /* make sure the match is unique */
250 if ((commands[i+1].name) &&
251 (strncmp(name, commands[i+1].name, namelen) == 0))
252 return ((command_t *)NULL);
253 else
254 return (&commands[i]);
257 return ((command_t *)NULL);
260 static void com_help(char *arg)
262 register int i;
263 command_t *cmd;
265 if (!*arg) {
266 /* print help for all commands */
267 for (i = 0; commands[i].name; i++) {
268 printf("%s\t\t%s.\n", commands[i].name,
269 commands[i].doc);
272 } else if ((cmd = find_command(arg))) {
273 printf("%s\t\t%s.\n", cmd->name, cmd->doc);
275 } else {
276 int printed = 0;
278 printf("No `%s' command. Valid command names are:\n", arg);
280 for (i = 0; commands[i].name; i++) {
281 /* Print in six columns. */
282 if (printed == 6) {
283 printed = 0;
284 printf ("\n");
287 printf ("%s\t", commands[i].name);
288 printed++;
291 printf("\n\nTry `help [command]\' for more information.\n");
295 static void execute_command(char *line)
297 register int i;
298 command_t *command;
299 char *word;
301 /* Isolate the command word. */
302 i = 0;
303 while (line[i] && whitespace(line[i]))
304 i++;
305 word = line + i;
307 while (line[i] && !whitespace(line[i]))
308 i++;
310 if (line[i])
311 line[i++] = '\0';
313 command = find_command(word);
315 if (!command) {
316 fprintf(stderr, "%s: No such command. There is `help\'.\n",
317 word);
318 return;
321 /* Get argument to command, if any. */
322 while (whitespace(line[i]))
323 i++;
325 word = line + i;
327 /* invoke the command function. */
328 (*command->func)(word);
332 /* Strip whitespace from the start and end of string. */
333 static char *stripwhite(char *string)
335 register char *s, *t;
337 s = string;
338 while (whitespace(*s))
339 s++;
341 if (*s == '\0')
342 return s;
344 t = s + strlen (s) - 1;
345 while (t > s && whitespace(*t))
346 t--;
347 *++t = '\0';
349 return s;
352 static char *dupstr(char *s)
354 char *r = malloc(strlen(s) + 1);
355 strcpy(r, s);
356 return r;
359 /* Readline generator function for command completion. */
360 static char *command_generator (const char *text, int state)
362 static int list_index, len;
363 char *name;
365 /* If this is a new word to complete, initialize now. This
366 includes saving the length of TEXT for efficiency, and
367 initializing the index variable to 0. */
368 if (!state) {
369 list_index = 0;
370 len = strlen (text);
373 /* Return the next name which partially matches from the
374 command list. */
375 while ((name = commands[list_index].name)) {
376 list_index++;
378 if (strncmp(name, text, len) == 0)
379 return dupstr(name);
382 return (char *) NULL; /* No names matched. */
385 static void command_loop()
387 char *line, *cmd;
388 char prompt[32];
390 snprintf(prompt, sizeof(prompt), "%s> ", package);
392 /* Allow conditional parsing of the ~/.inputrc file. */
393 rl_readline_name = package;
395 /* Define a custom completion function. */
396 rl_completion_entry_function = command_generator;
398 /* Read and execute commands until the user quits. */
399 while (!done) {
401 line = readline(prompt);
403 if (line == NULL) { /* EOF? */
404 printf("\n"); /* close out prompt */
405 done = 1;
406 break;
409 /* Remove leading and trailing whitespace from the line. */
410 cmd = stripwhite(line);
412 /* If anything left, add to history and execute it. */
413 if (*cmd)
415 add_history(cmd);
416 execute_command(cmd);
419 free(line); /* realine() called malloc() */
423 int main(int argc, char *argv[])
425 jack_status_t status;
427 /* basename $0 */
428 package = strrchr(argv[0], '/');
429 if (package == 0)
430 package = argv[0];
431 else
432 package++;
434 /* open a connection to the JACK server */
435 client = jack_client_open (package, JackNullOption, &status);
436 if (client == NULL) {
437 fprintf (stderr, "jack_client_open() failed, "
438 "status = 0x%2.0x\n", status);
439 return 1;
442 signal(SIGQUIT, signal_handler);
443 signal(SIGTERM, signal_handler);
444 signal(SIGHUP, signal_handler);
445 signal(SIGINT, signal_handler);
447 jack_on_shutdown(client, jack_shutdown, 0);
449 if (jack_activate(client)) {
450 fprintf(stderr, "cannot activate client");
451 return 1;
454 /* execute commands until done */
455 command_loop();
457 jack_client_close(client);
458 exit(0);