|
|
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 = ¶mSlot[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:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.