Correct install target
[jack2.git] / example-clients / transport.c
blob685768a0ce257f2d481bebf3fde71cb330b97570
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 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 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 void signal_handler(int sig)
122 jack_client_close(client);
123 fprintf(stderr, "signal received, exiting ...\n");
124 exit(0);
128 /* Command functions: see commands[] table following. */
130 void com_activate(char *arg)
132 if (jack_activate(client)) {
133 fprintf(stderr, "cannot activate client");
137 void com_deactivate(char *arg)
139 if (jack_deactivate(client)) {
140 fprintf(stderr, "cannot deactivate client");
144 void com_exit(char *arg)
146 done = 1;
149 void com_help(char *); /* forward declaration */
151 void com_locate(char *arg)
153 jack_nframes_t frame = 0;
155 if (*arg != '\0')
156 frame = atoi(arg);
158 jack_transport_locate(client, frame);
161 void com_master(char *arg)
163 int cond = (*arg != '\0');
164 if (jack_set_timebase_callback(client, cond, timebase, NULL) != 0)
165 fprintf(stderr, "Unable to take over timebase.\n");
168 void com_play(char *arg)
170 jack_transport_start(client);
173 void com_release(char *arg)
175 jack_release_timebase(client);
178 void com_stop(char *arg)
180 jack_transport_stop(client);
183 /* Change the tempo for the entire timeline, not just from the current
184 * location. */
185 void com_tempo(char *arg)
187 float tempo = 120.0;
189 if (*arg != '\0')
190 tempo = atof(arg);
192 time_beats_per_minute = tempo;
193 time_reset = 1;
196 /* Set sync timeout in seconds. */
197 void com_timeout(char *arg)
199 double timeout = 2.0;
201 if (*arg != '\0')
202 timeout = atof(arg);
204 jack_set_sync_timeout(client, (jack_time_t) (timeout*1000000));
208 /* Command parsing based on GNU readline info examples. */
210 typedef void cmd_function_t(char *); /* command function type */
212 /* Transport command table. */
213 typedef struct {
214 char *name; /* user printable name */
215 cmd_function_t *func; /* function to call */
216 char *doc; /* documentation */
217 } command_t;
219 /* command table must be in alphabetical order */
220 command_t commands[] = {
221 {"activate", com_activate, "Call jack_activate()"},
222 {"exit", com_exit, "Exit transport program"},
223 {"deactivate", com_deactivate, "Call jack_deactivate()"},
224 {"help", com_help, "Display help text [<command>]"},
225 {"locate", com_locate, "Locate to frame <position>"},
226 {"master", com_master, "Become timebase master "
227 "[<conditionally>]"},
228 {"play", com_play, "Start transport rolling"},
229 {"quit", com_exit, "Synonym for `exit'"},
230 {"release", com_release, "Release timebase"},
231 {"stop", com_stop, "Stop transport"},
232 {"tempo", com_tempo, "Set beat tempo <beats_per_min>"},
233 {"timeout", com_timeout, "Set sync timeout in <seconds>"},
234 {"?", com_help, "Synonym for `help'" },
235 {(char *)NULL, (cmd_function_t *)NULL, (char *)NULL }
238 command_t *find_command(char *name)
240 register int i;
241 size_t namelen;
243 if ((name == NULL) || (*name == '\0'))
244 return ((command_t *)NULL);
246 namelen = strlen(name);
247 for (i = 0; commands[i].name; i++)
248 if (strncmp(name, commands[i].name, namelen) == 0) {
250 /* make sure the match is unique */
251 if ((commands[i+1].name) &&
252 (strncmp(name, commands[i+1].name, namelen) == 0))
253 return ((command_t *)NULL);
254 else
255 return (&commands[i]);
258 return ((command_t *)NULL);
261 void com_help(char *arg)
263 register int i;
264 command_t *cmd;
266 if (!*arg) {
267 /* print help for all commands */
268 for (i = 0; commands[i].name; i++) {
269 printf("%s\t\t%s.\n", commands[i].name,
270 commands[i].doc);
273 } else if ((cmd = find_command(arg))) {
274 printf("%s\t\t%s.\n", cmd->name, cmd->doc);
276 } else {
277 int printed = 0;
279 printf("No `%s' command. Valid command names are:\n", arg);
281 for (i = 0; commands[i].name; i++) {
282 /* Print in six columns. */
283 if (printed == 6) {
284 printed = 0;
285 printf ("\n");
288 printf ("%s\t", commands[i].name);
289 printed++;
292 printf("\n\nTry `help [command]\' for more information.\n");
296 void execute_command(char *line)
298 register int i;
299 command_t *command;
300 char *word;
302 /* Isolate the command word. */
303 i = 0;
304 while (line[i] && whitespace(line[i]))
305 i++;
306 word = line + i;
308 while (line[i] && !whitespace(line[i]))
309 i++;
311 if (line[i])
312 line[i++] = '\0';
314 command = find_command(word);
316 if (!command) {
317 fprintf(stderr, "%s: No such command. There is `help\'.\n",
318 word);
319 return;
322 /* Get argument to command, if any. */
323 while (whitespace(line[i]))
324 i++;
326 word = line + i;
328 /* invoke the command function. */
329 (*command->func)(word);
333 /* Strip whitespace from the start and end of string. */
334 char *stripwhite(char *string)
336 register char *s, *t;
338 s = string;
339 while (whitespace(*s))
340 s++;
342 if (*s == '\0')
343 return s;
345 t = s + strlen (s) - 1;
346 while (t > s && whitespace(*t))
347 t--;
348 *++t = '\0';
350 return s;
353 char *dupstr(char *s)
355 char *r = malloc(strlen(s) + 1);
356 strcpy(r, s);
357 return r;
360 /* Readline generator function for command completion. */
361 char *command_generator (const char *text, int state)
363 static int list_index, len;
364 char *name;
366 /* If this is a new word to complete, initialize now. This
367 includes saving the length of TEXT for efficiency, and
368 initializing the index variable to 0. */
369 if (!state) {
370 list_index = 0;
371 len = strlen (text);
374 /* Return the next name which partially matches from the
375 command list. */
376 while ((name = commands[list_index].name)) {
377 list_index++;
379 if (strncmp(name, text, len) == 0)
380 return dupstr(name);
383 return (char *) NULL; /* No names matched. */
386 void command_loop()
388 char *line, *cmd;
389 char prompt[32];
391 snprintf(prompt, sizeof(prompt), "%s> ", package);
393 /* Allow conditional parsing of the ~/.inputrc file. */
394 rl_readline_name = package;
396 /* Define a custom completion function. */
397 rl_completion_entry_function = command_generator;
399 /* Read and execute commands until the user quits. */
400 while (!done) {
402 line = readline(prompt);
404 if (line == NULL) { /* EOF? */
405 printf("\n"); /* close out prompt */
406 done = 1;
407 break;
410 /* Remove leading and trailing whitespace from the line. */
411 cmd = stripwhite(line);
413 /* If anything left, add to history and execute it. */
414 if (*cmd)
416 add_history(cmd);
417 execute_command(cmd);
420 free(line); /* realine() called malloc() */
424 int main(int argc, char *argv[])
426 jack_status_t status;
428 /* basename $0 */
429 package = strrchr(argv[0], '/');
430 if (package == 0)
431 package = argv[0];
432 else
433 package++;
435 /* open a connection to the JACK server */
436 client = jack_client_open (package, JackNullOption, &status);
437 if (client == NULL) {
438 fprintf (stderr, "jack_client_open() failed, "
439 "status = 0x%2.0x\n", status);
440 return 1;
443 signal(SIGQUIT, signal_handler);
444 signal(SIGTERM, signal_handler);
445 signal(SIGHUP, signal_handler);
446 signal(SIGINT, signal_handler);
448 jack_on_shutdown(client, jack_shutdown, 0);
450 if (jack_activate(client)) {
451 fprintf(stderr, "cannot activate client");
452 return 1;
455 /* execute commands until done */
456 command_loop();
458 jack_client_close(client);
459 exit(0);