Annotation of ntddk/src/mmedia/synth/dll/adlib.c, revision 1.1

1.1     ! root        1: /*
        !             2:  * Copyright (c) 1992 Microsoft Corporation
        !             3:  */
        !             4: 
        !             5: 
        !             6: /*
        !             7:  * definition of interface functions to the adlib midi device type.
        !             8:  *
        !             9:  * These functions are called from midi.c when the kernel driver
        !            10:  * has decreed that this is an adlib-compatible device.
        !            11:  *
        !            12:  * Geraint Davies, Dec 92
        !            13:  */
        !            14: 
        !            15: #include <windows.h>
        !            16: #include <mmsystem.h>
        !            17: #include <mmddk.h>
        !            18: #include "driver.h"
        !            19: #include "adlib.h"
        !            20: 
        !            21: /*
        !            22:  * overview
        !            23:  *
        !            24:  * The FM synthesis chip consists of 18 operator cells or 'slots'. Each slot
        !            25:  * can produce a sine wave modified by a number of parameters such
        !            26:  * as frequency, output level and envelope shape (attack/decay/sustain
        !            27:  * release). Slots are arranged in pairs, with one slot modulating
        !            28:  * the sine wave of another to produce the harmonics desired for
        !            29:  * a given instrument sound. Thus one pair of slots is a 'voice' and can
        !            30:  * play one note at a time.
        !            31:  *
        !            32:  * In percussive mode (which we always use), there are 6 melodic voices
        !            33:  * available, and one voice for the base drum. The remaining four slots
        !            34:  * are used singly rather than in pairs to produce four further percussive
        !            35:  * voices. The 6 melodic voices will be changed to any given timbre
        !            36:  * as needed. The five percussive voices are fixed to particular instrument
        !            37:  * timbres: bass drum, snare, tom-tom, hi-hat and cymbal.
        !            38:  *
        !            39:  * To play a note, we first find a free voice of the appropriate type. If
        !            40:  * there are none free, we use the oldest busy one. We then set the
        !            41:  * parameters for both operator slots from the patch table - this table gives
        !            42:  * parameter settings for the different instrument timbres available.
        !            43:  * We adjust the output level and frequency depending on the
        !            44:  * note played and the velocity it was played with, and then switch on the
        !            45:  * note.
        !            46:  */
        !            47: 
        !            48: /* --- typedefs ------------------------------------------------- */
        !            49: 
        !            50: #define NUMVOICES     (11)             // number of voices we have
        !            51: #define NUMMELODIC    (6)              // number of melodic voices
        !            52: #define NUMPERCUSSIVE (5)              // number of percussive voices
        !            53: 
        !            54: #define MAXPATCH      180              // nr of patches (including drums)
        !            55: #define MAXVOLUME    0x7f
        !            56: #define NUMLOCPARAM    14               // number of loc params per slot
        !            57: 
        !            58: #define FIRSTDRUMNOTE  35
        !            59: #define LASTDRUMNOTE   81
        !            60: #define NUMDRUMNOTES   (LASTDRUMNOTE - FIRSTDRUMNOTE + 1)
        !            61: 
        !            62: #define MAX_PITCH    0x3fff            // maximum pitch bend value
        !            63: #define MID_PITCH    0x2000            // mid pitch bend value (no shift)
        !            64: #define PITCHRANGE       2             // total bend range +- 2 semitones
        !            65: #define NR_STEP_PITCH    25            // steps per half-tone for pitch bend
        !            66: #define MID_C            60            // MIDI standard mid C
        !            67: #define CHIP_MID_C       48            // sound chip mid C
        !            68: 
        !            69: /*
        !            70:  * to write to the device, we write a SYNTH_DATA port,data pair
        !            71:  * to the kernel driver.
        !            72:  */
        !            73: #define SndOutput(p,d) MidiSendFM(p, d)
        !            74: 
        !            75: 
        !            76: 
        !            77: 
        !            78: /****************************************************************************
        !            79:  *
        !            80:  *   definitions of sound chip parameters
        !            81:  */
        !            82: 
        !            83: // parameters of each voice
        !            84: #define prmKsl          0         // key scale level (KSL) - level controller
        !            85: #define prmMulti        1         // frequency multiplier (MULTI) - oscillator
        !            86: #define prmFeedBack     2         // modulation feedback (FB) - oscillator
        !            87: #define prmAttack       3         // attack rate (AR) - envelope generator
        !            88: #define prmSustain      4         // sustain level (SL) - envelope generator
        !            89: #define prmStaining     5         // sustaining sound (SS) - envelope generator
        !            90: #define prmDecay        6         // decay rate (DR) - envelope generator
        !            91: #define prmRelease      7         // release rate (RR) - envelope generator
        !            92: #define prmLevel        8         // output level (OL) - level controller
        !            93: #define prmAm           9         // amplitude vibrato (AM) - level controller
        !            94: #define prmVib          10        // frequency vibrator (VIB) - oscillator
        !            95: #define prmKsr          11        // envelope scaling (KSR) - envelope generator
        !            96: #define prmFm           12        // fm=0, additive=1 (FM) (operator 0 only)
        !            97: #define prmWaveSel      13        // wave select
        !            98: 
        !            99: // global parameters
        !           100: #define prmAmDepth      14
        !           101: #define prmVibDepth     15
        !           102: #define prmNoteSel      16
        !           103: #define prmPercussion   17
        !           104: 
        !           105: // percussive voice numbers (0-5 are melodic voices, 2 op):
        !           106: #define BD              6         // bass drum (2 op)
        !           107: #define SD              7         // snare drum (1 op)
        !           108: #define TOM             8         // tom-tom (1 op)
        !           109: #define CYMB            9         // cymbal (1 op)
        !           110: #define HIHAT           10        // hi-hat (1 op)
        !           111: 
        !           112: // In percussive mode, the last 4 voices (SD TOM HH CYMB) are created
        !           113: // using melodic voices 7 & 8. A noise generator uses channels 7 & 8
        !           114: // frequency information for creating rhythm instruments. Best results
        !           115: // are obtained by setting TOM two octaves below mid C and SD 7 half-tones
        !           116: // above TOM. In this implementation, only the TOM pitch may vary, with the
        !           117: // SD always set 7 half-tones above.
        !           118: #define TOM_PITCH        24      // best frequency, in range of 0 to 95
        !           119: #define TOM_TO_SD        7       // 7 half-tones between voice 7 & 8
        !           120: #define SD_PITCH         (TOM_PITCH + TOM_TO_SD)
        !           121: 
        !           122: /****************************************************************************
        !           123:  *
        !           124:  *   bank file support
        !           125:  *
        !           126:  ***************************************************************************/
        !           127: 
        !           128: #define BANK_SIG_LEN        6
        !           129: #define BANK_FILLER_SIZE    8
        !           130: 
        !           131: typedef BYTE *HPBYTE;
        !           132: typedef BYTE NEAR * NPBYTE;
        !           133: typedef WORD NEAR * NPWORD;
        !           134: 
        !           135: // instrument bank file header
        !           136: typedef struct {
        !           137:     char      majorVersion;
        !           138:     char      minorVersion;
        !           139:     char      sig[BANK_SIG_LEN];            // signature: "ADLIB-"
        !           140:     WORD      nrDefined;                    // number of list entries used
        !           141:     WORD      nrEntry;                      // number of total entries in list
        !           142:     long      offsetIndex;                  // offset of start of list of names
        !           143:     long      offsetTimbre;                 // offset of start of data
        !           144:     char      filler[BANK_FILLER_SIZE];     // must be zero
        !           145: } BANKHDR, NEAR *NPBANKHDR, FAR *LPBANKHDR;
        !           146: 
        !           147: 
        !           148: // all the parameters for one slot
        !           149: typedef struct {
        !           150:     BYTE ksl;            // KSL
        !           151:     BYTE freqMult;       // MULTI
        !           152:     BYTE feedBack;       // FB
        !           153:     BYTE attack;         // AR
        !           154:     BYTE sustLevel;      // SL
        !           155:     BYTE sustain;        // SS
        !           156:     BYTE decay;          // DR
        !           157:     BYTE release;        // RR
        !           158:     BYTE output;         // OL
        !           159:     BYTE am;             // AM
        !           160:     BYTE vib;            // VIB
        !           161:     BYTE ksr;            // KSR
        !           162:     BYTE fm;             // FM
        !           163: } OPERATOR, NEAR *NPOPERATOR, FAR *LPOPERATOR;
        !           164: 
        !           165: // definition of a particular instrument sound or timbre -
        !           166: // one of these per patch
        !           167: typedef struct {
        !           168:     BYTE      mode;             // 0 = melodic, 1 = percussive
        !           169:     BYTE      percVoice;        // if mode == 1, voice number to be used
        !           170:     OPERATOR  op0;             // params for slot 0
        !           171:     OPERATOR  op1;             // params for slot 1
        !           172:     BYTE      wave0;            // waveform for operator 0
        !           173:     BYTE      wave1;            // waveform for operator 1
        !           174: } TIMBRE, NEAR *NPTIMBRE, FAR *LPTIMBRE;
        !           175: 
        !           176: typedef struct drumpatch_tag {
        !           177:     BYTE patch;                 // the patch to use
        !           178:     BYTE note;                  // the note to play
        !           179: } DRUMPATCH;
        !           180: 
        !           181: // format of drumkit.dat file
        !           182: typedef struct drumfilepatch_tag {
        !           183:     BYTE key;                   // the key to map
        !           184:     BYTE patch;                 // the patch to use
        !           185:     BYTE note;                  // the note to play
        !           186: } DRUMFILEPATCH, *PDRUMFILEPATCH;
        !           187: 
        !           188: 
        !           189: typedef struct _VOICE {
        !           190:     BYTE alloc;                 // is voice allocated?
        !           191:     BYTE note;                  // note that is currently being played
        !           192:     BYTE channel;               // channel that it is being played on
        !           193:     BYTE volume;                // current volume setting of voice
        !           194:     DWORD dwTimeStamp;          // time voice was allocated
        !           195: } VOICE;
        !           196: 
        !           197: #define GetLocPrm(slot_num, prm)   ((WORD)paramSlot[slot_num][prm])
        !           198: 
        !           199: 
        !           200: /* --- module data ---------------------------------------------- */
        !           201: 
        !           202: // this table gives the offset of each slot within the chip.
        !           203: BYTE offsetSlot[] = {
        !           204:          0,  1,  2,  3,  4,  5,
        !           205:          8,  9, 10, 11, 12, 13,
        !           206:         16, 17, 18, 19, 20, 21
        !           207: };
        !           208: 
        !           209: static BYTE percMasks[] = {
        !           210:         0x10, 0x08, 0x04, 0x02, 0x01 };
        !           211: 
        !           212: // voice number associated with each slot (melodic mode only)
        !           213: static BYTE voiceSlot[] = {
        !           214:         0, 1, 2,
        !           215:         0, 1, 2,
        !           216:         3, 4, 5,
        !           217:         3, 4, 5,
        !           218:         6, 7, 8,
        !           219:         6, 7, 8,
        !           220: };
        !           221: 
        !           222: // slot numbers for percussive voices (0 indicates that there is only one slot)
        !           223: static BYTE slotPerc[][2] = {
        !           224:         {12, 15},        // Bass Drum
        !           225:         {16, 0},         // SD
        !           226:         {14, 0},         // TOM
        !           227:         {17, 0},         // TOP-CYM
        !           228:         {13, 0}          // HH
        !           229: };
        !           230: 
        !           231: // slot numbers as a function of the voice and the operator (melodic only)
        !           232: static BYTE slotVoice[][2] = {
        !           233:         {0, 3},          // voice 0
        !           234:         {1, 4},          // 1
        !           235:         {2, 5},          // 2
        !           236:         {6, 9},          // 3
        !           237:         {7, 10},         // 4
        !           238:         {8, 11},         // 5
        !           239:         {12, 15},        // 6
        !           240:         {13, 16},        // 7
        !           241:         {14, 17}         // 8
        !           242: };
        !           243: 
        !           244: // this table indicates if the slot is a modulator (0) or a carrier (1).
        !           245: BYTE operSlot[] = {
        !           246:         0, 0, 0,           // 1 2 3
        !           247:         1, 1, 1,           // 4 5 6
        !           248:         0, 0, 0,           // 7 8 9
        !           249:         1, 1, 1,           // 10 11 12
        !           250:         0, 0, 0,           // 13 14 15
        !           251:         1, 1, 1,           // 16 17 18
        !           252: };
        !           253: 
        !           254: // this array adjusts the octave of a note for certain patches.
        !           255: static char patchKeyOffset[] = {
        !           256:        0, -12,  12,   0,   0,  12, -12,   0,   0, -24,   // 0 - 9
        !           257:        0,   0,   0,   0,   0,   0,   0,   0, -12,   0,   // 10 - 19
        !           258:        0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   // 20 - 29
        !           259:        0,   0,  12,  12,  12,   0,   0,  12,  12,   0,   // 30 - 39
        !           260:      -12, -12,   0,  12, -12, -12,   0,  12,   0,   0,   // 40 - 49
        !           261:      -12,   0,   0,   0,  12,  12,   0,   0,  12,   0,   // 50 - 59
        !           262:        0,   0,  12,   0,   0,   0,  12,  12,   0,  12,   // 60 - 69
        !           263:        0,   0, -12,   0, -12, -12,   0,   0, -12, -12,   // 70 - 79
        !           264:        0,   0,   0,   0,   0, -12, -19,   0,   0, -12,   // 80 - 89
        !           265:        0,   0,   0,   0,   0,   0, -31, -12,   0,  12,   // 90 - 99
        !           266:       12,  12,  12,   0,  12,   0,  12,   0,   0,   0,   // 100 - 109
        !           267:        0,  12,   0,   0,   0,   0,  12,  12,  12,   0,   // 110 - 119
        !           268:        0,   0,   0,   0, -24, -36,   0,   0};            // 120 - 127
        !           269: 
        !           270: static BYTE loudervol[128] = {
        !           271:     0,   0,  65,  65,  66,  66,  67,  67,         // 0 - 7
        !           272:    68,  68,  69,  69,  70,  70,  71,  71,         // 8 - 15
        !           273:    72,  72,  73,  73,  74,  74,  75,  75,         // 16 - 23
        !           274:    76,  76,  77,  77,  78,  78,  79,  79,         // 24 - 31
        !           275:    80,  80,  81,  81,  82,  82,  83,  83,         // 32 - 39
        !           276:    84,  84,  85,  85,  86,  86,  87,  87,         // 40 - 47
        !           277:    88,  88,  89,  89,  90,  90,  91,  91,         // 48 - 55
        !           278:    92,  92,  93,  93,  94,  94,  95,  95,         // 56 - 63
        !           279:    96,  96,  97,  97,  98,  98,  99,  99,         // 64 - 71
        !           280:   100, 100, 101, 101, 102, 102, 103, 103,         // 72 - 79
        !           281:   104, 104, 105, 105, 106, 106, 107, 107,         // 80 - 87
        !           282:   108, 108, 109, 109, 110, 110, 111, 111,         // 88 - 95
        !           283:   112, 112, 113, 113, 114, 114, 115, 115,         // 96 - 103
        !           284:   116, 116, 117, 117, 118, 118, 119, 119,         // 104 - 111
        !           285:   120, 120, 121, 121, 122, 122, 123, 123,         // 112 - 119
        !           286:   124, 124, 125, 125, 126, 126, 127, 127};        // 120 - 127
        !           287: 
        !           288: /*
        !           289:  * attenuation setting for each slot - combination
        !           290:  * of the channel attenuation for this channel, and the
        !           291:  * velocity (converted to attenuation). Combine with
        !           292:  * global attenuation and timbre output attenuation before
        !           293:  * writing to device.
        !           294:  */
        !           295: WORD slotAtten[18];            // vol-control attenuation of slots
        !           296: 
        !           297: WORD wSynthAtten;              // overall volume setting       
        !           298: 
        !           299: BYTE gbChanAtten[NUMCHANNELS]; // attenuation for each channel
        !           300: 
        !           301: VOICE voices[11];              // 9 voices if melodic mode or 11 if percussive
        !           302: BYTE voiceKeyOn[11];           // keyOn bit for each voice (implicit 0 init)
        !           303: BYTE paramSlot[18][NUMLOCPARAM]; // all the parameters of slots...
        !           304: 
        !           305: BYTE percBits;              // control bits of percussive voices
        !           306: 
        !           307: WORD fNumNotes[NR_STEP_PITCH][12];
        !           308: PWORD fNumFreqPtr[11];      // lines of fNumNotes table (one per voice)
        !           309: int  halfToneOffset[11];    // one per voice
        !           310: BYTE notePitch[11];         // pitch value for each voice (implicit 0 init)
        !           311: 
        !           312: // patches - parameters for different instrument timbres
        !           313: TIMBRE    patches[MAXPATCH];            // patch data
        !           314: DRUMPATCH drumpatch[NUMDRUMNOTES];      // drum kit data
        !           315: 
        !           316: 
        !           317: DWORD dwAge = 0;                // voice relative age
        !           318: 
        !           319: 
        !           320: 
        !           321: 
        !           322: /* --- internal functions --------------------------------------- */
        !           323: 
        !           324: 
        !           325: 
        !           326: /* -- initialisation --- */
        !           327: /****************************************************************************
        !           328:  * @doc INTERNAL
        !           329:  *
        !           330:  * @api int | LoadPatches | Reads the patch set from the BANK resource and
        !           331:  *     builds the <p patches> array.
        !           332:  *
        !           333:  * @rdesc Returns the number of patches loaded, or 0 if an error occurs.
        !           334:  ***************************************************************************/
        !           335: static BYTE PatchRes[] = {
        !           336: #include "adlib.dat"
        !           337: };
        !           338: 
        !           339: int  LoadPatches(void)
        !           340: {
        !           341: #ifdef WIN16
        !           342:     HANDLE hResInfo;
        !           343:     HANDLE hResData;
        !           344: #endif
        !           345:     LPSTR  lpRes;
        !           346:     int    iPatches;
        !           347:     DWORD  dwOffset;
        !           348:     DWORD  dwResSize;
        !           349:     LPTIMBRE  lpBankTimbre;
        !           350:     LPTIMBRE  lpPatchTimbre;
        !           351:     LPBANKHDR lpBankHdr;
        !           352: 
        !           353: 
        !           354:     // find resource and get its size
        !           355: #ifdef WIN16
        !           356:     hResInfo = FindResource(ghInstance, MAKEINTRESOURCE(DEFAULTBANK), MAKEINTRESOURCE(RT_BANK));
        !           357:     if (!hResInfo) {
        !           358:         D1("Default bank resource not found");
        !           359:         return 0;
        !           360:     }
        !           361:     dwResSize = (DWORD)SizeofResource(ghInstance, hResInfo);
        !           362: 
        !           363:     // load and lock resource
        !           364:     hResData = LoadResource(ghInstance, hResInfo);
        !           365:     if (!hResData) {
        !           366:         D1("Bank resource not loaded");
        !           367:         return 0;
        !           368:     }
        !           369:     lpRes = LockResource(hResData);
        !           370:     if (!lpRes) {
        !           371:         D1("Bank resource not locked");
        !           372:         return 0;
        !           373:     }
        !           374: #else
        !           375:     lpRes = PatchRes;
        !           376:     dwResSize = sizeof(PatchRes);
        !           377: #endif
        !           378: 
        !           379:     // read the bank resource, loading patches as we find them
        !           380: 
        !           381:     D2("loading patches");
        !           382:     lpBankHdr = (LPBANKHDR)lpRes;
        !           383:     dwOffset = lpBankHdr->offsetTimbre;                // point to first one
        !           384: 
        !           385:     for (iPatches = 0; iPatches < MAXPATCH; iPatches++) {
        !           386: 
        !           387:         lpBankTimbre = (LPTIMBRE)(lpRes + dwOffset);
        !           388:         lpPatchTimbre = &patches[iPatches];
        !           389:         *lpPatchTimbre = *lpBankTimbre;
        !           390: 
        !           391:         dwOffset += sizeof(TIMBRE);
        !           392:         if (dwOffset + sizeof(TIMBRE) > dwResSize) {
        !           393:             D1("Attempt to read past end of bank resource");
        !           394:             break;
        !           395:         }
        !           396:     }
        !           397: 
        !           398: #ifdef WIN16
        !           399:     UnlockResource(hResData);
        !           400:     FreeResource(hResData);
        !           401: #endif
        !           402: 
        !           403:     return iPatches;
        !           404: }
        !           405: 
        !           406: 
        !           407: /****************************************************************************
        !           408:  * @doc INTERNAL
        !           409:  *
        !           410:  * @api int | LoadDrumPatches | Reads the drum kit patch set from the
        !           411:  *     DRUMKIT resource and builds the <p drumpatch> array.
        !           412:  *
        !           413:  * @comm Each entry of the <t drumpatch> array (representing a key number
        !           414:  *     from the "drum patch") consists of a patch number and note number
        !           415:  *     from some other patch.
        !           416:  *
        !           417:  * @rdesc Returns the number of patches loaded, or 0 if an error occurs.
        !           418:  ***************************************************************************/
        !           419: 
        !           420: static BYTE DrumRes[] = {
        !           421: #include "drumkit.dat"
        !           422: };
        !           423: 
        !           424: int LoadDrumPatches(void)
        !           425: {
        !           426: #ifdef WIN16
        !           427:     HANDLE hResInfo;
        !           428:     HANDLE hResData;
        !           429: #endif
        !           430:     LPSTR  lpRes;
        !           431:     int    iPatches;
        !           432:     int    key;
        !           433:     DWORD  dwOffset;
        !           434:     DWORD  dwResSize;
        !           435:     PDRUMFILEPATCH lpResPatch;
        !           436: 
        !           437: #ifdef WIN16
        !           438:     // find resource and get its size
        !           439:     hResInfo = FindResource(ghInstance, MAKEINTRESOURCE(DEFAULTDRUMKIT), MAKEINTRESOURCE(RT_DRUMKIT));
        !           440:     if (!hResInfo) {
        !           441:         D1("Default drum resource not found");
        !           442:         return 0;
        !           443:     }
        !           444:     dwResSize = (DWORD)SizeofResource(ghInstance, hResInfo);
        !           445: 
        !           446:     // load and lock resource
        !           447:     hResData = LoadResource(ghInstance, hResInfo);
        !           448:     if (!hResData) {
        !           449:         D1("Drum resource not loaded");
        !           450:         return 0;
        !           451:     }
        !           452:     lpRes = LockResource(hResData);
        !           453:     if (!lpRes) {
        !           454:         D1("Drum resource not locked");
        !           455:         return 0;
        !           456:     }
        !           457: #else
        !           458:     lpRes = DrumRes;
        !           459:     dwResSize = sizeof(DrumRes);
        !           460: #endif
        !           461: 
        !           462:     // read the drum resource, loading patches as we find them
        !           463: 
        !           464:     D2("reading drum data");
        !           465:     dwOffset = 0;
        !           466:     for (iPatches = 0; iPatches < NUMDRUMNOTES; iPatches++) {
        !           467: 
        !           468:         lpResPatch = (PDRUMFILEPATCH)(lpRes + dwOffset);
        !           469:         key = lpResPatch->key;
        !           470:         if ((key >= FIRSTDRUMNOTE) && (key <= LASTDRUMNOTE)) {
        !           471:             drumpatch[key - FIRSTDRUMNOTE].patch = lpResPatch->patch;
        !           472:             drumpatch[key - FIRSTDRUMNOTE].note = lpResPatch->note;
        !           473:         }
        !           474:         else {
        !           475:             D1("Drum patch key out of range");
        !           476:         }
        !           477: 
        !           478:         dwOffset += sizeof(DRUMFILEPATCH);
        !           479:         if (dwOffset > dwResSize) {
        !           480:             D1("Attempt to read past end of drum resource");
        !           481:             break;
        !           482:         }
        !           483:     }
        !           484: 
        !           485: #ifdef WIN16
        !           486:     UnlockResource(hResData);
        !           487:     FreeResource(hResData);
        !           488: #endif
        !           489: 
        !           490:     return iPatches;
        !           491: }
        !           492: 
        !           493: /****************************************************************************
        !           494:  * @doc INTERNAL
        !           495:  *
        !           496:  * @api long | CalcPremFNum | Calculates some magic number that is used in
        !           497:  *     setting the values in the <p fNumNotes> table.
        !           498:  *
        !           499:  * @parm int | numDeltaDemiTon | Numerator (-100 to +100).
        !           500:  *
        !           501:  * @parm int | denDeltaDemiTon | Denominator (1 to 100).
        !           502:  *
        !           503:  * @comm If the numerator (numDeltaDemiTon) is positive, the frequency is
        !           504:  *     increased; if negative, it is decreased.  The function calculates:
        !           505:  *         f8 = Fb(1 + 0.06 num /den)          (where Fb = 26044 * 2 / 25)
        !           506:  *         fNum8 = f8 * 65536 * 72 / 3.58e6
        !           507:  *
        !           508:  * @rdesc Returns fNum8, which is the binary value of the frequency 260.44 (C)
        !           509:  *     shifted by +/- <p numdeltaDemiTon> / <p denDeltaDemiTon> * 8.
        !           510:  ***************************************************************************/
        !           511: long NEAR CalcPremFNum(int numDeltaDemiTon, int denDeltaDemiTon)
        !           512: {
        !           513:     long f8;
        !           514:     long fNum8;
        !           515:     long d100;
        !           516: 
        !           517:     d100 = denDeltaDemiTon * 100;
        !           518:     f8 = (d100 + 6 * numDeltaDemiTon) * (26044L * 2L);
        !           519:     f8 /= d100 * 25;
        !           520: 
        !           521:     fNum8 = f8 * 16384;
        !           522:     fNum8 *= 9L;
        !           523:     fNum8 /= 179L * 625L;
        !           524: 
        !           525:     return fNum8;
        !           526: }
        !           527: 
        !           528: /****************************************************************************
        !           529:  * @doc INTERNAL
        !           530:  *
        !           531:  * @api void | SetFNum | Initializes a line in the frequency table with
        !           532:  *     shifted frequency values.  The values are shifted a fraction (num/den)
        !           533:  *     of a half-tone.
        !           534:  *
        !           535:  * @parm NPWORD | fNumVec | The line from the frequency table.
        !           536:  *
        !           537:  * @parm int | num | Numerator.
        !           538:  *
        !           539:  * @parm int | den | Denominator.
        !           540:  *
        !           541:  * @xref CalcPremFNum
        !           542:  *
        !           543:  * @rdesc There is no return value.
        !           544:  ***************************************************************************/
        !           545: void NEAR SetFNum(NPWORD fNumVec, int num, int den)
        !           546: {
        !           547:     int  i;
        !           548:     long val;
        !           549: 
        !           550:     *fNumVec++ = (WORD)((4 + (val = CalcPremFNum(num, den))) >> 3);
        !           551:     for (i = 1; i < 12; i++) {
        !           552:         val *= 106;
        !           553:         *fNumVec++ = (WORD)((4 + (val /= 100)) >> 3);
        !           554:     }
        !           555: }
        !           556: 
        !           557: /****************************************************************************
        !           558:  * @doc INTERNAL
        !           559:  *
        !           560:  * @api void | InitFNums | Initializes all lines of the frequency table
        !           561:  *     (the <p fNumNotes> array). Each line represents 12 half-tones shifted
        !           562:  *     by (n / NR_STEP_PITCH), where 'n' is the line number and ranges from
        !           563:  *     1 to NR_STEP_PITCH.
        !           564:  *
        !           565:  * @rdesc There is no return value.
        !           566:  ***************************************************************************/
        !           567: void NEAR InitFNums(void)
        !           568: {
        !           569:     WORD i;
        !           570:     WORD num;           // numerator
        !           571:     WORD numStep;       // step value for numerator
        !           572:     WORD row;           // row in the frequency table
        !           573: 
        !           574:     // calculate each row in the fNumNotes table
        !           575:     numStep = 100 / NR_STEP_PITCH;
        !           576:     for (num = row = 0; row < NR_STEP_PITCH; row++, num += numStep)
        !           577:         SetFNum(fNumNotes[row], num, 100);
        !           578: 
        !           579:     // fNumFreqPtr has an element for each voice, pointing to the
        !           580:     // appropriate row in the fNumNotes table.  They're all initialized
        !           581:     // to the first row, which represents no pitch shift.
        !           582:     for (i = 0; i < 11; i++) {
        !           583:         fNumFreqPtr[i] = fNumNotes[0];
        !           584:         halfToneOffset[i] = 0;
        !           585:     }
        !           586: }
        !           587: 
        !           588: /****************************************************************************
        !           589:  * @doc INTERNAL
        !           590:  *
        !           591:  * @api void | SoundChut | Sets the frequency of voice <p voice> to 0 Hz.
        !           592:  *
        !           593:  * @parm BYTE | voice | Specifies which voice to set.
        !           594:  *
        !           595:  * @rdesc There is no return value.
        !           596:  ***************************************************************************/
        !           597: void NEAR SoundChut(BYTE voice)
        !           598: {
        !           599: 
        !           600:     SndOutput((BYTE)(0xA0 | voice), 0);
        !           601:     SndOutput((BYTE)(0xB0 | voice), 0);
        !           602: }
        !           603: 
        !           604: /* --- write parameters to chip ------------------------*/
        !           605: 
        !           606: /*
        !           607:  * switch chip to rhythm (percussive) mode, set the amplitude and
        !           608:  * vibrato depth and switch on any percussion voices that are on
        !           609:  */
        !           610: void SndSAmVibRhythm(void)
        !           611: {
        !           612: 
        !           613:     // we always set amdepth, vibdepth to 0 and perc mode on
        !           614: 
        !           615:     // t1 = (BYTE)(amDepth ? 0x80 : 0);
        !           616:     // t1 |= vibDepth ? 0x40 : 0;
        !           617:     // t1 |= (percussionmode ? 0x20 : 0);
        !           618: 
        !           619:     SndOutput((BYTE)0xBD, (BYTE)(0x20|percBits));
        !           620: }
        !           621: 
        !           622: /****************************************************************************
        !           623:  * @doc INTERNAL
        !           624:  *
        !           625:  * @api void | SndSNoteSel | Sets the NoteSel value.
        !           626:  *
        !           627:  * @rdesc There is no return value.
        !           628:  ***************************************************************************/
        !           629: void SndSNoteSel(BOOL bNoteSel)
        !           630: {
        !           631:     SndOutput(0x08, (BYTE)(bNoteSel ? 64 : 0));
        !           632: }
        !           633: 
        !           634: /****************************************************************************
        !           635:  * @doc INTERNAL
        !           636:  *
        !           637:  * @api void | SndSKslLevel | Sets the KSL and LEVEL values.
        !           638:  *
        !           639:  * @parm BYTE | slot | Specifies which slot to set.
        !           640:  *
        !           641:  * @rdesc There is no return value.
        !           642:  ***************************************************************************/
        !           643: void SndSKslLevel(BYTE slot)
        !           644: {
        !           645:     WORD t1;
        !           646: 
        !           647:     t1 = GetLocPrm(slot, prmLevel) & 0x3f;
        !           648: 
        !           649:     t1 += slotAtten[slot];
        !           650:     t1 = min (t1, 0x3f);
        !           651:     t1 |= GetLocPrm(slot, prmKsl) << 6;
        !           652:     SndOutput((BYTE)(0x40 | offsetSlot[slot]), (BYTE) t1);
        !           653: }
        !           654: 
        !           655: 
        !           656: /****************************************************************************
        !           657:  * @doc INTERNAL
        !           658:  *
        !           659:  * @api void | SndSAVEK | Sets the AM, VIB, EG-TYP (sustaining), KSR, and
        !           660:  *     MULTI values.
        !           661:  *
        !           662:  * @parm BYTE | slot | Specifies which slot to set.
        !           663:  *
        !           664:  * @rdesc There is no return value.
        !           665:  ***************************************************************************/
        !           666: void SndSAVEK(BYTE slot)
        !           667: {
        !           668:     BYTE t1;
        !           669: 
        !           670:     t1 = (BYTE)(GetLocPrm(slot, prmAm) ? 0x80 : 0);
        !           671:     t1 += GetLocPrm(slot, prmVib) ? 0x40 : 0;
        !           672:     t1 += GetLocPrm(slot, prmStaining) ? 0x20 : 0;
        !           673:     t1 += GetLocPrm(slot, prmKsr) ? 0x10 : 0;
        !           674:     t1 += GetLocPrm(slot, prmMulti) & 0xf;
        !           675:     SndOutput((BYTE)(0x20 | offsetSlot[slot]), t1);
        !           676: }
        !           677: 
        !           678: /****************************************************************************
        !           679:  * @doc INTERNAL
        !           680:  *
        !           681:  * @api void | SndSFeedFm | Sets the FEEDBACK and FM (connection) values.
        !           682:  *     Applicable only to operator 0 for melodic voices.
        !           683:  *
        !           684:  * @parm BYTE | slot | Specifies which slot to set.
        !           685:  *
        !           686:  * @rdesc There is no return value.
        !           687:  ***************************************************************************/
        !           688: void SndSFeedFm(BYTE slot)
        !           689: {
        !           690:     BYTE t1;
        !           691: 
        !           692:     if (operSlot[slot])
        !           693:         return;
        !           694: 
        !           695:     t1 = (BYTE)(GetLocPrm(slot, prmFeedBack) << 1);
        !           696:     t1 |= GetLocPrm(slot, prmFm) ? 0 : 1;
        !           697:     SndOutput((BYTE)(0xC0 | voiceSlot[slot]), t1);
        !           698: }
        !           699: 
        !           700: /****************************************************************************
        !           701:  * @doc INTERNAL
        !           702:  *
        !           703:  * @api void | SndSAttDecay | Sets the ATTACK and DECAY values.
        !           704:  *
        !           705:  * @parm BYTE | slot | Specifies which slot to set.
        !           706:  *
        !           707:  * @rdesc There is no return value.
        !           708:  ***************************************************************************/
        !           709: void SndSAttDecay(BYTE slot)
        !           710: {
        !           711:     BYTE t1;
        !           712: 
        !           713:     t1 = (BYTE)(GetLocPrm(slot, prmAttack) << 4);
        !           714:     t1 |= GetLocPrm(slot, prmDecay) & 0xf;
        !           715:     SndOutput((BYTE)(0x60 | offsetSlot[slot]), t1);
        !           716: }
        !           717: 
        !           718: /****************************************************************************
        !           719:  * @doc INTERNAL
        !           720:  *
        !           721:  * @api void | SndSSusRelease | Sets the SUSTAIN and RELEASE values.
        !           722:  *
        !           723:  * @parm BYTE | slot | Specifies which slot to set.
        !           724:  *
        !           725:  * @rdesc There is no return value.
        !           726:  ***************************************************************************/
        !           727: void SndSSusRelease(BYTE slot)
        !           728: {
        !           729:     BYTE t1;
        !           730: 
        !           731:     t1 = (BYTE)(GetLocPrm(slot, prmSustain) << 4);
        !           732:     t1 |= GetLocPrm(slot, prmRelease) & 0xf;
        !           733:     SndOutput((BYTE)(0x80 | offsetSlot[slot]), t1);
        !           734: }
        !           735: 
        !           736: /****************************************************************************
        !           737:  * @doc INTERNAL
        !           738:  *
        !           739:  * @api void | SndWaveSelect | Sets the wave-select parameter.
        !           740:  *
        !           741:  * @parm BYTE | slot | Specifies which slot to set.
        !           742:  *
        !           743:  * @rdesc There is no return value.
        !           744:  ***************************************************************************/
        !           745: void SndWaveSelect(BYTE slot)
        !           746: {
        !           747:     BYTE wave;
        !           748: 
        !           749:     wave = (BYTE)(GetLocPrm(slot, prmWaveSel) & 0x03);
        !           750: 
        !           751:     SndOutput((BYTE)(0xE0 | offsetSlot[slot]), wave);
        !           752: }
        !           753: 
        !           754: 
        !           755: /****************************************************************************
        !           756:  * @doc INTERNAL
        !           757:  *
        !           758:  * @api void | SndSetAllPrm | Transfers all the parameters from slot <p slot>
        !           759:  *     to the chip.
        !           760:  *
        !           761:  * @parm BYTE | slot | Specifies which slot to set.
        !           762:  *
        !           763:  * @rdesc There is no return value.
        !           764:  ***************************************************************************/
        !           765: void SndSetAllPrm(BYTE slot)
        !           766: {
        !           767: 
        !           768:     /* global am-depth and vib-depth settings */
        !           769:     SndSAmVibRhythm();
        !           770: 
        !           771:     /* note sel is always false */
        !           772:     SndSNoteSel(FALSE);
        !           773: 
        !           774:     /* slot-specific parameters */
        !           775: 
        !           776:     /* initialise volume to minimum to avoid clicks if the note is
        !           777:      * off but still playing (during decay phase).
        !           778:      *
        !           779:      * only applies to carrier slots.
        !           780:      */
        !           781:     if (operSlot[slot]) {
        !           782:        /* its a carrier slot */
        !           783:         slotAtten[slot] = 0x3f;                // max attenuation
        !           784:     }
        !           785:     SndSKslLevel(slot);
        !           786: 
        !           787:     SndSFeedFm(slot);
        !           788:     SndSAttDecay(slot);
        !           789:     SndSSusRelease(slot);
        !           790:     SndSAVEK(slot);
        !           791:     SndWaveSelect(slot);
        !           792: }
        !           793: 
        !           794: 
        !           795: 
        !           796: /* --- setting slot parameters ------------------ */
        !           797: 
        !           798: /****************************************************************************
        !           799:  * @doc INTERNAL
        !           800:  *
        !           801:  * @api void | SetSlotParam | Sets the 14 parameters (13 in <p param>,
        !           802:  *     1 in <p waveSel>) of slot <p slot>. Updates both the parameter array
        !           803:  *     and the chip.
        !           804:  *
        !           805:  * @parm BYTE | slot | Specifies which slot to set.
        !           806:  *
        !           807:  * @parm NPBYTE | param | Pointer to the new parameter array.
        !           808:  *
        !           809:  * @parm BYTE | waveSel | The new waveSel value.
        !           810:  *
        !           811:  * @rdesc There is no return value.
        !           812:  ***************************************************************************/
        !           813: void SetSlotParam(BYTE slot, NPOPERATOR pOper, BYTE waveSel)
        !           814: {
        !           815:     LPBYTE ptr;
        !           816: 
        !           817:     ptr = &paramSlot[slot][0];
        !           818: 
        !           819:     *ptr++ = pOper->ksl;
        !           820:     *ptr++ = pOper->freqMult;
        !           821:     *ptr++ = pOper->feedBack;
        !           822:     *ptr++ = pOper->attack;
        !           823:     *ptr++ = pOper->sustLevel;
        !           824:     *ptr++ = pOper->sustain;
        !           825:     *ptr++ = pOper->decay;
        !           826:     *ptr++ = pOper->release;
        !           827:     *ptr++ = pOper->output;
        !           828:     *ptr++ = pOper->am;
        !           829:     *ptr++ = pOper->vib;
        !           830:     *ptr++ = pOper->ksr;
        !           831:     *ptr++ = pOper->fm;
        !           832: 
        !           833:     *ptr = waveSel & 0x3;
        !           834: 
        !           835:     // set default volume settings
        !           836:     slotAtten[slot] = 0;
        !           837: 
        !           838:     SndSetAllPrm(slot);
        !           839: }
        !           840: 
        !           841: /*
        !           842:  * set this voice up to the correct parameters for a timbre
        !           843:  * copy the parameters to the slot array and write them to the
        !           844:  * chip
        !           845:  *
        !           846:  */
        !           847: 
        !           848: void SetVoiceTimbre(BYTE voice, NPTIMBRE pTimbre)
        !           849: {
        !           850: 
        !           851:     if (voice < BD) {        // melodic only
        !           852:        SetSlotParam(slotVoice[voice][0], &pTimbre->op0, pTimbre->wave0);
        !           853:        SetSlotParam(slotVoice[voice][1], &pTimbre->op1, pTimbre->wave1);
        !           854:     }
        !           855:     else if (voice == BD) {                   // bass drum
        !           856:        SetSlotParam(slotPerc[0][0], &pTimbre->op0, pTimbre->wave0);
        !           857:        SetSlotParam(slotPerc[0][1], &pTimbre->op1, pTimbre->wave1);
        !           858:     } else {                                    // percussion, 1 slot
        !           859:        SetSlotParam(slotPerc[voice - BD][0], &pTimbre->op0, pTimbre->wave0);
        !           860:     }
        !           861: }
        !           862: 
        !           863: 
        !           864: 
        !           865: /* --- frequency calculation -------------------- */
        !           866: 
        !           867: 
        !           868: /* convert the given pitch (0 .. 95) into a frequency
        !           869:  * and write to the chip.
        !           870:  *
        !           871:  * this will turn the note on if keyon is true.
        !           872:  */
        !           873: VOID SetFreq(BYTE voice, BYTE pitch, BYTE keyOn)
        !           874: {
        !           875:     WORD  FNum;
        !           876:     BYTE  t1;
        !           877: 
        !           878: 
        !           879:     // remember the keyon and pitch of the voice
        !           880: 
        !           881:     voiceKeyOn[voice] = keyOn;
        !           882:     notePitch[voice] = pitch;
        !           883: 
        !           884:     pitch += halfToneOffset[voice];
        !           885:     if (pitch > 95)
        !           886:         pitch = 95;
        !           887: 
        !           888:     // get the FNum for the voice
        !           889:     FNum = * (fNumFreqPtr[voice] + pitch % 12);
        !           890: 
        !           891:     // output the FNum, KeyOn and Block values
        !           892:     SndOutput((BYTE)(0xA0 | voice), (BYTE)FNum);   // FNum bits 0 - 7 (D0 - D7)
        !           893:     t1 = (BYTE)(keyOn ? 32 : 0);                   // Key On (D5)
        !           894:     t1 += (pitch / 12 << 2);                       // Block (D2 - D4)
        !           895:     t1 += (0x3 & (FNum >> 8));                     // FNum bits 8 - 9 (D0 - D1)
        !           896:     SndOutput((BYTE)(0xB0 | voice), t1);
        !           897: }
        !           898: 
        !           899: 
        !           900: /****************************************************************************
        !           901:  * @doc INTERNAL
        !           902:  *
        !           903:  * @api void | ChangePitch | This routine sets the <t halfToneOffset[]> and
        !           904:  *     <t fNumFreqPtr[]> arrays.  These two global variables are used to
        !           905:  *     determine the frequency variation to use when a note is played.
        !           906:  *
        !           907:  * @parm BYTE | voice | Specifies which voice to use.
        !           908:  *
        !           909:  * @parm WORD | pitchBend | Specifies the pitch bend value (0 to 0x3fff,
        !           910:  *     where 0x2000 is exact tuning).
        !           911:  *
        !           912:  * @rdesc There is no return value.
        !           913:  ***************************************************************************/
        !           914: void  ChangePitch(BYTE voice, WORD pitchBend)
        !           915: {
        !           916:     int     t1;
        !           917:     int     t2;
        !           918:     int     delta;
        !           919:     int pitchrange;
        !           920: 
        !           921:     /* pitch bend range 0-3fff is +-PITCHRANGE semitones. We move
        !           922:      * NR_STEP_PITCH steps per semitone.
        !           923:      * so the bend range is +- (PITCHRANGE * NR_STEP_PITCH) steps.
        !           924:      */
        !           925:     pitchrange = PITCHRANGE * NR_STEP_PITCH;
        !           926: 
        !           927:     t1 = (int)(((long)((int)pitchBend - MID_PITCH) * pitchrange) / MID_PITCH);
        !           928: 
        !           929:     if (t1 < 0)
        !           930:     {
        !           931:        t2 = NR_STEP_PITCH - 1 - t1;
        !           932:        halfToneOffset[voice] = -(t2 / NR_STEP_PITCH);
        !           933:        delta = (t2 - NR_STEP_PITCH + 1) % NR_STEP_PITCH;
        !           934:        if (delta) {
        !           935:            delta = NR_STEP_PITCH - delta;
        !           936:        }
        !           937:     }
        !           938:     else
        !           939:     {
        !           940:        halfToneOffset[voice] = t1 / NR_STEP_PITCH;
        !           941:        delta = t1 % NR_STEP_PITCH;
        !           942:     }
        !           943:     fNumFreqPtr[voice] = fNumNotes[delta];
        !           944: 
        !           945: }
        !           946: 
        !           947: /****************************************************************************
        !           948:  * @doc INTERNAL
        !           949:  *
        !           950:  * @api void | SetVoicePitch | Changes the pitch value of a voice.  Does
        !           951:  *     not affect the percussive voices, except for the bass drum.  The change
        !           952:  *     takes place immediately.
        !           953:  *
        !           954:  * @parm BYTE | voice | Specifies which voice to set.
        !           955:  *
        !           956:  * @parm WORD | pitchBend | Specifies the new pitch bend value (0 to 0x3fff,
        !           957:  *     where 0x2000 == exact tuning).
        !           958:  *
        !           959:  * @comm The variation in pitch is a function of the previous call to
        !           960:  *     <f SetPitchRange> and the value of <p pitchBend>.  A value of 0 means
        !           961:  *     -half-tone * pitchRangeStep, 0x2000 means no variation (exact pitch) and
        !           962:  *      0x3fff means +half-tone * pitchRangeStep.
        !           963:  *
        !           964:  * @rdesc There is no return value.
        !           965:  ***************************************************************************/
        !           966: void SetVoicePitch(BYTE voice, WORD pitchBend)
        !           967: {
        !           968:     if (voice <= BD) {       // melodic and bass drum voices
        !           969:         if (pitchBend > MAX_PITCH) {
        !           970:            pitchBend = MAX_PITCH;
        !           971:        }
        !           972:         ChangePitch(voice, pitchBend);
        !           973:         SetFreq(voice, notePitch[voice], voiceKeyOn[voice]);
        !           974:     }
        !           975: }
        !           976: 
        !           977: /* --- volume calculation ------------------------ */
        !           978: 
        !           979: /*
        !           980:  * set the attenuation for a slot (combine the channel attenuation
        !           981:  * setting with the key velocity).
        !           982:  */
        !           983: VOID SetVoiceAtten(BYTE voice, BYTE bChannel, BYTE bVelocity)
        !           984: {
        !           985:     BYTE bAtten;
        !           986:     BYTE   slot;
        !           987:     PBYTE slots;
        !           988: 
        !           989:     // scale up the volume since the patch maps are too quiet
        !           990:     //bVelocity = loudervol[bVelocity];
        !           991: 
        !           992: 
        !           993:     /*
        !           994:      * set channel attenuation
        !           995:      */
        !           996:     bAtten = gbVelocityAtten[bVelocity >> 2] + gbChanAtten[bChannel];
        !           997: 
        !           998: 
        !           999:     /*
        !          1000:      * add on any global (volume-setting) attenuation
        !          1001:      */
        !          1002:     bAtten += wSynthAtten;
        !          1003: 
        !          1004: 
        !          1005:     /* save this for each non-modifier slot */
        !          1006: 
        !          1007:     if (voice <= BD) {      // melodic voice
        !          1008:         slots = slotVoice[voice];
        !          1009: 
        !          1010:         slotAtten[slots[1]] = bAtten;
        !          1011:         SndSKslLevel(slots[1]);
        !          1012: 
        !          1013:         if (!GetLocPrm(slots[0], prmFm)) {
        !          1014:             // additive synthesis: set volume of first slot too
        !          1015:            slotAtten[slots[0]] = bAtten;
        !          1016:             SndSKslLevel(slots[0]);
        !          1017:         }
        !          1018:     }
        !          1019:     else {                                  // percussive voice
        !          1020:         slot = slotPerc[voice - BD][0];
        !          1021:        slotAtten[slot] = bAtten;
        !          1022:         SndSKslLevel(slot);
        !          1023:     }
        !          1024: }
        !          1025: 
        !          1026: /* adjust each slot attenuation to allow for a global volume
        !          1027:  * change - note that we only need to change the
        !          1028:  * carrier, not the modifier slots.
        !          1029:  *
        !          1030:  * change for channel bChannel, or all channels if bChannel is
        !          1031:  * 0xff
        !          1032:  */
        !          1033: VOID ChangeAtten(BYTE bChannel, int iChangeAtten)
        !          1034: {
        !          1035:     BYTE voice;
        !          1036:     BYTE   slot;
        !          1037:     PBYTE slots;
        !          1038: 
        !          1039:     /* find voices using this channel */
        !          1040:     for (voice = 0; voice < NUMVOICES; voice++) {
        !          1041:        if ((voices[voice].channel == bChannel) || (bChannel == 0xff)) {
        !          1042: 
        !          1043:            if (voice <= BD) {
        !          1044: 
        !          1045:                /* melodic voice */
        !          1046:                slots = slotVoice[voice];
        !          1047: 
        !          1048:                slotAtten[slots[1]] += iChangeAtten;
        !          1049:                SndSKslLevel(slots[1]);
        !          1050: 
        !          1051:                if (!GetLocPrm(slots[0], prmFm)) {
        !          1052:                    // additive synthesis: set volume of first slot too
        !          1053:                    slotAtten[slots[0]] += iChangeAtten;
        !          1054:                    SndSKslLevel(slots[0]);
        !          1055:                }
        !          1056:            } else {
        !          1057:                slot = slotPerc[voice - BD][0];
        !          1058:                slotAtten[slot] += iChangeAtten;
        !          1059:                SndSKslLevel(slot);
        !          1060:            }
        !          1061:        }
        !          1062:     }
        !          1063: }
        !          1064: 
        !          1065: 
        !          1066: /* --- note on/off ------------------------------- */
        !          1067: 
        !          1068: /* switch off the note a given voice is playing */
        !          1069: void NoteOff(BYTE voice)
        !          1070: {
        !          1071: 
        !          1072:     if (voice < BD) {
        !          1073: 
        !          1074:        /* melodic voice */
        !          1075:        SetFreq(voice, notePitch[voice], 0);
        !          1076:     } else {
        !          1077:        percBits &= ~percMasks[voice-BD];
        !          1078:        SndSAmVibRhythm();
        !          1079:     }
        !          1080: }
        !          1081: 
        !          1082: /* switch on a voice at a given note (0 - 127, where 60 is middle c) */
        !          1083: void NoteOn(BYTE voice, BYTE bNote)
        !          1084: {
        !          1085:     BYTE bPitch;
        !          1086: 
        !          1087:     /* convert note to chip pitch */
        !          1088:     if (bNote < (MID_C - CHIP_MID_C)) {
        !          1089:        bPitch = 0;
        !          1090:     } else {
        !          1091:        bPitch = bNote - (MID_C - CHIP_MID_C);
        !          1092:     }
        !          1093: 
        !          1094:     if (voice < BD) {
        !          1095:        /* melodic voice */
        !          1096: 
        !          1097:        /* set frequency and start note */
        !          1098:        SetFreq(voice, bPitch, 1);
        !          1099:     } else {
        !          1100:        /*
        !          1101:         * nb we don't change the pitch of some percussion instruments.
        !          1102:         *
        !          1103:         * also note that for percussive instruments (including BD),
        !          1104:         * the note-on setting should always be 0. You switch the percussion
        !          1105:         * on by writing to the AmVibRhythm register.
        !          1106:         */
        !          1107: 
        !          1108:        if (voice == BD) {
        !          1109:            SetFreq(voice, bPitch, 0);
        !          1110:        } else if (voice == TOM) {
        !          1111:            /*
        !          1112:             * for best sounds, we do change the TOM freq, but we always keep
        !          1113:             * the SD 7 semi-tones above TOM.
        !          1114:             */
        !          1115:            SetFreq(TOM, bPitch, 0);
        !          1116:            SetFreq(SD, (BYTE)(bPitch + TOM_TO_SD), 0);
        !          1117:        }
        !          1118:        /* other instruments never change */
        !          1119: 
        !          1120:        percBits |= percMasks[voice - BD];
        !          1121:        SndSAmVibRhythm();
        !          1122:     }
        !          1123: }
        !          1124: 
        !          1125: 
        !          1126: 
        !          1127: /* -- voice allocation -------- */
        !          1128: 
        !          1129: /*
        !          1130:  * find the voice that is currently playing a given channel/note pair
        !          1131:  * (if any)
        !          1132:  */
        !          1133: BYTE
        !          1134: FindVoice(BYTE bNote, BYTE bChannel)
        !          1135: {
        !          1136:     BYTE i;
        !          1137: 
        !          1138:     for (i = 0; i < (BYTE)NUMVOICES; i++) {
        !          1139:        if ((voices[i].alloc) && (voices[i].note == bNote)
        !          1140:           && (voices[i].channel == bChannel)) {
        !          1141:            voices[i].dwTimeStamp = dwAge++;
        !          1142:            return(i);
        !          1143:        }
        !          1144:     }
        !          1145: 
        !          1146:     /* no voice is playing this */
        !          1147:     return(0xff);
        !          1148: }
        !          1149: 
        !          1150: /*
        !          1151:  * mark a voice as unused
        !          1152:  */
        !          1153: VOID FreeVoice(voice)
        !          1154: {
        !          1155:     voices[voice].alloc = 0;
        !          1156: }
        !          1157: 
        !          1158: 
        !          1159: /*
        !          1160:  * GetNewVoice - allocate a voice to play this note. if no voices are
        !          1161:  * free, re-use the note with the oldest timestamp.
        !          1162:  */
        !          1163: BYTE GetNewVoice(BYTE patch, BYTE note, BYTE channel)
        !          1164: {
        !          1165:     BYTE  i;
        !          1166:     BYTE  voice;
        !          1167:     BYTE  bVoiceToUse, bVoiceSame, bVoiceOldest;
        !          1168:     DWORD dwOldestTime = dwAge + 1;            // init to past current "time"
        !          1169:     DWORD dwOldestSame = dwAge + 1;
        !          1170:     DWORD dwOldestOff = dwAge + 1;
        !          1171: 
        !          1172:     if (patches[patch].mode) {            // it's a percussive patch
        !          1173:         voice = patches[patch].percVoice;       // use fixed percussion voice
        !          1174:         voices[voice].alloc = TRUE;
        !          1175:         voices[voice].note = note;
        !          1176:         voices[voice].channel = channel;
        !          1177:         voices[voice].dwTimeStamp = MAKELONG(patch, 0);
        !          1178: 
        !          1179:         SetVoiceTimbre(voice, &patches[patch]);
        !          1180: 
        !          1181:         return voice;
        !          1182:     }
        !          1183: 
        !          1184:     bVoiceToUse = bVoiceSame = bVoiceOldest = 0xff;
        !          1185: 
        !          1186:     // find a free melodic voice to use
        !          1187:     for (i = 0; i < (BYTE)NUMMELODIC; i++) {    // it's a melodic patch
        !          1188:         if (!voices[i].alloc) {
        !          1189: 
        !          1190:            if (voices[i].dwTimeStamp < dwOldestOff) {
        !          1191:                bVoiceToUse = i;
        !          1192:                dwOldestOff = voices[i].dwTimeStamp;
        !          1193:            }
        !          1194: 
        !          1195:         } else if (voices[i].channel == channel) {
        !          1196:            if (voices[i].dwTimeStamp < dwOldestSame) {
        !          1197:                dwOldestSame = voices[i].dwTimeStamp;
        !          1198:                bVoiceSame = i;
        !          1199:            }
        !          1200:         } else if (voices[i].dwTimeStamp < dwOldestTime) {
        !          1201:                 dwOldestTime = voices[i].dwTimeStamp;
        !          1202:                 bVoiceOldest = i;                // remember oldest one to steal
        !          1203:         }
        !          1204:     }
        !          1205: 
        !          1206:     // choose a free voice if we have found one. If not, choose the
        !          1207:     // oldest voice of the same channel. if none, choose the oldest voice.
        !          1208:     if (bVoiceToUse == 0xff) {
        !          1209:        if (bVoiceSame != 0xff) {
        !          1210:            bVoiceToUse = bVoiceSame;
        !          1211:        } else {
        !          1212:            bVoiceToUse = bVoiceOldest;
        !          1213:        }
        !          1214:     }
        !          1215: 
        !          1216: 
        !          1217:     if (voices[bVoiceToUse].alloc)  {      // if we stole it, turn it off
        !          1218:         NoteOff(bVoiceToUse);
        !          1219:     }
        !          1220: 
        !          1221:     voices[bVoiceToUse].alloc = 1;
        !          1222:     voices[bVoiceToUse].note = note;
        !          1223:     voices[bVoiceToUse].channel = channel;
        !          1224:     voices[bVoiceToUse].dwTimeStamp = dwAge++;
        !          1225: 
        !          1226:     SetVoiceTimbre(bVoiceToUse, &patches[patch]);
        !          1227: 
        !          1228:     return bVoiceToUse;
        !          1229: }
        !          1230: 
        !          1231: 
        !          1232: /* --- externally-called functions ------------------------------ */
        !          1233: 
        !          1234: /*
        !          1235:  * Adlib_NoteOn - This turns a note on. (Including drums, with
        !          1236:  *     a patch # of the drum Note + 128)
        !          1237:  *
        !          1238:  * inputs
        !          1239:  *      BYTE    bPatch - MIDI patch number
        !          1240:  *     BYTE    bNote - MIDI note number
        !          1241:  *      BYTE    bChannel - MIDI channel #
        !          1242:  *     BYTE    bVelocity - Velocity #
        !          1243:  *     short   iBend - current pitch bend from -32768, to 32767
        !          1244:  * returns
        !          1245:  *     none
        !          1246:  */
        !          1247: VOID NEAR PASCAL Adlib_NoteOn (BYTE bPatch,
        !          1248:        BYTE bNote, BYTE bChannel, BYTE bVelocity,
        !          1249:        short iBend)
        !          1250: {
        !          1251: 
        !          1252:     BYTE voice;
        !          1253:     WORD wBend;
        !          1254: 
        !          1255:     if (bVelocity == 0) {               // 0 velocity means note off
        !          1256:         Adlib_NoteOff(bPatch, bNote, bChannel);
        !          1257:        return;
        !          1258:     }
        !          1259: 
        !          1260:     // octave registration for melodic patches
        !          1261:     if (bPatch < 128) {
        !          1262:        bNote += patchKeyOffset[bPatch];
        !          1263:        if ((bNote < 0) || (bNote > 127)) {
        !          1264:            bNote -= patchKeyOffset[bPatch];
        !          1265:        }
        !          1266:     }
        !          1267: 
        !          1268: 
        !          1269:     if (bPatch >= 128) {
        !          1270:        /*
        !          1271:         * it's a percussion note
        !          1272:         */
        !          1273: 
        !          1274:        bNote = bPatch - 128;
        !          1275: 
        !          1276:         if ((bNote < FIRSTDRUMNOTE) || (bNote > LASTDRUMNOTE)) {
        !          1277:             return;
        !          1278:        }
        !          1279: 
        !          1280:        /* use the drum patch table to map the note to a given
        !          1281:         * TIMBRE/note pair
        !          1282:         */
        !          1283:        bPatch = drumpatch[bNote - FIRSTDRUMNOTE].patch;
        !          1284:        bNote = drumpatch[bNote - FIRSTDRUMNOTE].note;
        !          1285: 
        !          1286:        /* each drum patch plays on one specific voice.
        !          1287:         * find that voice
        !          1288:         */
        !          1289:        voice = patches[bPatch].percVoice;
        !          1290:        
        !          1291:        /* switch note off if playing */
        !          1292:        if (voices[voice].alloc) {
        !          1293:            NoteOff(voice);
        !          1294:        }
        !          1295: 
        !          1296:        /* call GetNewVoice to set the voice params and timestamp
        !          1297:         * even if we found it.
        !          1298:         */
        !          1299:        voice = GetNewVoice(bPatch, bNote, bChannel);
        !          1300: 
        !          1301:     } else {
        !          1302: 
        !          1303:        /* switch note off if it's playing  */
        !          1304:        if ( (voice = FindVoice(bNote, bChannel)) != 0xFF ) {
        !          1305:                NoteOff(voice);
        !          1306:        } else {
        !          1307:            voice = GetNewVoice(bPatch, bNote, bChannel);
        !          1308:        }
        !          1309:     }
        !          1310: 
        !          1311:     /* convert the velocity to an attenuation setting, and
        !          1312:      * write that to the device
        !          1313:      */
        !          1314:     SetVoiceAtten(voice, bChannel, bVelocity);
        !          1315: 
        !          1316:     /*
        !          1317:      * apply pitch bend. note that we are passed a pitch bend in the
        !          1318:      * range 8000-7fff, but our code assumes 0-3fff, so we convert here.
        !          1319:      */
        !          1320:     wBend = (((WORD)iBend + 0x8000) >> 2) & 0x3fff;
        !          1321:     SetVoicePitch(voice, wBend);
        !          1322: 
        !          1323:     // play the note
        !          1324:     NoteOn(voice, bNote);
        !          1325: 
        !          1326: }
        !          1327: 
        !          1328: 
        !          1329: /* Adlib_NoteOff - This turns a note off. (Including drums,
        !          1330:  *     with a patch # of the drum note + 128)
        !          1331:  *
        !          1332:  * inputs
        !          1333:  *     BYTE    bPatch - MIDI patch #
        !          1334:  *     BYTE    bNote - MIDI note number
        !          1335:  *     BYTE    bChannel - MIDI channel #
        !          1336:  * returns
        !          1337:  *     none
        !          1338:  */
        !          1339: VOID FAR PASCAL Adlib_NoteOff (BYTE bPatch,
        !          1340:        BYTE bNote, BYTE bChannel)
        !          1341: {
        !          1342:     BYTE bVoice;
        !          1343: 
        !          1344:     if (bPatch > 127) {
        !          1345: 
        !          1346:        /* drum note. These all use a fixed voice */
        !          1347: 
        !          1348: 
        !          1349:         if ((bNote < FIRSTDRUMNOTE) || (bNote > LASTDRUMNOTE)) {
        !          1350:             return;
        !          1351:        }
        !          1352: 
        !          1353:        /* use the drum patch table to map the note to a given
        !          1354:         * TIMBRE/note pair
        !          1355:         */
        !          1356:        bPatch = drumpatch[bNote - FIRSTDRUMNOTE].patch;
        !          1357:        bNote = drumpatch[bNote - FIRSTDRUMNOTE].note;
        !          1358:        bVoice = patches[bPatch].percVoice;
        !          1359:        
        !          1360:        /* switch note off if playing our patch */
        !          1361:        if (LOWORD(voices[bVoice].dwTimeStamp) == bPatch) {
        !          1362:            NoteOff(bVoice);
        !          1363:        }
        !          1364:     } else {
        !          1365: 
        !          1366:        bVoice = FindVoice(bNote, bChannel);
        !          1367:        if (bVoice != 0xff) {
        !          1368: 
        !          1369:            if (voices[bVoice].note) {
        !          1370:                NoteOff(bVoice);
        !          1371:                FreeVoice(bVoice);
        !          1372:            }
        !          1373:        }
        !          1374:     }
        !          1375: }
        !          1376: 
        !          1377: /* Adlib_AllNotesOff - turn off all notes
        !          1378:  *
        !          1379:  * inputs - none
        !          1380:  * returns - none
        !          1381:  */
        !          1382: VOID Adlib_AllNotesOff(void)
        !          1383: {
        !          1384: 
        !          1385:     BYTE i;
        !          1386: 
        !          1387:     for (i = 0; i < NUMVOICES; i++) {
        !          1388:        NoteOff(i);
        !          1389:     }
        !          1390: }
        !          1391: 
        !          1392: 
        !          1393: /* Adlib_NewVolume - This should be called if a volume level
        !          1394:  *     has changed. This will adjust the levels of all the playing
        !          1395:  *     voices.
        !          1396:  *
        !          1397:  * inputs
        !          1398:  *     WORD    wLeft   - left attenuation (1.5 db units)
        !          1399:  *     WORD    wRight  - right attenuation (ignore if mono)
        !          1400:  * returns
        !          1401:  *     none
        !          1402:  */
        !          1403: VOID FAR PASCAL Adlib_NewVolume (WORD wLeft, WORD wRight)
        !          1404: {
        !          1405:     /* ignore the right attenuation since this is a mono device */
        !          1406:     int iChange;
        !          1407: 
        !          1408:     wLeft = min(wLeft, wRight) << 1;
        !          1409:     iChange = wLeft - wSynthAtten;
        !          1410: 
        !          1411:     wSynthAtten = wLeft;
        !          1412: 
        !          1413:     /* change attenuation for all channels */
        !          1414:     ChangeAtten(0xff, iChange);
        !          1415: }
        !          1416: 
        !          1417: 
        !          1418: 
        !          1419: /* Adlib_ChannelVolume - set the volume level for an individual channel.
        !          1420:  *
        !          1421:  * inputs
        !          1422:  *     BYTE    bChannel - channel number to change
        !          1423:  *     WORD    wAtten  - attenuation in 1.5 db units
        !          1424:  *
        !          1425:  * returns
        !          1426:  *     none
        !          1427:  */
        !          1428: VOID FAR PASCAL Adlib_ChannelVolume(BYTE bChannel, WORD wAtten)
        !          1429: {      
        !          1430:     int iChange;
        !          1431: 
        !          1432:     iChange = wAtten - gbChanAtten[bChannel];
        !          1433: 
        !          1434:     gbChanAtten[bChannel] = wAtten;
        !          1435: 
        !          1436: 
        !          1437:     /* change attenuation for this channel */
        !          1438:     ChangeAtten(bChannel, iChange);
        !          1439: }
        !          1440: 
        !          1441: 
        !          1442: /* Adlib_SetPan - set the left-right pan position.
        !          1443:  *
        !          1444:  * inputs
        !          1445:  *      BYTE    bChannel  - channel number to alter
        !          1446:  *     BYTE    bPan   - 0 for left, 127 for right or somewhere in the middle.
        !          1447:  *
        !          1448:  * returns - none
        !          1449:  *
        !          1450:  * does nothing - this is a mono device
        !          1451:  */
        !          1452: VOID FAR PASCAL Adlib_SetPan(BYTE bChannel, BYTE bPan)
        !          1453: {
        !          1454: 
        !          1455:     /* do nothing */
        !          1456: }
        !          1457: 
        !          1458: 
        !          1459: /* Adlib_PitchBend - This pitch bends a channel.
        !          1460:  *
        !          1461:  * inputs
        !          1462:  *     BYTE    bChannel - channel
        !          1463:  *     short   iBend - Values from -32768 to 32767, being
        !          1464:  *                     -2 to +2 half steps
        !          1465:  * returns
        !          1466:  *     none
        !          1467:  */
        !          1468: VOID NEAR PASCAL Adlib_PitchBend (BYTE bChannel, short iBend)
        !          1469: {
        !          1470:     BYTE i;
        !          1471:     WORD w;
        !          1472: 
        !          1473:     /* note that our code expects 0 - 0x3fff not 0x8000 - 0x7fff */
        !          1474:     w = (((WORD) iBend + 0x8000) >> 2) & 0x3fff;
        !          1475: 
        !          1476:     for (i = 0; i < NUMVOICES; i++) {
        !          1477:        if ((voices[i].alloc) && (voices[i].channel == bChannel)) {
        !          1478:            SetVoicePitch(i,  w);
        !          1479:        }
        !          1480:     }
        !          1481: }
        !          1482: 
        !          1483: 
        !          1484: 
        !          1485: /* Adlib_BoardInit - initialise board and load patches as necessary.
        !          1486:  *
        !          1487:  * inputs - none
        !          1488:  * returns - 0 for success or the error code
        !          1489:  */
        !          1490: WORD Adlib_BoardInit(void)
        !          1491: {
        !          1492:     BYTE i;
        !          1493: 
        !          1494:     wSynthAtten = 0;
        !          1495: 
        !          1496:     /* build the freq table */
        !          1497:     InitFNums();
        !          1498: 
        !          1499:     /* silence and free all voices */
        !          1500:     for (i = 0; i <= 8; i++) {
        !          1501:        SoundChut(i);
        !          1502:        FreeVoice(i);
        !          1503:     }
        !          1504: 
        !          1505:     /* switch to percussive mode and set fixed frequencies */
        !          1506:     SetFreq(TOM, TOM_PITCH, 0);
        !          1507:     SetFreq(SD, SD_PITCH, 0);
        !          1508:     percBits = 0;
        !          1509:     SndSAmVibRhythm();
        !          1510: 
        !          1511:     /* init all slots to sine-wave */
        !          1512:     for (i= 0; i < 18; i++) {
        !          1513:        SndOutput((BYTE)(0xE0 | offsetSlot[i]), 0);
        !          1514:     }
        !          1515:     /* enable wave-form selection */
        !          1516:     SndOutput(1, 0x20);
        !          1517: 
        !          1518:     LoadPatches();
        !          1519:     LoadDrumPatches();
        !          1520: 
        !          1521: // don't initialise - the data is static and will thus
        !          1522: // be initialised to 0 at load time. no other change should be made
        !          1523: // since the mci sequencer will not re-send channel volume change
        !          1524: // messages.
        !          1525: //
        !          1526: //    /* init all channels to loudest */
        !          1527: //    for (i = 0; i < NUMCHANNELS; i++) {
        !          1528: //     gbChanAtten[i] = 4;
        !          1529: //    }
        !          1530: 
        !          1531:     return(0);
        !          1532: }
        !          1533: 
        !          1534: 
        !          1535: /*
        !          1536:  * Adlib_BoardReset - silence the board and set all voices off.
        !          1537:  */
        !          1538: VOID Adlib_BoardReset(void)
        !          1539: {
        !          1540:     BYTE i;
        !          1541: 
        !          1542:     /* silence and free all voices */
        !          1543:     for (i = 0; i <= 8; i++) {
        !          1544:        SoundChut(i);
        !          1545:        FreeVoice(i);
        !          1546:     }
        !          1547: 
        !          1548:     /* switch to percussive mode and set fixed frequencies */
        !          1549:     SetFreq(TOM, TOM_PITCH, 0);
        !          1550:     SetFreq(SD, SD_PITCH, 0);
        !          1551:     percBits = 0;
        !          1552:     SndSAmVibRhythm();
        !          1553: 
        !          1554: }
        !          1555: 
        !          1556: 
        !          1557: 

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.