JACK tools
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

514 lines
12KB

  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. #include <stdio.h>
  21. #include <errno.h>
  22. #include <unistd.h>
  23. #include <signal.h>
  24. #include <stdlib.h>
  25. #include <string.h>
  26. #include <readline/readline.h>
  27. #include <readline/history.h>
  28. #include <jack/jack.h>
  29. #include <jack/transport.h>
  30. char *package; /* program name */
  31. int done = 0;
  32. jack_client_t *client;
  33. double last_tick;
  34. /* Time and tempo variables. These are global to the entire,
  35. * transport timeline. There is no attempt to keep a true tempo map.
  36. * The default time signature is: "march time", 4/4, 120bpm
  37. */
  38. float time_beats_per_bar = 4.0;
  39. float time_beat_type = 4.0;
  40. double time_ticks_per_beat = 1920.0;
  41. double time_beats_per_minute = 120.0;
  42. volatile int time_reset = 1; /* true when time values change */
  43. volatile int avr_set = 0;
  44. float audio_frames_per_video_frame;
  45. /* JACK timebase callback.
  46. *
  47. * Runs in the process thread. Realtime, must not wait.
  48. */
  49. void timebase(jack_transport_state_t state, jack_nframes_t nframes,
  50. jack_position_t *pos, int new_pos, void *arg)
  51. {
  52. double min; /* minutes since frame 0 */
  53. long abs_tick; /* ticks since frame 0 */
  54. long abs_beat; /* beats since frame 0 */
  55. if (new_pos || time_reset) {
  56. pos->valid = JackPositionBBT;
  57. pos->beats_per_bar = time_beats_per_bar;
  58. pos->beat_type = time_beat_type;
  59. pos->ticks_per_beat = time_ticks_per_beat;
  60. pos->beats_per_minute = time_beats_per_minute;
  61. time_reset = 0; /* time change complete */
  62. /* Compute BBT info from frame number. This is relatively
  63. * simple here, but would become complex if we supported tempo
  64. * or time signature changes at specific locations in the
  65. * transport timeline.
  66. */
  67. min = pos->frame / ((double) pos->frame_rate * 60.0);
  68. abs_tick = min * pos->beats_per_minute * pos->ticks_per_beat;
  69. abs_beat = abs_tick / pos->ticks_per_beat;
  70. pos->bar = abs_beat / pos->beats_per_bar;
  71. pos->beat = abs_beat - (pos->bar * pos->beats_per_bar) + 1;
  72. last_tick = abs_tick - (abs_beat * pos->ticks_per_beat);
  73. pos->bar_start_tick = pos->bar * pos->beats_per_bar *
  74. pos->ticks_per_beat;
  75. pos->bar++; /* adjust start to bar 1 */
  76. #if 0
  77. /* some debug code... */
  78. fprintf(stderr, "\nnew position: %" PRIu32 "\tBBT: %3"
  79. PRIi32 "|%" PRIi32 "|%04" PRIi32 "\n",
  80. pos->frame, pos->bar, pos->beat, pos->tick);
  81. #endif
  82. } else {
  83. /* Compute BBT info based on previous period. */
  84. last_tick +=
  85. nframes * pos->ticks_per_beat * pos->beats_per_minute
  86. / (pos->frame_rate * 60);
  87. while (last_tick >= pos->ticks_per_beat) {
  88. last_tick -= pos->ticks_per_beat;
  89. if (++pos->beat > pos->beats_per_bar) {
  90. pos->beat = 1;
  91. ++pos->bar;
  92. pos->bar_start_tick +=
  93. pos->beats_per_bar
  94. * pos->ticks_per_beat;
  95. }
  96. }
  97. }
  98. pos->tick = (int)(last_tick + 0.5);
  99. if (avr_set) {
  100. pos->valid |= JackAudioVideoRatio;
  101. pos->audio_frames_per_video_frame = audio_frames_per_video_frame;
  102. }
  103. }
  104. int jack_process(jack_nframes_t nframes, void *arg)
  105. {
  106. return 0;
  107. }
  108. void jack_shutdown(void *arg)
  109. {
  110. #if defined(RL_READLINE_VERSION) && RL_READLINE_VERSION >= 0x0400
  111. rl_cleanup_after_signal();
  112. #endif
  113. fprintf(stderr, "JACK shut down, exiting ...\n");
  114. exit(1);
  115. }
  116. void signal_handler(int sig)
  117. {
  118. jack_client_close(client);
  119. fprintf(stderr, "signal received, exiting ...\n");
  120. exit(0);
  121. }
  122. /* Command functions: see commands[] table following. */
  123. void com_activate(char *arg)
  124. {
  125. if (jack_activate(client)) {
  126. fprintf(stderr, "cannot activate client");
  127. }
  128. }
  129. void com_deactivate(char *arg)
  130. {
  131. if (jack_deactivate(client)) {
  132. fprintf(stderr, "cannot deactivate client");
  133. }
  134. }
  135. void com_exit(char *arg)
  136. {
  137. done = 1;
  138. }
  139. void com_help(char *); /* forward declaration */
  140. void com_locate(char *arg)
  141. {
  142. jack_nframes_t frame = 0;
  143. if (*arg != '\0')
  144. frame = atoi(arg);
  145. jack_transport_locate(client, frame);
  146. }
  147. void com_master(char *arg)
  148. {
  149. int cond = (*arg != '\0');
  150. if (jack_set_timebase_callback(client, cond, timebase, NULL) != 0)
  151. fprintf(stderr, "Unable to take over timebase.\n");
  152. }
  153. void com_play(char *arg)
  154. {
  155. jack_transport_start(client);
  156. }
  157. void com_release(char *arg)
  158. {
  159. jack_release_timebase(client);
  160. }
  161. void com_stop(char *arg)
  162. {
  163. jack_transport_stop(client);
  164. }
  165. /* Change the tempo for the entire timeline, not just from the current
  166. * location. */
  167. void com_tempo(char *arg)
  168. {
  169. float tempo = 120.0;
  170. if (*arg != '\0')
  171. tempo = atof(arg);
  172. time_beats_per_minute = tempo;
  173. time_reset = 1;
  174. }
  175. /* Set sync timeout in seconds. */
  176. void com_timeout(char *arg)
  177. {
  178. double timeout = 2.0;
  179. if (*arg != '\0')
  180. timeout = atof(arg);
  181. jack_set_sync_timeout(client, (jack_time_t) (timeout*1000000));
  182. }
  183. /* Toggle between play and stop state */
  184. static void com_toggle(char *arg)
  185. {
  186. jack_position_t current;
  187. jack_transport_state_t transport_state;
  188. transport_state = jack_transport_query (client, &current);
  189. switch (transport_state) {
  190. case JackTransportStopped:
  191. com_play( arg );
  192. break;
  193. case JackTransportRolling:
  194. com_stop( arg );
  195. break;
  196. case JackTransportStarting:
  197. fprintf(stderr, "state: Starting - no transport toggling");
  198. break;
  199. default:
  200. fprintf(stderr, "unexpected state: no transport toggling");
  201. }
  202. }
  203. /* Change the tempo for the entire timeline, not just from the current
  204. * location. */
  205. void com_av_ratio(char *arg)
  206. {
  207. float avr = 0;
  208. if (*arg != '\0')
  209. avr = atof(arg);
  210. audio_frames_per_video_frame = avr;
  211. avr_set = 1;
  212. }
  213. /* Command parsing based on GNU readline info examples. */
  214. typedef void cmd_function_t(char *); /* command function type */
  215. /* Transport command table. */
  216. typedef struct {
  217. char *name; /* user printable name */
  218. cmd_function_t *func; /* function to call */
  219. char *doc; /* documentation */
  220. } command_t;
  221. /* command table must be in alphabetical order */
  222. command_t commands[] = {
  223. {"activate", com_activate, "Call jack_activate()"},
  224. {"avr", com_av_ratio, "Set audio/video frame ratio <audio frames per video frame>"},
  225. {"exit", com_exit, "Exit transport program"},
  226. {"deactivate", com_deactivate, "Call jack_deactivate()"},
  227. {"help", com_help, "Display help text [<command>]"},
  228. {"locate", com_locate, "Locate to frame <position>"},
  229. {"master", com_master, "Become timebase master "
  230. "[<conditionally>]"},
  231. {"play", com_play, "Start transport rolling"},
  232. {"quit", com_exit, "Synonym for `exit'"},
  233. {"release", com_release, "Release timebase"},
  234. {"stop", com_stop, "Stop transport"},
  235. {"tempo", com_tempo, "Set beat tempo <beats_per_min>"},
  236. {"timeout", com_timeout, "Set sync timeout in <seconds>"},
  237. {"toggle", com_toggle, "Toggle transport rolling"},
  238. {"?", com_help, "Synonym for `help'" },
  239. {(char *)NULL, (cmd_function_t *)NULL, (char *)NULL }
  240. };
  241. command_t *find_command(char *name)
  242. {
  243. register int i;
  244. size_t namelen;
  245. if ((name == NULL) || (*name == '\0'))
  246. return ((command_t *)NULL);
  247. namelen = strlen(name);
  248. for (i = 0; commands[i].name; i++)
  249. 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]);
  256. }
  257. return ((command_t *)NULL);
  258. }
  259. void com_help(char *arg)
  260. {
  261. register int i;
  262. command_t *cmd;
  263. if (!*arg) {
  264. /* print help for all commands */
  265. for (i = 0; commands[i].name; i++) {
  266. printf("%s\t\t%s.\n", commands[i].name,
  267. commands[i].doc);
  268. }
  269. } else if ((cmd = find_command(arg))) {
  270. printf("%s\t\t%s.\n", cmd->name, cmd->doc);
  271. } else {
  272. int printed = 0;
  273. printf("No `%s' command. Valid command names are:\n", arg);
  274. for (i = 0; commands[i].name; i++) {
  275. /* Print in six columns. */
  276. if (printed == 6) {
  277. printed = 0;
  278. printf ("\n");
  279. }
  280. printf ("%s\t", commands[i].name);
  281. printed++;
  282. }
  283. printf("\n\nTry `help [command]\' for more information.\n");
  284. }
  285. }
  286. void execute_command(char *line)
  287. {
  288. register int i;
  289. command_t *command;
  290. char *word;
  291. /* Isolate the command word. */
  292. i = 0;
  293. while (line[i] && whitespace(line[i]))
  294. i++;
  295. word = line + i;
  296. while (line[i] && !whitespace(line[i]))
  297. i++;
  298. if (line[i])
  299. line[i++] = '\0';
  300. command = find_command(word);
  301. if (!command) {
  302. fprintf(stderr, "%s: No such command. There is `help\'.\n",
  303. word);
  304. return;
  305. }
  306. /* Get argument to command, if any. */
  307. while (whitespace(line[i]))
  308. i++;
  309. word = line + i;
  310. /* invoke the command function. */
  311. (*command->func)(word);
  312. }
  313. /* Strip whitespace from the start and end of string. */
  314. char *stripwhite(char *string)
  315. {
  316. register char *s, *t;
  317. s = string;
  318. while (whitespace(*s))
  319. s++;
  320. if (*s == '\0')
  321. return s;
  322. t = s + strlen (s) - 1;
  323. while (t > s && whitespace(*t))
  324. t--;
  325. *++t = '\0';
  326. return s;
  327. }
  328. char *dupstr(char *s)
  329. {
  330. char *r = malloc(strlen(s) + 1);
  331. strcpy(r, s);
  332. return r;
  333. }
  334. /* Readline generator function for command completion. */
  335. char *command_generator (const char *text, int state)
  336. {
  337. static int list_index, len;
  338. char *name;
  339. /* If this is a new word to complete, initialize now. This
  340. includes saving the length of TEXT for efficiency, and
  341. initializing the index variable to 0. */
  342. if (!state) {
  343. list_index = 0;
  344. len = strlen (text);
  345. }
  346. /* Return the next name which partially matches from the
  347. command list. */
  348. while ((name = commands[list_index].name)) {
  349. list_index++;
  350. if (strncmp(name, text, len) == 0)
  351. return dupstr(name);
  352. }
  353. return (char *) NULL; /* No names matched. */
  354. }
  355. void command_loop()
  356. {
  357. char *line, *cmd;
  358. char prompt[32];
  359. snprintf(prompt, sizeof(prompt), "%s> ", package);
  360. /* Allow conditional parsing of the ~/.inputrc file. */
  361. rl_readline_name = package;
  362. /* Define a custom completion function. */
  363. rl_completion_entry_function = command_generator;
  364. /* Read and execute commands until the user quits. */
  365. while (!done) {
  366. line = readline(prompt);
  367. if (line == NULL) { /* EOF? */
  368. printf("\n"); /* close out prompt */
  369. done = 1;
  370. break;
  371. }
  372. /* Remove leading and trailing whitespace from the line. */
  373. cmd = stripwhite(line);
  374. /* If anything left, add to history and execute it. */
  375. if (*cmd)
  376. {
  377. add_history(cmd);
  378. execute_command(cmd);
  379. }
  380. free(line); /* realine() called malloc() */
  381. }
  382. }
  383. int main(int argc, char *argv[])
  384. {
  385. jack_status_t status;
  386. /* basename $0 */
  387. package = strrchr(argv[0], '/');
  388. if (package == 0)
  389. package = argv[0];
  390. else
  391. package++;
  392. /* open a connection to the JACK server */
  393. client = jack_client_open (package, JackNullOption, &status);
  394. if (client == NULL) {
  395. fprintf (stderr, "jack_client_open() failed, "
  396. "status = 0x%2.0x\n", status);
  397. return 1;
  398. }
  399. signal(SIGQUIT, signal_handler);
  400. signal(SIGTERM, signal_handler);
  401. signal(SIGHUP, signal_handler);
  402. signal(SIGINT, signal_handler);
  403. jack_set_process_callback(client, jack_process, 0);
  404. jack_on_shutdown(client, jack_shutdown, 0);
  405. if (jack_activate(client)) {
  406. fprintf(stderr, "cannot activate client");
  407. return 1;
  408. }
  409. /* execute commands until done */
  410. command_loop();
  411. jack_client_close(client);
  412. exit(0);
  413. }