|
|
1.1 ! root 1: /* ! 2: * Copyright (c) 1992 Microsoft Corporation ! 3: */ ! 4: ! 5: /* ! 6: * Interface functions for the OPL3 midi device type. ! 7: * ! 8: * These functions are called from midi.c when the kernel driver ! 9: * has decreed that this is an opl3-compatible device. ! 10: * ! 11: * Geraint Davies, Dec 92 ! 12: */ ! 13: ! 14: #include <windows.h> ! 15: #include <mmsystem.h> ! 16: #include <mmddk.h> ! 17: #include "driver.h" ! 18: #include "opl3.h" ! 19: ! 20: /* --- typedefs ----------------------------------------------- */ ! 21: ! 22: /* typedefs for MIDI patches */ ! 23: #define NUMOPS (4) ! 24: #define PATCH_1_4OP (0) /* use 4-operator patch */ ! 25: #define PATCH_2_2OP (1) /* use two 2-operator patches */ ! 26: #define PATCH_1_2OP (2) /* use one 2-operator patch */ ! 27: ! 28: #define RIFF_PATCH (mmioFOURCC('P','t','c','h')) ! 29: #define RIFF_FM4 (mmioFOURCC('f','m','4',' ')) ! 30: #define NUM4VOICES (6) /* # 4-op voices that can play at once */ ! 31: #define NUM2VOICES (6) /* # 2operator voices */ ! 32: #define NUMVOICES (NUM4VOICES + NUM2VOICES) ! 33: ! 34: typedef struct _operStruct { ! 35: BYTE bAt20; /* flags which are send to 0x20 on fm */ ! 36: BYTE bAt40; /* flags seet to 0x40 */ ! 37: /* the note velocity & midi velocity affect total level */ ! 38: BYTE bAt60; /* flags sent to 0x60 */ ! 39: BYTE bAt80; /* flags sent to 0x80 */ ! 40: BYTE bAtE0; /* flags send to 0xe0 */ ! 41: } operStruct; ! 42: ! 43: typedef struct _noteStruct { ! 44: operStruct op[NUMOPS]; /* operators */ ! 45: BYTE bAtA0[2]; /* send to 0xA0, A3 */ ! 46: BYTE bAtB0[2]; /* send to 0xB0, B3 */ ! 47: /* use in a patch, the block should be 4 to indicate ! 48: normal pitch, 3 => octave below, etc. */ ! 49: BYTE bAtC0[2]; /* sent to 0xc0, C3 */ ! 50: BYTE bOp; /* see PATCH_??? */ ! 51: BYTE bDummy; /* place holder */ ! 52: } noteStruct; ! 53: ! 54: ! 55: typedef struct _patchStruct { ! 56: noteStruct note; /* note. This is all in the structure at the moment */ ! 57: } patchStruct; ! 58: ! 59: /* MIDI */ ! 60: ! 61: typedef struct _voiceStruct { ! 62: BYTE bNote; /* note played */ ! 63: BYTE bChannel; /* channel played on */ ! 64: BYTE bPatch; /* what patch is the note, ! 65: drums patch = drum note + 128 */ ! 66: BYTE bOn; /* TRUE if note is on, FALSE if off */ ! 67: BYTE bVelocity; /* velocity */ ! 68: BYTE bJunk; /* filler */ ! 69: DWORD dwTime; /* time that was turned on/off; ! 70: 0 time indicates that its not in use */ ! 71: DWORD dwOrigPitch[2]; /* original pitch, for pitch bend */ ! 72: BYTE bBlock[2]; /* value sent to the block */ ! 73: } voiceStruct; ! 74: ! 75: /* --- module data -------------------------------------------- */ ! 76: ! 77: /* a bit of tuning information */ ! 78: #define FSAMP (50000.0) /* sampling frequency */ ! 79: #define PITCH(x) ((DWORD)((x) * (double) (1L << 19) / FSAMP)) ! 80: /* x is the desired frequency, ! 81: == FNUM at b=1 */ ! 82: #define EQUAL (1.059463094359) ! 83: #ifdef EUROPE ! 84: # define A (442.0) ! 85: #else ! 86: # define A (440.0) ! 87: #endif ! 88: #define ASHARP (A * EQUAL) ! 89: #define B (ASHARP * EQUAL) ! 90: #define C (B * EQUAL / 2.0) ! 91: #define CSHARP (C * EQUAL) ! 92: #define D (CSHARP * EQUAL) ! 93: #define DSHARP (D * EQUAL) ! 94: #define E (DSHARP * EQUAL) ! 95: #define F (E * EQUAL) ! 96: #define FSHARP (F * EQUAL) ! 97: #define G (FSHARP * EQUAL) ! 98: #define GSHARP (G * EQUAL) ! 99: ! 100: /* volume */ ! 101: WORD wSynthAttenL = 0; /* in 1.5dB steps */ ! 102: WORD wSynthAttenR = 0; /* in 1.5dB steps */ ! 103: ! 104: /* patch library */ ! 105: patchStruct FAR * glpPatch = NULL; /* points to the patches */ ! 106: ! 107: /* voices being played */ ! 108: voiceStruct gVoice[NUMVOICES]; /* info on what voice is where */ ! 109: static DWORD gdwCurTime = 1; /* for note on/off */ ! 110: ! 111: BYTE gbCur4opReg = 0; /* current value to 4-operator connection */ ! 112: ! 113: /* channel volumes */ ! 114: BYTE gbChanAtten[NUMCHANNELS]; /* attenuation of each channel, in .75 db steps */ ! 115: BYTE gbStereoMask[NUMCHANNELS]; /* mask for left/right for stereo midi files */ ! 116: ! 117: /* operator offset location */ ! 118: static WORD BCODE gw4OpOffset[NUM4VOICES][NUMOPS] = { ! 119: {0x000,0x003,0x008,0x00b}, {0x001,0x004,0x009,0x00c}, ! 120: {0x002,0x005,0x00a,0x00d}, {0x100,0x103,0x108,0x10b}, ! 121: {0x101,0x104,0x109,0x10c}, {0x102,0x105,0x10a,0x10d}}; ! 122: static WORD BCODE gw2OpOffset[NUM2VOICES][2] = { ! 123: {0x010,0x013}, {0x011,0x014}, {0x012,0x015}, ! 124: {0x110,0x113}, {0x111,0x114}, {0x112,0x115} ! 125: }; ! 126: ! 127: /* voice offset location */ ! 128: static WORD BCODE gw4VoiceOffset[NUM4VOICES] = { ! 129: 0x000, 0x001, 0x002, 0x100, 0x101, 0x102}; ! 130: static WORD BCODE gw2VoiceOffset[NUM2VOICES] = { ! 131: 0x006, 0x007, 0x008, 0x106, 0x107, 0x108}; ! 132: ! 133: /* pitch values, from middle c, to octave above it */ ! 134: static DWORD BCODE gdwPitch[12] = { ! 135: PITCH(C), PITCH(CSHARP), PITCH(D), PITCH(DSHARP), ! 136: PITCH(E), PITCH(F), PITCH(FSHARP), PITCH(G), ! 137: PITCH(GSHARP), PITCH(A), PITCH(ASHARP), PITCH(B)}; ! 138: ! 139: ! 140: ! 141: /* --- internal functions -------------------------------------- */ ! 142: ! 143: ! 144: /*************************************************************** ! 145: Opl3_FMNote - This turns on a FM-synthesizer note. ! 146: ! 147: inputs ! 148: WORD wNote - the note number from 0 to NUMVOICES. ! 149: If wNote < NUM4VOICES then dealing with 4 operator synth, ! 150: else using 2-operator synth. ! 151: noteStruct FAR * lpSN - structure containing information about ! 152: what is to be played. ! 153: returns ! 154: none ! 155: */ ! 156: VOID NEAR PASCAL Opl3_FMNote (WORD wNote, noteStruct FAR * lpSN) ! 157: { ! 158: WORD i; ! 159: WORD wOffset; ! 160: operStruct FAR * lpOS; ! 161: ! 162: // D1 ("\nOpl3_FMNote"); ! 163: /* write out a note off, just to make sure */ ! 164: MidiSendFM (AD_BLOCK + ! 165: ((wNote < NUM4VOICES) ? gw4VoiceOffset[wNote] : gw2VoiceOffset[wNote - NUM4VOICES]), ! 166: (BYTE)0); ! 167: ! 168: /* write out information specifying if we are ! 169: to use 2 or 4 operators */ ! 170: if (wNote < NUM4VOICES) { ! 171: if (lpSN->bOp == PATCH_1_4OP) ! 172: gbCur4opReg |= (1 << wNote); ! 173: else ! 174: gbCur4opReg &= (~(1 << wNote)); ! 175: MidiSendFM (AD_CONNECTION, gbCur4opReg); ! 176: }; ! 177: /* else send out nothing */ ! 178: ! 179: /* writing the operator information */ ! 180: for (i = 0; i < (WORD)((wNote < NUM4VOICES) ? NUMOPS : 2); i++) { ! 181: wOffset = (wNote < NUM4VOICES) ? ! 182: gw4OpOffset[wNote][i] : gw2OpOffset[wNote - NUM4VOICES][i]; ! 183: lpOS = &lpSN->op[i]; ! 184: MidiSendFM (0x20 + wOffset, lpOS->bAt20); ! 185: MidiSendFM (0x40 + wOffset, lpOS->bAt40); ! 186: MidiSendFM (0x60 + wOffset, lpOS->bAt60); ! 187: MidiSendFM (0x80 + wOffset, lpOS->bAt80); ! 188: MidiSendFM (0xE0 + wOffset, lpOS->bAtE0); ! 189: }; ! 190: /* write out the voice information */ ! 191: wOffset = (wNote < NUM4VOICES) ? ! 192: gw4VoiceOffset[wNote] : gw2VoiceOffset[wNote - NUM4VOICES]; ! 193: MidiSendFM (0xa0 + wOffset, lpSN->bAtA0[0]); ! 194: MidiSendFM (0xc0 + wOffset, lpSN->bAtC0[0]); ! 195: if (wNote < NUM4VOICES) { ! 196: MidiSendFM (0xc3 + wOffset, lpSN->bAtC0[1]); ! 197: MidiSendFM (0xa3 + wOffset, lpSN->bAtA0[1]); ! 198: MidiSendFM (0xb3 + wOffset, (BYTE)(lpSN->bAtB0[1] | 0x20) /* note on */); ! 199: }; ! 200: MidiSendFM (0xb0 + wOffset, (BYTE)(lpSN->bAtB0[0] | 0x20) /* note on */); ! 201: ! 202: /* done */ ! 203: } ! 204: ! 205: /*************************************************************** ! 206: Opl3_FindEmptySlot - This finds an empty note-slot for a MIDI voice. ! 207: If there are no empty slots then this looks for the oldest ! 208: off note. It this doesnt work then it looks\ ! 209: for the oldest on-note of the same patch. If all notes are still on then this ! 210: finds the oldest turned-on-note. ! 211: ! 212: inputs ! 213: BYTE bPatch - MIDI patch that will replace it ! 214: BYTE b4Op - if TRUE then looking through 4-operator voices, ! 215: else looking through 2-operator voices ! 216: returns ! 217: WORD - note slot # ! 218: */ ! 219: WORD NEAR PASCAL Opl3_FindEmptySlot (BYTE bPatch, BYTE b4Op) ! 220: { ! 221: WORD i, found; ! 222: DWORD dwOldest; ! 223: ! 224: // D1 ("\nOpl3_FindEmptySlot"); ! 225: /* first, look for a slot with a time == 0 */ ! 226: for (i = (b4Op ? 0 : NUM4VOICES); i < (WORD)(b4Op ? NUM4VOICES : NUMVOICES); i++) ! 227: if (!gVoice[i].dwTime) ! 228: return i; ! 229: ! 230: /* now, look for a slot of the oldest off-note */ ! 231: dwOldest = 0xffffffff; ! 232: found = 0xffff; ! 233: for (i = (b4Op ? 0 : NUM4VOICES); i < (WORD)(b4Op ? NUM4VOICES : NUMVOICES); i++) ! 234: if (!gVoice[i].bOn && (gVoice[i].dwTime < dwOldest)) { ! 235: ! 236: dwOldest = gVoice[i].dwTime; ! 237: found = i; ! 238: }; ! 239: if (found != 0xffff) ! 240: return found; ! 241: ! 242: /* now, look for a slot of the oldest note with the same patch */ ! 243: dwOldest = 0xffffffff; ! 244: found = 0xffff; ! 245: for (i = (b4Op ? 0 : NUM4VOICES); i < (WORD)(b4Op ? NUM4VOICES : NUMVOICES); i++) ! 246: if ((gVoice[i].bPatch == bPatch) && ! 247: (gVoice[i].dwTime < dwOldest)) { ! 248: ! 249: dwOldest = gVoice[i].dwTime; ! 250: found = i; ! 251: }; ! 252: if (found != 0xffff) ! 253: return found; ! 254: ! 255: /* now, just look for the oldest voice */ ! 256: found = (b4Op ? 0 : NUM4VOICES); ! 257: dwOldest = gVoice[found].dwTime; ! 258: for (i = (found + 1); i < (WORD)(b4Op ? NUM4VOICES : NUMVOICES); i++) ! 259: if (gVoice[i].dwTime < dwOldest) { ! 260: ! 261: dwOldest = gVoice[i].dwTime; ! 262: found = i; ! 263: }; ! 264: ! 265: return found; ! 266: } ! 267: ! 268: ! 269: /*************************************************************** ! 270: Opl3_FindFullSlot - This finds a slot with a specific note, ! 271: and channel. If it is not found then 0xffff is ! 272: returned. ! 273: ! 274: inputs ! 275: BYTE bNote - MIDI note number ! 276: BYTE bChannel - MIDI channel # ! 277: returns ! 278: WORD - note slot #, or 0xffff if cant find ! 279: */ ! 280: WORD NEAR PASCAL Opl3_FindFullSlot ( ! 281: BYTE bNote, BYTE bChannel) ! 282: { ! 283: WORD i; ! 284: ! 285: // D1("\nOpl3_FindFullSlot"); ! 286: for (i = 0; i < NUMVOICES; i++) ! 287: if ((bChannel == gVoice[i].bChannel) && ! 288: (bNote == gVoice[i].bNote) && (gVoice[i].bOn)) ! 289: return i; ! 290: ! 291: /* couldnt find it */ ! 292: return 0xffff; ! 293: } ! 294: ! 295: /************************************************************** ! 296: Opl3_CalcBend - This calculates the effects of pitch bend ! 297: on an original value. ! 298: ! 299: inputs ! 300: DWORD dwOrig - original frequency ! 301: short iBend - from -32768 to 32768, -2 half steps to +2 ! 302: returns ! 303: DWORD - new frequency ! 304: */ ! 305: DWORD NEAR PASCAL Opl3_CalcBend (DWORD dwOrig, short iBend) ! 306: { ! 307: DWORD dw; ! 308: // D1 ("\nOpl3_CalcBend"); ! 309: ! 310: /* do different things depending upon positive or ! 311: negative bend */ ! 312: if (iBend > 0) ! 313: { ! 314: dw = (DWORD)((iBend * (LONG)(256.0 * (EQUAL * EQUAL - 1.0))) >> 8); ! 315: dwOrig += (DWORD)(AsULMUL(dw, dwOrig) >> 15); ! 316: } ! 317: else if (iBend < 0) ! 318: { ! 319: dw = (DWORD)(((-iBend) * (LONG)(256.0 * (1.0 - 1.0 / EQUAL / EQUAL))) >> 8); ! 320: dwOrig -= (DWORD)(AsULMUL(dw, dwOrig) >> 15); ! 321: } ! 322: ! 323: return dwOrig; ! 324: } ! 325: ! 326: ! 327: /***************************************************************** ! 328: Opl3_CalcFAndB - Calculates the FNumber and Block given ! 329: a frequency. ! 330: ! 331: inputs ! 332: DWORD dwPitch - pitch ! 333: returns ! 334: WORD - High byte contains the 0xb0 section of the ! 335: block and fNum, and the low byte contains the ! 336: 0xa0 section of the fNumber ! 337: */ ! 338: WORD NEAR PASCAL Opl3_CalcFAndB (DWORD dwPitch) ! 339: { ! 340: BYTE bBlock; ! 341: ! 342: // D1("\nOpl3_CalcFAndB"); ! 343: /* bBlock is like an exponential to dwPitch (or FNumber) */ ! 344: for (bBlock = 1; dwPitch >= 0x400; dwPitch >>= 1, bBlock++) ! 345: ; ! 346: ! 347: if (bBlock > 0x07) ! 348: bBlock = 0x07; /* we cant do anything about this */ ! 349: ! 350: /* put in high two bits of F-num into bBlock */ ! 351: return ((WORD) bBlock << 10) | (WORD) dwPitch; ! 352: } ! 353: ! 354: ! 355: /************************************************************** ! 356: Opl3_CalcVolume - This calculates the attenuation for an operator. ! 357: ! 358: inputs ! 359: BYTE bOrigAtten - original attenuation in 0.75 dB units ! 360: BYTE bChannel - MIDI channel ! 361: BYTE bVelocity - velocity of the note ! 362: BYTE bOper - operator number (from 0 to 3) ! 363: BYTE bMode - voice mode (from 0 through 7 for ! 364: modulator/carrier selection) ! 365: returns ! 366: BYTE - new attenuation in 0.75 dB units, maxing out at 0x3f. ! 367: */ ! 368: BYTE NEAR PASCAL Opl3_CalcVolume (BYTE bOrigAtten, BYTE bChannel, ! 369: BYTE bVelocity, BYTE bOper, BYTE bMode) ! 370: { ! 371: BYTE bVolume; ! 372: WORD wTemp; ! 373: WORD wMin; ! 374: ! 375: switch (bMode) { ! 376: case 0: ! 377: bVolume = (BYTE)(bOper == 3); ! 378: break; ! 379: case 1: ! 380: bVolume = (BYTE)((bOper == 1) || (bOper == 3)); ! 381: break; ! 382: case 2: ! 383: bVolume = (BYTE)((bOper == 0) || (bOper == 3)); ! 384: break; ! 385: case 3: ! 386: bVolume = (BYTE)(bOper != 1); ! 387: break; ! 388: case 4: ! 389: bVolume = (BYTE)((bOper == 1) || (bOper == 3)); ! 390: break; ! 391: case 5: ! 392: bVolume = (BYTE)(bOper >= 1); ! 393: break; ! 394: case 6: ! 395: bVolume = (BYTE)(bOper <= 2); ! 396: break; ! 397: case 7: ! 398: bVolume = TRUE; ! 399: break; ! 400: }; ! 401: if (!bVolume) ! 402: return bOrigAtten; /* this is a modulator wave */ ! 403: ! 404: wMin =(wSynthAttenL < wSynthAttenR) ? wSynthAttenL : wSynthAttenR; ! 405: wTemp = bOrigAtten + ((wMin << 1) + ! 406: gbChanAtten[bChannel] + gbVelocityAtten[bVelocity >> 2]); ! 407: return (wTemp > 0x3f) ? (BYTE) 0x3f : (BYTE) wTemp; ! 408: } ! 409: ! 410: /************************************************************** ! 411: Opl3_CalcStereoMask - This calculates the stereo mask. ! 412: ! 413: inputs ! 414: BYTE bChannel - MIDI channel ! 415: returns ! 416: BYTE - mask (for register 0xc0-c8) for eliminating the ! 417: left/right/both channels ! 418: */ ! 419: BYTE NEAR PASCAL Opl3_CalcStereoMask (BYTE bChannel) ! 420: { ! 421: WORD wLeft, wRight; ! 422: ! 423: /* figure out the basic levels of the 2 channels */ ! 424: wLeft = (wSynthAttenL << 1) + gbChanAtten[bChannel]; ! 425: wRight = (wSynthAttenR << 1) + gbChanAtten[bChannel]; ! 426: ! 427: /* if both are too quiet then mask to nothing */ ! 428: if ((wLeft > 0x3f) && (wRight > 0x3f)) ! 429: return 0xcf; ! 430: ! 431: /* if one channel is significantly quieter than the other than ! 432: eliminate it */ ! 433: if ((wLeft + 8) < wRight) ! 434: return (BYTE)(0xef & gbStereoMask[bChannel]); /* right is too quiet so eliminate */ ! 435: else if ((wRight + 8) < wLeft) ! 436: return (BYTE)(0xdf & gbStereoMask[bChannel]); /* left too quiet so eliminate */ ! 437: else ! 438: return (BYTE)(gbStereoMask[bChannel]); /* use both channels */ ! 439: } ! 440: ! 441: ! 442: ! 443: /* ! 444: * opl3_setvolume ! 445: * ! 446: * set the volume on channel bChannel (all channels if 0xff). ! 447: */ ! 448: static VOID Opl3_setvolume(BYTE bChannel) ! 449: { ! 450: WORD i, j, wTemp, wOffset; ! 451: noteStruct FAR * lpPS; ! 452: BYTE b4Op, bMode, bStereo; ! 453: ! 454: /* loop through all the notes looking for the right ! 455: * channel. Anything with the right channel gets its vol. changed ! 456: */ ! 457: for (i = 0; i < NUMVOICES; i++) { ! 458: if ((gVoice[i].bChannel == bChannel) || (bChannel == 0xff)) { ! 459: ! 460: /* get a pointer to the patch*/ ! 461: lpPS = &(glpPatch + gVoice[i].bPatch)->note; ! 462: b4Op = (BYTE)(lpPS->bOp != PATCH_1_2OP); ! 463: ! 464: /* moify level for each operator, but only if they ! 465: are carrier waves*/ ! 466: if (b4Op) { ! 467: bMode = (BYTE)((lpPS->bAtC0[0] & 0x01) * 2 | ! 468: (lpPS->bAtC0[1] & 0x01)); ! 469: if (lpPS->bOp == PATCH_2_2OP) ! 470: bMode += 4; ! 471: } ! 472: else ! 473: bMode = (BYTE) ( (lpPS->bAtC0[0] & 0x01) * 2 + 4); ! 474: ! 475: for (j = 0; j < (WORD)(b4Op ? NUMOPS : 2); j++) { ! 476: wTemp = (BYTE) Opl3_CalcVolume ( ! 477: (BYTE)(lpPS->op[j].bAt40 & (BYTE) 0x3f), gVoice[i].bChannel, ! 478: gVoice[i].bVelocity, (BYTE) j, bMode); ! 479: ! 480: /* write the new value out */ ! 481: wOffset = (i < NUM4VOICES) ? ! 482: gw4OpOffset[i][j] : gw2OpOffset[i - NUM4VOICES][j]; ! 483: MidiSendFM (0x40 + wOffset, ! 484: (BYTE) ((lpPS->op[j].bAt40 & (BYTE)0xc0) | (BYTE) wTemp)); ! 485: }; ! 486: ! 487: /* do stereo panning, but cutting off a left or right ! 488: channel if necessary */ ! 489: bStereo = Opl3_CalcStereoMask(gVoice[i].bChannel); ! 490: wOffset = (i < NUM4VOICES) ? ! 491: gw4VoiceOffset[i] : gw2VoiceOffset[i - NUM4VOICES]; ! 492: MidiSendFM (0xc0 + wOffset, ! 493: (BYTE)(lpPS->bAtC0[0] & bStereo)); ! 494: if (b4Op) ! 495: MidiSendFM (0xc3 + wOffset, ! 496: (BYTE) (lpPS->bAtC0[1] & bStereo)); ! 497: } ! 498: } ! 499: } ! 500: ! 501: /* --- externally called functions ---------------------------- */ ! 502: ! 503: /* ! 504: * Opl3_NoteOn - This turns a note on. (Including drums, with ! 505: * a patch # of the drum Note + 128) ! 506: * ! 507: * inputs ! 508: * BYTE bPatch - MIDI patch number ! 509: * BYTE bNote - MIDI note number ! 510: * BYTE bChannel - MIDI channel # ! 511: * BYTE bVelocity - Velocity # ! 512: * short iBend - current pitch bend from -32768, to 32767 ! 513: * returns ! 514: * none ! 515: */ ! 516: VOID NEAR PASCAL Opl3_NoteOn (BYTE bPatch, ! 517: BYTE bNote, BYTE bChannel, BYTE bVelocity, ! 518: short iBend) ! 519: { ! 520: WORD wTemp, i, j; ! 521: BYTE bTemp, bMode, bStereo; ! 522: patchStruct FAR * lpPS; ! 523: DWORD dwBasicPitch, dwPitch[2]; ! 524: noteStruct NS; ! 525: BYTE b4Op; /* use a 4-operator voice */ ! 526: ! 527: /* get a pointer to the patch*/ ! 528: lpPS = glpPatch + bPatch; ! 529: ! 530: /* find out the basic pitch according to our ! 531: note. This may be adjusted because of ! 532: pitch bends or special qualities for the note */ ! 533: dwBasicPitch = gdwPitch[bNote % 12]; ! 534: bTemp = bNote / (BYTE)12; ! 535: if (bTemp > (BYTE) (60 / 12)) ! 536: dwBasicPitch = AsLSHL(dwBasicPitch, (BYTE)(bTemp - (BYTE)(60/12))); ! 537: else if (bTemp < (BYTE) (60/12)) ! 538: dwBasicPitch = AsULSHR(dwBasicPitch, (BYTE)((BYTE) (60/12) - bTemp)); ! 539: ! 540: /* copy the note information over and modify ! 541: the total level and pitch according to ! 542: the velocity, midi volume, and tuning */ ! 543: AsMemCopy ((LPSTR) &NS, (LPSTR) &lpPS->note, sizeof(noteStruct)); ! 544: b4Op = (BYTE)(NS.bOp != PATCH_1_2OP); ! 545: ! 546: for (j = 0; j < (WORD)(b4Op ? 2 : 1); j++) { ! 547: /* modify pitch */ ! 548: dwPitch[j] = dwBasicPitch; ! 549: bTemp = (BYTE)((NS.bAtB0[j] >> 2) & 0x07); ! 550: if (bTemp > 4) ! 551: dwPitch[j] = AsLSHL(dwPitch[j], (BYTE)(bTemp - (BYTE)4)); ! 552: else if (bTemp < 4) ! 553: dwPitch[j] = AsULSHR(dwPitch[j], (BYTE)((BYTE)4 - bTemp)); ! 554: ! 555: wTemp = Opl3_CalcFAndB (Opl3_CalcBend (dwPitch[j], iBend)); ! 556: NS.bAtA0[j] = (BYTE) wTemp; ! 557: NS.bAtB0[j] = (BYTE)0x20 | (BYTE) (wTemp >> 8); ! 558: }; ! 559: ! 560: /* modify level for each operator, but only if they ! 561: are carrier waves*/ ! 562: if (b4Op) { ! 563: bMode = (BYTE)((NS.bAtC0[0] & 0x01) * 2 | (NS.bAtC0[1] & 0x01)); ! 564: if (NS.bOp == PATCH_2_2OP) ! 565: bMode += 4; ! 566: } ! 567: else ! 568: bMode = (BYTE) ( (NS.bAtC0[0] & 0x01) * 2 + 4); ! 569: ! 570: for (i = 0; i < (WORD)(b4Op ? NUMOPS : 2); i++) { ! 571: wTemp = (BYTE) Opl3_CalcVolume ( ! 572: (BYTE)(NS.op[i].bAt40 & (BYTE) 0x3f), bChannel, ! 573: bVelocity, (BYTE) i, bMode); ! 574: NS.op[i].bAt40 = (NS.op[i].bAt40 & (BYTE)0xc0) | (BYTE) wTemp; ! 575: }; ! 576: ! 577: /* do stereo panning, but cutting off a left or right ! 578: channel if necessary */ ! 579: bStereo = Opl3_CalcStereoMask(bChannel); ! 580: NS.bAtC0[0] &= bStereo; ! 581: if (b4Op) ! 582: NS.bAtC0[1] &= bStereo; ! 583: ! 584: /* find an empty slot, and use it */ ! 585: wTemp = Opl3_FindEmptySlot (bPatch, b4Op); ! 586: Opl3_FMNote (wTemp, &NS); ! 587: gVoice[wTemp].bNote = bNote; ! 588: gVoice[wTemp].bChannel = bChannel; ! 589: gVoice[wTemp].bPatch = bPatch; ! 590: gVoice[wTemp].bVelocity = bVelocity; ! 591: gVoice[wTemp].bOn = TRUE; ! 592: gVoice[wTemp].dwTime = gdwCurTime++; ! 593: gVoice[wTemp].dwOrigPitch[0] = dwPitch[0]; /* not including bend */ ! 594: gVoice[wTemp].dwOrigPitch[1] = dwPitch[1]; /* not including bend */ ! 595: gVoice[wTemp].bBlock[0] = NS.bAtB0[0]; ! 596: gVoice[wTemp].bBlock[1] = NS.bAtB0[1]; ! 597: ! 598: return; ! 599: } ! 600: ! 601: ! 602: /* Opl3_NoteOff - This turns a note off. (Including drums, ! 603: * with a patch # of the drum note + 128) ! 604: * ! 605: * inputs ! 606: * BYTE bPatch - MIDI patch # ! 607: * BYTE bNote - MIDI note number ! 608: * BYTE bChannel - MIDI channel # ! 609: * returns ! 610: * none ! 611: */ ! 612: VOID FAR PASCAL Opl3_NoteOff (BYTE bPatch, ! 613: BYTE bNote, BYTE bChannel) ! 614: { ! 615: WORD wTemp; ! 616: ! 617: // D1("\nOpl3_NoteOff"); ! 618: ! 619: /* find the note slot */ ! 620: wTemp = Opl3_FindFullSlot (bNote, bChannel); ! 621: if (wTemp != 0xffff) { ! 622: ! 623: /* shut off the note portion */ ! 624: /* we have the note slot. turn it off */ ! 625: if (wTemp < NUM4VOICES) { ! 626: MidiSendFM (AD_BLOCK + gw4VoiceOffset[wTemp], ! 627: (BYTE)(gVoice[wTemp].bBlock[0] & 0x1f)); ! 628: MidiSendFM (AD_BLOCK + gw4VoiceOffset[wTemp] + 3, ! 629: (BYTE)(gVoice[wTemp].bBlock[1] & 0x1f)); ! 630: } ! 631: else ! 632: MidiSendFM (AD_BLOCK + gw2VoiceOffset[wTemp - NUM4VOICES], ! 633: (BYTE)(gVoice[wTemp].bBlock[0] & 0x1f)); ! 634: ! 635: /* note this */ ! 636: gVoice[wTemp].bOn = FALSE; ! 637: gVoice[wTemp].bBlock[0] &= 0x1f; ! 638: gVoice[wTemp].bBlock[1] &= 0x1f; ! 639: gVoice[wTemp].dwTime = gdwCurTime; ! 640: }; ! 641: ! 642: } ! 643: ! 644: /* ! 645: * Opl3_AllNotesOff - turn off all notes ! 646: * ! 647: */ ! 648: VOID Opl3_AllNotesOff(void) ! 649: { ! 650: BYTE i; ! 651: ! 652: for (i = 0; i < NUMVOICES; i++) { ! 653: Opl3_NoteOff (gVoice[i].bPatch, gVoice[i].bNote, gVoice[i].bChannel); ! 654: } ! 655: } ! 656: ! 657: ! 658: ! 659: /* Opl3_NewVolume - This should be called if a volume level ! 660: * has changed. This will adjust the levels of all the playing ! 661: * voices. ! 662: * ! 663: * inputs ! 664: * WORD wLeft - left attenuation (1.5 db units) ! 665: * WORD wRight - right attenuation (ignore if mono) ! 666: * returns ! 667: * none ! 668: */ ! 669: VOID FAR PASCAL Opl3_NewVolume (WORD wLeft, WORD wRight) ! 670: { ! 671: ! 672: /* make sure that we are actually open */ ! 673: if (!glpPatch) ! 674: return; ! 675: ! 676: wSynthAttenL = wLeft; ! 677: wSynthAttenR = wRight; ! 678: ! 679: Opl3_setvolume(0xff); ! 680: ! 681: } ! 682: ! 683: ! 684: ! 685: /* Opl3_ChannelVolume - set the volume level for an individual channel. ! 686: * ! 687: * inputs ! 688: * BYTE bChannel - channel number to change ! 689: * WORD wAtten - attenuation in 1.5 db units ! 690: * ! 691: * returns ! 692: * none ! 693: */ ! 694: VOID FAR PASCAL Opl3_ChannelVolume(BYTE bChannel, WORD wAtten) ! 695: { ! 696: gbChanAtten[bChannel] = wAtten; ! 697: ! 698: Opl3_setvolume(bChannel); ! 699: } ! 700: ! 701: ! 702: ! 703: /* Opl3_SetPan - set the left-right pan position. ! 704: * ! 705: * inputs ! 706: * BYTE bChannel - channel number to alter ! 707: * BYTE bPan - 0 for left, 127 for right or somewhere in the middle. ! 708: * ! 709: * returns - none ! 710: */ ! 711: VOID FAR PASCAL Opl3_SetPan(BYTE bChannel, BYTE bPan) ! 712: { ! 713: /* change the pan level */ ! 714: if (bPan > (64 + 16)) ! 715: gbStereoMask[bChannel] = 0xef; /* let only right channel through */ ! 716: else if (bPan < (64 - 16)) ! 717: gbStereoMask[bChannel] = 0xdf; /* let only left channel through */ ! 718: else ! 719: gbStereoMask[bChannel] = 0xff; /* let both channels */ ! 720: ! 721: /* change any curently playing patches */ ! 722: Opl3_setvolume(bChannel); ! 723: } ! 724: ! 725: ! 726: /* Opl3_PitchBend - This pitch bends a channel. ! 727: * ! 728: * inputs ! 729: * BYTE bChannel - channel ! 730: * short iBend - Values from -32768 to 32767, being ! 731: * -2 to +2 half steps ! 732: * returns ! 733: * none ! 734: */ ! 735: VOID NEAR PASCAL Opl3_PitchBend (BYTE bChannel, short iBend) ! 736: { ! 737: WORD i, wTemp[2], j; ! 738: DWORD dwNew; ! 739: ! 740: /* loop through all the notes looking for the right ! 741: * channel. Anything with the right channel gets its pitch bent ! 742: */ ! 743: for (i = 0; i < NUMVOICES; i++) { ! 744: if (gVoice[i].bChannel == bChannel) { ! 745: for (j = 0; j < (WORD)((i < NUM4VOICES) ? 2 : 1); j++) { ! 746: dwNew = Opl3_CalcBend (gVoice[i].dwOrigPitch[j], iBend); ! 747: wTemp[j] = Opl3_CalcFAndB (dwNew); ! 748: gVoice[i].bBlock[j] = (gVoice[i].bBlock[j] & (BYTE)0xe0) | ! 749: (BYTE) (wTemp[j] >> 8); ! 750: } ! 751: ! 752: if (i < NUM4VOICES) { ! 753: MidiSendFM (AD_BLOCK + gw4VoiceOffset[i], ! 754: gVoice[i].bBlock[0]); ! 755: MidiSendFM (AD_BLOCK + gw4VoiceOffset[i] + 3, ! 756: gVoice[i].bBlock[1]); ! 757: MidiSendFM (AD_FNUMBER + gw4VoiceOffset[i], ! 758: (BYTE) wTemp[0]); ! 759: MidiSendFM (AD_FNUMBER + gw4VoiceOffset[i] + 3, ! 760: (BYTE) wTemp[1]); ! 761: } else { ! 762: MidiSendFM (AD_BLOCK + gw2VoiceOffset[i - NUM4VOICES], ! 763: gVoice[i].bBlock[0]); ! 764: MidiSendFM (AD_FNUMBER + gw2VoiceOffset[i - NUM4VOICES], ! 765: (BYTE) wTemp[0]); ! 766: } ! 767: } ! 768: } ! 769: } ! 770: ! 771: static TCHAR BCODE gszDefPatchLib[] = TEXT("SYNTH.PAT"); ! 772: static TCHAR BCODE gszIniKeyPatchLib[] = INI_STR_PATCHLIB; ! 773: static TCHAR BCODE gszIniDrvSection[] = INI_DRIVER; ! 774: static TCHAR BCODE gszIniDrvFile[] = INI_SOUND; ! 775: static TCHAR BCODE gszSysIniSection[] = TEXT("synth.dll"); ! 776: static TCHAR BCODE gszSysIniFile[] = TEXT("System.Ini"); ! 777: ! 778: /** static DWORD NEAR PASCAL DrvGetProfileString(LPSTR szKeyName, LPSTR szDef, LPSTR szBuf, UINT wBufLen) ! 779: * ! 780: * DESCRIPTION: ! 781: * ! 782: * ! 783: * ARGUMENTS: ! 784: * (LPSTR szKeyName, LPSTR szDef, LPSTR szBuf, WORD wBufLen) ! 785: * HINT wSystem - if TRUE write/read to system.ini ! 786: * ! 787: * RETURN (static DWORD NEAR PASCAL): ! 788: * ! 789: * ! 790: * NOTES: ! 791: * ! 792: ** cjp */ ! 793: ! 794: static DWORD NEAR PASCAL DrvGetProfileString(LPTSTR szKeyName, LPTSTR szDef, LPTSTR szBuf, UINT wBufLen, ! 795: UINT wSystem) ! 796: { ! 797: return GetPrivateProfileString(wSystem ? gszSysIniSection : gszIniDrvSection, szKeyName, szDef, ! 798: szBuf, wBufLen, wSystem ? gszSysIniFile : gszIniDrvFile); ! 799: } /* DrvGetProfileString() */ ! 800: ! 801: /* Opl3_BoardInit - initialise board and load patches as necessary. ! 802: * ! 803: * inputs - none ! 804: * returns - 0 for success or the error code ! 805: */ ! 806: WORD Opl3_BoardInit(void) ! 807: { ! 808: HMMIO hmmio; ! 809: MMCKINFO mmckinfo, mm2; ! 810: TCHAR szPatchLib[STRINGLEN]; /* patch libarary */ ! 811: ! 812: D1 ("\nOpl3_Init"); ! 813: ! 814: /* Check we haven't already initialized */ ! 815: if (glpPatch != NULL) { ! 816: return 0; ! 817: } ! 818: ! 819: /* should the load patches be moved to the init section? */ ! 820: DrvGetProfileString(gszIniKeyPatchLib, gszDefPatchLib, ! 821: szPatchLib, sizeof(szPatchLib), FALSE); ! 822: ! 823: ! 824: /* allocate the memory, and fill it up from the patch ! 825: * library. The name of the library has been set previously ! 826: * and written into szPatchLib ! 827: */ ! 828: glpPatch = (patchStruct FAR *)GlobalLock(GlobalAlloc(GMEM_MOVEABLE|GMEM_SHARE|GMEM_ZEROINIT, sizeof(patchStruct) * NUMPATCHES)); ! 829: if (!glpPatch) { ! 830: D1 ("Opl3_Init: could not allocate patch container memory!"); ! 831: return ERR_OUTOFMEMORY; ! 832: } ! 833: ! 834: hmmio = mmioOpen (szPatchLib, NULL, MMIO_READ); ! 835: if (hmmio) { ! 836: mmioDescend (hmmio, &mmckinfo, NULL, 0); ! 837: if ((mmckinfo.ckid == FOURCC_RIFF) && ! 838: (mmckinfo.fccType == RIFF_PATCH)) { ! 839: ! 840: mm2.ckid = RIFF_FM4; ! 841: if (!mmioDescend (hmmio, &mm2, &mmckinfo, MMIO_FINDCHUNK)) { ! 842: /* we have found the synthesis chunk */ ! 843: if (mm2.cksize > (sizeof(patchStruct)*NUMPATCHES)) ! 844: mm2.cksize = sizeof(patchStruct)*NUMPATCHES; ! 845: mmioRead (hmmio, (LPSTR) glpPatch, mm2.cksize); ! 846: } else { ! 847: D1("\nBad mmioDescend2"); ! 848: }; ! 849: } else { ! 850: D1("\nBad mmioDescend1"); ! 851: }; ! 852: ! 853: mmioClose (hmmio, 0); ! 854: } else { ! 855: ! 856: TCHAR szAlert[50]; ! 857: TCHAR szErrorBuffer[255]; ! 858: ! 859: LoadString(ghModule, SR_ALERT, szAlert, sizeof(szAlert)); ! 860: LoadString(ghModule, SR_ALERT_NOPATCH, szErrorBuffer, sizeof(szErrorBuffer)); ! 861: MessageBox(NULL, szErrorBuffer, szAlert, MB_OK|MB_ICONHAND); ! 862: D1 ("\nBad mmioOpen"); ! 863: }; ! 864: ! 865: ! 866: return 0; /* done */ ! 867: } ! 868: ! 869: ! 870: /* ! 871: * Opl3_BoardReset - silence the board and set all voices off. ! 872: */ ! 873: VOID Opl3_BoardReset(void) ! 874: { ! 875: ! 876: BYTE i; ! 877: ! 878: /* make sure all notes turned off */ ! 879: Opl3_AllNotesOff(); ! 880: ! 881: ! 882: /* ---- silence the chip -------- */ ! 883: ! 884: /* tell the FM chip to use 4-operator mode, and ! 885: fill in any other random variables */ ! 886: MidiSendFM (AD_NEW, 0x01); ! 887: MidiSendFM (AD_MASK, 0x60); ! 888: MidiSendFM (AD_CONNECTION, 0x3f); ! 889: MidiSendFM (AD_NTS, 0x00); ! 890: ! 891: ! 892: /* turn off the drums, and use high vibrato/modulation */ ! 893: MidiSendFM (AD_DRUM, 0xc0); ! 894: ! 895: /* turn off all the oscillators */ ! 896: for (i = 0; i < 0x15; i++) { ! 897: MidiSendFM (AD_LEVEL + i, 0x3f); ! 898: MidiSendFM (AD_LEVEL2 + i, 0x3f); ! 899: }; ! 900: ! 901: /* turn off all the voices */ ! 902: for (i = 0; i < 0x08; i++) { ! 903: MidiSendFM (AD_BLOCK + i, 0x00); ! 904: MidiSendFM (AD_BLOCK2 + i, 0x00); ! 905: }; ! 906: ! 907: ! 908: /* clear all of the voice slots to empty */ ! 909: for (i = 0; i < NUMVOICES; i++) ! 910: gVoice[i].dwTime = 0; ! 911: ! 912: /* start attenuations at -3 dB, which is 90 MIDI level */ ! 913: for (i = 0; i < NUMCHANNELS; i++) { ! 914: gbChanAtten[i] = 4; ! 915: gbStereoMask[i] = 0xff; ! 916: }; ! 917: ! 918: } ! 919: ! 920: ! 921:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.