| @@ -1,805 +1,805 @@ | | | @@ -1,805 +1,805 @@ |
1 | /* $NetBSD: midirecord.c,v 1.7 2015/03/01 09:56:54 mrg Exp $ */ | | 1 | /* $NetBSD: midirecord.c,v 1.8 2015/03/04 13:34:49 christos Exp $ */ |
2 | | | 2 | |
3 | /* | | 3 | /* |
4 | * Copyright (c) 2014 Matthew R. Green | | 4 | * Copyright (c) 2014 Matthew R. Green |
5 | * All rights reserved. | | 5 | * All rights reserved. |
6 | * | | 6 | * |
7 | * Redistribution and use in source and binary forms, with or without | | 7 | * Redistribution and use in source and binary forms, with or without |
8 | * modification, are permitted provided that the following conditions | | 8 | * modification, are permitted provided that the following conditions |
9 | * are met: | | 9 | * are met: |
10 | * 1. Redistributions of source code must retain the above copyright | | 10 | * 1. Redistributions of source code must retain the above copyright |
11 | * notice, this list of conditions and the following disclaimer. | | 11 | * notice, this list of conditions and the following disclaimer. |
12 | * 2. Redistributions in binary form must reproduce the above copyright | | 12 | * 2. Redistributions in binary form must reproduce the above copyright |
13 | * notice, this list of conditions and the following disclaimer in the | | 13 | * notice, this list of conditions and the following disclaimer in the |
14 | * documentation and/or other materials provided with the distribution. | | 14 | * documentation and/or other materials provided with the distribution. |
15 | * | | 15 | * |
16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | | 16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
17 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | | 17 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
18 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | | 18 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
19 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | | 19 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
20 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, | | 20 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
21 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | | 21 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED | | 22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
23 | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | | 23 | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
24 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | 24 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
25 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | | 25 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
26 | * SUCH DAMAGE. | | 26 | * SUCH DAMAGE. |
27 | */ | | 27 | */ |
28 | | | 28 | |
29 | /* | | 29 | /* |
30 | * midirecord(1), similar to audiorecord(1). | | 30 | * midirecord(1), similar to audiorecord(1). |
31 | */ | | 31 | */ |
32 | | | 32 | |
33 | #include <sys/cdefs.h> | | 33 | #include <sys/cdefs.h> |
34 | | | 34 | |
35 | #ifndef lint | | 35 | #ifndef lint |
36 | __RCSID("$NetBSD: midirecord.c,v 1.7 2015/03/01 09:56:54 mrg Exp $"); | | 36 | __RCSID("$NetBSD: midirecord.c,v 1.8 2015/03/04 13:34:49 christos Exp $"); |
37 | #endif | | 37 | #endif |
38 | | | 38 | |
39 | #include <sys/param.h> | | 39 | #include <sys/param.h> |
40 | #include <sys/midiio.h> | | 40 | #include <sys/midiio.h> |
41 | #include <sys/ioctl.h> | | 41 | #include <sys/ioctl.h> |
42 | #include <sys/time.h> | | 42 | #include <sys/time.h> |
43 | #include <sys/uio.h> | | 43 | #include <sys/uio.h> |
44 | | | 44 | |
45 | #include <err.h> | | 45 | #include <err.h> |
46 | #include <errno.h> | | 46 | #include <errno.h> |
47 | #include <ctype.h> | | 47 | #include <ctype.h> |
48 | #include <fcntl.h> | | 48 | #include <fcntl.h> |
49 | #include <paths.h> | | 49 | #include <paths.h> |
50 | #include <signal.h> | | 50 | #include <signal.h> |
51 | #include <stdio.h> | | 51 | #include <stdio.h> |
52 | #include <stdlib.h> | | 52 | #include <stdlib.h> |
53 | #include <string.h> | | 53 | #include <string.h> |
54 | #include <unistd.h> | | 54 | #include <unistd.h> |
55 | #include <util.h> | | 55 | #include <util.h> |
56 | #include <assert.h> | | 56 | #include <assert.h> |
57 | #include <stdbool.h> | | 57 | #include <stdbool.h> |
58 | | | 58 | |
59 | #include "libaudio.h" | | 59 | #include "libaudio.h" |
60 | | | 60 | |
61 | static const char *midi_device; | | 61 | static const char *midi_device; |
62 | static unsigned *filt_devnos = NULL; | | 62 | static unsigned *filt_devnos = NULL; |
63 | static unsigned *filt_chans = NULL; | | 63 | static unsigned *filt_chans = NULL; |
64 | static unsigned num_filt_devnos, num_filt_chans; | | 64 | static unsigned num_filt_devnos, num_filt_chans; |
65 | static char *raw_output; | | 65 | static char *raw_output; |
66 | static int midifd; | | 66 | static int midifd; |
67 | static int aflag, qflag, oflag; | | 67 | static int aflag, qflag, oflag; |
68 | static bool debug = false; | | 68 | static bool debug = false; |
69 | int verbose; | | 69 | int verbose; |
70 | static int outfd, rawfd = -1; | | 70 | static int outfd, rawfd = -1; |
71 | static ssize_t data_size; | | 71 | static ssize_t data_size; |
72 | static struct timeval record_time; | | 72 | static struct timeval record_time; |
73 | static struct timeval start_time; | | 73 | static struct timeval start_time; |
74 | static int tempo = 120; | | 74 | static int tempo = 120; |
75 | static unsigned notes_per_beat = 24; | | 75 | static unsigned notes_per_beat = 24; |
76 | static bool ignore_timer_fail = false; | | 76 | static bool ignore_timer_fail = false; |
77 | static bool stdout_mode = false; | | 77 | static bool stdout_mode = false; |
78 | | | 78 | |
79 | static void debug_log(const char *, size_t, const char *, ...) | | 79 | static void debug_log(const char *, size_t, const char *, ...) |
80 | __printflike(3, 4); | | 80 | __printflike(3, 4); |
81 | static size_t midi_event_local_to_output(seq_event_t, u_char *, size_t); | | 81 | static size_t midi_event_local_to_output(seq_event_t, u_char *, size_t); |
82 | static size_t midi_event_timer_wait_abs_to_output(seq_event_t, u_char *, | | 82 | static size_t midi_event_timer_wait_abs_to_output(seq_event_t, u_char *, |
83 | size_t); | | 83 | size_t); |
84 | static size_t midi_event_timer_to_output(seq_event_t, u_char *, size_t); | | 84 | static size_t midi_event_timer_to_output(seq_event_t, u_char *, size_t); |
85 | static size_t midi_event_chn_common_to_output(seq_event_t, u_char *, size_t); | | 85 | static size_t midi_event_chn_common_to_output(seq_event_t, u_char *, size_t); |
86 | static size_t midi_event_chn_voice_to_output(seq_event_t, u_char *, size_t); | | 86 | static size_t midi_event_chn_voice_to_output(seq_event_t, u_char *, size_t); |
87 | static size_t midi_event_sysex_to_output(seq_event_t, u_char *, size_t); | | 87 | static size_t midi_event_sysex_to_output(seq_event_t, u_char *, size_t); |
88 | static size_t midi_event_fullsize_to_output(seq_event_t, u_char *, size_t); | | 88 | static size_t midi_event_fullsize_to_output(seq_event_t, u_char *, size_t); |
89 | static size_t midi_event_to_output(seq_event_t, u_char *, size_t); | | 89 | static size_t midi_event_to_output(seq_event_t, u_char *, size_t); |
90 | static int timeleft(struct timeval *, struct timeval *); | | 90 | static int timeleft(struct timeval *, struct timeval *); |
91 | static bool filter_array(unsigned, unsigned *, size_t); | | 91 | static bool filter_array(unsigned, unsigned *, size_t); |
92 | static bool filter_dev(unsigned); | | 92 | static bool filter_dev(unsigned); |
93 | static bool filter_chan(unsigned); | | 93 | static bool filter_chan(unsigned); |
94 | static bool filter_devchan(unsigned, unsigned); | | 94 | static bool filter_devchan(unsigned, unsigned); |
95 | static void parse_ints(const char *, unsigned **, unsigned *, const char *); | | 95 | static void parse_ints(const char *, unsigned **, unsigned *, const char *); |
96 | static void cleanup(int) __dead; | | 96 | static void cleanup(int) __dead; |
97 | static void rewrite_header(void); | | 97 | static void rewrite_header(void); |
98 | static void write_midi_header(void); | | 98 | static void write_midi_header(void); |
99 | static void write_midi_trailer(void); | | 99 | static void write_midi_trailer(void); |
100 | static void usage(void) __dead; | | 100 | static void usage(void) __dead; |
101 | | | 101 | |
102 | #define PATH_DEV_MUSIC "/dev/music" | | 102 | #define PATH_DEV_MUSIC "/dev/music" |
103 | | | 103 | |
104 | int | | 104 | int |
105 | main(int argc, char *argv[]) | | 105 | main(int argc, char *argv[]) |
106 | { | | 106 | { |
107 | u_char *buffer; | | 107 | u_char *buffer; |
108 | size_t bufsize = 0; | | 108 | size_t bufsize = 0; |
109 | int ch, no_time_limit = 1; | | 109 | int ch, no_time_limit = 1; |
110 | | | 110 | |
111 | while ((ch = getopt(argc, argv, "aB:c:Dd:f:hn:oqr:t:T:V")) != -1) { | | 111 | while ((ch = getopt(argc, argv, "aB:c:Dd:f:hn:oqr:t:T:V")) != -1) { |
112 | switch (ch) { | | 112 | switch (ch) { |
113 | case 'a': | | 113 | case 'a': |
114 | aflag++; | | 114 | aflag++; |
115 | break; | | 115 | break; |
116 | case 'B': | | 116 | case 'B': |
117 | bufsize = strsuftoll("read buffer size", optarg, | | 117 | bufsize = strsuftoll("read buffer size", optarg, |
118 | 1, UINT_MAX); | | 118 | 1, UINT_MAX); |
119 | break; | | 119 | break; |
120 | case 'c': | | 120 | case 'c': |
121 | parse_ints(optarg, &filt_chans, &num_filt_chans, | | 121 | parse_ints(optarg, &filt_chans, &num_filt_chans, |
122 | "channels"); | | 122 | "channels"); |
123 | break; | | 123 | break; |
124 | case 'D': | | 124 | case 'D': |
125 | debug++; | | 125 | debug = true; |
126 | break; | | 126 | break; |
127 | case 'd': | | 127 | case 'd': |
128 | parse_ints(optarg, &filt_devnos, &num_filt_devnos, | | 128 | parse_ints(optarg, &filt_devnos, &num_filt_devnos, |
129 | "devices"); | | 129 | "devices"); |
130 | break; | | 130 | break; |
131 | case 'f': | | 131 | case 'f': |
132 | midi_device = optarg; | | 132 | midi_device = optarg; |
133 | ignore_timer_fail = true; | | 133 | ignore_timer_fail = true; |
134 | break; | | 134 | break; |
135 | case 'n': | | 135 | case 'n': |
136 | decode_uint(optarg, ¬es_per_beat); | | 136 | decode_uint(optarg, ¬es_per_beat); |
137 | break; | | 137 | break; |
138 | case 'o': | | 138 | case 'o': |
139 | oflag++; /* time stamp starts at proc start */ | | 139 | oflag++; /* time stamp starts at proc start */ |
140 | break; | | 140 | break; |
141 | case 'q': | | 141 | case 'q': |
142 | qflag++; | | 142 | qflag++; |
143 | break; | | 143 | break; |
144 | case 'r': | | 144 | case 'r': |
145 | raw_output = optarg; | | 145 | raw_output = optarg; |
146 | break; | | 146 | break; |
147 | case 't': | | 147 | case 't': |
148 | no_time_limit = 0; | | 148 | no_time_limit = 0; |
149 | decode_time(optarg, &record_time); | | 149 | decode_time(optarg, &record_time); |
150 | break; | | 150 | break; |
151 | case 'T': | | 151 | case 'T': |
152 | decode_int(optarg, &tempo); | | 152 | decode_int(optarg, &tempo); |
153 | break; | | 153 | break; |
154 | case 'V': | | 154 | case 'V': |
155 | verbose++; | | 155 | verbose++; |
156 | break; | | 156 | break; |
157 | /* case 'h': */ | | 157 | /* case 'h': */ |
158 | default: | | 158 | default: |
159 | usage(); | | 159 | usage(); |
160 | /* NOTREACHED */ | | 160 | /* NOTREACHED */ |
161 | } | | 161 | } |
162 | } | | 162 | } |
163 | argc -= optind; | | 163 | argc -= optind; |
164 | argv += optind; | | 164 | argv += optind; |
165 | | | 165 | |
166 | if (argc != 1) | | 166 | if (argc != 1) |
167 | usage(); | | 167 | usage(); |
168 | | | 168 | |
169 | /* | | 169 | /* |
170 | * work out the buffer size to use, and allocate it. don't allow it | | 170 | * work out the buffer size to use, and allocate it. don't allow it |
171 | * to be too small. | | 171 | * to be too small. |
172 | */ | | 172 | */ |
173 | if (bufsize < 32) | | 173 | if (bufsize < 32) |
174 | bufsize = 32 * 1024; | | 174 | bufsize = 32 * 1024; |
175 | buffer = malloc(bufsize); | | 175 | buffer = malloc(bufsize); |
176 | if (buffer == NULL) | | 176 | if (buffer == NULL) |
177 | err(1, "couldn't malloc buffer of %d size", (int)bufsize); | | 177 | err(1, "couldn't malloc buffer of %d size", (int)bufsize); |
178 | | | 178 | |
179 | /* | | 179 | /* |
180 | * open the music device | | 180 | * open the music device |
181 | */ | | 181 | */ |
182 | if (midi_device == NULL && (midi_device = getenv("MIDIDEVICE")) == NULL) | | 182 | if (midi_device == NULL && (midi_device = getenv("MIDIDEVICE")) == NULL) |
183 | midi_device = PATH_DEV_MUSIC; | | 183 | midi_device = PATH_DEV_MUSIC; |
184 | midifd = open(midi_device, O_RDONLY); | | 184 | midifd = open(midi_device, O_RDONLY); |
185 | if (midifd < 0) | | 185 | if (midifd < 0) |
186 | err(1, "failed to open %s", midi_device); | | 186 | err(1, "failed to open %s", midi_device); |
187 | | | 187 | |
188 | /* open the output file */ | | 188 | /* open the output file */ |
189 | if (argv[0][0] != '-' || argv[0][1] != '\0') { | | 189 | if (argv[0][0] != '-' || argv[0][1] != '\0') { |
190 | int mode = O_CREAT|(aflag ? O_APPEND : O_TRUNC)|O_WRONLY; | | 190 | int mode = O_CREAT|(aflag ? O_APPEND : O_TRUNC)|O_WRONLY; |
191 | | | 191 | |
192 | outfd = open(*argv, mode, 0666); | | 192 | outfd = open(*argv, mode, 0666); |
193 | if (outfd < 0) | | 193 | if (outfd < 0) |
194 | err(1, "could not open %s", *argv); | | 194 | err(1, "could not open %s", *argv); |
195 | } else { | | 195 | } else { |
196 | stdout_mode = true; | | 196 | stdout_mode = true; |
197 | outfd = STDOUT_FILENO; | | 197 | outfd = STDOUT_FILENO; |
198 | } | | 198 | } |
199 | | | 199 | |
200 | /* open the raw output file */ | | 200 | /* open the raw output file */ |
201 | if (raw_output) { | | 201 | if (raw_output) { |
202 | int mode = O_CREAT|(aflag ? O_APPEND : O_TRUNC)|O_WRONLY; | | 202 | int mode = O_CREAT|(aflag ? O_APPEND : O_TRUNC)|O_WRONLY; |
203 | | | 203 | |
204 | rawfd = open(raw_output, mode, 0666); | | 204 | rawfd = open(raw_output, mode, 0666); |
205 | if (rawfd < 0) | | 205 | if (rawfd < 0) |
206 | err(1, "could not open %s", raw_output); | | 206 | err(1, "could not open %s", raw_output); |
207 | } | | 207 | } |
208 | | | 208 | |
209 | /* start the midi timer */ | | 209 | /* start the midi timer */ |
210 | if (ioctl(midifd, SEQUENCER_TMR_START, NULL) < 0) { | | 210 | if (ioctl(midifd, SEQUENCER_TMR_START, NULL) < 0) { |
211 | if (ignore_timer_fail) | | 211 | if (ignore_timer_fail) |
212 | warn("failed to start midi timer"); | | 212 | warn("failed to start midi timer"); |
213 | else | | 213 | else |
214 | err(1, "failed to start midi timer"); | | 214 | err(1, "failed to start midi timer"); |
215 | } | | 215 | } |
216 | | | 216 | |
217 | /* set the timebase */ | | 217 | /* set the timebase */ |
218 | if (ioctl(midifd, SEQUENCER_TMR_TIMEBASE, ¬es_per_beat) < 0) { | | 218 | if (ioctl(midifd, SEQUENCER_TMR_TIMEBASE, ¬es_per_beat) < 0) { |
219 | if (ignore_timer_fail) | | 219 | if (ignore_timer_fail) |
220 | warn("SEQUENCER_TMR_TIMEBASE: notes_per_beat %d", | | 220 | warn("SEQUENCER_TMR_TIMEBASE: notes_per_beat %d", |
221 | notes_per_beat); | | 221 | notes_per_beat); |
222 | else | | 222 | else |
223 | err(1, "SEQUENCER_TMR_TIMEBASE: notes_per_beat %d", | | 223 | err(1, "SEQUENCER_TMR_TIMEBASE: notes_per_beat %d", |
224 | notes_per_beat); | | 224 | notes_per_beat); |
225 | } | | 225 | } |
226 | | | 226 | |
227 | /* set the tempo */ | | 227 | /* set the tempo */ |
228 | if (ioctl(midifd, SEQUENCER_TMR_TEMPO, &tempo) < 0) { | | 228 | if (ioctl(midifd, SEQUENCER_TMR_TEMPO, &tempo) < 0) { |
229 | if (ignore_timer_fail) | | 229 | if (ignore_timer_fail) |
230 | warn("SEQUENCER_TMR_TIMEBASE: tempo %d", tempo); | | 230 | warn("SEQUENCER_TMR_TIMEBASE: tempo %d", tempo); |
231 | else | | 231 | else |
232 | err(1, "SEQUENCER_TMR_TIMEBASE: tempo %d", tempo); | | 232 | err(1, "SEQUENCER_TMR_TIMEBASE: tempo %d", tempo); |
233 | } | | 233 | } |
234 | | | 234 | |
235 | signal(SIGINT, cleanup); | | 235 | signal(SIGINT, cleanup); |
236 | | | 236 | |
237 | data_size = 0; | | 237 | data_size = 0; |
238 | | | 238 | |
239 | if (verbose) | | 239 | if (verbose) |
240 | fprintf(stderr, "tempo=%d notes_per_beat=%d\n", | | 240 | fprintf(stderr, "tempo=%d notes_per_beat=%u\n", |
241 | tempo, notes_per_beat); | | 241 | tempo, notes_per_beat); |
242 | | | 242 | |
243 | if (!no_time_limit && verbose) | | 243 | if (!no_time_limit && verbose) |
244 | fprintf(stderr, "recording for %lu seconds, %lu microseconds\n", | | 244 | fprintf(stderr, "recording for %lu seconds, %lu microseconds\n", |
245 | (u_long)record_time.tv_sec, (u_long)record_time.tv_usec); | | 245 | (u_long)record_time.tv_sec, (u_long)record_time.tv_usec); |
246 | | | 246 | |
247 | /* Create a midi header. */ | | 247 | /* Create a midi header. */ |
248 | write_midi_header(); | | 248 | write_midi_header(); |
249 | | | 249 | |
250 | (void)gettimeofday(&start_time, NULL); | | 250 | (void)gettimeofday(&start_time, NULL); |
251 | while (no_time_limit || timeleft(&start_time, &record_time)) { | | 251 | while (no_time_limit || timeleft(&start_time, &record_time)) { |
252 | seq_event_t e; | | 252 | seq_event_t e; |
253 | size_t wrsize; | | 253 | size_t wrsize; |
254 | size_t rdsize; | | 254 | size_t rdsize; |
255 | | | 255 | |
256 | rdsize = (size_t)read(midifd, &e, sizeof e); | | 256 | rdsize = (size_t)read(midifd, &e, sizeof e); |
257 | if (rdsize == 0) | | 257 | if (rdsize == 0) |
258 | break; | | 258 | break; |
259 | | | 259 | |
260 | if (rdsize != sizeof e) | | 260 | if (rdsize != sizeof e) |
261 | err(1, "read failed"); | | 261 | err(1, "read failed"); |
262 | | | 262 | |
263 | if (rawfd != -1 && write(rawfd, &e, sizeof e) != sizeof e) | | 263 | if (rawfd != -1 && write(rawfd, &e, sizeof e) != sizeof e) |
264 | err(1, "write to raw file failed"); | | 264 | err(1, "write to raw file failed"); |
265 | | | 265 | |
266 | /* convert 'e' into something useful for output */ | | 266 | /* convert 'e' into something useful for output */ |
267 | wrsize = midi_event_to_output(e, buffer, bufsize); | | 267 | wrsize = midi_event_to_output(e, buffer, bufsize); |
268 | | | 268 | |
269 | if (wrsize) { | | 269 | if (wrsize) { |
270 | if ((size_t)write(outfd, buffer, wrsize) != wrsize) | | 270 | if ((size_t)write(outfd, buffer, wrsize) != wrsize) |
271 | err(1, "write failed"); | | 271 | err(1, "write failed"); |
272 | data_size += wrsize; | | 272 | data_size += wrsize; |
273 | } | | 273 | } |
274 | } | | 274 | } |
275 | cleanup(0); | | 275 | cleanup(0); |
276 | } | | 276 | } |
277 | | | 277 | |
278 | static void | | 278 | static void |
279 | debug_log(const char *file, size_t line, const char *fmt, ...) | | 279 | debug_log(const char *file, size_t line, const char *fmt, ...) |
280 | { | | 280 | { |
281 | va_list ap; | | 281 | va_list ap; |
282 | | | 282 | |
283 | if (!debug) | | 283 | if (!debug) |
284 | return; | | 284 | return; |
285 | fprintf(stderr, "%s:%zd: ", file, line); | | 285 | fprintf(stderr, "%s:%zu: ", file, line); |
286 | va_start(ap, fmt); | | 286 | va_start(ap, fmt); |
287 | vfprintf(stderr, fmt, ap); | | 287 | vfprintf(stderr, fmt, ap); |
288 | va_end(ap); | | 288 | va_end(ap); |
289 | fprintf(stderr, "\n"); | | 289 | fprintf(stderr, "\n"); |
290 | } | | 290 | } |
291 | | | 291 | |
292 | #define LOG(fmt...) \ | | 292 | #define LOG(fmt...) \ |
293 | debug_log(__func__, __LINE__, fmt) | | 293 | debug_log(__func__, __LINE__, fmt) |
294 | | | 294 | |
295 | | | 295 | |
296 | /* | | 296 | /* |
297 | * handle a SEQ_LOCAL event. NetBSD /dev/music doesn't generate these. | | 297 | * handle a SEQ_LOCAL event. NetBSD /dev/music doesn't generate these. |
298 | */ | | 298 | */ |
299 | static size_t | | 299 | static size_t |
300 | midi_event_local_to_output(seq_event_t e, u_char *buffer, size_t bufsize) | | 300 | midi_event_local_to_output(seq_event_t e, u_char *buffer, size_t bufsize) |
301 | { | | 301 | { |
302 | size_t size = 0; | | 302 | size_t size = 0; |
303 | | | 303 | |
304 | LOG("UNHANDLED SEQ_COCAL"); | | 304 | LOG("UNHANDLED SEQ_COCAL"); |
305 | | | 305 | |
306 | return size; | | 306 | return size; |
307 | } | | 307 | } |
308 | | | 308 | |
309 | /* | | 309 | /* |
310 | * convert a midi absolute time event to a variable length delay | | 310 | * convert a midi absolute time event to a variable length delay |
311 | */ | | 311 | */ |
312 | static size_t | | 312 | static size_t |
313 | midi_event_timer_wait_abs_to_output( | | 313 | midi_event_timer_wait_abs_to_output( |
314 | seq_event_t e, | | 314 | seq_event_t e, |
315 | u_char *buffer, | | 315 | u_char *buffer, |
316 | size_t bufsize) | | 316 | size_t bufsize) |
317 | { | | 317 | { |
318 | static unsigned prev_div; | | 318 | static unsigned prev_div; |
319 | unsigned cur_div; | | 319 | unsigned cur_div; |
320 | unsigned val = 0, xdiv; | | 320 | unsigned val = 0, xdiv; |
321 | int vallen = 0, i; | | 321 | int vallen = 0, i; |
322 | | | 322 | |
323 | if (prev_div == 0 && !oflag) | | 323 | if (prev_div == 0 && !oflag) |
324 | prev_div = e.t_WAIT_ABS.divisions; | | 324 | prev_div = e.t_WAIT_ABS.divisions; |
325 | cur_div = e.t_WAIT_ABS.divisions; | | 325 | cur_div = e.t_WAIT_ABS.divisions; |
326 | | | 326 | |
327 | xdiv = cur_div - prev_div; | | 327 | xdiv = cur_div - prev_div; |
328 | if (xdiv) { | | 328 | if (xdiv) { |
329 | while (xdiv) { | | 329 | while (xdiv) { |
330 | uint32_t extra = val ? 0x80 : 0; | | 330 | uint32_t extra = val ? 0x80 : 0; |
331 | | | 331 | |
332 | val <<= 8; | | 332 | val <<= 8; |
333 | val |= (xdiv & 0x7f) | extra; | | 333 | val |= (xdiv & 0x7f) | extra; |
334 | xdiv >>= 7; | | 334 | xdiv >>= 7; |
335 | vallen++; | | 335 | vallen++; |
336 | } | | 336 | } |
337 | } else | | 337 | } else |
338 | vallen = 1; | | 338 | vallen = 1; |
339 | | | 339 | |
340 | for (i = 0; i < vallen; i++) { | | 340 | for (i = 0; i < vallen; i++) { |
341 | buffer[i] = val & 0xff; | | 341 | buffer[i] = val & 0xff; |
342 | val >>= 8; | | 342 | val >>= 8; |
343 | } | | 343 | } |
344 | for (; i < 4; i++) | | 344 | for (; i < 4; i++) |
345 | buffer[i] = 0; | | 345 | buffer[i] = 0; |
346 | LOG("TMR_WAIT_ABS: new div %x (cur %x prev %x): bufval (len=%u): " | | 346 | LOG("TMR_WAIT_ABS: new div %x (cur %x prev %x): bufval (len=%u): " |
347 | "%02x:%02x:%02x:%02x", | | 347 | "%02x:%02x:%02x:%02x", |
348 | cur_div - prev_div, cur_div, prev_div, | | 348 | cur_div - prev_div, cur_div, prev_div, |
349 | vallen, buffer[0], buffer[1], buffer[2], buffer[3]); | | 349 | vallen, buffer[0], buffer[1], buffer[2], buffer[3]); |
350 | | | 350 | |
351 | prev_div = cur_div; | | 351 | prev_div = cur_div; |
352 | | | 352 | |
353 | return vallen; | | 353 | return vallen; |
354 | } | | 354 | } |
355 | | | 355 | |
356 | /* | | 356 | /* |
357 | * handle a SEQ_TIMING event. | | 357 | * handle a SEQ_TIMING event. |
358 | */ | | 358 | */ |
359 | static size_t | | 359 | static size_t |
360 | midi_event_timer_to_output(seq_event_t e, u_char *buffer, size_t bufsize) | | 360 | midi_event_timer_to_output(seq_event_t e, u_char *buffer, size_t bufsize) |
361 | { | | 361 | { |
362 | size_t size = 0; | | 362 | size_t size = 0; |
363 | | | 363 | |
364 | LOG("SEQ_TIMING"); | | 364 | LOG("SEQ_TIMING"); |
365 | switch (e.timing.op) { | | 365 | switch (e.timing.op) { |
366 | case TMR_WAIT_REL: | | 366 | case TMR_WAIT_REL: |
367 | /* NetBSD /dev/music doesn't generate these. */ | | 367 | /* NetBSD /dev/music doesn't generate these. */ |
368 | LOG("UNHANDLED TMR_WAIT_REL: divisions: %x", e.t_WAIT_REL.divisions); | | 368 | LOG("UNHANDLED TMR_WAIT_REL: divisions: %x", e.t_WAIT_REL.divisions); |
369 | break; | | 369 | break; |
370 | | | 370 | |
371 | case TMR_WAIT_ABS: | | 371 | case TMR_WAIT_ABS: |
372 | size = midi_event_timer_wait_abs_to_output(e, buffer, bufsize); | | 372 | size = midi_event_timer_wait_abs_to_output(e, buffer, bufsize); |
373 | break; | | 373 | break; |
374 | | | 374 | |
375 | case TMR_STOP: | | 375 | case TMR_STOP: |
376 | case TMR_START: | | 376 | case TMR_START: |
377 | case TMR_CONTINUE: | | 377 | case TMR_CONTINUE: |
378 | case TMR_TEMPO: | | 378 | case TMR_TEMPO: |
379 | case TMR_ECHO: | | 379 | case TMR_ECHO: |
380 | case TMR_CLOCK: | | 380 | case TMR_CLOCK: |
381 | case TMR_SPP: | | 381 | case TMR_SPP: |
382 | case TMR_TIMESIG: | | 382 | case TMR_TIMESIG: |
383 | /* NetBSD /dev/music doesn't generate these. */ | | 383 | /* NetBSD /dev/music doesn't generate these. */ |
384 | LOG("UNHANDLED timer op: %x", e.timing.op); | | 384 | LOG("UNHANDLED timer op: %x", e.timing.op); |
385 | break; | | 385 | break; |
386 | | | 386 | |
387 | default: | | 387 | default: |
388 | LOG("unknown timer op: %x", e.timing.op); | | 388 | LOG("unknown timer op: %x", e.timing.op); |
389 | break; | | 389 | break; |
390 | } | | 390 | } |
391 | | | 391 | |
392 | return size; | | 392 | return size; |
393 | } | | 393 | } |
394 | | | 394 | |
395 | /* | | 395 | /* |
396 | * handle a SEQ_CHN_COMMON event. | | 396 | * handle a SEQ_CHN_COMMON event. |
397 | */ | | 397 | */ |
398 | static size_t | | 398 | static size_t |
399 | midi_event_chn_common_to_output(seq_event_t e, u_char *buffer, size_t bufsize) | | 399 | midi_event_chn_common_to_output(seq_event_t e, u_char *buffer, size_t bufsize) |
400 | { | | 400 | { |
401 | size_t size = 0; | | 401 | size_t size = 0; |
402 | | | 402 | |
403 | assert(e.common.channel < 16); | | 403 | assert(e.common.channel < 16); |
404 | LOG("SEQ_CHN_COMMON"); | | 404 | LOG("SEQ_CHN_COMMON"); |
405 | | | 405 | |
406 | if (filter_devchan(e.common.device, e.common.channel)) | | 406 | if (filter_devchan(e.common.device, e.common.channel)) |
407 | return 0; | | 407 | return 0; |
408 | | | 408 | |
409 | switch (e.common.op) { | | 409 | switch (e.common.op) { |
410 | case MIDI_CTL_CHANGE: | | 410 | case MIDI_CTL_CHANGE: |
411 | buffer[0] = MIDI_CTL_CHANGE | e.c_CTL_CHANGE.channel; | | 411 | buffer[0] = MIDI_CTL_CHANGE | e.c_CTL_CHANGE.channel; |
412 | buffer[1] = e.c_CTL_CHANGE.controller; | | 412 | buffer[1] = e.c_CTL_CHANGE.controller; |
413 | buffer[2] = e.c_CTL_CHANGE.value; | | 413 | buffer[2] = e.c_CTL_CHANGE.value; |
414 | LOG("MIDI_CTL_CHANGE: channel %x ctrl %x val %x", | | 414 | LOG("MIDI_CTL_CHANGE: channel %x ctrl %x val %x", |
415 | e.c_CTL_CHANGE.channel, e.c_CTL_CHANGE.controller, | | 415 | e.c_CTL_CHANGE.channel, e.c_CTL_CHANGE.controller, |
416 | e.c_CTL_CHANGE.value); | | 416 | e.c_CTL_CHANGE.value); |
417 | size = 3; | | 417 | size = 3; |
418 | break; | | 418 | break; |
419 | | | 419 | |
420 | case MIDI_PGM_CHANGE: | | 420 | case MIDI_PGM_CHANGE: |
421 | buffer[0] = MIDI_PGM_CHANGE | e.c_PGM_CHANGE.channel; | | 421 | buffer[0] = MIDI_PGM_CHANGE | e.c_PGM_CHANGE.channel; |
422 | buffer[1] = e.c_PGM_CHANGE.program; | | 422 | buffer[1] = e.c_PGM_CHANGE.program; |
423 | LOG("MIDI_PGM_CHANGE: channel %x program %x", | | 423 | LOG("MIDI_PGM_CHANGE: channel %x program %x", |
424 | e.c_PGM_CHANGE.channel, e.c_PGM_CHANGE.program); | | 424 | e.c_PGM_CHANGE.channel, e.c_PGM_CHANGE.program); |
425 | size = 2; | | 425 | size = 2; |
426 | break; | | 426 | break; |
427 | | | 427 | |
428 | case MIDI_CHN_PRESSURE: | | 428 | case MIDI_CHN_PRESSURE: |
429 | buffer[0] = MIDI_CHN_PRESSURE | e.c_CHN_PRESSURE.channel; | | 429 | buffer[0] = MIDI_CHN_PRESSURE | e.c_CHN_PRESSURE.channel; |
430 | buffer[1] = e.c_CHN_PRESSURE.pressure; | | 430 | buffer[1] = e.c_CHN_PRESSURE.pressure; |
431 | LOG("MIDI_CHN_PRESSURE: channel %x pressure %x", | | 431 | LOG("MIDI_CHN_PRESSURE: channel %x pressure %x", |
432 | e.c_CHN_PRESSURE.channel, e.c_CHN_PRESSURE.pressure); | | 432 | e.c_CHN_PRESSURE.channel, e.c_CHN_PRESSURE.pressure); |
433 | size = 2; | | 433 | size = 2; |
434 | break; | | 434 | break; |
435 | | | 435 | |
436 | case MIDI_PITCH_BEND: | | 436 | case MIDI_PITCH_BEND: |
437 | buffer[0] = MIDI_PITCH_BEND | e.c_PITCH_BEND.channel; | | 437 | buffer[0] = MIDI_PITCH_BEND | e.c_PITCH_BEND.channel; |
438 | /* 14 bits split over 2 data bytes, lsb first */ | | 438 | /* 14 bits split over 2 data bytes, lsb first */ |
439 | buffer[1] = e.c_PITCH_BEND.value & 0x7f; | | 439 | buffer[1] = e.c_PITCH_BEND.value & 0x7f; |
440 | buffer[2] = (e.c_PITCH_BEND.value >> 7) & 0x7f; | | 440 | buffer[2] = (e.c_PITCH_BEND.value >> 7) & 0x7f; |
441 | LOG("MIDI_PITCH_BEND: channel %x val %x", | | 441 | LOG("MIDI_PITCH_BEND: channel %x val %x", |
442 | e.c_PITCH_BEND.channel, e.c_PITCH_BEND.value); | | 442 | e.c_PITCH_BEND.channel, e.c_PITCH_BEND.value); |
443 | size = 3; | | 443 | size = 3; |
444 | break; | | 444 | break; |
445 | | | 445 | |
446 | default: | | 446 | default: |
447 | LOG("unknown common op: %x", e.voice.op); | | 447 | LOG("unknown common op: %x", e.voice.op); |
448 | break; | | 448 | break; |
449 | } | | 449 | } |
450 | | | 450 | |
451 | return size; | | 451 | return size; |
452 | } | | 452 | } |
453 | | | 453 | |
454 | /* | | 454 | /* |
455 | * handle a SEQ_CHN_VOICE event. | | 455 | * handle a SEQ_CHN_VOICE event. |
456 | */ | | 456 | */ |
457 | static size_t | | 457 | static size_t |
458 | midi_event_chn_voice_to_output(seq_event_t e, u_char *buffer, size_t bufsize) | | 458 | midi_event_chn_voice_to_output(seq_event_t e, u_char *buffer, size_t bufsize) |
459 | { | | 459 | { |
460 | size_t size = 0; | | 460 | size_t size = 0; |
461 | | | 461 | |
462 | assert(e.common.channel < 16); | | 462 | assert(e.common.channel < 16); |
463 | LOG("SEQ_CHN_VOICE"); | | 463 | LOG("SEQ_CHN_VOICE"); |
464 | | | 464 | |
465 | if (filter_devchan(e.voice.device, e.voice.channel)) | | 465 | if (filter_devchan(e.voice.device, e.voice.channel)) |
466 | return 0; | | 466 | return 0; |
467 | | | 467 | |
468 | switch (e.voice.op) { | | 468 | switch (e.voice.op) { |
469 | case MIDI_NOTEOFF: | | 469 | case MIDI_NOTEOFF: |
470 | buffer[0] = MIDI_NOTEOFF | e.c_NOTEOFF.channel; | | 470 | buffer[0] = MIDI_NOTEOFF | e.c_NOTEOFF.channel; |
471 | buffer[1] = e.c_NOTEOFF.key; | | 471 | buffer[1] = e.c_NOTEOFF.key; |
472 | buffer[2] = e.c_NOTEOFF.velocity; | | 472 | buffer[2] = e.c_NOTEOFF.velocity; |
473 | | | 473 | |
474 | LOG("MIDI_NOTEOFF: channel %x key %x velocity %x", | | 474 | LOG("MIDI_NOTEOFF: channel %x key %x velocity %x", |
475 | e.c_NOTEOFF.channel, e.c_NOTEOFF.key, e.c_NOTEOFF.velocity); | | 475 | e.c_NOTEOFF.channel, e.c_NOTEOFF.key, e.c_NOTEOFF.velocity); |
476 | size = 3; | | 476 | size = 3; |
477 | break; | | 477 | break; |
478 | | | 478 | |
479 | case MIDI_NOTEON: | | 479 | case MIDI_NOTEON: |
480 | buffer[0] = MIDI_NOTEON | e.c_NOTEON.channel; | | 480 | buffer[0] = MIDI_NOTEON | e.c_NOTEON.channel; |
481 | buffer[1] = e.c_NOTEON.key; | | 481 | buffer[1] = e.c_NOTEON.key; |
482 | buffer[2] = e.c_NOTEON.velocity; | | 482 | buffer[2] = e.c_NOTEON.velocity; |
483 | | | 483 | |
484 | LOG("MIDI_NOTEON: channel %x key %x velocity %x", | | 484 | LOG("MIDI_NOTEON: channel %x key %x velocity %x", |
485 | e.c_NOTEON.channel, e.c_NOTEON.key, e.c_NOTEON.velocity); | | 485 | e.c_NOTEON.channel, e.c_NOTEON.key, e.c_NOTEON.velocity); |
486 | size = 3; | | 486 | size = 3; |
487 | break; | | 487 | break; |
488 | | | 488 | |
489 | case MIDI_KEY_PRESSURE: | | 489 | case MIDI_KEY_PRESSURE: |
490 | buffer[0] = MIDI_KEY_PRESSURE | e.c_KEY_PRESSURE.channel; | | 490 | buffer[0] = MIDI_KEY_PRESSURE | e.c_KEY_PRESSURE.channel; |
491 | buffer[1] = e.c_KEY_PRESSURE.key; | | 491 | buffer[1] = e.c_KEY_PRESSURE.key; |
492 | buffer[2] = e.c_KEY_PRESSURE.pressure; | | 492 | buffer[2] = e.c_KEY_PRESSURE.pressure; |
493 | | | 493 | |
494 | LOG("MIDI_KEY_PRESSURE: channel %x key %x pressure %x", | | 494 | LOG("MIDI_KEY_PRESSURE: channel %x key %x pressure %x", |
495 | e.c_KEY_PRESSURE.channel, e.c_KEY_PRESSURE.key, | | 495 | e.c_KEY_PRESSURE.channel, e.c_KEY_PRESSURE.key, |
496 | e.c_KEY_PRESSURE.pressure); | | 496 | e.c_KEY_PRESSURE.pressure); |
497 | size = 3; | | 497 | size = 3; |
498 | break; | | 498 | break; |
499 | | | 499 | |
500 | default: | | 500 | default: |
501 | LOG("unknown voice op: %x", e.voice.op); | | 501 | LOG("unknown voice op: %x", e.voice.op); |
502 | break; | | 502 | break; |
503 | } | | 503 | } |
504 | | | 504 | |
505 | return size; | | 505 | return size; |
506 | } | | 506 | } |
507 | | | 507 | |
508 | /* | | 508 | /* |
509 | * handle a SEQ_SYSEX event. NetBSD /dev/music doesn't generate these. | | 509 | * handle a SEQ_SYSEX event. NetBSD /dev/music doesn't generate these. |
510 | */ | | 510 | */ |
511 | static size_t | | 511 | static size_t |
512 | midi_event_sysex_to_output(seq_event_t e, u_char *buffer, size_t bufsize) | | 512 | midi_event_sysex_to_output(seq_event_t e, u_char *buffer, size_t bufsize) |
513 | { | | 513 | { |
514 | size_t size = 0; | | 514 | size_t size = 0; |
515 | | | 515 | |
516 | LOG("UNHANDLED SEQ_SYSEX"); | | 516 | LOG("UNHANDLED SEQ_SYSEX"); |
517 | | | 517 | |
518 | return size; | | 518 | return size; |
519 | } | | 519 | } |
520 | | | 520 | |
521 | /* | | 521 | /* |
522 | * handle a SEQ_FULLSIZE event. NetBSD /dev/music doesn't generate these. | | 522 | * handle a SEQ_FULLSIZE event. NetBSD /dev/music doesn't generate these. |
523 | */ | | 523 | */ |
524 | static size_t | | 524 | static size_t |
525 | midi_event_fullsize_to_output(seq_event_t e, u_char *buffer, size_t bufsize) | | 525 | midi_event_fullsize_to_output(seq_event_t e, u_char *buffer, size_t bufsize) |
526 | { | | 526 | { |
527 | size_t size = 0; | | 527 | size_t size = 0; |
528 | | | 528 | |
529 | LOG("UNHANDLED SEQ_FULLSIZE"); | | 529 | LOG("UNHANDLED SEQ_FULLSIZE"); |
530 | | | 530 | |
531 | return size; | | 531 | return size; |
532 | } | | 532 | } |
533 | | | 533 | |
534 | /* | | 534 | /* |
535 | * main handler for MIDI events. | | 535 | * main handler for MIDI events. |
536 | */ | | 536 | */ |
537 | static size_t | | 537 | static size_t |
538 | midi_event_to_output(seq_event_t e, u_char *buffer, size_t bufsize) | | 538 | midi_event_to_output(seq_event_t e, u_char *buffer, size_t bufsize) |
539 | { | | 539 | { |
540 | size_t size = 0; | | 540 | size_t size = 0; |
541 | | | 541 | |
542 | /* XXX so far we only process 4 byte returns */ | | 542 | /* XXX so far we only process 4 byte returns */ |
543 | assert(bufsize >= 4); | | 543 | assert(bufsize >= 4); |
544 | | | 544 | |
545 | LOG("event: %02x:%02x:%02x:%02x %02x:%02x:%02x:%02x", e.tag, | | 545 | LOG("event: %02x:%02x:%02x:%02x %02x:%02x:%02x:%02x", e.tag, |
546 | e.unknown.byte[0], e.unknown.byte[1], | | 546 | e.unknown.byte[0], e.unknown.byte[1], |
547 | e.unknown.byte[2], e.unknown.byte[3], | | 547 | e.unknown.byte[2], e.unknown.byte[3], |
548 | e.unknown.byte[4], e.unknown.byte[5], | | 548 | e.unknown.byte[4], e.unknown.byte[5], |
549 | e.unknown.byte[6]); | | 549 | e.unknown.byte[6]); |
550 | | | 550 | |
551 | switch (e.tag) { | | 551 | switch (e.tag) { |
552 | case SEQ_LOCAL: | | 552 | case SEQ_LOCAL: |
553 | size = midi_event_local_to_output(e, buffer, bufsize); | | 553 | size = midi_event_local_to_output(e, buffer, bufsize); |
554 | break; | | 554 | break; |
555 | | | 555 | |
556 | case SEQ_TIMING: | | 556 | case SEQ_TIMING: |
557 | size = midi_event_timer_to_output(e, buffer, bufsize); | | 557 | size = midi_event_timer_to_output(e, buffer, bufsize); |
558 | break; | | 558 | break; |
559 | | | 559 | |
560 | case SEQ_CHN_COMMON: | | 560 | case SEQ_CHN_COMMON: |
561 | size = midi_event_chn_common_to_output(e, buffer, bufsize); | | 561 | size = midi_event_chn_common_to_output(e, buffer, bufsize); |
562 | break; | | 562 | break; |
563 | | | 563 | |
564 | case SEQ_CHN_VOICE: | | 564 | case SEQ_CHN_VOICE: |
565 | size = midi_event_chn_voice_to_output(e, buffer, bufsize); | | 565 | size = midi_event_chn_voice_to_output(e, buffer, bufsize); |
566 | break; | | 566 | break; |
567 | | | 567 | |
568 | case SEQ_SYSEX: | | 568 | case SEQ_SYSEX: |
569 | size = midi_event_sysex_to_output(e, buffer, bufsize); | | 569 | size = midi_event_sysex_to_output(e, buffer, bufsize); |
570 | break; | | 570 | break; |
571 | | | 571 | |
572 | case SEQ_FULLSIZE: | | 572 | case SEQ_FULLSIZE: |
573 | size = midi_event_fullsize_to_output(e, buffer, bufsize); | | 573 | size = midi_event_fullsize_to_output(e, buffer, bufsize); |
574 | break; | | 574 | break; |
575 | | | 575 | |
576 | default: | | 576 | default: |
577 | errx(1, "don't understand midi tag %x", e.tag); | | 577 | errx(1, "don't understand midi tag %x", e.tag); |
578 | } | | 578 | } |
579 | | | 579 | |
580 | return size; | | 580 | return size; |
581 | } | | 581 | } |
582 | | | 582 | |
583 | static bool | | 583 | static bool |
584 | filter_array(unsigned val, unsigned *array, size_t arraylen) | | 584 | filter_array(unsigned val, unsigned *array, size_t arraylen) |
585 | { | | 585 | { |
586 | | | 586 | |
587 | if (array == NULL) | | 587 | if (array == NULL) |
588 | return false; | | 588 | return false; |
589 | | | 589 | |
590 | for (; arraylen; arraylen--) | | 590 | for (; arraylen; arraylen--) |
591 | if (array[arraylen - 1] == val) | | 591 | if (array[arraylen - 1] == val) |
592 | return false; | | 592 | return false; |
593 | | | 593 | |
594 | return true; | | 594 | return true; |
595 | } | | 595 | } |
596 | | | 596 | |
597 | static bool | | 597 | static bool |
598 | filter_dev(unsigned device) | | 598 | filter_dev(unsigned device) |
599 | { | | 599 | { |
600 | | | 600 | |
601 | if (filter_array(device, filt_devnos, num_filt_devnos)) | | 601 | if (filter_array(device, filt_devnos, num_filt_devnos)) |
602 | return true; | | 602 | return true; |
603 | | | 603 | |
604 | return false; | | 604 | return false; |
605 | } | | 605 | } |
606 | | | 606 | |
607 | static bool | | 607 | static bool |
608 | filter_chan(unsigned channel) | | 608 | filter_chan(unsigned channel) |
609 | { | | 609 | { |
610 | | | 610 | |
611 | if (filter_array(channel, filt_chans, num_filt_chans)) | | 611 | if (filter_array(channel, filt_chans, num_filt_chans)) |
612 | return true; | | 612 | return true; |
613 | | | 613 | |
614 | return false; | | 614 | return false; |
615 | } | | 615 | } |
616 | | | 616 | |
617 | static bool | | 617 | static bool |
618 | filter_devchan(unsigned device, unsigned channel) | | 618 | filter_devchan(unsigned device, unsigned channel) |
619 | { | | 619 | { |
620 | | | 620 | |
621 | if (filter_dev(device) || filter_chan(channel)) | | 621 | if (filter_dev(device) || filter_chan(channel)) |
622 | return true; | | 622 | return true; |
623 | | | 623 | |
624 | return false; | | 624 | return false; |
625 | } | | 625 | } |
626 | | | 626 | |
627 | static int | | 627 | static int |
628 | timeleft(struct timeval *start_tvp, struct timeval *record_tvp) | | 628 | timeleft(struct timeval *start_tvp, struct timeval *record_tvp) |
629 | { | | 629 | { |
630 | struct timeval now, diff; | | 630 | struct timeval now, diff; |
631 | | | 631 | |
632 | (void)gettimeofday(&now, NULL); | | 632 | (void)gettimeofday(&now, NULL); |
633 | timersub(&now, start_tvp, &diff); | | 633 | timersub(&now, start_tvp, &diff); |
634 | timersub(record_tvp, &diff, &now); | | 634 | timersub(record_tvp, &diff, &now); |
635 | | | 635 | |
636 | return (now.tv_sec > 0 || (now.tv_sec == 0 && now.tv_usec > 0)); | | 636 | return (now.tv_sec > 0 || (now.tv_sec == 0 && now.tv_usec > 0)); |
637 | } | | 637 | } |
638 | | | 638 | |
639 | static void | | 639 | static void |
640 | parse_ints(const char *str, unsigned **arrayp, unsigned *sizep, const char *msg) | | 640 | parse_ints(const char *str, unsigned **arrayp, unsigned *sizep, const char *msg) |
641 | { | | 641 | { |
642 | unsigned count = 1, u, longest = 0, c = 0; | | 642 | unsigned count = 1, u, longest = 0, c = 0; |
643 | unsigned *ip; | | 643 | unsigned *ip; |
644 | const char *s, *os; | | 644 | const char *s, *os; |
645 | char *num_buf; | | 645 | char *num_buf; |
646 | | | 646 | |
647 | /* | | 647 | /* |
648 | * count all the comma separated values, and figre out | | 648 | * count all the comma separated values, and figre out |
649 | * the longest one. | | 649 | * the longest one. |
650 | */ | | 650 | */ |
651 | for (s = str; *s; s++) { | | 651 | for (s = str; *s; s++) { |
652 | c++; | | 652 | c++; |
653 | if (*s == ',') { | | 653 | if (*s == ',') { |
654 | count++; | | 654 | count++; |
655 | if (c > longest) | | 655 | if (c > longest) |
656 | longest = c; | | 656 | longest = c; |
657 | c = 0; | | 657 | c = 0; |
658 | } | | 658 | } |
659 | } | | 659 | } |
660 | *sizep = count; | | 660 | *sizep = count; |
661 | | | 661 | |
662 | num_buf = malloc(longest + 1); | | 662 | num_buf = malloc(longest + 1); |
663 | ip = malloc(sizeof(*ip) * count); | | 663 | ip = malloc(sizeof(*ip) * count); |
664 | if (!ip || !num_buf) | | 664 | if (!ip || !num_buf) |
665 | errx(1, "malloc failed"); | | 665 | errx(1, "malloc failed"); |
666 | | | 666 | |
667 | for (count = 0, s = os = str, u = 0; *s; s++) { | | 667 | for (count = 0, s = os = str, u = 0; *s; s++) { |
668 | if (*s == ',') { | | 668 | if (*s == ',') { |
669 | num_buf[u] = '\0'; | | 669 | num_buf[u] = '\0'; |
670 | decode_uint(num_buf, &ip[count++]); | | 670 | decode_uint(num_buf, &ip[count++]); |
671 | os = s + 1; | | 671 | os = s + 1; |
672 | u = 0; | | 672 | u = 0; |
673 | } else | | 673 | } else |
674 | num_buf[u++] = *s; | | 674 | num_buf[u++] = *s; |
675 | } | | 675 | } |
676 | num_buf[u] = '\0'; | | 676 | num_buf[u] = '\0'; |
677 | decode_uint(num_buf, &ip[count++]); | | 677 | decode_uint(num_buf, &ip[count++]); |
678 | *arrayp = ip; | | 678 | *arrayp = ip; |
679 | | | 679 | |
680 | if (verbose) { | | 680 | if (verbose) { |
681 | fprintf(stderr, "Filtering %s in:", msg); | | 681 | fprintf(stderr, "Filtering %s in:", msg); |
682 | for (size_t i = 0; i < *sizep; i++) | | 682 | for (size_t i = 0; i < *sizep; i++) |
683 | fprintf(stderr, " %u", ip[i]); | | 683 | fprintf(stderr, " %u", ip[i]); |
684 | fprintf(stderr, "\n"); | | 684 | fprintf(stderr, "\n"); |
685 | } | | 685 | } |
686 | | | 686 | |
687 | free(num_buf); | | 687 | free(num_buf); |
688 | } | | 688 | } |
689 | | | 689 | |
690 | static void | | 690 | static void |
691 | cleanup(int signo) | | 691 | cleanup(int signo) |
692 | { | | 692 | { |
693 | | | 693 | |
694 | write_midi_trailer(); | | 694 | write_midi_trailer(); |
695 | rewrite_header(); | | 695 | rewrite_header(); |
696 | | | 696 | |
697 | if (ioctl(midifd, SEQUENCER_TMR_STOP, NULL) < 0) { | | 697 | if (ioctl(midifd, SEQUENCER_TMR_STOP, NULL) < 0) { |
698 | if (ignore_timer_fail) | | 698 | if (ignore_timer_fail) |
699 | warn("failed to stop midi timer"); | | 699 | warn("failed to stop midi timer"); |
700 | else | | 700 | else |
701 | err(1, "failed to stop midi timer"); | | 701 | err(1, "failed to stop midi timer"); |
702 | } | | 702 | } |
703 | | | 703 | |
704 | close(outfd); | | 704 | close(outfd); |
705 | close(midifd); | | 705 | close(midifd); |
706 | if (signo != 0) | | 706 | if (signo != 0) |
707 | (void)raise_default_signal(signo); | | 707 | (void)raise_default_signal(signo); |
708 | | | 708 | |
709 | exit(0); | | 709 | exit(0); |
710 | } | | 710 | } |
711 | | | 711 | |
712 | static void | | 712 | static void |
713 | rewrite_header(void) | | 713 | rewrite_header(void) |
714 | { | | 714 | { |
715 | | | 715 | |
716 | /* can't do this here! */ | | 716 | /* can't do this here! */ |
717 | if (stdout_mode) | | 717 | if (stdout_mode) |
718 | return; | | 718 | return; |
719 | | | 719 | |
720 | if (lseek(outfd, (off_t)0, SEEK_SET) == (off_t)-1) | | 720 | if (lseek(outfd, (off_t)0, SEEK_SET) == (off_t)-1) |
721 | err(1, "could not seek to start of file for header rewrite"); | | 721 | err(1, "could not seek to start of file for header rewrite"); |
722 | write_midi_header(); | | 722 | write_midi_header(); |
723 | } | | 723 | } |
724 | | | 724 | |
725 | #define BYTE1(x) ((x) & 0xff) | | 725 | #define BYTE1(x) ((x) & 0xff) |
726 | #define BYTE2(x) (((x) >> 8) & 0xff) | | 726 | #define BYTE2(x) (((x) >> 8) & 0xff) |
727 | #define BYTE3(x) (((x) >> 16) & 0xff) | | 727 | #define BYTE3(x) (((x) >> 16) & 0xff) |
728 | #define BYTE4(x) (((x) >> 24) & 0xff) | | 728 | #define BYTE4(x) (((x) >> 24) & 0xff) |
729 | | | 729 | |
730 | static void | | 730 | static void |
731 | write_midi_header(void) | | 731 | write_midi_header(void) |
732 | { | | 732 | { |
733 | unsigned char header[] = { | | 733 | unsigned char header[] = { |
734 | 'M', 'T', 'h', 'd', | | 734 | 'M', 'T', 'h', 'd', |
735 | 0, 0, 0, 6, | | 735 | 0, 0, 0, 6, |
736 | 0, 1, | | 736 | 0, 1, |
737 | 0, 0, /* ntracks */ | | 737 | 0, 0, /* ntracks */ |
738 | 0, 0, /* notes per beat */ | | 738 | 0, 0, /* notes per beat */ |
739 | }; | | 739 | }; |
740 | /* XXX only support one track so far */ | | 740 | /* XXX only support one track so far */ |
741 | unsigned ntracks = 1; | | 741 | unsigned ntracks = 1; |
742 | unsigned char track[] = { | | 742 | unsigned char track[] = { |
743 | 'M', 'T', 'r', 'k', | | 743 | 'M', 'T', 'r', 'k', |
744 | 0, 0, 0, 0, | | 744 | 0, 0, 0, 0, |
745 | }; | | 745 | }; |
746 | unsigned char bpm[] = { | | 746 | unsigned char bpm[] = { |
747 | 0, 0xff, 0x51, 0x3, | | 747 | 0, 0xff, 0x51, 0x3, |
748 | 0, 0, 0, /* inverse tempo */ | | 748 | 0, 0, 0, /* inverse tempo */ |
749 | }; | | 749 | }; |
750 | unsigned total_size = data_size + sizeof header + sizeof track + sizeof bpm; | | 750 | unsigned total_size = data_size + sizeof header + sizeof track + sizeof bpm; |
751 | | | 751 | |
752 | header[10] = BYTE2(ntracks); | | 752 | header[10] = BYTE2(ntracks); |
753 | header[11] = BYTE1(ntracks); | | 753 | header[11] = BYTE1(ntracks); |
754 | header[12] = BYTE2(notes_per_beat); | | 754 | header[12] = BYTE2(notes_per_beat); |
755 | header[13] = BYTE1(notes_per_beat); | | 755 | header[13] = BYTE1(notes_per_beat); |
756 | | | 756 | |
757 | track[4] = BYTE4(total_size); | | 757 | track[4] = BYTE4(total_size); |
758 | track[5] = BYTE3(total_size); | | 758 | track[5] = BYTE3(total_size); |
759 | track[6] = BYTE2(total_size); | | 759 | track[6] = BYTE2(total_size); |
760 | track[7] = BYTE1(total_size); | | 760 | track[7] = BYTE1(total_size); |
761 | | | 761 | |
762 | #define TEMPO_INV(x) (60000000UL / (x)) | | 762 | #define TEMPO_INV(x) (60000000UL / (x)) |
763 | bpm[4] = BYTE3(TEMPO_INV(tempo)); | | 763 | bpm[4] = BYTE3(TEMPO_INV(tempo)); |
764 | bpm[5] = BYTE2(TEMPO_INV(tempo)); | | 764 | bpm[5] = BYTE2(TEMPO_INV(tempo)); |
765 | bpm[6] = BYTE1(TEMPO_INV(tempo)); | | 765 | bpm[6] = BYTE1(TEMPO_INV(tempo)); |
766 | | | 766 | |
767 | if (write(outfd, header, sizeof header) != sizeof header) | | 767 | if (write(outfd, header, sizeof header) != sizeof header) |
768 | err(1, "write of header failed"); | | 768 | err(1, "write of header failed"); |
769 | if (write(outfd, track, sizeof track) != sizeof track) | | 769 | if (write(outfd, track, sizeof track) != sizeof track) |
770 | err(1, "write of track header failed"); | | 770 | err(1, "write of track header failed"); |
771 | if (write(outfd, bpm, sizeof bpm) != sizeof bpm) | | 771 | if (write(outfd, bpm, sizeof bpm) != sizeof bpm) |
772 | err(1, "write of bpm header failed"); | | 772 | err(1, "write of bpm header failed"); |
773 | | | 773 | |
774 | LOG("wrote header: ntracks=%u notes_per_beat=%u tempo=%d total_size=%u", | | 774 | LOG("wrote header: ntracks=%u notes_per_beat=%u tempo=%d total_size=%u", |
775 | ntracks, notes_per_beat, tempo, total_size); | | 775 | ntracks, notes_per_beat, tempo, total_size); |
776 | } | | 776 | } |
777 | | | 777 | |
778 | static void | | 778 | static void |
779 | write_midi_trailer(void) | | 779 | write_midi_trailer(void) |
780 | { | | 780 | { |
781 | unsigned char trailer[] = { | | 781 | unsigned char trailer[] = { |
782 | 0, 0xff, 0x2f, 0, | | 782 | 0, 0xff, 0x2f, 0, |
783 | }; | | 783 | }; |
784 | | | 784 | |
785 | if (write(outfd, trailer, sizeof trailer) != sizeof trailer) | | 785 | if (write(outfd, trailer, sizeof trailer) != sizeof trailer) |
786 | err(1, "write of trailer failed"); | | 786 | err(1, "write of trailer failed"); |
787 | } | | 787 | } |
788 | | | 788 | |
789 | static void | | 789 | static void |
790 | usage(void) | | 790 | usage(void) |
791 | { | | 791 | { |
792 | | | 792 | |
793 | fprintf(stderr, "Usage: %s [-aDfhqV] [options] {outfile|-}\n", | | 793 | fprintf(stderr, "Usage: %s [-aDfhqV] [options] {outfile|-}\n", |
794 | getprogname()); | | 794 | getprogname()); |
795 | fprintf(stderr, "Options:\n" | | 795 | fprintf(stderr, "Options:\n" |
796 | "\t-B buffer size\n" | | 796 | "\t-B buffer size\n" |
797 | "\t-c channels\n" | | 797 | "\t-c channels\n" |
798 | "\t-d devices\n" | | 798 | "\t-d devices\n" |
799 | "\t-f sequencerdev\n" | | 799 | "\t-f sequencerdev\n" |
800 | "\t-n notesperbeat\n" | | 800 | "\t-n notesperbeat\n" |
801 | "\t-r raw_output\n" | | 801 | "\t-r raw_output\n" |
802 | "\t-T tempo\n" | | 802 | "\t-T tempo\n" |
803 | "\t-t recording time\n"); | | 803 | "\t-t recording time\n"); |
804 | exit(EXIT_FAILURE); | | 804 | exit(EXIT_FAILURE); |
805 | } | | 805 | } |