bump micro version
[jack.git] / tools / transport.c
bloba1b0ab792348f11b5a6bcc93bcabf0c596b674ed
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>
32 char *package; /* program name */
33 int done = 0;
34 jack_client_t *client;
36 /* Time and tempo variables. These are global to the entire,
37 * transport timeline. There is no attempt to keep a true tempo map.
38 * The default time signature is: "march time", 4/4, 120bpm
40 float time_beats_per_bar = 4.0;
41 float time_beat_type = 4.0;
42 double time_ticks_per_beat = 1920.0;
43 double time_beats_per_minute = 120.0;
44 volatile int time_reset = 1; /* true when time values change */
45 volatile int avr_set = 0;
46 float audio_frames_per_video_frame;
48 /* JACK timebase callback.
50 * Runs in the process thread. Realtime, must not wait.
52 void timebase(jack_transport_state_t state, jack_nframes_t nframes,
53 jack_position_t *pos, int new_pos, void *arg)
55 double min; /* minutes since frame 0 */
56 long abs_tick; /* ticks since frame 0 */
57 long abs_beat; /* beats since frame 0 */
59 if (new_pos || time_reset) {
61 pos->valid = JackPositionBBT;
62 pos->beats_per_bar = time_beats_per_bar;
63 pos->beat_type = time_beat_type;
64 pos->ticks_per_beat = time_ticks_per_beat;
65 pos->beats_per_minute = time_beats_per_minute;
67 time_reset = 0; /* time change complete */
69 /* Compute BBT info from frame number. This is relatively
70 * simple here, but would become complex if we supported tempo
71 * or time signature changes at specific locations in the
72 * transport timeline.
75 min = pos->frame / ((double) pos->frame_rate * 60.0);
76 abs_tick = min * pos->beats_per_minute * pos->ticks_per_beat;
77 abs_beat = abs_tick / pos->ticks_per_beat;
79 pos->bar = abs_beat / pos->beats_per_bar;
80 pos->beat = abs_beat - (pos->bar * pos->beats_per_bar) + 1;
81 pos->tick = abs_tick - (abs_beat * pos->ticks_per_beat);
82 pos->bar_start_tick = pos->bar * pos->beats_per_bar *
83 pos->ticks_per_beat;
84 pos->bar++; /* adjust start to bar 1 */
86 #if 0
87 /* some debug code... */
88 fprintf(stderr, "\nnew position: %" PRIu32 "\tBBT: %3"
89 PRIi32 "|%" PRIi32 "|%04" PRIi32 "\n",
90 pos->frame, pos->bar, pos->beat, pos->tick);
91 #endif
93 } else {
95 /* Compute BBT info based on previous period. */
96 pos->tick +=
97 nframes * pos->ticks_per_beat * pos->beats_per_minute
98 / (pos->frame_rate * 60);
100 while (pos->tick >= pos->ticks_per_beat) {
101 pos->tick -= pos->ticks_per_beat;
102 if (++pos->beat > pos->beats_per_bar) {
103 pos->beat = 1;
104 ++pos->bar;
105 pos->bar_start_tick +=
106 pos->beats_per_bar
107 * pos->ticks_per_beat;
112 if (avr_set) {
113 pos->valid |= JackAudioVideoRatio;
114 pos->audio_frames_per_video_frame = audio_frames_per_video_frame;
118 void jack_shutdown(void *arg)
120 #if defined(RL_READLINE_VERSION) && RL_READLINE_VERSION >= 0x0400
121 rl_cleanup_after_signal();
122 #endif
123 fprintf(stderr, "JACK shut down, exiting ...\n");
124 exit(1);
127 void signal_handler(int sig)
129 jack_client_close(client);
130 fprintf(stderr, "signal received, exiting ...\n");
131 exit(0);
135 /* Command functions: see commands[] table following. */
137 void com_activate(char *arg)
139 if (jack_activate(client)) {
140 fprintf(stderr, "cannot activate client");
144 void com_deactivate(char *arg)
146 if (jack_deactivate(client)) {
147 fprintf(stderr, "cannot deactivate client");
151 void com_exit(char *arg)
153 done = 1;
156 void com_help(char *); /* forward declaration */
158 void com_locate(char *arg)
160 jack_nframes_t frame = 0;
162 if (*arg != '\0')
163 frame = atoi(arg);
165 jack_transport_locate(client, frame);
168 void com_master(char *arg)
170 int cond = (*arg != '\0');
171 if (jack_set_timebase_callback(client, cond, timebase, NULL) != 0)
172 fprintf(stderr, "Unable to take over timebase.\n");
175 void com_play(char *arg)
177 jack_transport_start(client);
180 void com_release(char *arg)
182 jack_release_timebase(client);
185 void com_stop(char *arg)
187 jack_transport_stop(client);
190 /* Change the tempo for the entire timeline, not just from the current
191 * location. */
192 void com_tempo(char *arg)
194 float tempo = 120.0;
196 if (*arg != '\0')
197 tempo = atof(arg);
199 time_beats_per_minute = tempo;
200 time_reset = 1;
203 /* Set sync timeout in seconds. */
204 void com_timeout(char *arg)
206 double timeout = 2.0;
208 if (*arg != '\0')
209 timeout = atof(arg);
211 jack_set_sync_timeout(client, (jack_time_t) (timeout*1000000));
215 /* Change the tempo for the entire timeline, not just from the current
216 * location. */
217 void com_av_ratio(char *arg)
219 float avr = 0;
221 if (*arg != '\0')
222 avr = atof(arg);
224 audio_frames_per_video_frame = avr;
225 avr_set = 1;
228 /* Command parsing based on GNU readline info examples. */
230 typedef void cmd_function_t(char *); /* command function type */
232 /* Transport command table. */
233 typedef struct {
234 char *name; /* user printable name */
235 cmd_function_t *func; /* function to call */
236 char *doc; /* documentation */
237 } command_t;
239 /* command table must be in alphabetical order */
240 command_t commands[] = {
241 {"activate", com_activate, "Call jack_activate()"},
242 {"avr", com_av_ratio, "Set audio/video frame ratio <audio frames per video frame>"},
243 {"exit", com_exit, "Exit transport program"},
244 {"deactivate", com_deactivate, "Call jack_deactivate()"},
245 {"help", com_help, "Display help text [<command>]"},
246 {"locate", com_locate, "Locate to frame <position>"},
247 {"master", com_master, "Become timebase master "
248 "[<conditionally>]"},
249 {"play", com_play, "Start transport rolling"},
250 {"quit", com_exit, "Synonym for `exit'"},
251 {"release", com_release, "Release timebase"},
252 {"stop", com_stop, "Stop transport"},
253 {"tempo", com_tempo, "Set beat tempo <beats_per_min>"},
254 {"timeout", com_timeout, "Set sync timeout in <seconds>"},
255 {"?", com_help, "Synonym for `help'" },
256 {(char *)NULL, (cmd_function_t *)NULL, (char *)NULL }
259 command_t *find_command(char *name)
261 register int i;
262 size_t namelen;
264 if ((name == NULL) || (*name == '\0'))
265 return ((command_t *)NULL);
267 namelen = strlen(name);
268 for (i = 0; commands[i].name; i++)
269 if (strncmp(name, commands[i].name, namelen) == 0) {
271 /* make sure the match is unique */
272 if ((commands[i+1].name) &&
273 (strncmp(name, commands[i+1].name, namelen) == 0))
274 return ((command_t *)NULL);
275 else
276 return (&commands[i]);
279 return ((command_t *)NULL);
282 void com_help(char *arg)
284 register int i;
285 command_t *cmd;
287 if (!*arg) {
288 /* print help for all commands */
289 for (i = 0; commands[i].name; i++) {
290 printf("%s\t\t%s.\n", commands[i].name,
291 commands[i].doc);
294 } else if ((cmd = find_command(arg))) {
295 printf("%s\t\t%s.\n", cmd->name, cmd->doc);
297 } else {
298 int printed = 0;
300 printf("No `%s' command. Valid command names are:\n", arg);
302 for (i = 0; commands[i].name; i++) {
303 /* Print in six columns. */
304 if (printed == 6) {
305 printed = 0;
306 printf ("\n");
309 printf ("%s\t", commands[i].name);
310 printed++;
313 printf("\n\nTry `help [command]\' for more information.\n");
317 void execute_command(char *line)
319 register int i;
320 command_t *command;
321 char *word;
323 /* Isolate the command word. */
324 i = 0;
325 while (line[i] && whitespace(line[i]))
326 i++;
327 word = line + i;
329 while (line[i] && !whitespace(line[i]))
330 i++;
332 if (line[i])
333 line[i++] = '\0';
335 command = find_command(word);
337 if (!command) {
338 fprintf(stderr, "%s: No such command. There is `help\'.\n",
339 word);
340 return;
343 /* Get argument to command, if any. */
344 while (whitespace(line[i]))
345 i++;
347 word = line + i;
349 /* invoke the command function. */
350 (*command->func)(word);
354 /* Strip whitespace from the start and end of string. */
355 char *stripwhite(char *string)
357 register char *s, *t;
359 s = string;
360 while (whitespace(*s))
361 s++;
363 if (*s == '\0')
364 return s;
366 t = s + strlen (s) - 1;
367 while (t > s && whitespace(*t))
368 t--;
369 *++t = '\0';
371 return s;
374 char *dupstr(char *s)
376 char *r = malloc(strlen(s) + 1);
377 strcpy(r, s);
378 return r;
381 /* Readline generator function for command completion. */
382 char *command_generator (const char *text, int state)
384 static int list_index, len;
385 char *name;
387 /* If this is a new word to complete, initialize now. This
388 includes saving the length of TEXT for efficiency, and
389 initializing the index variable to 0. */
390 if (!state) {
391 list_index = 0;
392 len = strlen (text);
395 /* Return the next name which partially matches from the
396 command list. */
397 while ((name = commands[list_index].name)) {
398 list_index++;
400 if (strncmp(name, text, len) == 0)
401 return dupstr(name);
404 return (char *) NULL; /* No names matched. */
407 void command_loop()
409 char *line, *cmd;
410 char prompt[32];
412 snprintf(prompt, sizeof(prompt), "%s> ", package);
414 /* Allow conditional parsing of the ~/.inputrc file. */
415 rl_readline_name = package;
417 /* Define a custom completion function. */
418 rl_completion_entry_function = command_generator;
420 /* Read and execute commands until the user quits. */
421 while (!done) {
423 line = readline(prompt);
425 if (line == NULL) { /* EOF? */
426 printf("\n"); /* close out prompt */
427 done = 1;
428 break;
431 /* Remove leading and trailing whitespace from the line. */
432 cmd = stripwhite(line);
434 /* If anything left, add to history and execute it. */
435 if (*cmd)
437 add_history(cmd);
438 execute_command(cmd);
441 free(line); /* realine() called malloc() */
445 int main(int argc, char *argv[])
447 jack_status_t status;
449 /* basename $0 */
450 package = strrchr(argv[0], '/');
451 if (package == 0)
452 package = argv[0];
453 else
454 package++;
456 /* open a connection to the JACK server */
457 client = jack_client_open (package, JackNullOption, &status);
458 if (client == NULL) {
459 fprintf (stderr, "jack_client_open() failed, "
460 "status = 0x%2.0x\n", status);
461 return 1;
464 signal(SIGQUIT, signal_handler);
465 signal(SIGTERM, signal_handler);
466 signal(SIGHUP, signal_handler);
467 signal(SIGINT, signal_handler);
469 jack_on_shutdown(client, jack_shutdown, 0);
471 if (jack_activate(client)) {
472 fprintf(stderr, "cannot activate client");
473 return 1;
476 /* execute commands until done */
477 command_loop();
479 jack_client_close(client);
480 exit(0);