command-line utility to translate between Mackie HUI and Open Sound Control
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.

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