command-line utility to translate between Mackie HUI and Open Sound Control
Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

623 Zeilen
17KB

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