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.

416 lines
9.7KB

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