2 * transport.c -- JACK transport master example client.
4 * Copyright (C) 2003 Jack O'Quin.
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.
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 */
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
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
*
84 pos
->bar
++; /* adjust start to bar 1 */
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
);
95 /* Compute BBT info based on previous period. */
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
) {
105 pos
->bar_start_tick
+=
107 * pos
->ticks_per_beat
;
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();
123 fprintf(stderr
, "JACK shut down, exiting ...\n");
127 void signal_handler(int sig
)
129 jack_client_close(client
);
130 fprintf(stderr
, "signal received, exiting ...\n");
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
)
156 void com_help(char *); /* forward declaration */
158 void com_locate(char *arg
)
160 jack_nframes_t frame
= 0;
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
192 void com_tempo(char *arg
)
199 time_beats_per_minute
= tempo
;
203 /* Set sync timeout in seconds. */
204 void com_timeout(char *arg
)
206 double timeout
= 2.0;
211 jack_set_sync_timeout(client
, (jack_time_t
) (timeout
*1000000));
215 /* Change the tempo for the entire timeline, not just from the current
217 void com_av_ratio(char *arg
)
224 audio_frames_per_video_frame
= avr
;
228 /* Command parsing based on GNU readline info examples. */
230 typedef void cmd_function_t(char *); /* command function type */
232 /* Transport command table. */
234 char *name
; /* user printable name */
235 cmd_function_t
*func
; /* function to call */
236 char *doc
; /* documentation */
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
)
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
);
276 return (&commands
[i
]);
279 return ((command_t
*)NULL
);
282 void com_help(char *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
,
294 } else if ((cmd
= find_command(arg
))) {
295 printf("%s\t\t%s.\n", cmd
->name
, cmd
->doc
);
300 printf("No `%s' command. Valid command names are:\n", arg
);
302 for (i
= 0; commands
[i
].name
; i
++) {
303 /* Print in six columns. */
309 printf ("%s\t", commands
[i
].name
);
313 printf("\n\nTry `help [command]\' for more information.\n");
317 void execute_command(char *line
)
323 /* Isolate the command word. */
325 while (line
[i
] && whitespace(line
[i
]))
329 while (line
[i
] && !whitespace(line
[i
]))
335 command
= find_command(word
);
338 fprintf(stderr
, "%s: No such command. There is `help\'.\n",
343 /* Get argument to command, if any. */
344 while (whitespace(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
;
360 while (whitespace(*s
))
366 t
= s
+ strlen (s
) - 1;
367 while (t
> s
&& whitespace(*t
))
374 char *dupstr(char *s
)
376 char *r
= malloc(strlen(s
) + 1);
381 /* Readline generator function for command completion. */
382 char *command_generator (const char *text
, int state
)
384 static int list_index
, len
;
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. */
395 /* Return the next name which partially matches from the
397 while ((name
= commands
[list_index
].name
)) {
400 if (strncmp(name
, text
, len
) == 0)
404 return (char *) NULL
; /* No names matched. */
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. */
423 line
= readline(prompt
);
425 if (line
== NULL
) { /* EOF? */
426 printf("\n"); /* close out prompt */
431 /* Remove leading and trailing whitespace from the line. */
432 cmd
= stripwhite(line
);
434 /* If anything left, add to history and execute it. */
438 execute_command(cmd
);
441 free(line
); /* realine() called malloc() */
445 int main(int argc
, char *argv
[])
447 jack_status_t status
;
450 package
= strrchr(argv
[0], '/');
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
);
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");
476 /* execute commands until done */
479 jack_client_close(client
);