The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/midisyn.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

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

Cache object: d3daaad1381ed853eaca243625ac03e0


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.