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.

482 lines
11KB

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