Root/midi2osc/midi2osc.c

Source at commit 58d192ba4d8d3f51ec1a31600c3388239e9bf124 created 7 years 5 months ago.
By Werner Almesberger, midi2osc: fix translation of Note On and Pitch; add debug output
1/*
2 * midi2osc.c - MIDI to OSC forwarder
3 *
4 * Written 2011 by Werner Almesberger
5 * Copyright 2011 Werner Almesberger
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 */
12
13
14#include <stdint.h>
15#include <stdlib.h>
16#include <stdio.h>
17#include <alsa/asoundlib.h>
18#include "lo/lo.h"
19
20
21#define NAME "midi2osc"
22
23
24static int debug = 0;
25
26
27static struct map {
28    int chan_in, ctrl_in;
29    int chan_out, ctrl_out;
30    int value;
31    struct map *next;
32} *mappings = NULL;
33
34
35static void add(int chan_in, int ctrl_in, int chan_out, int ctrl_out,
36    int value)
37{
38    struct map *new;
39
40    new = malloc(sizeof(struct map));
41    new->chan_in = chan_in;
42    new->ctrl_in = ctrl_in;
43    new->chan_out = chan_out;
44    new->ctrl_out = ctrl_out;
45    new->value = value;
46    new->next = mappings;
47    mappings = new;
48}
49
50
51static void add_mapping(const char *s)
52{
53    unsigned chan_in, ctrl_in, chan_out, ctrl_out, value;
54
55    if (sscanf(s, "c%u.%u=c%u.%u=%u",
56        &chan_in, &ctrl_in, &chan_out, &ctrl_out, &value) == 5)
57        add(chan_in, ctrl_in, chan_out, ctrl_out, value);
58    else if (sscanf(s, "c%u.%u=c%u.%u",
59        &chan_in, &ctrl_in, &chan_out, &ctrl_out) == 4)
60        add(chan_in, ctrl_in, chan_out, ctrl_out, -1);
61    else if (sscanf(s, "c%u.%u=c%u", &chan_in, &ctrl_in, &chan_out) == 3)
62        add(chan_in, ctrl_in, chan_out, -1, -1);
63    else if (sscanf(s, "c%u=c%u.%u", &chan_in, &chan_out, &ctrl_out) == 3)
64        add(chan_in, -1, chan_out, ctrl_out, -1);
65    else if (sscanf(s, "c.%u=c%u.%u", &ctrl_in, &chan_out, &ctrl_out) == 3)
66        add(-1, ctrl_in, chan_out, ctrl_out, -1);
67    else if (sscanf(s, "c%u=c%u", &chan_in, &chan_out) == 2)
68        add(chan_in, -1, chan_out, -1, -1);
69    else {
70        fprintf(stderr, "unrecognized mapping syntax\n");
71        exit(1);
72    }
73}
74
75
76static void map(uint8_t *chan, uint8_t *ctrl, uint8_t *value)
77{
78    const struct map *m;
79
80    for (m = mappings; m; m = m->next)
81        if ((m->chan_in == -1 || m->chan_in == *chan) &&
82            (m->ctrl_in == -1 || m->ctrl_in == *ctrl)) {
83            if (m->chan_out != -1)
84                *chan = m->chan_out;
85            if (m->ctrl_out != -1)
86                *ctrl = m->ctrl_out;
87            if (m->value != -1)
88                *value = m->value;
89            return;
90        }
91}
92
93
94static void forward(snd_seq_t *midi, lo_address osc)
95{
96    snd_seq_event_t *ev;
97    uint8_t msg[4] = { 0, };
98    uint8_t chan, ctrl, value;
99
100    while (snd_seq_event_input(midi, &ev)) {
101        switch (ev->type) {
102        case SND_SEQ_EVENT_NOTEON:
103            if (debug)
104                fprintf(stderr, "note c%u.%u=%u\n",
105                    ev->data.note.channel,
106                    ev->data.note.note,
107                    ev->data.note.velocity);
108            msg[1] = 0x90 | ev->data.note.channel;
109            msg[2] = ev->data.note.note;
110            msg[3] = ev->data.note.velocity;
111            break;
112        case SND_SEQ_EVENT_CONTROLLER:
113            chan = ev->data.control.channel;
114            ctrl = ev->data.control.param;
115            value = ev->data.control.value;
116            map(&chan, &ctrl, &value);
117            if (debug)
118                fprintf(stderr,
119                    "control c%u.%u=%u -> c%u.%u=%u\n",
120                    ev->data.control.channel,
121                    ev->data.control.param,
122                    ev->data.control.value,
123                    chan, ctrl, value);
124            msg[1] = 0xb0 | chan;
125            msg[2] = ctrl;
126            msg[3] = value;
127            break;
128        case SND_SEQ_EVENT_PITCHBEND:
129            if (debug)
130                fprintf(stderr, "pitch c%u=%u\n",
131                    ev->data.control.channel,
132                    ev->data.control.value);
133            msg[1] = 0xe0 | ev->data.control.channel;
134            msg[2] = ev->data.control.value & 0x7f;
135            msg[3] = ev->data.control.value >> 7;
136            break;
137        default:
138            /* Flickernoise currently doesn't support any others */
139            if (debug)
140                fprintf(stderr, "unrecognized MIDI event\n");
141            snd_seq_free_event(ev);
142            continue;
143        }
144        if (lo_send(osc, "/midi", "m", msg) < 0)
145            fprintf(stderr, "%s\n", lo_address_errstr(osc));
146        snd_seq_free_event(ev);
147    }
148}
149
150
151static void usage(const char *name)
152{
153    fprintf(stderr,
154"usage: %s [-d] [mapping ...] hostname [port]\n\n"
155" mappings are of the form\n"
156" c[<chan>][.<control>]=c<chan>[.<control>[=<value>]]\n\n"
157" -d debug mode: print all MIDI messages\n",
158    name);
159    exit(1);
160}
161
162
163int main(int argc, char **argv)
164{
165    const char *port = "4444"; /* Milkymist One OSC port */
166    lo_address osc;
167    snd_seq_t *midi;
168    int c, arg;
169
170    while ((c = getopt(argc, argv, "d")) != EOF)
171        switch (c) {
172        case 'd':
173            debug = 1;
174            break;
175        default:
176            usage(*argv);
177        }
178
179    for (arg = optind; arg != argc; arg++) {
180        if (!strchr(argv[arg], '='))
181            break;
182        add_mapping(argv[arg]);
183    }
184    switch (argc-arg) {
185    case 1:
186        break;
187    case 2:
188        port = argv[arg+1];
189        break;
190    default:
191        usage(*argv);
192    }
193
194    osc = lo_address_new(argv[arg], port);
195    if (!osc) {
196        fprintf(stderr, "invalid address %s %s\n", argv[arg], port);
197        exit(1);
198    }
199
200    if (snd_seq_open(&midi, "hw", SND_SEQ_OPEN_INPUT,0) < 0) {
201        fprintf(stderr, "can't open ALSA sequencer\n");
202        exit(1);
203    }
204    snd_seq_set_client_name(midi, NAME);
205    if (snd_seq_create_simple_port(midi, NAME,
206              SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE,
207              SND_SEQ_PORT_TYPE_APPLICATION) < 0) {
208        fprintf(stderr, "can't create sequencer port\n");
209        exit(1);
210    }
211
212    forward(midi, osc);
213
214    return 0;
215}
216

Archive Download this file

Branches:
master



interactive