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

Cache object: fafa1301ebe88369e98e423cbd947763


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