command-line utility to translate between Mackie HUI and Open Sound Control
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

268 行
7.0KB

  1. /*
  2. * Programmer: Dominik Schmidt-Philipp <schmidt-philipp@kulturteknologi.no>
  3. * Filename: main.c
  4. *
  5. * Dedicated to seleomlivet
  6. *
  7. */
  8. #include <alsa/asoundlib.h>
  9. #include <pthread.h> /* for threading */
  10. #include <lo/lo.h>
  11. #include "mapping.h"
  12. typedef struct {
  13. snd_rawmidi_t* midiin;
  14. snd_rawmidi_t* midiout;
  15. lo_address osc_out;
  16. unsigned char hui_in_zone;
  17. } housicIO;
  18. typedef struct {
  19. char type;
  20. char* address;
  21. } Item;
  22. char *ZONES[] = {
  23. "channel_strip_1",
  24. "channel_strip_2",
  25. "channel_strip_3",
  26. "channel_strip_4",
  27. "channel_strip_5",
  28. "channel_strip_6",
  29. "channel_strip_7",
  30. "channel_strip_8",
  31. "keyboard_shortcuts",
  32. "window",
  33. "channel_selection",
  34. "assignment_1",
  35. "assignment_2",
  36. "cursor",
  37. "transport_main",
  38. "transport_add1",
  39. "transport_add2",
  40. "monitor_input",
  41. "monitor_output",
  42. "num_pad_1",
  43. "num_pad_2",
  44. "num_pad_3",
  45. "timecode",
  46. "auto_enable",
  47. "auto_mode",
  48. "status_group",
  49. "edit",
  50. "fn_keys",
  51. "parameter_edit",
  52. "misc"
  53. };
  54. char *BUTTONS[0x1e][8] = {
  55. {"fader","select","mute","solo","auto","v-sel","insert","rec_rdy"},
  56. {"fader","select","mute","solo","auto","v-sel","insert","rec_rdy"},
  57. {"fader","select","mute","solo","auto","v-sel","insert","rec_rdy"},
  58. {"fader","select","mute","solo","auto","v-sel","insert","rec_rdy"},
  59. {"fader","select","mute","solo","auto","v-sel","insert","rec_rdy"},
  60. {"fader","select","mute","solo","auto","v-sel","insert","rec_rdy"},
  61. {"fader","select","mute","solo","auto","v-sel","insert","rec_rdy"},
  62. {"fader","select","mute","solo","auto","v-sel","insert","rec_rdy"},
  63. {"ctrl","shift","editmode","undo","alt","option","edittool","save"},
  64. {"mix","edit","transprt","mem-loc","status","alt"},
  65. {"chan_left","bank_left","chanl_right","bank_right"},
  66. {"output","input","pan","send_e","send_d","send_c","send_b","send_a"},
  67. {"assign","default","suspend","shift","mute","bypass","recrdyall"},
  68. {"down","left","mode","right","up","scrub","shuttle"},
  69. {"talkback","rewind","fast_fwd","stop","play","record"},
  70. {"rtz","end","on_line","loop","quick_punch"},
  71. {"audition","pre","in","out","post"},
  72. {"input_3 ","input_2","input_1","mute","discrete"},
  73. {"output_3","output_2","output_1","dim","mono"},
  74. {"0","1","4","2","5",".","3","6"},
  75. {"enter","+"},
  76. {"7","8","9","-","clr","=","divide","*"},
  77. {"timecode","feet","beats","rudesolo"},
  78. {"plug_in","pan","fader","sendmute","send","mute"},
  79. {"trim","latch","read","off","write","touch"},
  80. {"phase","monitor","auto","suspend","create","group"},
  81. {"paste","cut","capture","delete","copy","separate"},
  82. {"f1","f2","f3","f4","f5","f6","f7","f8"},
  83. {"insert","assign","select_1","select_2","select_3","select_4","bypass","compare"},
  84. {"switch_1","switch_2","click","beep"}
  85. };
  86. // function declarations:
  87. void alsa_error (const char *format, ...);
  88. void* midiinfunction (void * arg);
  89. void midi_in_dispatch (void * arg, unsigned char * m);
  90. void lo_error (int num, const char *m, const char *path);
  91. int osc_in_handler (const char *path, const char *types, lo_arg ** argv,
  92. int argc, void *data, void *user_data);
  93. void hui_in_button (void *arg, unsigned char m);
  94. void osc_send (void *arg, Item el, float value);
  95. void register_osc_receives();
  96. int main(int argc, char *argv[]) {
  97. int status;
  98. int mode = SND_RAWMIDI_SYNC;
  99. pthread_t midiinthread;
  100. lo_server_thread st = lo_server_thread_new("7770", lo_error);
  101. housicIO IOs = {NULL,NULL,0,0};
  102. IOs.osc_out = lo_address_new(NULL,"7771");
  103. const char* portname = "virtual";
  104. // MIDI
  105. if ((status = snd_rawmidi_open(&IOs.midiin, NULL, portname, mode)) < 0) {
  106. alsa_error("Problem opening MIDI input: %s", snd_strerror(status));
  107. exit(1);
  108. }
  109. status = pthread_create(&midiinthread, NULL, midiinfunction, &IOs);
  110. // OSC
  111. register_osc_receives();
  112. lo_server_thread_add_method(st, NULL, NULL, osc_in_handler, NULL);
  113. lo_server_thread_start(st);
  114. // wait for MIDI thread to end (will not happen)
  115. pthread_join(midiinthread, NULL);
  116. return 0;
  117. }
  118. //////////////////////////////
  119. //
  120. // midiinfunction -- Thread function which waits around until a MIDI
  121. // input byte arrives and then react correspondingly
  122. //
  123. void register_osc_receives() {
  124. }
  125. //////////////////////////////
  126. //
  127. // midiinfunction -- Thread function which waits around until a MIDI
  128. // input byte arrives and then react correspondingly
  129. //
  130. void *midiinfunction(void *arg) {
  131. housicIO* IOs = (housicIO*)arg;
  132. snd_rawmidi_t* midiin = IOs->midiin;
  133. int status;
  134. int i = 0;
  135. char buffer[3];
  136. unsigned char message[3];
  137. while (1) {
  138. if ((status = snd_rawmidi_read(midiin, buffer, 3)) < 0) {
  139. alsa_error("Problem reading MIDI input: %s", snd_strerror(status));
  140. }
  141. // in case of MIDI running status, value bytes need to not override status byte
  142. for (i=1; i<=status;i++) {
  143. message[3-i] = (unsigned char) buffer[status-i];
  144. }
  145. midi_in_dispatch(IOs,message);
  146. fflush(stdout);
  147. }
  148. }
  149. //////////////////////////////
  150. //
  151. // midi_in_dispatch -- Thread function which waits around until a MIDI
  152. // input byte arrives and then react correspondingly
  153. //
  154. void midi_in_dispatch(void *arg, unsigned char *m) {
  155. housicIO* IOs = (housicIO*)arg;
  156. if (m[0] == 0xb0) {
  157. // received a CC message on MIDIchannel 1
  158. if (m[1] == 0x0f) {
  159. // received a zone select
  160. IOs->hui_in_zone = m[2];
  161. } else {
  162. if (m[1] == 0x2f) {
  163. // should always be true, if HUI is working.
  164. hui_in_button(IOs,m[2]);
  165. }
  166. }
  167. }
  168. }
  169. //////////////////////////////
  170. //
  171. // hui_in_button -- user interacted with a button on the HUI
  172. //
  173. void hui_in_button(void *arg, unsigned char m) {
  174. housicIO* IOs = (housicIO*)arg;
  175. char address[32];
  176. char zone = IOs->hui_in_zone;
  177. int dir = m & 0xf0;
  178. int port = m & 0x0f;
  179. sprintf(address, "/%s/%s",ZONES[zone],BUTTONS[zone][port]);
  180. Item ee = {'b',address};
  181. if (dir == 0x40) {
  182. // down
  183. osc_send(IOs, ee, 1);
  184. } else if (dir == 0x00) {
  185. // up
  186. osc_send(IOs, ee, 0);
  187. }
  188. }
  189. //////////////////////////////
  190. //
  191. // osc_send --
  192. //
  193. void osc_send(void *arg, Item el, float value) {
  194. housicIO* IOs = (housicIO*)arg;
  195. lo_send(IOs->osc_out, el.address, "f", value );
  196. }
  197. //////////////////////////////
  198. //
  199. // osc_in_handler -- receive incoming OSC messages
  200. //
  201. int osc_in_handler (const char *path, const char *types, lo_arg ** argv,
  202. int argc, void *data, void *user_data) {
  203. int i;
  204. printf("path: <%s>\n", path);
  205. for (i = 0; i < argc; i++) {
  206. printf("arg %d '%c' ", i, types[i]);
  207. lo_arg_pp((lo_type)types[i], argv[i]);
  208. printf("\n");
  209. }
  210. printf("\n");
  211. fflush(stdout);
  212. return 1;
  213. }
  214. //////////////////////////////
  215. //
  216. // error -- print error message
  217. //
  218. void alsa_error(const char *format, ...) {
  219. va_list ap;
  220. va_start(ap, format);
  221. vfprintf(stderr, format, ap);
  222. va_end(ap);
  223. putc('\n', stderr);
  224. }
  225. void lo_error(int num, const char *msg, const char *path)
  226. {
  227. printf("liblo server error %d in path %s: %s\n", num, path, msg);
  228. fflush(stdout);
  229. }