jack1 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.

436 lines
10KB

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