Werner's Miscellanea
Sign in or create your account | Project List | Help
Werner's Miscellanea Git Source Tree
Root/
Source at commit a9ee51921bbb2435a8b3df66b38f3029d288f264 created 9 years 11 months ago. By Werner Almesberger, ircstat/ML: update for 2014-03 | |
---|---|
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 | |
24 | static int debug = 0; |
25 | |
26 | |
27 | static 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 | |
35 | static 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 | |
51 | static 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 | |
76 | static 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 | |
94 | static 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_PGMCHANGE: |
129 | if (debug) |
130 | fprintf(stderr, "prog 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; |
135 | msg[3] = 0; |
136 | break; |
137 | case SND_SEQ_EVENT_PITCHBEND: |
138 | if (debug) |
139 | fprintf(stderr, "pitch c%u=%u\n", |
140 | ev->data.control.channel, |
141 | ev->data.control.value); |
142 | msg[1] = 0xe0 | ev->data.control.channel; |
143 | msg[2] = ev->data.control.value & 0x7f; |
144 | msg[3] = ev->data.control.value >> 7; |
145 | break; |
146 | default: |
147 | /* Flickernoise currently doesn't support any others */ |
148 | if (debug) |
149 | fprintf(stderr, "unrecognized MIDI event %u\n", |
150 | ev->type); |
151 | snd_seq_free_event(ev); |
152 | continue; |
153 | } |
154 | if (lo_send(osc, "/midi", "m", msg) < 0) |
155 | fprintf(stderr, "%s\n", lo_address_errstr(osc)); |
156 | snd_seq_free_event(ev); |
157 | } |
158 | } |
159 | |
160 | |
161 | static void usage(const char *name) |
162 | { |
163 | fprintf(stderr, |
164 | "usage: %s [-d] [mapping ...] hostname [port]\n\n" |
165 | " mappings are of the form\n" |
166 | " c[<chan>][.<control>]=c<chan>[.<control>[=<value>]]\n\n" |
167 | " -d debug mode: print all MIDI messages\n", |
168 | name); |
169 | exit(1); |
170 | } |
171 | |
172 | |
173 | int main(int argc, char **argv) |
174 | { |
175 | const char *port = "4444"; /* Milkymist One OSC port */ |
176 | lo_address osc; |
177 | snd_seq_t *midi; |
178 | int c, arg; |
179 | |
180 | while ((c = getopt(argc, argv, "d")) != EOF) |
181 | switch (c) { |
182 | case 'd': |
183 | debug = 1; |
184 | break; |
185 | default: |
186 | usage(*argv); |
187 | } |
188 | |
189 | for (arg = optind; arg != argc; arg++) { |
190 | if (!strchr(argv[arg], '=')) |
191 | break; |
192 | add_mapping(argv[arg]); |
193 | } |
194 | switch (argc-arg) { |
195 | case 1: |
196 | break; |
197 | case 2: |
198 | port = argv[arg+1]; |
199 | break; |
200 | default: |
201 | usage(*argv); |
202 | } |
203 | |
204 | osc = lo_address_new(argv[arg], port); |
205 | if (!osc) { |
206 | fprintf(stderr, "invalid address %s %s\n", argv[arg], port); |
207 | exit(1); |
208 | } |
209 | |
210 | if (snd_seq_open(&midi, "hw", SND_SEQ_OPEN_INPUT,0) < 0) { |
211 | fprintf(stderr, "can't open ALSA sequencer\n"); |
212 | exit(1); |
213 | } |
214 | snd_seq_set_client_name(midi, NAME); |
215 | if (snd_seq_create_simple_port(midi, NAME, |
216 | SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE, |
217 | SND_SEQ_PORT_TYPE_APPLICATION) < 0) { |
218 | fprintf(stderr, "can't create sequencer port\n"); |
219 | exit(1); |
220 | } |
221 | |
222 | forward(midi, osc); |
223 | |
224 | return 0; |
225 | } |
226 |
Branches:
master