home *** CD-ROM | disk | FTP | other *** search
/ Sound Sensations! / sound_sensations.iso / miscprog / ad-prog / adlib.c < prev    next >
C/C++ Source or Header  |  1990-04-19  |  25KB  |  982 lines

  1. /*
  2.      Copyright Ad Lib Inc., 1990
  3.  
  4.      This file is part of the Ad Lib Programmer's Manual product and is
  5.      subject to copyright laws.  As such, you may make copies of this file
  6.      only for the purpose of having backup copies.  This file may not be
  7.      redistributed in any form whatsoever.
  8.  
  9.      If you find yourself in possession of this file without having received
  10.      it directly from Ad Lib Inc., then you are in violation of copyright
  11.      laws, which is a form of theft.
  12. */
  13.  
  14. /**
  15.     ADLIB.C, low level sound driver V1.04
  16.     ===== To be used with OUTCHIP.ASM & SETFREQ.ASM files. =====
  17.  
  18.     Copyright Ad Lib Inc, 1988, 1989, 1990
  19.  
  20.     1988/06/22, Marc Savary.
  21.     1989/04/20, Marc savary.
  22.     1990/03/15, Marc Savary.    Modification of frequency calculation (SETFREQ.ASM)
  23.                                 Correction of volume bugs on additive timbres
  24.  
  25.  
  26.     The following routines are public (see each routine for further
  27.     documentation):
  28.  
  29.         SoundColdInit (port)
  30.         SoundWarmInit()
  31.         SetMode (mode)
  32.         SetWaveSel (state)
  33.         SetPitchRange (pR)
  34.         SetGParam (amD, vibD, nSel)
  35.         SetVoiceTimbre (voice, paramArray)
  36.         SetVoiceVolume (voice, volume)
  37.         SetVoicePitch (voice, pitchBend)
  38.         NoteOn (voice, pitch)
  39.         NoteOff (voice)
  40.  
  41. **/
  42.  
  43. #include "adlib.h"
  44.  
  45. #include "cflags.h"
  46.  
  47. #ifdef TURBOC
  48.    #define  inp  inportb
  49. #endif
  50.  
  51. /* Declaring variables as near improves performance. */
  52. #define N_V near
  53.  
  54.  
  55. /*
  56.     In percussive mode, the last 4 voices  (SD TOM HH CYMB) are created
  57.     using melodic voices 7 & 8. A noise generator use channels 7 & 8
  58.     frequency information for creating rhythm instruments. Best results
  59.     are obtained by setting TOM two octaves below mid C and SD 7 half-tones
  60.     above TOM.
  61.     In this implementation, only the TOM pitch may vary, with the SD always
  62.     set 7 half-tones above it.
  63. */
  64.  
  65.  
  66. #define TOM_PITCH    24                /* best frequency, in range of 0 to 95 */
  67. #define TOM_TO_SD    7                /* 7 half-tones between voice 7 & 8 */
  68. #define SD_PITCH    (TOM_PITCH + TOM_TO_SD)
  69.  
  70. #define GetLocPrm(slot, prm) ((unsigned)paramSlot [slot] [prm])
  71.  
  72.  
  73.  
  74. /*
  75. -----------------------------------------------------------------
  76. */
  77.  
  78. unsigned near genAddr;    /* addr. of sound chip, in DS, used by OUTCHIP.ASM */
  79. int near     pitchRange;            /* pitch variation, half-tone [+1,+12] */
  80.  
  81. static int    N_V     modeWaveSel;        /* != 0 if used with the 'wave-select' parameters */
  82.  
  83. static char N_V percBits;                    /* control bits of percussive voices */
  84. static const char N_V percMasks[] = {
  85.     0x10, 0x08, 0x04, 0x02, 0x01
  86.     };
  87.  
  88. static char N_V voiceNote [9];          /* pitch of last note-on of each voice */
  89. static char N_V voiceKeyOn [9];     /* state of keyOn bit of each voice */
  90. static unsigned N_V vPitchBend [9]; /* current pitch bend of each voice */
  91. static char N_V bxRegister [9];     /* current val. of 0xB0 - 0xB8 reg */
  92. static char N_V lVoiceVolume [11];      /* volume for each of 11 logical voices */
  93.  
  94. static unsigned N_V modeVoices;        /* 9 or 11, depending on 'percussion'*/
  95.  
  96.  
  97.  
  98. /* definition of the ELECTRIC-PIANO voice (opr0 & opr1) */
  99. static const char N_V pianoParamsOp0 [nbLocParam] = {
  100.     1, 1, 3, 15, 5, 0, 1, 3, 15, 0, 0, 0, 1, 0 };
  101. static const char N_V pianoParamsOp1 [nbLocParam] = {
  102.     0, 1, 1, 15, 7, 0, 2, 4, 0, 0, 0, 1, 0, 0 };
  103.  
  104. /* definition of default percussive voices: */
  105. static const char N_V bdOpr0[] =  {0,  0, 0, 10,  4, 0, 8, 12, 11, 0, 0, 0, 1, 0 };
  106. static const char N_V bdOpr1[] =  {0,  0, 0, 13,  4, 0, 6, 15,  0, 0, 0, 0, 1, 0 };
  107. static const char N_V sdOpr[] =   {0, 12, 0, 15, 11, 0, 8,  5,  0, 0, 0, 0, 0, 0 };
  108. static const char N_V tomOpr[] =  {0,  4, 0, 15, 11, 0, 7,  5,  0, 0, 0, 0, 0, 0 };
  109. static const char N_V cymbOpr[] = {0,  1, 0, 15, 11, 0, 5,  5,  0, 0, 0, 0, 0, 0 };
  110. static const char N_V hhOpr[] =   {0,  1, 0, 15, 11, 0, 7,  5,  0, 0, 0, 0, 0, 0 };
  111.  
  112. static char N_V paramSlot [18] [nbLocParam];    /* all the parameters of slots...  */
  113.  
  114. static char    N_V amDepth;            /* chip global parameters .. */
  115. static char    N_V vibDepth;            /* ... */
  116. static char    N_V noteSel;            /* ... */
  117. static char    N_V percussion;            /* percussion mode parameter */
  118.  
  119. /* Slot numbers as a function of the voice and the operator.
  120.    (melodic mode only)
  121. */
  122. unsigned char N_V slotMVoice [9]  [2] = {
  123.     {0, 3},        /* voix 0 */
  124.     {1, 4},        /* 1 */
  125.     {2, 5},        /* 2 */
  126.     {6, 9},        /* 3 */
  127.     {7, 10},    /* 4 */
  128.     {8, 11},    /* 5 */
  129.     {12, 15},    /* 6 */
  130.     {13, 16},    /* 7 */
  131.     {14, 17}    /* 8 */
  132. };
  133.  
  134.  
  135. /* Slot numbers for the percussive voices.
  136.    255 indicates that there is only one slot used by a voice.
  137. */
  138. unsigned char N_V slotPVoice [11]  [2] = {
  139.     {0, 3},        /* voice 0 */
  140.     {1, 4},        /* 1 */
  141.     {2, 5},        /* 2 */
  142.     {6, 9},        /* 3 */
  143.     {7, 10},    /* 4 */
  144.     {8, 11},    /* 5 */
  145.     {12, 15},    /* Bass Drum: slot 12 et 15 */
  146.     {16, 255},    /* SD: slot 16 */
  147.     {14, 255},    /* TOM: slot 14 */
  148.     {17, 255},    /* TOP-CYM: slot 17 */
  149.     {13, 255}    /* HH: slot 13 */
  150. };
  151.  
  152. /* 
  153.     This table gives the offset of each slot within the chip.
  154.     offset = fn (slot)
  155. */
  156. static const char N_V offsetSlot[] = {
  157.      0,  1,  2,  3,  4,  5,
  158.      8,  9, 10, 11, 12, 13,
  159.     16, 17, 18, 19, 20, 21
  160. };
  161.  
  162.  
  163. /* This table indicates if the slot is a modulator (operator 0) or a
  164.    carrier (operator 1).
  165.    opr = fn (slot)
  166. */
  167. static const char N_V carrierSlot[] = {
  168.     0, 0, 0,        /* 1 2 3 */
  169.     1, 1, 1,        /* 4 5 6 */
  170.     0, 0, 0,         /* 7 8 9 */
  171.     1, 1, 1,         /* 10 11 12 */
  172.     0, 0, 0,         /* 13 14 15 */
  173.     1, 1, 1,        /* 16 17 18 */
  174. };
  175.  
  176. /* This table gives the voice number associated with each slot.
  177.    (melodic mode only)
  178.    voice = fn (slot)
  179. */
  180. static const char N_V voiceMSlot[] = {
  181.     0, 1, 2,
  182.     0, 1, 2,
  183.     3, 4, 5,
  184.     3, 4, 5,
  185.     6, 7, 8,
  186.     6, 7, 8,
  187. };
  188.  
  189.  
  190. /* This table gives the voice number  (0-10) associated with each
  191.    slot (percussive mode only),
  192.    voice = fn (slot)
  193. */
  194. static const char N_V voicePSlot[] = {
  195.     0, 1, 2,
  196.     0, 1, 2,
  197.     3, 4, 5,
  198.     3, 4, 5,
  199.     BD, HIHAT, TOM,
  200.     BD, SD, CYMB
  201. };
  202.  
  203.  
  204. /*----------------------------------------------------------
  205.    Function prototypes.
  206. */
  207.  
  208. extern SndOutput();            /* in file OUTCHIP.ASM */
  209. extern SetFreq();                /* in file SETFREQ.ASM */
  210.  
  211. static InitSlotParams();
  212. static SetSlotParam (unsigned, unsigned *, unsigned);
  213. static SndSetPrm (int, int);
  214. static SndSetAllPrm (int);
  215. static SndSKslLevel (int);
  216. static SndSNoteSel();
  217. static SndSFeedFm (int);
  218. static SndSAttDecay (int);
  219. static SndSSusRelease (int);
  220. static SndSAVEK (int);
  221. static SndSAmVibRhythm();
  222. static SndWaveSelect (int);
  223. static UpdateFNums (int);
  224. static int BoardInstalled();
  225.  
  226.  
  227. /*
  228. ----------------------------------------------------------
  229. */
  230.  
  231. /*
  232.     Must be called for start-up initialisation.
  233.  
  234.     Returns 0 if hardware not found.
  235. */
  236. int SoundColdInit (port)
  237.     unsigned port;            /* io port address of sound board (0x388) */
  238.     {
  239.     int hardware;
  240.  
  241.     genAddr = port;
  242.     hardware =  BoardInstalled();
  243.     SoundWarmInit();
  244.     return hardware;
  245.     }   /* SoundColdInit() */
  246.  
  247.  
  248. /*
  249. -----------------------------------------------
  250.     Initialize the chip in melodic mode (mode == 0),
  251.     set all 9 voices to electric-piano timbres,
  252.     set the 3 global parameters to zero,
  253.     set the pitch bend range to 1 half-tone,
  254.     set the pitch bend of each voice to 0x2000 (no detune),
  255.     set the volume of each voice to maximum level,
  256.     and enable the wave-select parameter.
  257. -----------------------------------------------
  258. */
  259. SoundWarmInit()
  260.     {
  261.     int i;
  262.  
  263.     for (i = 1; i <= 0xF5; i++)
  264.         SndOutput (i, 0);   /* clear all registers */
  265.     SndOutput (4, 6);       /* mask T1 & T2 */
  266.  
  267.     for (i = 0; i < 9; i++)  {      /* pitch bend for each voice = no detune */
  268.         vPitchBend [i] = MID_PITCH;
  269.         voiceKeyOn [i] = 0;
  270.         voiceNote [i] = 0;
  271.         }
  272.  
  273.     for (i = 0; i < 11; i++)
  274.         lVoiceVolume [i] = MAX_VOLUME;
  275.  
  276.     SetMode (0);                /* melodic mode */
  277.     SetGParam (0, 0, 0);        /* init global parameters */
  278.     SetPitchRange (1);          /* default pitch range is 1 half-tone */
  279.     SetWaveSel (1);
  280.     }   /* SoundWarmInit() */
  281.  
  282.  
  283.  
  284. /*
  285. ---------------------------------------------
  286.     Put the chip in melodic mode (mode == 0),
  287.     or in percussive mode  (mode != 0).
  288.  
  289.     If the melodic mode is chosen, all voices are
  290.     set to electric-piano, else the first 5 are set
  291.     to electric-piano, and the percussion voices
  292.     to their default timbres.
  293. ---------------------------------------------
  294. */
  295. SetMode (mode)
  296.     int mode;
  297.     {
  298.  
  299.     if (mode){
  300.         /* set the frequency for the last 4 percussion voices: */
  301.         voiceNote [TOM] = TOM_PITCH;
  302.         vPitchBend [TOM] = MID_PITCH;
  303.         UpdateFNums (TOM);
  304.  
  305.         voiceNote [SD] = SD_PITCH;
  306.         vPitchBend [SD] = MID_PITCH;
  307.         UpdateFNums (SD);
  308.         }
  309.     percussion = mode;
  310.     modeVoices = mode ? 11 : 9;
  311.     percBits = 0;
  312.  
  313.     InitSlotParams();
  314.     SndSAmVibRhythm();
  315.     }   /* SetMode() */
  316.  
  317.  
  318.  
  319.  
  320. /*
  321.     Enable (state != 0) / disable (state == 0)
  322.     the wave-select parameters.
  323.  
  324.     If you do not want to use the wave-select parameters, call
  325.     this function with a value of 0 AFTER calling SoundColdInit()
  326.     or SoundWarmInit().
  327. */
  328. SetWaveSel (state)
  329.     {
  330.     int i;
  331.  
  332.     modeWaveSel = state ? 0x20 : 0;
  333.     for (i = 0; i < 18; i++)
  334.         SndOutput (0xE0 + offsetSlot [i], 0);
  335.     SndOutput (1, modeWaveSel);
  336.     }   /* SetWaveSel() */
  337.  
  338.  
  339.  
  340. /*
  341.     Routine to change the pitch bend range. The value can be from
  342.     1 to 12 (in half-tones).
  343.  
  344.     For example, the value 12 means that the pitch bend will 
  345.     range from -12 (pitchBend == 0, see function 'SetVoicePitch()')
  346.     to +12 (pitchBend == 0x3fff) half-tones.
  347.  
  348.     The change will be effective as of the next call to
  349.     'SetVoicePitch()'.
  350. */
  351. SetPitchRange (pR)
  352.     unsigned pR;
  353.     {
  354.     if (pR > 12)
  355.         pR = 12;
  356.     if (pR < 1)
  357.         pR = 1;
  358.     pitchRange = pR;
  359.     }   /* SetPitchRange() */
  360.  
  361.  
  362. /*
  363. ----------------------------------------------
  364.     Set the 3 global parameters AmDepth,
  365.     VibDepth & NoteSel
  366.  
  367.     The change takes place immediately.
  368. ----------------------------------------------
  369. */
  370. SetGParam (amD, vibD, nSel)
  371.     int amD, vibD, nSel;
  372.     {
  373.     amDepth = amD;
  374.     vibDepth = vibD;
  375.     noteSel = nSel;
  376.  
  377.     SndSAmVibRhythm();
  378.     SndSNoteSel();
  379.     }   /* SetGParam() */
  380.  
  381.  
  382.  
  383.  
  384.  
  385. /*
  386. -------------------------------------------------
  387.     Set the parameters of the voice 'voice'.
  388.  
  389.     In melodic mode, 'voice' varies from 0 to 8.
  390.     In percussive mode, voices 0 to 5 are melodic
  391.     and 6 to 10 are percussive.
  392.  
  393.     A timbre (melodic or percussive) is defined as follows:
  394.     the 13 first parameters of operator 0  (ksl, multi, feedBack,
  395.     attack, sustain, eg-typem decay, release, level, am, vib, ksr, fm)
  396.     followed by the 13 parameters of operator 1 (if a percussive voice, all
  397.     the parameters are zero), followed by the wave-select parameter for
  398.     the operators 0 and 1.
  399.  
  400.     'paramArray' is structured as follows:
  401.         struct {
  402.             int opr0Prm [13];       first 13 parameters
  403.             int opr1Prm [13];       must be 0 if percussive timbre
  404.             int    opr0WaveSel;        last parameter
  405.             int opr1WaveSel;        must be 0 if percussive timbre
  406.         } TimbreDef;
  407.  
  408.     The old timbre files (*.INS) do not contain the parameters
  409.     'opr0WaveSel' and 'opr1WaveSel'.
  410.     Set these two parameters to zero if you are using the old file
  411.     format.
  412. ------------------------------------------------
  413. */
  414. SetVoiceTimbre (voice, paramArray)
  415.     unsigned voice;
  416.     unsigned * paramArray;
  417.     {
  418.     unsigned wave0, wave1;
  419.     unsigned * prm1, * wavePtr;
  420.     unsigned char * slots;
  421.  
  422.     if (voice >= modeVoices)
  423.         return;
  424.  
  425.     wavePtr = paramArray + 2 *  (nbLocParam -1);
  426.     wave0 = * wavePtr++;
  427.     wave1 = * wavePtr;
  428.     prm1 = paramArray + nbLocParam -1;
  429.  
  430.     if (percussion)
  431.         slots = slotPVoice [voice];
  432.     else
  433.         slots = slotMVoice [voice];
  434.     SetSlotParam (slots [0], paramArray, wave0);
  435.     if (slots [1] != 255)
  436.         SetSlotParam (slots [1], prm1, wave1);
  437.     }   /* SetVoiceTimbre() */
  438.  
  439.  
  440.  
  441.  
  442. /*
  443. --------------------------------------------------
  444.     Set the volume of the voice 'voice' to 'volume'.
  445.  
  446.     The resulting output level is (timbreVolume * volume / 127).
  447.     The change takes place immediately.
  448.  
  449.     0 <= volume <= 127
  450. --------------------------------------------------
  451. */
  452. SetVoiceVolume (voice, volume)
  453.     unsigned voice, volume;            /* 0 - 0x7f */
  454. {
  455.     unsigned char * slots;
  456.  
  457.     if (voice >= modeVoices)
  458.         return;
  459.     if (volume > MAX_VOLUME)
  460.         volume = MAX_VOLUME;
  461.     lVoiceVolume [voice] = volume;
  462.     if (percussion)
  463.         slots = slotPVoice [voice];
  464.     else
  465.         slots = slotMVoice [voice];
  466.     SndSKslLevel (slots [0]);
  467.     if (255 != slots [1])
  468.         SndSKslLevel (slots [1]);
  469. }   /* SetVoiceVolume() */
  470.  
  471.  
  472. /*
  473. -------------------------------------------------
  474.     Change the pitch value of a voice.
  475.  
  476.     The variation in pitch is a function of the previous call
  477.     to 'SetPitchRange()' and the value of 'pitchBend'.
  478.     A value of 0 means -half-tone * pitchRange,
  479.     0x2000 means no variation (exact pitch) and
  480.     0x3fff means +half-tone * pitchRange.
  481.  
  482.     Does not affect the percussive voices, except for the bass drum.
  483.  
  484.     The change takes place immediately.
  485.  
  486.     0 <= pitchBend <= 0x3fff, 0x2000 == exact tuning
  487. -------------------------------------------------
  488. */
  489. SetVoicePitch (voice, pitchBend)
  490.     unsigned voice;
  491.     unsigned pitchBend;
  492. {
  493.     if ((!percussion && voice < 9) || voice <= BD) {
  494.         /* melodic + bass-drum */
  495.         if (pitchBend > MAX_PITCH)
  496.             pitchBend = MAX_PITCH;
  497.         vPitchBend [voice] = pitchBend;
  498.         UpdateFNums (voice);
  499.         }
  500. }   /* SetVoicePitch() */
  501.  
  502.  
  503.  
  504. /*
  505. -----------------------------------------------------------
  506.     Routine to start a note playing.
  507.  
  508.     0 <= voice <= 8    in melodic mode,
  509.     0 <= voice <= 10 in percussive mode;
  510.     0 <= pitch <= 127, 60 == MID_C  (the card can play between 12 and 107 )
  511. -----------------------------------------------------------
  512. */
  513. NoteOn (voice, pitch)
  514.     unsigned voice;
  515.     int pitch;            /* 0 - 127 */
  516.     {
  517.     pitch -=  (MID_C - CHIP_MID_C);
  518.     if (pitch < 0)
  519.         pitch = 0;
  520.  
  521.     if ((!percussion && voice < 9) || voice < BD) {
  522.         /* this is a melodic voice */
  523.         voiceNote [voice] = pitch;
  524.         voiceKeyOn [voice] = 0x20;
  525.         UpdateFNums (voice);
  526.         }
  527.     else if (percussion && voice <= HIHAT) {
  528.         /* this is a percussive voice */
  529.         if (voice == BD) {
  530.             voiceNote [BD] = pitch;
  531.             UpdateFNums (voice);
  532.             }
  533.         else if (voice == TOM) {
  534.             /* for the last 4 percussions, only the TOM may change in frequency,
  535.                 modifying the three others: */
  536.             if (voiceNote [TOM] != pitch) {
  537.                 voiceNote [TOM] = pitch;
  538.                 voiceNote [SD] = pitch +TOM_TO_SD;
  539.                 UpdateFNums (TOM);
  540.                 UpdateFNums (SD);
  541.                 }
  542.             }
  543.         percBits |= percMasks [voice - BD];
  544.         SndSAmVibRhythm();
  545.         }
  546.     }   /* NoteOn() */
  547.  
  548.  
  549. /*
  550.     Routine to stop playing the note which was started in 'NoteOn()'.
  551.  
  552.     0 <= voice <= 8    in melodic mode,
  553.     0 <= voice <= 10 in percussive mode;
  554. */
  555. NoteOff (voice)
  556.     unsigned voice;    
  557.     {
  558.     if ((!percussion && voice < 9) || voice < BD) {
  559.         voiceKeyOn [voice] = 0;
  560.         bxRegister [voice] &= ~0x20;
  561.         SndOutput (0xB0 +voice, bxRegister [voice]);
  562.         }
  563.     else if (percussion && voice <= HIHAT) {
  564.         percBits &= ~percMasks [voice - BD];
  565.         SndSAmVibRhythm();
  566.         }
  567.     }   /* NoteOff() */
  568.  
  569.  
  570.  
  571.  
  572.  
  573. /*
  574. ------------------------------------------------------------------------
  575.     static functions ...
  576. ------------------------------------------------------------------------
  577. */
  578.  
  579.  
  580. /*
  581.     In melodic mode, initialize all voices to electric-pianos.
  582.  
  583.     In percussive mode, initialize the first 6 voices to electric-pianos
  584.     and the percussive voices to their default timbres.
  585. */
  586. static InitSlotParams()
  587. {
  588.     int i;
  589.     
  590.     for (i = 0; i < 18; i++)
  591.         if (carrierSlot [i])
  592.             SetCharSlotParam (i, pianoParamsOp1, 0);
  593.         else
  594.             SetCharSlotParam (i, pianoParamsOp0, 0);
  595.     if (percussion) {
  596.         SetCharSlotParam (12, bdOpr0, 0);
  597.         SetCharSlotParam (15, bdOpr1, 0);
  598.         SetCharSlotParam (16, sdOpr, 0);
  599.         SetCharSlotParam (14, tomOpr, 0);
  600.         SetCharSlotParam (17, cymbOpr, 0);
  601.         SetCharSlotParam (13, hhOpr, 0);
  602.         }
  603. }    /* InitSlotParams() */
  604.  
  605.  
  606.  
  607.  
  608. /*
  609.     Used to change the parameter 'param' of the slot 'slot'
  610.     with the value 'val'. The chip registers are updated.
  611. */
  612. SetASlotParam (slot, param, val)
  613.     int slot, val;
  614.     int param;        /* parameter number */
  615. {
  616.     paramSlot [slot] [param] = val;
  617.     SndSetPrm (slot, param);
  618. }    /* SetASlotParam() */
  619.  
  620.     
  621.  
  622.  
  623.  
  624.  
  625.  
  626. /*
  627. ------------------------------------------------------
  628.     Set the 14 parameters  (13 in 'param', 1 in 'waveSel')
  629.     of slot 'slot'. Update the parameter array and the chip.
  630. ------------------------------------------------------
  631. */
  632. static SetSlotParam (slot, param, waveSel)
  633.     unsigned slot, * param, waveSel;
  634. {
  635.     int i, k;
  636.     char * ptr;
  637.  
  638.     for (i = 0, ptr = ¶mSlot [slot] [0]; i < nbLocParam -1; i++)
  639.         *ptr++ = *param++;
  640.     *ptr = waveSel &= 0x3;
  641.     SndSetAllPrm (slot);
  642. }    /* SetSlotParam() */
  643.  
  644. SetCharSlotParam (slot, cParam, waveSel)
  645.     unsigned slot, waveSel;
  646.     unsigned char * cParam;
  647.     {
  648.     unsigned param [nbLocParam];
  649.     int i;
  650.  
  651.     for (i = 0; i < nbLocParam -1; i++)
  652.         param [i] = *cParam++;
  653.     SetSlotParam (slot, param, waveSel);
  654.     }    /* SetCharSlotParam() */
  655.  
  656.  
  657. /*
  658. -----------------------------------------------
  659.     Update the parameter 'prm' for the slot 'slot'.
  660.     Update the chip registers.
  661. -----------------------------------------------
  662. */
  663. static SndSetPrm (slot, prm)
  664.     int slot, prm;
  665. {
  666.  
  667.     switch (prm) {
  668.         case prmPercussion:
  669.         case prmAmDepth:
  670.         case prmVibDepth:
  671.             SndSAmVibRhythm();
  672.             break;
  673.  
  674.         case prmNoteSel:
  675.             SndSNoteSel();
  676.             break;
  677.  
  678.         case prmKsl:
  679.         case prmLevel:
  680.             SndSKslLevel (slot);
  681.             break;
  682.  
  683.         case prmFm:
  684.         case prmFeedBack:
  685.             SndSFeedFm (slot);
  686.             break;
  687.  
  688.         case prmAttack:
  689.         case prmDecay:
  690.             SndSAttDecay (slot);
  691.             break;
  692.  
  693.         case prmRelease:
  694.         case prmSustain:
  695.             SndSSusRelease (slot);
  696.             break;
  697.  
  698.         case prmMulti:
  699.         case prmVib:
  700.         case prmStaining:
  701.         case prmKsr:
  702.         case prmAm:
  703.             SndSAVEK (slot);
  704.             break;
  705.  
  706.         case prmWaveSel:
  707.             SndWaveSelect (slot);
  708.             break;
  709.         }
  710. }    /* SndSetPrm() */
  711.  
  712.  
  713.  
  714. /*-------------------------------------------------
  715.     Transfer all the parameters from slot 'slot' to
  716.     the chip.
  717. */
  718. static SndSetAllPrm (slot)
  719. {
  720.     SndSAmVibRhythm();
  721.     SndSNoteSel();
  722.     SndSKslLevel (slot);
  723.     SndSFeedFm (slot);
  724.     SndSAttDecay (slot);
  725.     SndSSusRelease (slot);
  726.     SndSAVEK (slot);
  727.     SndWaveSelect (slot);
  728. }    /* SndSetAllPrm() */
  729.     
  730.  
  731. /*
  732.     Write to the register which controls output level and does
  733.     key-on/key-offs for the percussive voice slots.
  734. */
  735. static SndSKslLevel (slot)
  736. {
  737.     unsigned t1, vc, singleSlot;
  738.  
  739.     if (percussion)
  740.         vc = voicePSlot [slot];
  741.     else
  742.         vc = voiceMSlot [slot];
  743.  
  744.     t1 = 63 - (GetLocPrm (slot, prmLevel) & 63);    /* amplitude */
  745.     singleSlot = percussion && vc > BD;
  746.  
  747.     if  ((carrierSlot [slot] || !GetLocPrm (slot, prmFm) || singleSlot))
  748.         /* Change the 0 - 127 volume change value to 0 - 63 for the chip.
  749.            (MAX_VOLUME+1)/2 is added to avoid round-off errors. */
  750.         t1 = (t1 * lVoiceVolume [vc] + (MAX_VOLUME+1)/2 ) >> LOG2_VOLUME;
  751.  
  752.     t1 = 63 - t1;
  753.     t1 |= GetLocPrm (slot, prmKsl) << 6;
  754.     SndOutput (0x40 + (int)offsetSlot [slot], t1);
  755. }
  756.  
  757.  
  758.  
  759. /* --------------------------------------------
  760.     Write to the register which controls the note select parameter.
  761. */
  762. static SndSNoteSel()
  763. {
  764.     SndOutput (0x08, noteSel ? 64 : 0);
  765. }    /* SndSNoteSel() */
  766.  
  767.  
  768.  
  769. /* --------------------------------------------
  770.     FEED-BACK and FM (connection).
  771.     Applicable only to operator 0 in melodic mode.
  772. */
  773. static SndSFeedFm (slot)
  774. {
  775.     unsigned t1;
  776.  
  777.     if (carrierSlot [slot])
  778.         return;
  779.     t1 = GetLocPrm (slot, prmFeedBack) << 1;
  780.     t1 |= GetLocPrm (slot, prmFm) ? 0 : 1;
  781.     SndOutput (0xC0 + (int)voiceMSlot [slot], t1);
  782. }
  783.  
  784.  
  785. /*
  786.     ATTACK, DECAY
  787. */
  788. static SndSAttDecay (slot)
  789. {
  790.     unsigned t1;
  791.  
  792.     t1 = GetLocPrm (slot, prmAttack) << 4;
  793.     t1 |= GetLocPrm (slot, prmDecay) & 0xf;
  794.     SndOutput (0x60 + (int)offsetSlot [slot], t1);
  795. }
  796.  
  797.  
  798. /*
  799.     SUSTAIN, RELEASE
  800. */
  801. static SndSSusRelease (slot)
  802. {
  803.     unsigned t1;
  804.  
  805.     t1 = GetLocPrm (slot, prmSustain) << 4;
  806.     t1 |= GetLocPrm (slot, prmRelease) & 0xf;
  807.     SndOutput (0x80 + (int)offsetSlot [slot], t1);
  808. }
  809.  
  810.  
  811.  
  812. /*
  813.     AM, VIB, EG-TYP (Sustaining), KSR, MULTI
  814. */
  815. static SndSAVEK (slot)
  816. {
  817.     unsigned t1;
  818.  
  819.     t1 = GetLocPrm (slot, prmAm) ? 0x80 : 0;
  820.     t1 += GetLocPrm (slot, prmVib) ? 0x40 : 0;
  821.     t1 += GetLocPrm (slot, prmStaining) ? 0x20 : 0;
  822.     t1 += GetLocPrm (slot, prmKsr) ? 0x10 : 0;
  823.     t1 += GetLocPrm (slot, prmMulti) & 0xf;
  824.     SndOutput (0x20 + (int)offsetSlot [slot], t1);
  825. }    /* SndSAVEK() */
  826.  
  827.  
  828. /*
  829.     Set the values: AM Depth, VIB depth & Rhythm (melo/perc mode)
  830. */
  831. static SndSAmVibRhythm()
  832. {
  833.     unsigned t1;
  834.  
  835.     t1 = amDepth ? 0x80 : 0;
  836.     t1 |= vibDepth ? 0x40 : 0;
  837.     t1 |= percussion ? 0x20 : 0;
  838.     t1 |= percBits;
  839.     SndOutput (0xBD, t1);
  840. }
  841.  
  842.  
  843. /*
  844.     Set the wave-select parameter.
  845. */
  846. static SndWaveSelect (slot)
  847.     {
  848.     unsigned wave;
  849.  
  850.     if (modeWaveSel)
  851.         wave = GetLocPrm (slot, prmWaveSel) & 0x03;
  852.     else
  853.         wave = 0;
  854.     SndOutput (0xE0 + offsetSlot [slot], wave);
  855.     }    /* SndWaveSelect() */
  856.  
  857.  
  858. /*
  859.     Change pitch, pitchBend & keyOn of voices 0 to 8, for melodic
  860.     or percussive mode.
  861. */
  862. static UpdateFNums (voice)
  863.     {
  864.     bxRegister [voice] = SetFreq (voice, voiceNote [voice],
  865.                                   vPitchBend [voice], voiceKeyOn [voice]);
  866.     }
  867.  
  868.  
  869.  
  870. /*
  871.     Return 0 if board is not installed.  The chip's timers are used to
  872.     determined if an Ad Lib card is present.  When being used, the timers
  873.     place specific values in the status register.  If we do not read the
  874.     correct values from the status register, then we can assume that no
  875.     board is present.
  876. */
  877. static int BoardInstalled()
  878.     {
  879.     unsigned t1, t2, i;
  880.  
  881.     SndOutput (4, 0x60);    /* mask T1 & T2 */
  882.     SndOutput (4, 0x80);    /* reset IRQ */
  883.     t1 = inp (genAddr);     /* read status register */
  884.     SndOutput (2, 0xff);    /* set timer-1 latch */
  885.     SndOutput (4, 0x21);    /* unmask & start T1 */
  886.  
  887.     for (i = 0; i < 200; i++)   /* 100 uSec delay for timer-1 overflow */
  888.         inp (genAddr);
  889.  
  890.     t2 = inp (genAddr);     /* read status register */
  891.     SndOutput (4, 0x60);
  892.     SndOutput (4, 0x80);
  893.  
  894.     return (t1 & 0xE0) == 0 && (t2 & 0xE0) == 0xC0;
  895.     }
  896.  
  897. /*----------------------------------------------------------------------*/
  898. #if 0
  899.  
  900. /* The following, OutFreq(), is provided as an alternative to SetFreq().
  901.    You may find it easier to understand as SetFreq is written in assembler.
  902.    However, SetFreq is faster so you should definitely use SetFreq. The
  903.    main purpose of including OutFreq() is to show you how to set the
  904.    f-number registers.
  905. */
  906.  
  907. static unsigned  freqNums [12] = {
  908. /*  C    C#   D    D#   E    F */
  909.    343, 363, 385, 408, 432, 458,
  910. /*  F#   G    G#   A    A#   B */
  911.    485, 514, 544, 577, 611, 647
  912. };
  913.  
  914. unsigned  OutFreq (voice, pitch, bend, keyOn)
  915.    int voice, pitch, bend, keyOn;
  916. {
  917.    unsigned effNbr, octave, t1;
  918.  
  919.    /* Integer division by 12 gives the octave (referred to as BLOCK
  920.       information in the manual). */
  921.    octave = (pitch / 12) - 1;
  922.  
  923.    /* The remainder of dividing by 12 gives the half-tone within the octave.
  924.       The freqNums table gives the value to place in the register given the
  925.       half-tone. */
  926.    effNbr = freqNums [pitch % 12];
  927.  
  928.    if (bend != 0x2000) {
  929.       /* Do a pitch bend.
  930.          pitchRange is the maximum interval for a pitch bend.
  931.          'bend' is the amount of pitch bend. */
  932.       long n;
  933.  
  934.       /* Below, the maximum interval is calculated in terms of fnums, and
  935.          then a fraction of this is added to the base value (effNbr). */
  936.       if (bend > 0x2000) {
  937.          bend -= 0x2000;
  938.          n = freqNums [(pitch + pitchRange) % 12];
  939.          if (n < effNbr) n <<= 1;
  940.          n = n - effNbr;                        /* interval as f-num */
  941.          effNbr = effNbr + ((n * bend) >> 13);  /* >> 13 is div by 0x2000 */
  942.  
  943.          /* If effNbr exceeds its maximum possible value, bring it into range
  944.             by dividing by two, which lowers the pitch by an octave, and add
  945.             1 to the octave to keep the pitch in the correct octave. */
  946.          while (effNbr > 1023) {
  947.             effNbr >>= 1;
  948.             octave++;
  949.          }
  950.       }
  951.       else {
  952.          bend = 0x2000 - bend;
  953.          n = freqNums [(pitch - pitchRange) % 12];
  954.          if (n > effNbr) n >>= 1;
  955.          n = effNbr - n;
  956.          effNbr = effNbr - ((n * bend) >> 13);
  957.  
  958.          /* If effNbr exceeds its minimum desirable value, bring it into range
  959.             by multiplying by two, which raises the pitch by an octave, and
  960.             subtract 1 from the octave to keep the pitch in the correct octave. */
  961.          while (effNbr < freqNums [0]) {
  962.             effNbr <<= 1;
  963.             octave--;
  964.          }
  965.       }
  966.    }
  967.  
  968.    /* Write the lower 8 bits of the f-number. */
  969.    SndOutput (0xA0 + voice, effNbr);
  970.  
  971.    /* Arrange the key-on bit, the octave and the upper two bits of the
  972.       f-number in the correct order and write to the register. */
  973.    t1 = keyOn | (octave << 2) | (effNbr >> 8);
  974.  
  975.    SndOutput (0xB0 + voice, t1);
  976.  
  977.    return (t1);
  978. }
  979.  
  980. #endif
  981.  
  982.