command-line utility to translate between Mackie HUI and Open Sound Control
Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

480 rindas
13KB

  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 <unistd.h>
  12. #define ZONE_COUNT 0x1e
  13. #define PORT_COUNT 196
  14. #define DISPLAY_COUNT 17
  15. #define HDR 0xf0, 0x00, 0x00, 0x66, 0x05, 0x00
  16. enum display_type {
  17. LABEL,
  18. VPOT,
  19. MAIN_DISPLAY
  20. };
  21. typedef struct {
  22. char zone;
  23. char port;
  24. void* io;
  25. } Hui_MIDI_port_t;
  26. typedef struct {
  27. int type;
  28. int id;
  29. void* io;
  30. } Hui_display_port_t;
  31. typedef struct {
  32. snd_rawmidi_t* midiin;
  33. snd_rawmidi_t* midiout;
  34. lo_address osc_out;
  35. lo_server_thread osc_in;
  36. unsigned char hui_in_zone;
  37. int hui_in_fader_hi[8];
  38. Hui_MIDI_port_t MIDIouts[PORT_COUNT];
  39. Hui_display_port_t display_outs[DISPLAY_COUNT];
  40. } housicIO;
  41. typedef struct {
  42. char type;
  43. char* address;
  44. } Item;
  45. char *ZONES[] = {
  46. "channel_strip_1",
  47. "channel_strip_2",
  48. "channel_strip_3",
  49. "channel_strip_4",
  50. "channel_strip_5",
  51. "channel_strip_6",
  52. "channel_strip_7",
  53. "channel_strip_8",
  54. "keyboard_shortcuts",
  55. "window",
  56. "channel_selection",
  57. "assignment_1",
  58. "assignment_2",
  59. "cursor",
  60. "transport_main",
  61. "transport_add1",
  62. "transport_add2",
  63. "monitor_input",
  64. "monitor_output",
  65. "num_pad_1",
  66. "num_pad_2",
  67. "num_pad_3",
  68. "timecode",
  69. "auto_enable",
  70. "auto_mode",
  71. "status_group",
  72. "edit",
  73. "fn_keys",
  74. "parameter_edit",
  75. "misc"
  76. };
  77. char *BUTTONS[ZONE_COUNT][8] = {
  78. {"fader","select","mute","solo","auto","v-sel","insert","rec_rdy"},
  79. {"fader","select","mute","solo","auto","v-sel","insert","rec_rdy"},
  80. {"fader","select","mute","solo","auto","v-sel","insert","rec_rdy"},
  81. {"fader","select","mute","solo","auto","v-sel","insert","rec_rdy"},
  82. {"fader","select","mute","solo","auto","v-sel","insert","rec_rdy"},
  83. {"fader","select","mute","solo","auto","v-sel","insert","rec_rdy"},
  84. {"fader","select","mute","solo","auto","v-sel","insert","rec_rdy"},
  85. {"fader","select","mute","solo","auto","v-sel","insert","rec_rdy"},
  86. {"ctrl","shift","editmode","undo","alt","option","edittool","save"},
  87. {"mix","edit","transprt","mem-loc","status","alt"},
  88. {"chan_left","bank_left","chanl_right","bank_right"},
  89. {"output","input","pan","send_e","send_d","send_c","send_b","send_a"},
  90. {"assign","default","suspend","shift","mute","bypass","recrdyall"},
  91. {"down","left","mode","right","up","scrub","shuttle"},
  92. {"talkback","rewind","fast_fwd","stop","play","record"},
  93. {"rtz","end","on_line","loop","quick_punch"},
  94. {"audition","pre","in","out","post"},
  95. {"input_3 ","input_2","input_1","mute","discrete"},
  96. {"output_3","output_2","output_1","dim","mono"},
  97. {"0","1","4","2","5",".","3","6"},
  98. {"enter","+"},
  99. {"7","8","9","-","clr","=","divide","multiply"},
  100. {"timecode","feet","beats","rudesolo"},
  101. {"plug_in","pan","fader","sendmute","send","mute"},
  102. {"trim","latch","read","off","write","touch"},
  103. {"phase","monitor","auto","suspend","create","group"},
  104. {"paste","cut","capture","delete","copy","separate"},
  105. {"f1","f2","f3","f4","f5","f6","f7","f8"},
  106. {"insert","assign","select_1","select_2","select_3","select_4","bypass","compare"},
  107. {"switch_1","switch_2","click","beep"}
  108. };
  109. // function declarations:
  110. void alsa_error (const char *format, ...);
  111. void* midiinfunction (void * arg);
  112. void* ping (void * arg);
  113. void midi_in_dispatch (void * arg, unsigned char * m);
  114. void lo_error (int num, const char *m, const char *path);
  115. int osc_in_handler (const char *path, const char *types, lo_arg ** argv,
  116. int argc, void *data, void *user_data);
  117. void hui_in_button (void *arg, unsigned char m);
  118. void hui_in_scroll (void *arg, unsigned char port, unsigned char vv);
  119. void hui_in_fader (void *arg, unsigned char fader, int val);
  120. void osc_send (void *arg, Item el, float value);
  121. void register_osc_receives(void *arg);
  122. int osc_in_button_handler(const char *path, const char *types, lo_arg ** argv,
  123. int argc, void *data, void *user_data);
  124. int osc_in_display_handler (const char *path, const char *types, lo_arg ** argv,
  125. int argc, void *data, void *user_data);
  126. int main(int argc, char *argv[]) {
  127. int status;
  128. int mode = SND_RAWMIDI_SYNC;
  129. pthread_t midiinthread;
  130. pthread_t pingthread;
  131. housicIO IOs = {NULL,NULL,0,NULL,0,{0,0,0,0,0,0,0,0},0};
  132. IOs.osc_out = lo_address_new(NULL,"7771");
  133. IOs.osc_in = lo_server_thread_new("7770", lo_error);
  134. const char* portname = "virtual";
  135. // MIDI
  136. if ((status = snd_rawmidi_open(NULL, &IOs.midiout, portname, mode)) < 0) {
  137. alsa_error("Problem opening MIDI output: %s", snd_strerror(status));
  138. exit(1);
  139. }
  140. if ((status = snd_rawmidi_open(&IOs.midiin, NULL, portname, mode)) < 0) {
  141. alsa_error("Problem opening MIDI input: %s", snd_strerror(status));
  142. exit(1);
  143. }
  144. status = pthread_create(&pingthread,NULL, ping, &IOs);
  145. status = pthread_create(&midiinthread, NULL, midiinfunction, &IOs);
  146. // OSC
  147. register_osc_receives(&IOs);
  148. //lo_server_thread_add_method(IOs.osc_in, NULL, NULL, osc_in_handler, NULL);
  149. lo_server_thread_start(IOs.osc_in);
  150. // wait for MIDI thread to end (will not happen)
  151. pthread_join(midiinthread, NULL);
  152. return 0;
  153. }
  154. //////////////////////////////
  155. //
  156. // ping -- It is important to send a ping in regular intervals. This
  157. // will keep the HUI in online mode.
  158. //
  159. void *ping(void *arg) {
  160. housicIO* IOs = (housicIO*)arg;
  161. char message[] = {0x90,0x00,0x00};
  162. while (1) {
  163. sleep(1);
  164. snd_rawmidi_write(IOs->midiout,message,3);
  165. }
  166. }
  167. //////////////////////////////
  168. //
  169. // midiinfunction -- Thread function which waits around until a MIDI
  170. // input byte arrives and then react correspondingly
  171. //
  172. void register_osc_receives(void *arg) {
  173. housicIO* IOs = (housicIO*)arg;
  174. int zone_c;
  175. int button_c;
  176. char address[32];
  177. int address_c = 0;
  178. for (zone_c=0; zone_c<ZONE_COUNT; zone_c++) {
  179. for (button_c=0; button_c<8; button_c++) {
  180. if (BUTTONS[zone_c][button_c]) {
  181. Hui_MIDI_port_t out = {zone_c,button_c,IOs};
  182. IOs->MIDIouts[address_c] = out;
  183. sprintf(address,"/%s/%s",ZONES[zone_c],BUTTONS[zone_c][button_c]);
  184. lo_server_thread_add_method(IOs->osc_in,
  185. address,
  186. "i",
  187. osc_in_button_handler,
  188. &IOs->MIDIouts[address_c]);
  189. address_c++;
  190. }
  191. }
  192. }
  193. int i;
  194. int display_c = 0;
  195. for (i=0;i<9;i++) {
  196. Hui_display_port_t out = {LABEL,i,IOs};
  197. IOs->display_outs[display_c] = out;
  198. sprintf(address,"/label/%d",i);
  199. lo_server_thread_add_method(IOs->osc_in,
  200. address,
  201. "s",
  202. osc_in_display_handler,
  203. &IOs->display_outs[display_c]);
  204. display_c++;
  205. }
  206. for (i=0;i<8;i++) {
  207. Hui_display_port_t out = {MAIN_DISPLAY,i,IOs};
  208. IOs->display_outs[display_c] = out;
  209. sprintf(address,"/main_display/%d",i);
  210. lo_server_thread_add_method(IOs->osc_in,
  211. address,
  212. "s",
  213. osc_in_display_handler,
  214. &IOs->display_outs[display_c]);
  215. display_c++;
  216. }
  217. }
  218. int osc_in_display_handler(const char *path, const char *types, lo_arg ** argv,
  219. int argc, void *data, void *user_data) {
  220. Hui_display_port_t* out = (Hui_display_port_t*)user_data;
  221. housicIO* IOs = (housicIO*) out->io;
  222. char* val = (char*)argv[0];
  223. int i;
  224. int size;
  225. char message[52] = {HDR};
  226. if (out->type == LABEL) {
  227. size=13;
  228. message[6] = 0x10;
  229. message[7] = out->id;
  230. for (i=0; i<4; i++) {
  231. message[8+i] = val[i] ? val[i] & 0x7f : 0x20;
  232. }
  233. } else if (out->type == MAIN_DISPLAY) {
  234. size=19;
  235. message[6] = 0x12;
  236. message[7] = out->id;
  237. for (i=0; i<10; i++) {
  238. message[8+i] = (val[i] > 0x10) ? val[i] & 0x7f : 0x20;
  239. }
  240. }
  241. message[size-1] = 0xf7;
  242. snd_rawmidi_write(IOs->midiout,message,size);
  243. return 1;
  244. }
  245. //////////////////////////////
  246. //
  247. // osc_in_button_handler -- called when a button type message is
  248. // received and should be sent to the HUI
  249. //
  250. int osc_in_button_handler(const char *path, const char *types, lo_arg ** argv,
  251. int argc, void *data, void *user_data) {
  252. Hui_MIDI_port_t* out = (Hui_MIDI_port_t*)user_data;
  253. housicIO* IOs = (housicIO*) out->io;
  254. char value;
  255. int status;
  256. // char message[6];
  257. char out_data;
  258. value = argv[0]->i;
  259. out_data = value ? 0x40 : 0x00;
  260. out_data |= out->port;
  261. unsigned char message[6] = {0xb0,0x0c,out->zone,0xb0,0x2c,out_data};
  262. status = snd_rawmidi_write(IOs->midiout,message,6);
  263. printf("%s - %x %x %x %x %x %x\n",path,
  264. message[0],message[1],message[2],message[3],message[4],message[5]);
  265. return 1;
  266. }
  267. //////////////////////////////
  268. //
  269. // midiinfunction -- Thread function which waits around until a MIDI
  270. // input byte arrives and then react correspondingly
  271. //
  272. void *midiinfunction(void *arg) {
  273. housicIO* IOs = (housicIO*)arg;
  274. snd_rawmidi_t* midiin = IOs->midiin;
  275. int status;
  276. int i = 0;
  277. char buffer[3];
  278. unsigned char message[3];
  279. while (1) {
  280. if ((status = snd_rawmidi_read(midiin, buffer, 3)) < 0) {
  281. alsa_error("Problem reading MIDI input: %s", snd_strerror(status));
  282. }
  283. // in case of MIDI running status, value bytes need to not override status byte
  284. for (i=1; i<=status;i++) {
  285. message[3-i] = (unsigned char) buffer[status-i];
  286. }
  287. if (status==2) {
  288. message[0] = 0xb0;
  289. } else if(0) { // only for debugging
  290. printf("%d: %x %x %x - %x %x %x\n", status,
  291. buffer[0],buffer[1],buffer[2],
  292. message[0], message[1], message[2]);
  293. }
  294. midi_in_dispatch(IOs,message);
  295. fflush(stdout);
  296. }
  297. }
  298. //////////////////////////////
  299. //
  300. // midi_in_dispatch -- Thread function which waits around until a MIDI
  301. // input byte arrives and then react correspondingly
  302. //
  303. void midi_in_dispatch(void *arg, unsigned char *m) {
  304. housicIO* IOs = (housicIO*)arg;
  305. int fader_value;
  306. if (m[0] == 0xb0) {
  307. // received a CC message on MIDIchannel 1
  308. if (m[1] == 0x0f) {
  309. // received a zone select
  310. IOs->hui_in_zone = m[2];
  311. } else if (m[1] == 0x2f) { // switches
  312. hui_in_button(IOs,m[2]);
  313. } else if ((m[1] & 0xf0) == 0x40) { // V-pots:
  314. hui_in_scroll(IOs,m[1] & 0xf, m[2]);
  315. } else if (m[1] == 0x0d) { // jog wheel:
  316. hui_in_scroll(IOs,0xd,m[2]);
  317. } else if (m[1] < 0x08) { // fader Hi
  318. IOs->hui_in_fader_hi[m[1]] = (int) m[2];
  319. } else if ((m[1] & 0xf0) == 0x20) { // fader Lo
  320. fader_value = (IOs->hui_in_fader_hi[m[1] & 0x0f] << 2) | (m[2] >> 5);
  321. hui_in_fader(IOs,m[1] & 0x0f,fader_value);
  322. }
  323. }
  324. }
  325. //////////////////////////////
  326. //
  327. // hui_in_fader -- fader positions
  328. //
  329. void hui_in_fader(void *arg, unsigned char port, int val) {
  330. housicIO* IOs = (housicIO*)arg;
  331. char address[32];
  332. sprintf(address, "/fader/%d", port + 1);
  333. Item ee = {'f',address};
  334. osc_send(IOs, ee, val);
  335. }
  336. //////////////////////////////
  337. //
  338. // hui_in_scroll -- user interacted with a button on the HUI
  339. //
  340. void hui_in_scroll(void *arg, unsigned char port, unsigned char vv) {
  341. housicIO* IOs = (housicIO*)arg;
  342. char address[32];
  343. sprintf(address, "/vpot/%d", port + 1);
  344. Item ee = {'s', address};
  345. if (vv > 0x40) {
  346. osc_send(IOs, ee, vv - 0x40);
  347. } else if (vv < 0x40) {
  348. osc_send(IOs, ee, -vv);
  349. }
  350. }
  351. //////////////////////////////
  352. //
  353. // hui_in_button -- user interacted with a button on the HUI
  354. //
  355. void hui_in_button(void *arg, unsigned char m) {
  356. housicIO* IOs = (housicIO*)arg;
  357. char address[32];
  358. char zone = IOs->hui_in_zone;
  359. int dir = m & 0xf0;
  360. int port = m & 0x0f;
  361. sprintf(address, "/%s/%s",ZONES[zone],BUTTONS[zone][port]);
  362. Item ee = {'b',address};
  363. if (dir == 0x40) {
  364. // down
  365. osc_send(IOs, ee, 1);
  366. } else if (dir == 0x00) {
  367. // up
  368. osc_send(IOs, ee, 0);
  369. }
  370. }
  371. //////////////////////////////
  372. //
  373. // osc_send --
  374. //
  375. void osc_send(void *arg, Item el, float value) {
  376. housicIO* IOs = (housicIO*)arg;
  377. lo_send(IOs->osc_out, el.address, "f", value );
  378. }
  379. //////////////////////////////
  380. //
  381. // osc_in_handler -- receive incoming OSC messages
  382. //
  383. int osc_in_handler (const char *path, const char *types, lo_arg ** argv,
  384. int argc, void *data, void *user_data) {
  385. int i;
  386. printf("path: <%s>\n", path);
  387. for (i = 0; i < argc; i++) {
  388. printf("arg %d '%c' ", i, types[i]);
  389. lo_arg_pp((lo_type)types[i], argv[i]);
  390. printf("\n");
  391. }
  392. printf("\n");
  393. fflush(stdout);
  394. return 1;
  395. }
  396. //////////////////////////////
  397. //
  398. // error -- print error message
  399. //
  400. void alsa_error(const char *format, ...) {
  401. va_list ap;
  402. va_start(ap, format);
  403. vfprintf(stderr, format, ap);
  404. va_end(ap);
  405. putc('\n', stderr);
  406. }
  407. void lo_error(int num, const char *msg, const char *path)
  408. {
  409. printf("liblo server error %d in path %s: %s\n", num, path, msg);
  410. fflush(stdout);
  411. }