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.

367 lines
8.3KB

  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 <readline/readline.h>
  26. #include <readline/history.h>
  27. #include <jack/jack.h>
  28. #include <jack/transport.h>
  29. char *package; /* program name */
  30. int done = 0;
  31. jack_client_t *client;
  32. jack_transport_info_t tinfo; /* multi-threaded access */
  33. /* JACK process() handler.
  34. *
  35. * Runs in a separate realtime thread. Must not wait.
  36. */
  37. int process(jack_nframes_t nframes, void *arg)
  38. {
  39. jack_set_transport_info(client, &tinfo);
  40. /* frame number for next cycle */
  41. if (tinfo.transport_state != JackTransportStopped) {
  42. tinfo.frame += nframes;
  43. /* When looping, adjust the frame number periodically. Make
  44. * sure improper loop limits don't lock up the system in an
  45. * infinite while(). */
  46. if ((tinfo.transport_state == JackTransportLooping) &&
  47. (tinfo.loop_end > tinfo.loop_start)) {
  48. while (tinfo.frame >= tinfo.loop_end)
  49. tinfo.frame -= (tinfo.loop_end - tinfo.loop_start);
  50. }
  51. }
  52. return 0;
  53. }
  54. void jack_shutdown(void *arg)
  55. {
  56. exit(1);
  57. }
  58. void signal_handler(int sig)
  59. {
  60. jack_client_close(client);
  61. fprintf(stderr, "signal received, exiting ...\n");
  62. exit(0);
  63. }
  64. /* command functions in alphabetical order */
  65. void com_exit(char *arg)
  66. {
  67. done = 1;
  68. }
  69. void com_help(char *); /* forward declaration */
  70. void com_loop(char *arg)
  71. {
  72. tinfo.transport_state = JackTransportLooping;
  73. }
  74. void com_play(char *arg)
  75. {
  76. tinfo.transport_state = JackTransportRolling;
  77. }
  78. void com_rewind(char *arg)
  79. {
  80. tinfo.transport_state = JackTransportStopped;
  81. tinfo.frame = 0;
  82. }
  83. void com_stop(char *arg)
  84. {
  85. tinfo.transport_state = JackTransportStopped;
  86. }
  87. /* Command parsing based on GNU readline info examples. */
  88. typedef void cmd_function_t(char *); /* command function type */
  89. /* Transport command table. */
  90. typedef struct {
  91. char *name; /* User printable name of the function. */
  92. cmd_function_t *func; /* Function to call to do the job. */
  93. char *doc; /* Documentation for this function. */
  94. } command_t;
  95. /* command list, must be in order */
  96. command_t commands[] = {
  97. { "exit", com_exit, "Exit transport program" },
  98. { "help", com_help, "Display help text" },
  99. { "loop", com_loop, "Start transport looping" },
  100. { "play", com_play, "Start transport rolling" },
  101. { "quit", com_exit, "Synonym for `exit'"},
  102. { "rewind", com_rewind, "Reset transport position to beginning" },
  103. { "stop", com_stop, "Stop transport" },
  104. { "?", com_help, "Synonym for `help'" },
  105. { (char *)NULL, (cmd_function_t *)NULL, (char *)NULL }
  106. };
  107. command_t *find_command(char *name)
  108. {
  109. register int i;
  110. size_t namelen;
  111. if ((name == NULL) || (*name == '\0'))
  112. return ((command_t *)NULL);
  113. namelen = strlen(name);
  114. for (i = 0; commands[i].name; i++)
  115. if (strncmp(name, commands[i].name, namelen) == 0) {
  116. /* make sure the match is unique */
  117. if ((commands[i+1].name) &&
  118. (strncmp(name, commands[i+1].name, namelen) == 0))
  119. return ((command_t *)NULL);
  120. else
  121. return (&commands[i]);
  122. }
  123. return ((command_t *)NULL);
  124. }
  125. void com_help(char *arg)
  126. {
  127. register int i;
  128. command_t *cmd;
  129. if (!*arg) {
  130. /* print help for all commands */
  131. for (i = 0; commands[i].name; i++) {
  132. printf("%s\t\t%s.\n", commands[i].name, commands[i].doc);
  133. }
  134. } else if ((cmd = find_command(arg))) {
  135. printf("%s\t\t%s.\n", cmd->name, cmd->doc);
  136. } else {
  137. int printed = 0;
  138. printf("No `%s' command. Valid command names are:\n", arg);
  139. for (i = 0; commands[i].name; i++) {
  140. /* Print in six columns. */
  141. if (printed == 6) {
  142. printed = 0;
  143. printf ("\n");
  144. }
  145. printf ("%s\t", commands[i].name);
  146. printed++;
  147. }
  148. printf("\n\nTry `help [command]\' for more information.\n");
  149. }
  150. }
  151. void execute_command(char *line)
  152. {
  153. register int i;
  154. command_t *command;
  155. char *word;
  156. /* Isolate the command word. */
  157. i = 0;
  158. while (line[i] && whitespace(line[i]))
  159. i++;
  160. word = line + i;
  161. while (line[i] && !whitespace(line[i]))
  162. i++;
  163. if (line[i])
  164. line[i++] = '\0';
  165. command = find_command(word);
  166. if (!command) {
  167. fprintf(stderr, "%s: No such command. There is `help\'.\n", word);
  168. return;
  169. }
  170. /* Get argument to command, if any. */
  171. while (whitespace(line[i]))
  172. i++;
  173. word = line + i;
  174. /* invoke the command function. */
  175. (*command->func)(word);
  176. }
  177. /* Strip whitespace from the start and end of string. */
  178. char *stripwhite(char *string)
  179. {
  180. register char *s, *t;
  181. s = string;
  182. while (whitespace(*s))
  183. s++;
  184. if (*s == '\0')
  185. return s;
  186. t = s + strlen (s) - 1;
  187. while (t > s && whitespace(*t))
  188. t--;
  189. *++t = '\0';
  190. return s;
  191. }
  192. char *dupstr(char *s)
  193. {
  194. char *r = malloc(strlen(s) + 1);
  195. strcpy(r, s);
  196. return r;
  197. }
  198. /* Readline generator function for command completion. */
  199. char *command_generator (const char *text, int state)
  200. {
  201. static int list_index, len;
  202. char *name;
  203. /* If this is a new word to complete, initialize now. This
  204. includes saving the length of TEXT for efficiency, and
  205. initializing the index variable to 0. */
  206. if (!state) {
  207. list_index = 0;
  208. len = strlen (text);
  209. }
  210. /* Return the next name which partially matches from the
  211. command list. */
  212. while ((name = commands[list_index].name)) {
  213. list_index++;
  214. if (strncmp(name, text, len) == 0)
  215. return dupstr(name);
  216. }
  217. return (char *) NULL; /* No names matched. */
  218. }
  219. void command_loop()
  220. {
  221. char *line, *cmd;
  222. char prompt[32];
  223. snprintf(prompt, sizeof(prompt), "%s> ", package);
  224. /* Allow conditional parsing of the ~/.inputrc file. */
  225. rl_readline_name = package;
  226. /* Define a custom completion function. */
  227. rl_completion_entry_function = command_generator;
  228. /* Read and execute commands until the user quits. */
  229. while (!done) {
  230. line = readline(prompt);
  231. if (line == NULL) { /* EOF? */
  232. printf("\n"); /* close out prompt */
  233. done = 1;
  234. break;
  235. }
  236. /* Remove leading and trailing whitespace from the line. */
  237. cmd = stripwhite(line);
  238. /* If anything left, add to history and execute it. */
  239. if (*cmd)
  240. {
  241. add_history(cmd);
  242. execute_command(cmd);
  243. }
  244. free(line); /* realine() called malloc() */
  245. }
  246. }
  247. void initialize_transport()
  248. {
  249. /* must run before jack_activate */
  250. tinfo.loop_start = 0; /* default loop is one second */
  251. tinfo.loop_end = jack_get_sample_rate(client);
  252. tinfo.valid = JackTransportState|JackTransportPosition|JackTransportLoop;
  253. com_rewind(NULL);
  254. }
  255. int main(int argc, char *argv[])
  256. {
  257. /* basename $0 */
  258. package = strrchr(argv[0], '/');
  259. if (package == 0)
  260. package = argv[0];
  261. else
  262. package++;
  263. /* become a new client of the JACK server */
  264. if ((client = jack_client_new(package)) == 0) {
  265. fprintf(stderr, "jack server not running?\n");
  266. return 1;
  267. }
  268. signal(SIGQUIT, signal_handler);
  269. signal(SIGTERM, signal_handler);
  270. signal(SIGHUP, signal_handler);
  271. signal(SIGINT, signal_handler);
  272. if (jack_engine_takeover_timebase(client) != 0) {
  273. fprintf(stderr, "Unable to take over timebase.\n");
  274. fprintf(stderr, "Is another transport master already running?\n");
  275. return 1;
  276. }
  277. jack_set_process_callback(client, process, 0);
  278. jack_on_shutdown(client, jack_shutdown, 0);
  279. initialize_transport();
  280. if (jack_activate(client)) {
  281. fprintf(stderr, "cannot activate client");
  282. return 1;
  283. }
  284. /* execute commands until done */
  285. command_loop();
  286. jack_client_close(client);
  287. exit(0);
  288. }