| 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 | |