|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639 |
- // housic - translate Mackie HUI MIDI data to OSC and back
- //
- // Copyright (C) 2020 Dominik Schmidt-Philipp
- //
- // This program is free software: you can redistribute it and/or modify it under
- // the terms of the GNU General Public License as published by the Free Software
- // Foundation, either version 3 of the License, or (at your option) any later
- // version.
- //
- // This program is distributed in the hope that it will be useful, but WITHOUT
- // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- // FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
- //
- // You should have received a copy of the GNU General Public License along with
- // this program. If not, see <http s ://www.gnu.org/licenses/>.
-
- /*
- * Programmer: Dominik Schmidt-Philipp <schmidt-philipp@kulturteknologi.no>
- * Filename: main.c
- *
- * Dedicated to seleomlivet
- *
- */
-
- #include <alsa/asoundlib.h>
- #include <pthread.h>
- #include <lo/lo.h>
- #include <unistd.h>
-
- #define ZONE_COUNT 0x1e
- #define PORT_COUNT 196
- #define DISPLAY_COUNT 38
- #define HDR 0xf0, 0x00, 0x00, 0x66, 0x05, 0x00
-
- enum display_type {
- LABEL,
- VPOT,
- MAIN_DISPLAY,
- TIMECODE,
- METER
- };
-
- /* enum vpot_mode { */
- /* POINTER, */
- /* CENTERED_FILL, */
- /* FILL, */
- /* WIDTH */
- /* }; */
-
- typedef struct {
- char zone;
- char port;
- void* io;
- } Hui_MIDI_port_t;
-
- typedef struct {
- int type;
- int id;
- char state;
- void* io;
- } Hui_display_port_t;
-
- typedef struct {
- snd_rawmidi_t* midiin;
- snd_rawmidi_t* midiout;
- lo_address osc_out;
- lo_server_thread osc_in;
- unsigned char hui_in_zone;
- int hui_in_fader_hi[8];
- Hui_MIDI_port_t MIDIouts[PORT_COUNT];
- Hui_display_port_t display_outs[DISPLAY_COUNT];
- } housicIO;
-
- typedef struct {
- char type;
- char* address;
- } Item;
-
-
- char *ZONES[] = {
- "channel_strip_1",
- "channel_strip_2",
- "channel_strip_3",
- "channel_strip_4",
- "channel_strip_5",
- "channel_strip_6",
- "channel_strip_7",
- "channel_strip_8",
- "keyboard_shortcuts",
- "window",
- "channel_selection",
- "assignment_1",
- "assignment_2",
- "cursor",
- "transport_main",
- "transport_add1",
- "transport_add2",
- "monitor_input",
- "monitor_output",
- "num_pad_1",
- "num_pad_2",
- "num_pad_3",
- "timecode",
- "auto_enable",
- "auto_mode",
- "status_group",
- "edit",
- "fn_keys",
- "parameter_edit",
- "misc"
- };
-
- char *BUTTONS[ZONE_COUNT][8] = {
- {"fader","select","mute","solo","auto","v-sel","insert","rec_rdy"},
- {"fader","select","mute","solo","auto","v-sel","insert","rec_rdy"},
- {"fader","select","mute","solo","auto","v-sel","insert","rec_rdy"},
- {"fader","select","mute","solo","auto","v-sel","insert","rec_rdy"},
- {"fader","select","mute","solo","auto","v-sel","insert","rec_rdy"},
- {"fader","select","mute","solo","auto","v-sel","insert","rec_rdy"},
- {"fader","select","mute","solo","auto","v-sel","insert","rec_rdy"},
- {"fader","select","mute","solo","auto","v-sel","insert","rec_rdy"},
- {"ctrl","shift","editmode","undo","alt","option","edittool","save"},
- {"mix","edit","transprt","mem-loc","status","alt"},
- {"chan_left","bank_left","chanl_right","bank_right"},
- {"output","input","pan","send_e","send_d","send_c","send_b","send_a"},
- {"assign","default","suspend","shift","mute","bypass","recrdyall"},
- {"down","left","mode","right","up","scrub","shuttle"},
- {"talkback","rewind","fast_fwd","stop","play","record"},
- {"rtz","end","on_line","loop","quick_punch"},
- {"audition","pre","in","out","post"},
- {"input_3 ","input_2","input_1","mute","discrete"},
- {"output_3","output_2","output_1","dim","mono"},
- {"0","1","4","2","5",".","3","6"},
- {"enter","+"},
- {"7","8","9","-","clr","=","divide","multiply"},
- {"timecode","feet","beats","rudesolo"},
- {"plug_in","pan","fader","sendmute","send","mute"},
- {"trim","latch","read","off","write","touch"},
- {"phase","monitor","auto","suspend","create","group"},
- {"paste","cut","capture","delete","copy","separate"},
- {"f1","f2","f3","f4","f5","f6","f7","f8"},
- {"insert","assign","select_1","select_2","select_3","select_4","bypass","compare"},
- {"switch_1","switch_2","click","beep"}
- };
-
-
-
- // function declarations:
- void alsa_error (const char *format, ...);
- void* midiinfunction (void * arg);
- void* ping (void * arg);
- void midi_in_dispatch (void * arg, unsigned char * m);
- void lo_error (int num, const char *m, const char *path);
- int osc_in_handler (const char *path, const char *types, lo_arg ** argv,
- int argc, void *data, void *user_data);
- void hui_in_button (void *arg, unsigned char m);
- void hui_in_scroll (void *arg, unsigned char port, unsigned char vv);
- void hui_in_fader (void *arg, unsigned char fader, int val);
- void osc_send (void *arg, Item el, float value);
- void register_osc_receives(void *arg);
- int osc_in_button_handler(const char *path, const char *types, lo_arg ** argv,
- int argc, void *data, void *user_data);
- int osc_in_display_handler (const char *path, const char *types, lo_arg ** argv,
- int argc, void *data, void *user_data);
- int osc_in_vpot_handler (const char *path, const char *types, lo_arg ** argv,
- int argc, void *data, void *user_data);
- int osc_in_meter_handler (const char *path, const char *types, lo_arg ** argv,
- int argc, void *data, void *user_data);
-
-
- int main(int argc, char *argv[]) {
- int status;
- int mode = SND_RAWMIDI_SYNC;
- pthread_t midiinthread;
- pthread_t pingthread;
- housicIO IOs = {NULL,NULL,0,NULL,0,{0,0,0,0,0,0,0,0},0};
- IOs.osc_out = lo_address_new(NULL,"7771");
- IOs.osc_in = lo_server_thread_new("7770", lo_error);
-
- const char* portname = "virtual";
-
- // MIDI
- if ((status = snd_rawmidi_open(NULL, &IOs.midiout, portname, mode)) < 0) {
- alsa_error("Problem opening MIDI output: %s", snd_strerror(status));
- exit(1);
- }
- if ((status = snd_rawmidi_open(&IOs.midiin, NULL, portname, mode)) < 0) {
- alsa_error("Problem opening MIDI input: %s", snd_strerror(status));
- exit(1);
- }
- status = pthread_create(&pingthread,NULL, ping, &IOs);
- status = pthread_create(&midiinthread, NULL, midiinfunction, &IOs);
-
-
- // OSC
- register_osc_receives(&IOs);
- //lo_server_thread_add_method(IOs.osc_in, NULL, NULL, osc_in_handler, NULL);
- lo_server_thread_start(IOs.osc_in);
-
- // wait for MIDI thread to end (will not happen)
- pthread_join(midiinthread, NULL);
-
-
- return 0;
- }
-
- //////////////////////////////
- //
- // ping -- It is important to send a ping in regular intervals. This
- // will keep the HUI in online mode.
- //
- void *ping(void *arg) {
- housicIO* IOs = (housicIO*)arg;
- char message[] = {0x90,0x00,0x00};
-
- while (1) {
- sleep(1);
- snd_rawmidi_write(IOs->midiout,message,3);
- }
- }
-
-
- //////////////////////////////
- //
- // midiinfunction -- Thread function which waits around until a MIDI
- // input byte arrives and then react correspondingly
- //
- void register_osc_receives(void *arg) {
- housicIO* IOs = (housicIO*)arg;
- int zone_c;
- int button_c;
- char address[32];
- int address_c = 0;
-
-
-
- for (zone_c=0; zone_c<ZONE_COUNT; zone_c++) {
- for (button_c=0; button_c<8; button_c++) {
- if (BUTTONS[zone_c][button_c]) {
- Hui_MIDI_port_t out = {zone_c,button_c,IOs};
- IOs->MIDIouts[address_c] = out;
- sprintf(address,"/%s/%s",ZONES[zone_c],BUTTONS[zone_c][button_c]);
- lo_server_thread_add_method(IOs->osc_in,
- address,
- "i",
- osc_in_button_handler,
- &IOs->MIDIouts[address_c]);
- address_c++;
- }
- }
- }
-
- int i;
- int display_c = 0;
-
- // Labels
- for (i=0;i<9;i++) {
- Hui_display_port_t out = {LABEL, i, 0, IOs};
- IOs->display_outs[display_c] = out;
- sprintf(address,"/label/%d",i);
- lo_server_thread_add_method(IOs->osc_in,
- address,
- "s",
- osc_in_display_handler,
- &IOs->display_outs[display_c]);
- display_c++;
- }
-
- // Main display:
- for (i=0;i<8;i++) {
- Hui_display_port_t out = {MAIN_DISPLAY, i, 0, IOs};
- IOs->display_outs[display_c] = out;
- sprintf(address,"/main_display/%d",i);
- lo_server_thread_add_method(IOs->osc_in,
- address,
- "s",
- osc_in_display_handler,
- &IOs->display_outs[display_c]);
- display_c++;
- }
-
- // Timecode
- Hui_display_port_t out = {TIMECODE, 0, 0, IOs};
- IOs->display_outs[display_c] = out;
- sprintf(address,"/timecode");
- lo_server_thread_add_method(IOs->osc_in,
- address,
- NULL,
- osc_in_display_handler,
- &IOs->display_outs[display_c]);
- display_c++;
-
- // Vpots
- for (i=0;i<12;i++) {
- Hui_display_port_t out = {VPOT, i, 0, IOs};
- IOs->display_outs[display_c] = out;
- sprintf(address,"/vpot/%d",i+1);
- lo_server_thread_add_method(IOs->osc_in,
- address,
- NULL,
- osc_in_vpot_handler,
- &IOs->display_outs[display_c]);
- display_c++;
- }
-
- // Meters
- for (i=0;i<8;i++) {
- Hui_display_port_t out = {METER, i, 0, IOs};
- IOs->display_outs[display_c] = out;
- sprintf(address,"/meter/%d",i+1);
- lo_server_thread_add_method(IOs->osc_in,
- address,
- "ff",
- osc_in_meter_handler,
- &IOs->display_outs[display_c]);
- display_c++;
- }
- }
-
- int osc_in_meter_handler(const char *path, const char *types, lo_arg ** argv,
- int argc, void *data, void *user_data) {
- Hui_display_port_t* out = (Hui_display_port_t*)user_data;
- housicIO* IOs = (housicIO*) out->io;
- unsigned char message[3];
- int i;
- char val;
- char side = 0;
- if (out->type == METER) {
- message[0] = 0xa0;
- message[1] = (0x0 | out->id);
- side = (char)argv[0]->f;
- if (side) {
- side = 0x10;
- }
- val = (char)argv[1]->f;
- if ( val <= 0xc) {
- message[2] = side | val;
- snd_rawmidi_write(IOs->midiout,message,3);
- }
- }
- }
-
- int osc_in_vpot_handler(const char *path, const char *types, lo_arg ** argv,
- int argc, void *data, void *user_data) {
- Hui_display_port_t* out = (Hui_display_port_t*)user_data;
- housicIO* IOs = (housicIO*) out->io;
- unsigned char message[3];
- int i;
- int size;
-
- if (out->type == VPOT) {
- size = 3;
- message[0] = 0xb0;
- for (i=0; i<argc; i++) {
- if (types[i] == 's') {
- // p: POINTER
- // c: CENTERED_FILL
- // f: FILL
- // w: WIDTH
- if (argv[i]->s == 'p' ) {
- out->state = (out->state & 0x0f) | 0x00;
- } else if (argv[i]->s == 'c' ) {
- out->state = (out->state & 0x0f) | 0x10;
- } else if (argv[i]->s == 'f' ) {
- out->state = (out->state & 0x0f) | 0x20;
- } else if (argv[i]->s == 'w' ) {
- out->state = (out->state & 0x0f) | 0x30;
- }
- } else if (types[i] == 'f') {
- if (argv[i]->f < 12) {
- out->state = ((unsigned char)argv[i]->f & 0x0f) | (out->state & 0xf0);
- }
- }
- }
- message[1] = (0x10 | out->id);
- message[2] = out->state;
- snd_rawmidi_write(IOs->midiout,message,size);
- }
- }
-
- int osc_in_display_handler(const char *path, const char *types, lo_arg ** argv,
- int argc, void *data, void *user_data) {
- Hui_display_port_t* out = (Hui_display_port_t*)user_data;
- housicIO* IOs = (housicIO*) out->io;
- char* val = (char*)argv[0];
- int i;
- int size;
- char message[52] = {HDR};
- char timecode[8];
-
- if (out->type == LABEL) {
- size=13;
- message[6] = 0x10;
- message[7] = out->id;
- for (i=0; i<4; i++) {
- message[8+i] = val[i] ? val[i] & 0x7f : 0x20;
- }
- } else if (out->type == MAIN_DISPLAY) {
- size=19;
- message[6] = 0x12;
- message[7] = out->id;
- for (i=0; i<10; i++) {
- message[8+i] = (val[i] > 0x10) ? val[i] & 0x7f : 0x20;
- }
- } else if (out->type == TIMECODE) {
- size=0;
- message[6] = 0x11;
- printf("%s \n",types);
- for (i=0; i<argc; i++) {
- // size keeps track of how many digits have been entered
- // make sure we only read 8 digits
- if (types[i] == 'f' && size < 8) {
- timecode[size] = (unsigned char) argv[i]->f & 0xf;
- size++;
- } else if (types[i] == 's') {
- if (argv[i]->s == '.') {
- // a dot behind a digit has 5th bit set
- timecode[size-1] = timecode[size-1] + 0x10;
- }
- }
- }
- for (i=0;i<size;i++) {
- // write timecode to message with a leading LSB
- // also LSB must be below 10 because it has no point (read doc)
- message[7+i] = i ? timecode[size-1-i]: timecode[size-1-i] & 0xf;
- }
- message[7+size] = 0xf7;
- size += 8;
- } else if (out->type == METER) {
-
- }
-
- message[size-1] = 0xf7;
-
- snd_rawmidi_write(IOs->midiout,message,size);
-
- return 1;
- }
-
-
-
- //////////////////////////////
- //
- // osc_in_button_handler -- called when a button type message is
- // received and should be sent to the HUI
- //
- int osc_in_button_handler(const char *path, const char *types, lo_arg ** argv,
- int argc, void *data, void *user_data) {
- Hui_MIDI_port_t* out = (Hui_MIDI_port_t*)user_data;
- housicIO* IOs = (housicIO*) out->io;
- char value;
- int status;
- // char message[6];
- char out_data;
-
- value = argv[0]->i;
- out_data = value ? 0x40 : 0x00;
- out_data |= out->port;
- unsigned char message[6] = {0xb0,0x0c,out->zone,0xb0,0x2c,out_data};
- status = snd_rawmidi_write(IOs->midiout,message,6);
- //printf("%s - %x %x %x %x %x %x\n",path,
- // message[0],message[1],message[2],message[3],message[4],message[5]);
- return 1;
- }
-
-
- //////////////////////////////
- //
- // midiinfunction -- Thread function which waits around until a MIDI
- // input byte arrives and then react correspondingly
- //
-
- void *midiinfunction(void *arg) {
- housicIO* IOs = (housicIO*)arg;
- snd_rawmidi_t* midiin = IOs->midiin;
- int status;
- int i = 0;
- char buffer[3];
- unsigned char message[3];
-
- while (1) {
- if ((status = snd_rawmidi_read(midiin, buffer, 3)) < 0) {
- alsa_error("Problem reading MIDI input: %s", snd_strerror(status));
- }
-
- // in case of MIDI running status, value bytes need to not override status byte
- for (i=1; i<=status;i++) {
- message[3-i] = (unsigned char) buffer[status-i];
- }
- if (status==2) {
- message[0] = 0xb0;
- } else if(0) { // only for debugging
- printf("%d: %x %x %x - %x %x %x\n", status,
- buffer[0],buffer[1],buffer[2],
- message[0], message[1], message[2]);
- }
-
-
- midi_in_dispatch(IOs,message);
-
- fflush(stdout);
- }
- }
-
-
-
- //////////////////////////////
- //
- // midi_in_dispatch -- Thread function which waits around until a MIDI
- // input byte arrives and then react correspondingly
- //
-
- void midi_in_dispatch(void *arg, unsigned char *m) {
- housicIO* IOs = (housicIO*)arg;
- int fader_value;
-
- if (m[0] == 0xb0) {
- // received a CC message on MIDIchannel 1
- if (m[1] == 0x0f) {
- // received a zone select
- IOs->hui_in_zone = m[2];
- } else if (m[1] == 0x2f) { // switches
- hui_in_button(IOs,m[2]);
- } else if ((m[1] & 0xf0) == 0x40) { // V-pots:
- hui_in_scroll(IOs,m[1] & 0xf, m[2]);
- } else if (m[1] == 0x0d) { // jog wheel:
- hui_in_scroll(IOs,0xd,m[2]);
- } else if (m[1] < 0x08) { // fader Hi
- IOs->hui_in_fader_hi[m[1]] = (int) m[2];
- } else if ((m[1] & 0xf0) == 0x20) { // fader Lo
- fader_value = (IOs->hui_in_fader_hi[m[1] & 0x0f] << 2) | (m[2] >> 5);
- hui_in_fader(IOs,m[1] & 0x0f,fader_value);
- }
- }
-
- }
-
- //////////////////////////////
- //
- // hui_in_fader -- fader positions
- //
- void hui_in_fader(void *arg, unsigned char port, int val) {
- housicIO* IOs = (housicIO*)arg;
- char address[32];
-
- sprintf(address, "/fader/%d", port + 1);
- Item ee = {'f',address};
- osc_send(IOs, ee, val);
- }
-
- //////////////////////////////
- //
- // hui_in_scroll -- user interacted with a button on the HUI
- //
- void hui_in_scroll(void *arg, unsigned char port, unsigned char vv) {
- housicIO* IOs = (housicIO*)arg;
- char address[32];
-
- sprintf(address, "/vpot/%d", port + 1);
- Item ee = {'s', address};
- if (vv > 0x40) {
- osc_send(IOs, ee, vv - 0x40);
- } else if (vv < 0x40) {
- osc_send(IOs, ee, -vv);
- }
- }
-
- //////////////////////////////
- //
- // hui_in_button -- user interacted with a button on the HUI
- //
- void hui_in_button(void *arg, unsigned char m) {
- housicIO* IOs = (housicIO*)arg;
- char address[32];
-
- char zone = IOs->hui_in_zone;
- int dir = m & 0xf0;
- int port = m & 0x0f;
-
- sprintf(address, "/%s/%s",ZONES[zone],BUTTONS[zone][port]);
- Item ee = {'b',address};
- if (dir == 0x40) {
- // down
- osc_send(IOs, ee, 1);
- } else if (dir == 0x00) {
- // up
- osc_send(IOs, ee, 0);
- }
- }
-
-
-
- //////////////////////////////
- //
- // osc_send --
- //
- void osc_send(void *arg, Item el, float value) {
- housicIO* IOs = (housicIO*)arg;
-
- lo_send(IOs->osc_out, el.address, "f", value );
- }
-
- //////////////////////////////
- //
- // osc_in_handler -- receive incoming OSC messages
- //
- int osc_in_handler (const char *path, const char *types, lo_arg ** argv,
- int argc, void *data, void *user_data) {
- int i;
-
- printf("path: <%s>\n", path);
- for (i = 0; i < argc; i++) {
- printf("arg %d '%c' ", i, types[i]);
- lo_arg_pp((lo_type)types[i], argv[i]);
- printf("\n");
- }
- printf("\n");
- fflush(stdout);
-
- return 1;
- }
-
- //////////////////////////////
- //
- // error -- print error message
- //
-
- void alsa_error(const char *format, ...) {
- va_list ap;
- va_start(ap, format);
- vfprintf(stderr, format, ap);
- va_end(ap);
- putc('\n', stderr);
- }
- void lo_error(int num, const char *msg, const char *path)
- {
- printf("liblo server error %d in path %s: %s\n", num, path, msg);
- fflush(stdout);
- }
|