command-line utility to translate between Mackie HUI and Open Sound Control
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

480 linhas
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. }