Annotation of ntddk/src/mmedia/synth/dll/opl3.c, revision 1.1.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.