** Warning: Cannot open xref database.
1 /*
2 * transport.c -- JACK transport master example client.
3 *
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.
10 *
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.
15 *
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.
19 */
20
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>
31
32 char *package; /* program name */
33 int done = 0;
34 jack_client_t *client;
35
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
39 */
40 float time_beats_per_bar = 4.0;
41 float time_beat_type = 0.25;
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
46 /* JACK timebase callback.
47 *
48 * Runs in the process thread. Realtime, must not wait.
49 */
50 void timebase(jack_transport_state_t state, jack_nframes_t nframes,
51 jack_position_t *pos, int new_pos, void *arg)
52 {
53 double min; /* minutes since frame 0 */
54 long abs_tick; /* ticks since frame 0 */
55 long abs_beat; /* beats since frame 0 */
56
57 if (new_pos || time_reset) {
58
59 pos->valid = JackPositionBBT;
60 pos->beats_per_bar = time_beats_per_bar;
61 pos->beat_type = time_beat_type;
62 pos->ticks_per_beat = time_ticks_per_beat;
63 pos->beats_per_minute = time_beats_per_minute;
64
65 time_reset = 0; /* time change complete */
66
67 /* Compute BBT info from frame number. This is relatively
68 * simple here, but would become complex if we supported tempo
69 * or time signature changes at specific locations in the
70 * transport timeline. */
71
72 min = pos->frame / ((double) pos->frame_rate * 60.0);
73 abs_tick = min * pos->beats_per_minute * pos->ticks_per_beat;
74 abs_beat = abs_tick / pos->ticks_per_beat;
75
76 pos->bar = abs_beat / pos->beats_per_bar;
77 pos->beat = abs_beat - (pos->bar * pos->beats_per_bar) + 1;
78 pos->tick = abs_tick - (abs_beat * pos->ticks_per_beat);
79 pos->bar_start_tick = pos->bar * pos->beats_per_bar *
80 pos->ticks_per_beat;
81 pos->bar++; /* adjust start to bar 1 */
82
83 #if 0
84 /* some debug code... */
85 fprintf(stderr, "\nnew position: %" PRIu32 "\tBBT: %3"
86 PRIi32 "|%" PRIi32 "|%04" PRIi32 "\n",
87 pos->frame, pos->bar, pos->beat, pos->tick);
88 #endif
89
90 } else {
91
92 /* Compute BBT info based on previous period. */
93 pos->tick +=
94 nframes * pos->ticks_per_beat * pos->beats_per_minute
95 / (pos->frame_rate * 60);
96
97 while (pos->tick >= pos->ticks_per_beat) {
98 pos->tick -= pos->ticks_per_beat;
99 if (++pos->beat > pos->beats_per_bar) {
100 pos->beat = 1;
101 ++pos->bar;
102 pos->bar_start_tick +=
103 pos->beats_per_bar
104 * pos->ticks_per_beat;
105 }
106 }
107 }
108 }
109
110 void jack_shutdown(void *arg)
111 {
112 #if defined(RL_READLINE_VERSION) && RL_READLINE_VERSION >= 0x0400
113 rl_cleanup_after_signal();
114 #endif
115 fprintf(stderr, "JACK shut down, exiting ...\n");
116 exit(1);
117 }
118
119 void signal_handler(int sig)
120 {
121 jack_client_close(client);
122 fprintf(stderr, "signal received, exiting ...\n");
123 exit(0);
124 }
125
126
127 /* Command functions: see commands[] table following. */
128
129 void com_activate(char *arg)
130 {
131 if (jack_activate(client)) {
132 fprintf(stderr, "cannot activate client");
133 }
134 }
135
136 void com_deactivate(char *arg)
137 {
138 if (jack_deactivate(client)) {
139 fprintf(stderr, "cannot deactivate client");
140 }
141 }
142
143 void com_exit(char *arg)
144 {
145 done = 1;
146 }
147
148 void com_help(char *); /* forward declaration */
149
150 void com_locate(char *arg)
151 {
152 jack_nframes_t frame = 0;
153
154 if (*arg != '\0')
155 frame = atoi(arg);
156
157 jack_transport_locate(client, frame);
158 }
159
160 void com_master(char *arg)
161 {
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");
165 }
166
167 void com_play(char *arg)
168 {
169 jack_transport_start(client);
170 }
171
172 void com_release(char *arg)
173 {
174 jack_release_timebase(client);
175 }
176
177 void com_stop(char *arg)
178 {
179 jack_transport_stop(client);
180 }
181
182 /* Change the tempo for the entire timeline, not just from the current
183 * location. */
184 void com_tempo(char *arg)
185 {
186 float tempo = 120.0;
187
188 if (*arg != '\0')
189 tempo = atof(arg);
190
191 time_beats_per_minute = tempo;
192 time_reset = 1;
193 }
194
195 /* Set sync timeout in seconds. */
196 void com_timeout(char *arg)
197 {
198 double timeout = 2.0;
199
200 if (*arg != '\0')
201 timeout = atof(arg);
202
203 jack_set_sync_timeout(client, (jack_time_t) (timeout*1000000));
204 }
205
206
207 /* Command parsing based on GNU readline info examples. */
208
209 typedef void cmd_function_t(char *); /* command function type */
210
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;
217
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 }
235 };
236
237 command_t *find_command(char *name)
238 {
239 register int i;
240 size_t namelen;
241
242 if ((name == NULL) || (*name == '\0'))
243 return ((command_t *)NULL);
244
245 namelen = strlen(name);
246 for (i = 0; commands[i].name; i++)
247 if (strncmp(name, commands[i].name, namelen) == 0) {
248
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]);
255 }
256
257 return ((command_t *)NULL);
258 }
259
260 void com_help(char *arg)
261 {
262 register int i;
263 command_t *cmd;
264
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);
270 }
271
272 } else if ((cmd = find_command(arg))) {
273 printf("%s\t\t%s.\n", cmd->name, cmd->doc);
274
275 } else {
276 int printed = 0;
277
278 printf("No `%s' command. Valid command names are:\n", arg);
279
280 for (i = 0; commands[i].name; i++) {
281 /* Print in six columns. */
282 if (printed == 6) {
283 printed = 0;
284 printf ("\n");
285 }
286
287 printf ("%s\t", commands[i].name);
288 printed++;
289 }
290
291 printf("\n\nTry `help [command]\' for more information.\n");
292 }
293 }
294
295 void execute_command(char *line)
296 {
297 register int i;
298 command_t *command;
299 char *word;
300
301 /* Isolate the command word. */
302 i = 0;
303 while (line[i] && whitespace(line[i]))
304 i++;
305 word = line + i;
306
307 while (line[i] && !whitespace(line[i]))
308 i++;
309
310 if (line[i])
311 line[i++] = '\0';
312
313 command = find_command(word);
314
315 if (!command) {
316 fprintf(stderr, "%s: No such command. There is `help\'.\n",
317 word);
318 return;
319 }
320
321 /* Get argument to command, if any. */
322 while (whitespace(line[i]))
323 i++;
324
325 word = line + i;
326
327 /* invoke the command function. */
328 (*command->func)(word);
329 }
330
331
332 /* Strip whitespace from the start and end of string. */
333 char *stripwhite(char *string)
334 {
335 register char *s, *t;
336
337 s = string;
338 while (whitespace(*s))
339 s++;
340
341 if (*s == '\0')
342 return s;
343
344 t = s + strlen (s) - 1;
345 while (t > s && whitespace(*t))
346 t--;
347 *++t = '\0';
348
349 return s;
350 }
351
352 char *dupstr(char *s)
353 {
354 char *r = malloc(strlen(s) + 1);
355 strcpy(r, s);
356 return r;
357 }
358
359 /* Readline generator function for command completion. */
360 char *command_generator (const char *text, int state)
361 {
362 static int list_index, len;
363 char *name;
364
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);
371 }
372
373 /* Return the next name which partially matches from the
374 command list. */
375 while ((name = commands[list_index].name)) {
376 list_index++;
377
378 if (strncmp(name, text, len) == 0)
379 return dupstr(name);
380 }
381
382 return (char *) NULL; /* No names matched. */
383 }
384
385 void command_loop()
386 {
387 char *line, *cmd;
388 char prompt[32];
389
390 snprintf(prompt, sizeof(prompt), "%s> ", package);
391
392 /* Allow conditional parsing of the ~/.inputrc file. */
393 rl_readline_name = package;
394
395 /* Define a custom completion function. */
396 rl_completion_entry_function = command_generator;
397
398 /* Read and execute commands until the user quits. */
399 while (!done) {
400
401 line = readline(prompt);
402
403 if (line == NULL) { /* EOF? */
404 printf("\n"); /* close out prompt */
405 done = 1;
406 break;
407 }
408
409 /* Remove leading and trailing whitespace from the line. */
410 cmd = stripwhite(line);
411
412 /* If anything left, add to history and execute it. */
413 if (*cmd)
414 {
415 add_history(cmd);
416 execute_command(cmd);
417 }
418
419 free(line); /* realine() called malloc() */
420 }
421 }
422
423 int main(int argc, char *argv[])
424 {
425 /* basename $0 */
426 package = strrchr(argv[0], '/');
427 if (package == 0)
428 package = argv[0];
429 else
430 package++;
431
432 /* become a new client of the JACK server */
433 if ((client = jack_client_new(package)) == 0) {
434 fprintf(stderr, "jack server not running?\n");
435 return 1;
436 }
437
438 signal(SIGQUIT, signal_handler);
439 signal(SIGTERM, signal_handler);
440 signal(SIGHUP, signal_handler);
441 signal(SIGINT, signal_handler);
442
443 jack_on_shutdown(client, jack_shutdown, 0);
444
445 if (jack_activate(client)) {
446 fprintf(stderr, "cannot activate client");
447 return 1;
448 }
449
450 /* execute commands until done */
451 command_loop();
452
453 jack_client_close(client);
454 exit(0);
455 }
456
This page was automatically generated by the
LXR engine.
Visit the LXR main site for more
information.