jack2 codebase
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.

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