1 | /* $NetBSD: midisyn.c,v 1.24 2012/04/09 10:18:16 plunky Exp $ */ |
2 | |
3 | /* |
4 | * Copyright (c) 1998, 2008 The NetBSD Foundation, Inc. |
5 | * All rights reserved. |
6 | * |
7 | * This code is derived from software contributed to The NetBSD Foundation |
8 | * by Lennart Augustsson (augustss@NetBSD.org), and by Andrew Doran. |
9 | * |
10 | * Redistribution and use in source and binary forms, with or without |
11 | * modification, are permitted provided that the following conditions |
12 | * are met: |
13 | * 1. Redistributions of source code must retain the above copyright |
14 | * notice, this list of conditions and the following disclaimer. |
15 | * 2. Redistributions in binary form must reproduce the above copyright |
16 | * notice, this list of conditions and the following disclaimer in the |
17 | * documentation and/or other materials provided with the distribution. |
18 | * |
19 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
20 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
21 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
22 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
23 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
29 | * POSSIBILITY OF SUCH DAMAGE. |
30 | */ |
31 | |
32 | #include <sys/cdefs.h> |
33 | __KERNEL_RCSID(0, "$NetBSD: midisyn.c,v 1.24 2012/04/09 10:18:16 plunky Exp $" ); |
34 | |
35 | #include <sys/param.h> |
36 | #include <sys/ioctl.h> |
37 | #include <sys/fcntl.h> |
38 | #include <sys/vnode.h> |
39 | #include <sys/select.h> |
40 | #include <sys/proc.h> |
41 | #include <sys/kmem.h> |
42 | #include <sys/systm.h> |
43 | #include <sys/syslog.h> |
44 | #include <sys/kernel.h> |
45 | #include <sys/audioio.h> |
46 | #include <sys/midiio.h> |
47 | #include <sys/device.h> |
48 | |
49 | #include <dev/audio_if.h> |
50 | #include <dev/midi_if.h> |
51 | #include <dev/midivar.h> |
52 | #include <dev/midisynvar.h> |
53 | |
54 | #ifdef AUDIO_DEBUG |
55 | #define DPRINTF(x) if (midisyndebug) printf x |
56 | #define DPRINTFN(n,x) if (midisyndebug >= (n)) printf x |
57 | int midisyndebug = 0; |
58 | #else |
59 | #define DPRINTF(x) |
60 | #define DPRINTFN(n,x) |
61 | #endif |
62 | |
63 | static int midisyn_findvoice(midisyn *, int, int); |
64 | static void midisyn_freevoice(midisyn *, int); |
65 | static uint_fast16_t midisyn_allocvoice(midisyn *, uint_fast8_t, uint_fast8_t); |
66 | static void midisyn_attackv_vel(midisyn *, uint_fast16_t, midipitch_t, |
67 | int16_t, uint_fast8_t); |
68 | |
69 | static midictl_notify midisyn_notify; |
70 | |
71 | static midipitch_t midisyn_clamp_pitch(midipitch_t); |
72 | static int16_t midisyn_adj_level(midisyn *, uint_fast8_t); |
73 | static midipitch_t midisyn_adj_pitch(midisyn *, uint_fast8_t); |
74 | static void midisyn_chan_releasev(midisyn *, uint_fast8_t, uint_fast8_t); |
75 | static void midisyn_upd_level(midisyn *, uint_fast8_t); |
76 | static void midisyn_upd_pitch(midisyn *, uint_fast8_t); |
77 | |
78 | static int midisyn_open(void *, int, |
79 | void (*iintr)(void *, int), |
80 | void (*ointr)(void *), void *arg); |
81 | static void midisyn_close(void *); |
82 | static int midisyn_sysrt(void *, int); |
83 | static void midisyn_getinfo(void *, struct midi_info *); |
84 | static int midisyn_ioctl(void *, u_long, void *, int, struct lwp *); |
85 | static void midisyn_get_locks(void *, kmutex_t **, kmutex_t **); |
86 | |
87 | const struct midi_hw_if midisyn_hw_if = { |
88 | midisyn_open, |
89 | midisyn_close, |
90 | midisyn_sysrt, |
91 | midisyn_getinfo, |
92 | midisyn_ioctl, |
93 | midisyn_get_locks, |
94 | }; |
95 | |
96 | static int midisyn_channelmsg(void *, int, int, u_char *, int); |
97 | static int midisyn_commonmsg(void *, int, u_char *, int); |
98 | static int midisyn_sysex(void *, u_char *, int); |
99 | |
100 | struct midi_hw_if_ext midisyn_hw_if_ext = { |
101 | .channel = midisyn_channelmsg, |
102 | .common = midisyn_commonmsg, |
103 | .sysex = midisyn_sysex, |
104 | }; |
105 | |
106 | struct channelstate { /* dyamically allocated in open() on account of size */ |
107 | /* volume state components in centibels; just sum for overall level */ |
108 | int16_t volume; |
109 | int16_t expression; |
110 | /* pitch state components in midipitch units; sum for overall effect */ |
111 | midipitch_t bend; |
112 | midipitch_t tuning_fine; |
113 | midipitch_t tuning_coarse; |
114 | /* used by bend handlers */ |
115 | int16_t bendraw; |
116 | int16_t pendingreset; |
117 | /* rearrange as more controls supported - 16 bits should last for a while */ |
118 | #define PEND_VOL 1 |
119 | #define PEND_EXP 2 |
120 | #define PEND_LEVEL (PEND_VOL|PEND_EXP) |
121 | #define PEND_PBS 4 |
122 | #define PEND_TNF 8 |
123 | #define PEND_TNC 16 |
124 | #define PEND_PITCH (PEND_PBS|PEND_TNF|PEND_TNC) |
125 | #define PEND_ALL (PEND_LEVEL|PEND_PITCH) |
126 | }; |
127 | |
128 | static int |
129 | midisyn_open(void *addr, int flags, void (*iintr)(void *, int), |
130 | void (*ointr)(void *), void *arg) |
131 | { |
132 | midisyn *ms = addr; |
133 | int rslt, error; |
134 | uint_fast8_t chan; |
135 | |
136 | KASSERT(ms->lock != NULL); |
137 | KASSERT(mutex_owned(ms->lock)); |
138 | DPRINTF(("midisyn_open: ms=%p ms->mets=%p\n" , ms, ms->mets)); |
139 | |
140 | mutex_exit(ms->lock); |
141 | ms->ctl.lock = ms->lock; |
142 | error = midictl_open(&ms->ctl); |
143 | if (error != 0) { |
144 | mutex_enter(ms->lock); |
145 | return error; |
146 | } |
147 | ms->chnstate = kmem_alloc(MIDI_MAX_CHANS * sizeof(*ms->chnstate), |
148 | KM_SLEEP); /* init'd by RESET below */ |
149 | mutex_enter(ms->lock); |
150 | |
151 | rslt = 0; |
152 | if (ms->mets->open) |
153 | rslt = (ms->mets->open(ms, flags)); |
154 | |
155 | /* |
156 | * Make the right initial things happen by faking receipt of RESET on |
157 | * all channels. The hw driver's ctlnotice() will be called in turn. |
158 | */ |
159 | for ( chan = 0 ; chan < MIDI_MAX_CHANS ; ++ chan ) |
160 | midisyn_notify(ms, MIDICTL_RESET, chan, 0); |
161 | |
162 | return rslt; |
163 | } |
164 | |
165 | static void |
166 | midisyn_close(void *addr) |
167 | { |
168 | midisyn *ms = addr; |
169 | struct midisyn_methods *fs; |
170 | int chan; |
171 | |
172 | KASSERT(mutex_owned(ms->lock)); |
173 | DPRINTF(("midisyn_close: ms=%p ms->mets=%p\n" , ms, ms->mets)); |
174 | fs = ms->mets; |
175 | |
176 | for (chan = 0; chan < MIDI_MAX_CHANS; chan++) |
177 | midisyn_notify(ms, MIDICTL_SOUND_OFF, chan, 0); |
178 | |
179 | if (fs->close) |
180 | fs->close(ms); |
181 | |
182 | mutex_exit(ms->lock); |
183 | midictl_close(&ms->ctl); |
184 | kmem_free(ms->chnstate, MIDI_MAX_CHANS * sizeof(*ms->chnstate)); |
185 | mutex_enter(ms->lock); |
186 | } |
187 | |
188 | static void |
189 | midisyn_getinfo(void *addr, struct midi_info *mi) |
190 | { |
191 | midisyn *ms = addr; |
192 | |
193 | KASSERT(mutex_owned(ms->lock)); |
194 | |
195 | mi->name = ms->name; |
196 | /* |
197 | * I was going to add a property here to suppress midi(4)'s warning |
198 | * about an output device that uses no transmit interrupt, on the |
199 | * assumption that as an onboard synth we handle "output" internally |
200 | * with nothing like the 320 us per byte busy wait of a dumb UART. |
201 | * Then I noticed that opl (at least as currently implemented) seems |
202 | * to need 40 us busy wait to set each register on an OPL2, and sets |
203 | * about 21 registers for every note-on. (Half of that is patch loading |
204 | * and could probably be reduced by different management of voices and |
205 | * patches.) For now I won't bother suppressing that warning.... |
206 | */ |
207 | mi->props = 0; |
208 | |
209 | midi_register_hw_if_ext(&midisyn_hw_if_ext); |
210 | } |
211 | |
212 | static void |
213 | midisyn_get_locks(void *addr, kmutex_t **intr, kmutex_t **proc) |
214 | { |
215 | midisyn *ms = addr; |
216 | |
217 | *intr = ms->lock; |
218 | *proc = NULL; |
219 | } |
220 | |
221 | static int |
222 | midisyn_ioctl(void *maddr, u_long cmd, void *addr, int flag, struct lwp *l) |
223 | { |
224 | midisyn *ms = maddr; |
225 | |
226 | KASSERT(mutex_owned(ms->lock)); |
227 | |
228 | if (ms->mets->ioctl) |
229 | return (ms->mets->ioctl(ms, cmd, addr, flag, l)); |
230 | else |
231 | return (EINVAL); |
232 | } |
233 | |
234 | static int |
235 | midisyn_findvoice(midisyn *ms, int chan, int note) |
236 | { |
237 | u_int cn; |
238 | int v; |
239 | |
240 | KASSERT(mutex_owned(ms->lock)); |
241 | |
242 | cn = MS_CHANNOTE(chan, note); |
243 | for (v = 0; v < ms->nvoice; v++) |
244 | if (ms->voices[v].chan_note == cn && ms->voices[v].inuse) |
245 | return (v); |
246 | return (-1); |
247 | } |
248 | |
249 | void |
250 | midisyn_init(midisyn *ms) |
251 | { |
252 | |
253 | KASSERT(ms->lock != NULL); |
254 | |
255 | /* |
256 | * XXX there should be a way for this function to indicate failure |
257 | * (other than panic) if some preconditions aren't met, for example |
258 | * if some nonoptional methods are missing. |
259 | */ |
260 | if (ms->mets->allocv == 0) { |
261 | ms->voices = kmem_zalloc(ms->nvoice * sizeof(struct voice), |
262 | KM_SLEEP); |
263 | ms->seqno = 1; |
264 | ms->mets->allocv = midisyn_allocvoice; |
265 | } |
266 | |
267 | if (ms->mets->attackv_vel == 0 && ms->mets->attackv != 0) |
268 | ms->mets->attackv_vel = midisyn_attackv_vel; |
269 | |
270 | ms->ctl = (midictl) { |
271 | .base_channel = 16, |
272 | .cookie = ms, |
273 | .notify = midisyn_notify |
274 | }; |
275 | |
276 | DPRINTF(("midisyn_init: ms=%p\n" , ms)); |
277 | } |
278 | |
279 | static void |
280 | midisyn_freevoice(midisyn *ms, int voice) |
281 | { |
282 | |
283 | KASSERT(mutex_owned(ms->lock)); |
284 | |
285 | if (ms->mets->allocv != midisyn_allocvoice) |
286 | return; |
287 | ms->voices[voice].inuse = 0; |
288 | } |
289 | |
290 | static uint_fast16_t |
291 | midisyn_allocvoice(midisyn *ms, uint_fast8_t chan, uint_fast8_t note) |
292 | { |
293 | int bestv, v; |
294 | u_int bestseq, s; |
295 | |
296 | KASSERT(mutex_owned(ms->lock)); |
297 | |
298 | /* Find a free voice, or if no free voice is found the oldest. */ |
299 | bestv = 0; |
300 | bestseq = ms->voices[0].seqno + (ms->voices[0].inuse ? 0x40000000 : 0); |
301 | for (v = 1; v < ms->nvoice; v++) { |
302 | s = ms->voices[v].seqno; |
303 | if (ms->voices[v].inuse) |
304 | s += 0x40000000; |
305 | if (s < bestseq) { |
306 | bestseq = s; |
307 | bestv = v; |
308 | } |
309 | } |
310 | DPRINTFN(10,("midisyn_allocvoice: v=%d seq=%d cn=%x inuse=%d\n" , |
311 | bestv, ms->voices[bestv].seqno, |
312 | ms->voices[bestv].chan_note, |
313 | ms->voices[bestv].inuse)); |
314 | #ifdef AUDIO_DEBUG |
315 | if (ms->voices[bestv].inuse) |
316 | DPRINTFN(1,("midisyn_allocvoice: steal %x\n" , |
317 | ms->voices[bestv].chan_note)); |
318 | #endif |
319 | ms->voices[bestv].chan_note = MS_CHANNOTE(chan, note); |
320 | ms->voices[bestv].seqno = ms->seqno++; |
321 | ms->voices[bestv].inuse = 1; |
322 | return (bestv); |
323 | } |
324 | |
325 | /* dummy attackv_vel that just adds vel into level for simple drivers */ |
326 | static void |
327 | midisyn_attackv_vel(midisyn *ms, uint_fast16_t voice, midipitch_t mp, |
328 | int16_t level_cB, uint_fast8_t vel) |
329 | { |
330 | |
331 | KASSERT(mutex_owned(ms->lock)); |
332 | |
333 | ms->voices[voice].velcB = midisyn_vol2cB((uint_fast16_t)vel << 7); |
334 | ms->mets->attackv(ms, voice, mp, level_cB + ms->voices[voice].velcB); |
335 | } |
336 | |
337 | static int |
338 | midisyn_sysrt(void *addr, int b) |
339 | { |
340 | |
341 | return 0; |
342 | } |
343 | |
344 | static int |
345 | midisyn_channelmsg(void *addr, int status, int chan, u_char *buf, int len) |
346 | { |
347 | midisyn *ms = addr; |
348 | int voice = 0; /* initialize to keep gcc quiet */ |
349 | struct midisyn_methods *fs; |
350 | |
351 | KASSERT(mutex_owned(ms->lock)); |
352 | |
353 | DPRINTF(("midisyn_channelmsg: ms=%p status=%#02x chan=%d\n" , |
354 | ms, status, chan)); |
355 | fs = ms->mets; |
356 | |
357 | switch (status) { |
358 | case MIDI_NOTEOFF: |
359 | /* |
360 | * for a device that leaves voice allocation to us--and that's |
361 | * all of 'em at the moment--the voice and release velocity |
362 | * should be the only necessary arguments to noteoff. what use |
363 | * are they making of note? checking... None. Cool. |
364 | * IF there is ever a device added that does its own allocation, |
365 | * extend the interface; this findvoice won't be what to do... |
366 | */ |
367 | voice = midisyn_findvoice(ms, chan, buf[1]); |
368 | if (voice >= 0) { |
369 | fs->releasev(ms, voice, buf[2]); |
370 | midisyn_freevoice(ms, voice); |
371 | } |
372 | break; |
373 | case MIDI_NOTEON: |
374 | /* |
375 | * what's called for here, given current drivers, is an i/f |
376 | * where midisyn computes a volume from vel*volume*expression* |
377 | * mastervolume and passes that result as a single arg. It can |
378 | * evolve later to support drivers that expose some of those |
379 | * bits separately (e.g. a driver could expose a mixer register |
380 | * on its sound card and use that for mastervolume). |
381 | */ |
382 | voice = fs->allocv(ms, chan, buf[1]); |
383 | ms->voices[voice].velcB = 0; /* assume driver handles vel */ |
384 | fs->attackv_vel(ms, voice, |
385 | midisyn_clamp_pitch(MIDIPITCH_FROM_KEY(buf[1]) + |
386 | midisyn_adj_pitch(ms, chan)), |
387 | midisyn_adj_level(ms,chan), buf[2]); |
388 | break; |
389 | case MIDI_KEY_PRESSURE: |
390 | /* |
391 | * unimplemented by the existing drivers. if we are doing |
392 | * voice allocation, find the voice that corresponds to this |
393 | * chan/note and define a method that passes the voice and |
394 | * pressure to the driver ... not the note, /it/ doesn't matter. |
395 | * For a driver that does its own allocation, a different |
396 | * method may be needed passing pressure, chan, note so it can |
397 | * find the right voice on its own. Be sure that whatever is |
398 | * done here is undone when midisyn_notify sees MIDICTL_RESET. |
399 | */ |
400 | break; |
401 | case MIDI_CTL_CHANGE: |
402 | midictl_change(&ms->ctl, chan, buf+1); |
403 | break; |
404 | case MIDI_PGM_CHANGE: |
405 | if (fs->pgmchg) |
406 | fs->pgmchg(ms, chan, buf[1]); |
407 | break; |
408 | case MIDI_CHN_PRESSURE: |
409 | /* |
410 | * unimplemented by the existing drivers. if driver exposes no |
411 | * distinct method, can use KEY_PRESSURE method for each voice |
412 | * on channel. Be sure that whatever is |
413 | * done here is undone when midisyn_notify sees MIDICTL_RESET. |
414 | */ |
415 | break; |
416 | case MIDI_PITCH_BEND: |
417 | /* |
418 | * Will work for most drivers that simply render the midipitch |
419 | * as we pass it (but not cms, which chops all the bits after |
420 | * the note number and then computes its own pitch :( ). If the |
421 | * driver has a repitchv method for voices already sounding, so |
422 | * much the better. |
423 | * The bending logic lives in the handler for bend sensitivity, |
424 | * so fake a change to that to kick it off. |
425 | */ |
426 | ms->chnstate[chan].bendraw = buf[2]<<7 | buf[1]; |
427 | ms->chnstate[chan].bendraw -= MIDI_BEND_NEUTRAL; |
428 | midisyn_notify(ms, MIDICTL_RPN, chan, |
429 | MIDI_RPN_PITCH_BEND_SENSITIVITY); |
430 | break; |
431 | } |
432 | return 0; |
433 | } |
434 | |
435 | static int |
436 | midisyn_commonmsg(void *addr, int status, u_char *buf, int len) |
437 | { |
438 | |
439 | return 0; |
440 | } |
441 | |
442 | static int |
443 | midisyn_sysex(void *addr, u_char *buf, int len) |
444 | { |
445 | |
446 | /* |
447 | * unimplemented by existing drivers. it is surely more sensible |
448 | * to do some parsing of well-defined sysex messages here, either |
449 | * handling them internally or calling specific methods on the |
450 | * driver after parsing out the details, than to ask every driver |
451 | * to deal with sysex messages poked at it a byte at a time. |
452 | */ |
453 | return 0; |
454 | } |
455 | |
456 | static void |
457 | midisyn_notify(void *cookie, midictl_evt evt, |
458 | uint_fast8_t chan, uint_fast16_t key) |
459 | { |
460 | struct midisyn *ms; |
461 | int drvhandled; |
462 | |
463 | ms = (struct midisyn *)cookie; |
464 | |
465 | KASSERT(mutex_owned(ms->lock)); |
466 | |
467 | drvhandled = 0; |
468 | if ( ms->mets->ctlnotice ) |
469 | drvhandled = ms->mets->ctlnotice(ms, evt, chan, key); |
470 | |
471 | switch ( evt | key ) { |
472 | case MIDICTL_RESET: |
473 | /* |
474 | * Re-read all ctls we use, revert pitchbend state. |
475 | * Can do it by faking change notifications. |
476 | */ |
477 | ms->chnstate[chan].pendingreset |= PEND_ALL; |
478 | midisyn_notify(ms, MIDICTL_CTLR, chan, |
479 | MIDI_CTRL_CHANNEL_VOLUME_MSB); |
480 | midisyn_notify(ms, MIDICTL_CTLR, chan, |
481 | MIDI_CTRL_EXPRESSION_MSB); |
482 | ms->chnstate[chan].bendraw = 0; /* MIDI_BEND_NEUTRAL - itself */ |
483 | midisyn_notify(ms, MIDICTL_RPN, chan, |
484 | MIDI_RPN_PITCH_BEND_SENSITIVITY); |
485 | midisyn_notify(ms, MIDICTL_RPN, chan, |
486 | MIDI_RPN_CHANNEL_FINE_TUNING); |
487 | midisyn_notify(ms, MIDICTL_RPN, chan, |
488 | MIDI_RPN_CHANNEL_COARSE_TUNING); |
489 | break; |
490 | case MIDICTL_NOTES_OFF: |
491 | if ( drvhandled ) |
492 | break; |
493 | /* releasev all voices sounding on chan; use normal vel 64 */ |
494 | midisyn_chan_releasev(ms, chan, 64); |
495 | break; |
496 | case MIDICTL_SOUND_OFF: |
497 | if ( drvhandled ) |
498 | break; |
499 | /* releasev all voices sounding on chan; use max vel 127 */ |
500 | /* it is really better for driver to handle this, instantly */ |
501 | midisyn_chan_releasev(ms, chan, 127); |
502 | break; |
503 | case MIDICTL_CTLR | MIDI_CTRL_CHANNEL_VOLUME_MSB: |
504 | ms->chnstate[chan].pendingreset &= ~PEND_VOL; |
505 | if ( drvhandled ) { |
506 | ms->chnstate[chan].volume = 0; |
507 | break; |
508 | } |
509 | ms->chnstate[chan].volume = midisyn_vol2cB( |
510 | midictl_read(&ms->ctl, chan, key, 100<<7)); |
511 | midisyn_upd_level(ms, chan); |
512 | break; |
513 | case MIDICTL_CTLR | MIDI_CTRL_EXPRESSION_MSB: |
514 | ms->chnstate[chan].pendingreset &= ~PEND_EXP; |
515 | if ( drvhandled ) { |
516 | ms->chnstate[chan].expression = 0; |
517 | break; |
518 | } |
519 | ms->chnstate[chan].expression = midisyn_vol2cB( |
520 | midictl_read(&ms->ctl, chan, key, 16383)); |
521 | midisyn_upd_level(ms, chan); |
522 | break; |
523 | /* |
524 | * SOFT_PEDAL: supporting this will be trickier; must apply only |
525 | * to notes subsequently struck, and must remember which voices |
526 | * they are for follow-on adjustments. For another day.... |
527 | */ |
528 | case MIDICTL_RPN | MIDI_RPN_PITCH_BEND_SENSITIVITY: |
529 | ms->chnstate[chan].pendingreset &= ~PEND_PBS; |
530 | if ( drvhandled ) |
531 | ms->chnstate[chan].bend = 0; |
532 | else { |
533 | uint16_t w; |
534 | int8_t semis, cents; |
535 | w = midictl_rpn_read(&ms->ctl, chan, key, 2<<7); |
536 | semis = w>>7; |
537 | cents = w&0x7f; |
538 | /* |
539 | * Mathematically, multiply semis by |
540 | * MIDIPITCH_SEMITONE*bendraw/8192. Practically, avoid |
541 | * shifting significant bits off by observing that |
542 | * MIDIPITCH_SEMITONE == 1<<14 and 8192 == 1<<13, so |
543 | * just take semis*bendraw<<1. Do the same with cents |
544 | * except <<1 becomes /50 (but rounded). |
545 | */ |
546 | ms->chnstate[chan].bend = |
547 | ( ms->chnstate[chan].bendraw * semis ) << 1; |
548 | ms->chnstate[chan].bend += |
549 | ((ms->chnstate[chan].bendraw * cents)/25 + 1) >> 1; |
550 | midisyn_upd_pitch(ms, chan); |
551 | } |
552 | break; |
553 | case MIDICTL_RPN | MIDI_RPN_CHANNEL_FINE_TUNING: |
554 | if ( drvhandled ) |
555 | ms->chnstate[chan].tuning_fine = 0; |
556 | else { |
557 | midipitch_t mp; |
558 | mp = midictl_rpn_read(&ms->ctl, chan, key, 8192); |
559 | /* |
560 | * Mathematically, subtract 8192 and scale by |
561 | * MIDIPITCH_SEMITONE/8192. Practically, subtract 8192 |
562 | * and then << 1. |
563 | */ |
564 | ms->chnstate[chan].tuning_fine = ( mp - 8192 ) << 1; |
565 | midisyn_upd_pitch(ms, chan); |
566 | } |
567 | break; |
568 | case MIDICTL_RPN | MIDI_RPN_CHANNEL_COARSE_TUNING: |
569 | ms->chnstate[chan].pendingreset &= ~PEND_TNC; |
570 | if ( drvhandled ) |
571 | ms->chnstate[chan].tuning_coarse = 0; |
572 | else { |
573 | midipitch_t mp; |
574 | /* |
575 | * By definition only the MSB of this parameter is used. |
576 | * Subtract 64 for a signed count of semitones; << 14 |
577 | * will convert to midipitch scale. |
578 | */ |
579 | mp = midictl_rpn_read(&ms->ctl, chan, key, 64<<7) >> 7; |
580 | ms->chnstate[chan].tuning_coarse = ( mp - 64 ) << 14; |
581 | midisyn_upd_pitch(ms, chan); |
582 | } |
583 | break; |
584 | } |
585 | } |
586 | |
587 | static midipitch_t |
588 | midisyn_clamp_pitch(midipitch_t mp) |
589 | { |
590 | |
591 | if ( mp <= 0 ) |
592 | return 0; |
593 | if ( mp >= MIDIPITCH_MAX ) |
594 | return MIDIPITCH_MAX; |
595 | return mp; |
596 | } |
597 | |
598 | static int16_t |
599 | midisyn_adj_level(midisyn *ms, uint_fast8_t chan) |
600 | { |
601 | int32_t level; |
602 | |
603 | KASSERT(mutex_owned(ms->lock)); |
604 | |
605 | level = ms->chnstate[chan].volume + ms->chnstate[chan].expression; |
606 | if ( level <= INT16_MIN ) |
607 | return INT16_MIN; |
608 | return level; |
609 | } |
610 | |
611 | static midipitch_t |
612 | midisyn_adj_pitch(midisyn *ms, uint_fast8_t chan) |
613 | { |
614 | struct channelstate *s = ms->chnstate + chan; |
615 | |
616 | KASSERT(mutex_owned(ms->lock)); |
617 | |
618 | return s->bend + s->tuning_fine +s->tuning_coarse; |
619 | } |
620 | |
621 | #define VOICECHAN_FOREACH_BEGIN(ms,vp,ch) \ |
622 | { \ |
623 | struct voice *vp, *_end_##vp; \ |
624 | for (vp=(ms)->voices,_end_##vp=vp+(ms)->nvoice; \ |
625 | vp < _end_##vp; ++ vp) { \ |
626 | if ( !vp->inuse ) \ |
627 | continue; \ |
628 | if ( MS_GETCHAN(vp) == (ch) ) \ |
629 | ; \ |
630 | else \ |
631 | continue; |
632 | #define VOICECHAN_FOREACH_END }} |
633 | |
634 | static void |
635 | midisyn_chan_releasev(midisyn *ms, uint_fast8_t chan, uint_fast8_t vel) |
636 | { |
637 | |
638 | KASSERT(mutex_owned(ms->lock)); |
639 | |
640 | VOICECHAN_FOREACH_BEGIN(ms,vp,chan) |
641 | ms->mets->releasev(ms, vp - ms->voices, vel); |
642 | midisyn_freevoice(ms, vp - ms->voices); |
643 | VOICECHAN_FOREACH_END |
644 | } |
645 | |
646 | static void |
647 | midisyn_upd_level(midisyn *ms, uint_fast8_t chan) |
648 | { |
649 | int32_t level; |
650 | int16_t chan_level; |
651 | |
652 | KASSERT(mutex_owned(ms->lock)); |
653 | |
654 | if ( NULL == ms->mets->relevelv ) |
655 | return; |
656 | |
657 | if ( ms->chnstate[chan].pendingreset & PEND_LEVEL ) |
658 | return; |
659 | |
660 | chan_level = midisyn_adj_level(ms, chan); |
661 | |
662 | VOICECHAN_FOREACH_BEGIN(ms,vp,chan) |
663 | level = vp->velcB + chan_level; |
664 | ms->mets->relevelv(ms, vp - ms->voices, |
665 | level <= INT16_MIN ? INT16_MIN : level); |
666 | VOICECHAN_FOREACH_END |
667 | } |
668 | |
669 | static void |
670 | midisyn_upd_pitch(midisyn *ms, uint_fast8_t chan) |
671 | { |
672 | midipitch_t chan_adj; |
673 | |
674 | KASSERT(mutex_owned(ms->lock)); |
675 | |
676 | if ( NULL == ms->mets->repitchv ) |
677 | return; |
678 | |
679 | if ( ms->chnstate[chan].pendingreset & PEND_PITCH ) |
680 | return; |
681 | |
682 | chan_adj = midisyn_adj_pitch(ms, chan); |
683 | |
684 | VOICECHAN_FOREACH_BEGIN(ms,vp,chan) |
685 | ms->mets->repitchv(ms, vp - ms->voices, |
686 | midisyn_clamp_pitch(chan_adj + |
687 | MIDIPITCH_FROM_KEY(vp->chan_note&0x7f))); |
688 | VOICECHAN_FOREACH_END |
689 | } |
690 | |
691 | #undef VOICECHAN_FOREACH_END |
692 | #undef VOICECHAN_FOREACH_BEGIN |
693 | |
694 | int16_t |
695 | midisyn_vol2cB(uint_fast16_t vol) |
696 | { |
697 | int16_t cB = 0; |
698 | int32_t v; |
699 | |
700 | if ( 0 == vol ) |
701 | return INT16_MIN; |
702 | /* |
703 | * Adjust vol to fall in the range 8192..16383. Each doubling is |
704 | * worth 12 dB. |
705 | */ |
706 | while ( vol < 8192 ) { |
707 | vol <<= 1; |
708 | cB -= 120; |
709 | } |
710 | v = vol; /* ensure evaluation in signed 32 bit below */ |
711 | /* |
712 | * The GM vol-to-dB formula is dB = 40 log ( v / 127 ) for 7-bit v. |
713 | * The vol and expression controllers are in 14-bit space so the |
714 | * equivalent is 40 log ( v / 16256 ) - that is, MSB 127 LSB 0 because |
715 | * the LSB is commonly unused. MSB 127 LSB 127 would then be a tiny |
716 | * bit over. |
717 | * 1 dB resolution is a little coarser than we'd like, so let's shoot |
718 | * for centibels, i.e. 400 log ( v / 16256 ), and shift everything left |
719 | * as far as will fit in 32 bits, which turns out to be a shift of 22. |
720 | * This minimax polynomial approximation is good to about a centibel |
721 | * on the range 8192..16256, a shade worse (1.4 or so) above that. |
722 | * 26385/10166 is the 6th convergent of the coefficient for v^2. |
723 | */ |
724 | cB += ( v * ( 124828 - ( v * 26385 ) / 10166 ) - 1347349038 ) >> 22; |
725 | return cB; |
726 | } |
727 | |
728 | /* |
729 | * MIDI RP-012 constitutes a MIDI Tuning Specification. The units are |
730 | * fractional-MIDIkeys, that is, the key number 00 - 7f left shifted |
731 | * 14 bits to provide a 14-bit fraction that divides each semitone. The |
732 | * whole thing is just a 21-bit number that is bent and tuned simply by |
733 | * adding and subtracting--the same offset is the same pitch change anywhere |
734 | * on the scale. One downside is that a cent is 163.84 of these units, so |
735 | * you can't expect a lengthy integer sum of cents to come out in tune; if you |
736 | * do anything in cents it is best to use them only for local adjustment of |
737 | * a pitch. |
738 | * |
739 | * This function converts a pitch in MIDItune units to Hz left-shifted 18 bits. |
740 | * That should leave you enough to shift down to whatever precision the hardware |
741 | * supports. |
742 | * |
743 | * Its prototype is exposed in <sys/midiio.h>. |
744 | */ |
745 | midihz18_t |
746 | midisyn_mp2hz18(midipitch_t mp) |
747 | { |
748 | int64_t t64a, t64b; |
749 | uint_fast8_t shift; |
750 | |
751 | /* |
752 | * Scale from the logarithmic MIDI-Tuning units to Hz<<18. Uses the |
753 | * continued-fraction form of a 2/2 rational function derived to |
754 | * cover the highest octave (mt 1900544..2097151 or 74.00.00..7f.7f.7f |
755 | * in RP-012-speak, the dotted bits are 7 wide) to produce Hz shifted |
756 | * left just as far as the maximum Hz will fit in a uint32, which |
757 | * turns out to be 18. Just shift off the result for lower octaves. |
758 | * Fit is within 1/4 MIDI tuning unit throughout (disclaimer: the |
759 | * comparison relied on the double-precision log in libm). |
760 | */ |
761 | |
762 | if ( 0 == mp ) |
763 | return 2143236; |
764 | |
765 | for ( shift = 0; mp < 1900544; ++ shift ) |
766 | mp += MIDIPITCH_OCTAVE; |
767 | |
768 | if ( 1998848 == mp ) |
769 | return UINT32_C(2463438621) >> shift; |
770 | |
771 | t64a = 0x5a1a0ee4; /* INT64_C(967879298788) gcc333: spurious warning */ |
772 | t64a |= (int64_t)0xe1 << 32; |
773 | t64a /= mp - 1998848; /* here's why 1998848 is special-cased above ;) */ |
774 | t64a += mp - 3704981; |
775 | t64b = 0x6763759d; /* INT64_C(8405905567872413) goofy warning again */ |
776 | t64b |= (int64_t)0x1ddd20 << 32; |
777 | t64b /= t64a; |
778 | t64b += UINT32_C(2463438619); |
779 | return (uint32_t)t64b >> shift; |
780 | } |
781 | |