/* * x1k2-midi-osc.c * * (c) 2023 Thomas White * * X1K2-midi-osc 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. * * X1K2-midi-osc 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 X1K2-midi-osc. If not, see . * */ #include #include #include #include #include #include struct faderpot { int id; int cc_val; int physical_value; int physical_value_known; int enabled; int congruent; int pickup_value; int has_button_and_led; int button; int led_green; int led_orange; int led_red; const char *type; }; struct encoder { int id; int cc_val; int fine_button; int fine_active; int has_led; int led_green; int led_orange; int led_red; }; struct button { const char *name; int note; int led_green; int led_orange; int led_red; }; struct button buttons[18]; struct encoder encoders[6]; struct faderpot faders[4]; struct faderpot potentiometers[12]; int n_buttons = 18; int n_encoders = 6; int n_faders = 4; int n_potentiometers = 12; snd_rawmidi_t *midi_out; static void send_note_on(snd_rawmidi_t *midi_out, int note) { unsigned char sbuf[3]; ssize_t r; sbuf[0] = 0x9e; sbuf[1] = note; sbuf[2] = 127; r = snd_rawmidi_write(midi_out, sbuf, 3); if ( r != 3 ) { printf("snd_rawmidi_write said %li\n", r); } usleep(1000); } static void send_note_off(snd_rawmidi_t *midi_out, int note) { unsigned char sbuf[3]; ssize_t r; sbuf[0] = 0x8e; sbuf[1] = note; sbuf[2] = 0; r = snd_rawmidi_write(midi_out, sbuf, 3); if ( r != 3 ) { printf("snd_rawmidi_write said %li\n", r); } usleep(1000); } static void init_fader(struct faderpot *fad, int id, int cc_val) { fad->id = id; fad->cc_val = cc_val; fad->physical_value = 0; fad->physical_value_known = 0; fad->pickup_value = 0; fad->has_button_and_led = 0; fad->enabled = 1; fad->type = "faders"; } static void init_potentiometer(struct faderpot *fad, int id, int cc_val, int button) { fad->id = id; fad->cc_val = cc_val; fad->physical_value = 0; fad->physical_value_known = 0; fad->pickup_value = 0; fad->congruent = 0; fad->has_button_and_led = 1; fad->button = button; fad->led_red = button; fad->led_orange = button+36; fad->led_green = button+72; fad->enabled = 0; fad->type = "potentiometers"; } static void faderpot_note_on(struct faderpot *fad) { /* Ignored for now */ } static void faderpot_note_off(struct faderpot *fad) { /* Ignored for now */ } static int in_range(int a, int val1, int val2) { return ((a>=val1) && (a<=val2)) || ((a>=val2) && (a<=val1)); } static void pot_set_led(struct faderpot *fad) { if ( fad->has_button_and_led ) { if ( fad->enabled ) { if ( fad->congruent ) { send_note_on(midi_out, fad->led_green); } else { send_note_on(midi_out, fad->led_orange); } } else { send_note_off(midi_out, fad->led_green); } } } static void faderpot_cc(struct faderpot *fad, int val, lo_address osc_send_addr) { if ( !fad->congruent ) { int inr = in_range(fad->pickup_value, fad->physical_value, val); if ( fad->physical_value_known && inr ) fad->congruent = 1; /* Special case for fader coming from zero, where we didn't * know its previous position. */ if ( (fad->pickup_value == 0) && (val <= 2) ) fad->congruent = 1; } if ( fad->enabled ) { pot_set_led(fad); if ( fad->congruent ) { char tmp[64]; snprintf(tmp, 64, "/x1k2/%s/%i/value-change", fad->type, fad->id); lo_send(osc_send_addr, tmp, "i", val); printf("sending %s = %i\n", tmp, val); } } fad->physical_value = val; fad->physical_value_known = 1; } static int pot_set_pickup_handler(const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *vp) { struct faderpot *fad = vp; fad->pickup_value = argv[0]->i; if ( fad->physical_value_known && (fad->pickup_value == fad->physical_value) ) { fad->congruent = 1; } else { fad->congruent = 0; } pot_set_led(fad); return 1; } static int pot_enable_handler(const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *vp) { struct faderpot *fad = vp; fad->enabled = 1; pot_set_led(fad); return 1; } static int pot_disable_handler(const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *vp) { struct faderpot *fad = vp; fad->enabled = 0; if ( fad->has_button_and_led ) { send_note_off(midi_out, fad->led_green); } return 1; } static void add_faderpot_methods(struct faderpot *fad, lo_server osc_server) { char tmp[256]; snprintf(tmp, 255, "/x1k2/%s/%i/set-pickup", fad->type, fad->id); lo_server_add_method(osc_server, tmp, "i", pot_set_pickup_handler, fad); snprintf(tmp, 255, "/x1k2/%s/%i/enable", fad->type, fad->id); lo_server_add_method(osc_server, tmp, "", pot_enable_handler, fad); snprintf(tmp, 255, "/x1k2/%s/%i/disable", fad->type, fad->id); lo_server_add_method(osc_server, tmp, "", pot_disable_handler, fad); } static void init_encoder_noled(struct encoder *enc, int id, int cc_val, int fine_button) { enc->id = id; enc->cc_val = cc_val; enc->fine_button = fine_button; enc->fine_active = 0; enc->has_led = 0; } static void init_encoder(struct encoder *enc, int id, int cc_val, int fine_button) { enc->id = id; enc->cc_val = cc_val; enc->fine_button = fine_button; enc->fine_active = 0; enc->has_led = 1; enc->led_red = fine_button; enc->led_orange = fine_button+36; enc->led_green = fine_button+72; } static int enc_set_led_handler(const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *vp) { struct encoder *enc = vp; if ( strcmp("red", &argv[0]->s) == 0 ) { send_note_on(midi_out, enc->led_red); } else if ( strcmp("orange", &argv[0]->s) == 0 ) { send_note_on(midi_out, enc->led_orange); } else if ( strcmp("green", &argv[0]->s) == 0 ) { send_note_on(midi_out, enc->led_green); } else if ( strcmp("off", &argv[0]->s) == 0 ) { send_note_off(midi_out, enc->led_red); } else { fprintf(stderr, "Unrecognised LED mode '%s'\n", &argv[0]->s); } return 1; } static void encoder_note_on(struct encoder *enc) { enc->fine_active = 1; } static void encoder_note_off(struct encoder *enc) { enc->fine_active = 0; } static void encoder_cc(struct encoder *enc, int val, lo_address osc_send_addr) { char tmp[256]; const char *v; if ( val == 1 ) { v = "inc"; } else if ( val == 127 ) { v = "dec"; } else { fprintf(stderr, "Invalid encoder value %i\n", val); return; } snprintf(tmp, 255, "/x1k2/encoders/%i/%s%s", enc->id, v, enc->fine_active ? "-fine" : ""); lo_send(osc_send_addr, tmp, ""); } static void add_encoder_methods(struct encoder *enc, lo_server osc_server) { char tmp[256]; snprintf(tmp, 255, "/x1k2/encoders/%i/set-led", enc->id); lo_server_add_method(osc_server, tmp, "s", enc_set_led_handler, enc); } static void init_button_full(struct button *but, const char *name, int note, int r, int o, int g) { but->name = name; but->note = note; but->led_red = r; but->led_orange = o; but->led_green = g; } static void init_button(struct button *but, const char *name, int note) { init_button_full(but, name, note, note, note+36, note+72); } static void button_note_off(struct button *but, lo_address osc_send_addr) { } static void button_note_on(struct button *but, lo_address osc_send_addr) { char tmp[256]; snprintf(tmp, 255, "/x1k2/buttons/%s/press", but->name); lo_send(osc_send_addr, tmp, ""); } static int button_set_led_handler(const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *vp) { struct button *but = vp; if ( strcmp("red", &argv[0]->s) == 0 ) { send_note_on(midi_out, but->led_red); } else if ( strcmp("orange", &argv[0]->s) == 0 ) { send_note_on(midi_out, but->led_orange); } else if ( strcmp("green", &argv[0]->s) == 0 ) { send_note_on(midi_out, but->led_green); } else if ( strcmp("off", &argv[0]->s) == 0 ) { /* Usually, turning off any one of the colours turns off the * LED, regardless of the current colour. However, the bottom * left button's LED is weird, I think because it's * also the "layer" button. It can only be switched off * from the same colour. So, we force it to be red. */ if ( but->led_red == 12 ) { send_note_on(midi_out, but->led_red); } send_note_off(midi_out, but->led_red); } else { fprintf(stderr, "Unrecognised LED mode '%s'\n", &argv[0]->s); } return 1; } static void add_button_methods(struct button *but, lo_server osc_server) { char tmp[256]; snprintf(tmp, 255, "/x1k2/buttons/%s/set-led", but->name); lo_server_add_method(osc_server, tmp, "s", button_set_led_handler, but); } static void show_help(const char *s) { printf("Syntax: %s [-h] [-d /dev/snd/midiXXXX]\n\n", s); printf("MIDI to OSC interface for A&H Xone:K2\n" "\n" " -h, --help Display this help message.\n" " -d, --device MIDI device name.\n"); } static void error_callback(int num, const char *msg, const char *path) { fprintf(stderr, "liblo error %i (%s) for path %s\n", num, msg, path); } static void handle_note_off(int note, int vel, lo_address osc_send_addr) { int i; for ( i=0; i 0 ); memmove(midi_buf, midi_buf+total_proc, 4096-total_proc); midi_buf_pos -= total_proc; } } } } while ( 1 ); snd_rawmidi_drain(midi_in); snd_rawmidi_close(midi_in); snd_rawmidi_drain(midi_out); snd_rawmidi_close(midi_out); return 0; }