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

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: 

unix.superglobalmegacorp.com

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