Browse Source

Merge remote-tracking branch 'initial_dev_2020/master'

master
Dom SP 3 years ago
parent
commit
90b66a0a47
4 changed files with 648 additions and 0 deletions
  1. +3
    -0
      .gitignore
  2. +14
    -0
      Makefile
  3. +9
    -0
      README
  4. +622
    -0
      main.c

+ 3
- 0
.gitignore View File

@@ -0,0 +1,3 @@
*~
*.o
housic

+ 14
- 0
Makefile View File

@@ -0,0 +1,14 @@
CC = gcc

exe = housic
src = $(wildcard *.c)
obj = $(src:.c=.o)

LDFLAGS = -lasound -llo -lpthread

$(exe): $(obj)
$(CC) -o $@ $^ $(LDFLAGS)

.PHONY: clean
clean:
rm -f $(obj) $(exe)

+ 9
- 0
README View File

@@ -0,0 +1,9 @@

HoUsIc
======

a small commandline programm to translate between Mackie HUI and Open Sound Control (OSC)

Communicates with a Mackie HUI via ALSA raw_midi driver.

(c) 2020, Dominik Schmidt-Philipp, freakaria.com

+ 622
- 0
main.c View File

@@ -0,0 +1,622 @@
/*
* Programmer: Dominik Schmidt-Philipp <schmidt-philipp@kulturteknologi.no>
* Filename: main.c
*
* Dedicated to seleomlivet
*
*/
#include <alsa/asoundlib.h>
#include <pthread.h> /* for threading */
#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);
}

Loading…
Cancel
Save