diff --git a/midi2osc/README b/midi2osc/README index 2fa6278..2415c36 100644 --- a/midi2osc/README +++ b/midi2osc/README @@ -19,3 +19,16 @@ To use it with the Milkymist One, - in the "Connections" window, drag a connection from each MIDI device you want to connect to midi2osc - MIDI events will now show up in the MIDI settings dialog, etc. + +midi2osc can also remap MIDI controls. The syntax is + +c[.]=c[.] + +where is a channel number and is the optional control +number. For example, + +./midi2osc c8.1=c0.1 c8.2=c0.2 c9.1=c0.3 c9.2=c0.4 c0.7=c0.5 c7.7=c0.6 m1 + +would map the joysticks and two faders of a Faderfox LV3 to the +controls 1 through 6 on channel 0, and send the OSC messages to +a host called "m1". diff --git a/midi2osc/midi2osc.c b/midi2osc/midi2osc.c index cba961e..5beeff2 100644 --- a/midi2osc/midi2osc.c +++ b/midi2osc/midi2osc.c @@ -24,10 +24,68 @@ static int debug = 0; +static struct map { + int chan_in, ctrl_in; + int chan_out, ctrl_out; + struct map *next; +} *mappings = NULL; + + +static void add(int chan_in, int ctrl_in, int chan_out, int ctrl_out) +{ + struct map *new; + + new = malloc(sizeof(struct map)); + new->chan_in = chan_in; + new->ctrl_in = ctrl_in; + new->chan_out = chan_out; + new->ctrl_out = ctrl_out; + new->next = mappings; + mappings = new; +} + + +static void add_mapping(const char *s) +{ + unsigned chan_in, ctrl_in, chan_out, ctrl_out; + + if (sscanf(s, "c%u.%u=c%u.%u", + &chan_in, &ctrl_in, &chan_out, &ctrl_out) == 4) + add(chan_in, ctrl_in, chan_out, ctrl_out); + else if (sscanf(s, "c%u.%u=c%u", &chan_in, &ctrl_in, &chan_out) == 3) + add(chan_in, ctrl_in, chan_out, -1); + else if (sscanf(s, "c%u=c%u.%u", &chan_in, &chan_out, &ctrl_out) == 3) + add(chan_in, -1, chan_out, ctrl_out); + else if (sscanf(s, "c%u=c%u", &chan_in, &chan_out) == 2) + add(chan_in, -1, chan_out, -1); + else { + fprintf(stderr, "unrecognized mapping syntax\n"); + exit(1); + } +} + + +static void map(uint8_t *chan, uint8_t *ctrl) +{ + const struct map *m; + + for (m = mappings; m; m = m->next) + if ((m->chan_in == -1 || m->chan_in == *chan) && + (m->ctrl_in == -1 || m->ctrl_in == *ctrl)) { + if (m->chan_out != -1) + *chan = m->chan_out; + if (m->ctrl_out != -1) + *ctrl = m->ctrl_out; + return; + } +} + + static void forward(snd_seq_t *midi, lo_address osc) { snd_seq_event_t *ev; uint8_t msg[4] = { 0, }; + uint8_t chan, ctrl; while (snd_seq_event_input(midi, &ev)) { switch (ev->type) { @@ -37,12 +95,17 @@ static void forward(snd_seq_t *midi, lo_address osc) msg[3] = ev->data.note.velocity; break; case SND_SEQ_EVENT_CONTROLLER: - msg[1] = 0xb0 | ev->data.control.channel; - msg[2] = ev->data.control.param; + chan = ev->data.control.channel; + ctrl = ev->data.control.param; + map(&chan, &ctrl); msg[3] = ev->data.control.value; if (debug) - fprintf(stderr, "CC(%u) %u %u\n", - ev->data.control.channel, msg[2], msg[3]); + fprintf(stderr, "c%u.%u(%u) -> c%u.%u\n", + ev->data.control.channel, + ev->data.control.param, msg[3], + chan, ctrl); + msg[1] = 0xb0 | chan; + msg[2] = ctrl; break; case SND_SEQ_EVENT_PITCHBEND: msg[1] = 0xe0 | ev->data.control.channel; @@ -64,7 +127,8 @@ static void forward(snd_seq_t *midi, lo_address osc) static void usage(const char *name) { fprintf(stderr, -"usage: %s hostname [-d] [port]\n\n" +"usage: %s hostname [-d] [mapping ...] [port]\n\n" +" mappings are of the form c[.]=c[.]\n\n" " -d debug mode: print all MIDI messages\n", name); exit(1); @@ -76,8 +140,7 @@ int main(int argc, char **argv) const char *port = "4444"; /* Milkymist One OSC port */ lo_address osc; snd_seq_t *midi; - int c; - + int c, arg; while ((c = getopt(argc, argv, "d")) != EOF) switch (c) { @@ -88,19 +151,24 @@ int main(int argc, char **argv) usage(*argv); } - switch (argc-optind) { + for (arg = optind; arg != argc; arg++) { + if (!strchr(argv[arg], '=')) + break; + add_mapping(argv[arg]); + } + switch (argc-arg) { case 1: break; case 2: - port = argv[optind+1]; + port = argv[arg+1]; break; default: usage(*argv); } - osc = lo_address_new(argv[optind], port); + osc = lo_address_new(argv[arg], port); if (!osc) { - fprintf(stderr, "invalid address %s %s\n", argv[optind], port); + fprintf(stderr, "invalid address %s %s\n", argv[arg], port); exit(1); }