File:  [WindowsNT SDKs] / ntddk / src / mmedia / synth / dll / opl3.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Thu Aug 9 18:31:12 2018 UTC (7 years, 9 months ago) by root
Branches: msft, MAIN
CVS tags: ntddk-nov-1993, HEAD
Microsoft Windows NT Build 511 (DDK SDK) 11-01-1993

/*
 * Copyright (c) 1992 Microsoft Corporation
 */

/*
 * Interface functions for the OPL3 midi device type.
 *
 * These functions are called from midi.c when the kernel driver
 * has decreed that this is an opl3-compatible device.
 *
 * Geraint Davies, Dec 92
 */

#include <windows.h>
#include <mmsystem.h>
#include <mmddk.h>
#include "driver.h"
#include "opl3.h"

/* --- typedefs ----------------------------------------------- */

/* typedefs for MIDI patches */
#define NUMOPS                  (4)
#define PATCH_1_4OP             (0) /* use 4-operator patch */
#define PATCH_2_2OP             (1) /* use two 2-operator patches */
#define PATCH_1_2OP             (2) /* use one 2-operator patch */

#define RIFF_PATCH              (mmioFOURCC('P','t','c','h'))
#define RIFF_FM4                (mmioFOURCC('f','m','4',' '))
#define NUM4VOICES              (6)     /* # 4-op voices that can play at once */
#define NUM2VOICES              (6)             /* # 2operator voices */
#define NUMVOICES               (NUM4VOICES + NUM2VOICES)

typedef struct _operStruct {
    BYTE    bAt20;              /* flags which are send to 0x20 on fm */
    BYTE    bAt40;              /* flags seet to 0x40 */
                                /* the note velocity & midi velocity affect total level */
    BYTE    bAt60;              /* flags sent to 0x60 */
    BYTE    bAt80;              /* flags sent to 0x80 */
    BYTE    bAtE0;              /* flags send to 0xe0 */
} operStruct;

typedef struct _noteStruct {
    operStruct op[NUMOPS];      /* operators */
    BYTE    bAtA0[2];           /* send to 0xA0, A3 */
    BYTE    bAtB0[2];           /* send to 0xB0, B3 */
                                /* use in a patch, the block should be 4 to indicate
                                    normal pitch, 3 => octave below, etc. */
    BYTE    bAtC0[2];           /* sent to 0xc0, C3 */
    BYTE    bOp;                /* see PATCH_??? */
    BYTE    bDummy;             /* place holder */
} noteStruct;


typedef struct _patchStruct {
    noteStruct note;            /* note. This is all in the structure at the moment */
} patchStruct;

/* MIDI */

typedef struct _voiceStruct {
        BYTE    bNote;                  /* note played */
        BYTE    bChannel;               /* channel played on */
        BYTE    bPatch;                 /* what patch is the note,
                                           drums patch = drum note + 128 */
        BYTE    bOn;                    /* TRUE if note is on, FALSE if off */
        BYTE	bVelocity;		/* velocity */
        BYTE	bJunk;			/* filler */
        DWORD   dwTime;                 /* time that was turned on/off;
                                           0 time indicates that its not in use */
        DWORD   dwOrigPitch[2];         /* original pitch, for pitch bend */
        BYTE    bBlock[2];              /* value sent to the block */
} voiceStruct;

/* --- module data -------------------------------------------- */

/* a bit of tuning information */
#define FSAMP                           (50000.0)     /* sampling frequency */
#define PITCH(x)                        ((DWORD)((x) * (double) (1L << 19) / FSAMP))
							/* x is the desired frequency,
								== FNUM at b=1 */
#define EQUAL                           (1.059463094359)
#ifdef EUROPE
#       define  A                                                       (442.0)
#else
#       define  A                           (440.0)
#endif
#define ASHARP                          (A * EQUAL)
#define B                               (ASHARP * EQUAL)
#define C                               (B * EQUAL / 2.0)
#define CSHARP                          (C * EQUAL)
#define D                               (CSHARP * EQUAL)
#define DSHARP                          (D * EQUAL)
#define E                               (DSHARP * EQUAL)
#define F                               (E * EQUAL)
#define FSHARP                          (F * EQUAL)
#define G                               (FSHARP * EQUAL)
#define GSHARP                          (G * EQUAL)

/* volume */
WORD    wSynthAttenL = 0;        /* in 1.5dB steps */
WORD    wSynthAttenR = 0;        /* in 1.5dB steps */

/* patch library */
patchStruct FAR * glpPatch = NULL;  /* points to the patches */

/* voices being played */
voiceStruct gVoice[NUMVOICES];  /* info on what voice is where */
static DWORD gdwCurTime = 1;    /* for note on/off */

BYTE    gbCur4opReg = 0;                /* current value to 4-operator connection */

/* channel volumes */
BYTE    gbChanAtten[NUMCHANNELS];       /* attenuation of each channel, in .75 db steps */
BYTE	gbStereoMask[NUMCHANNELS];		/* mask for left/right for stereo midi files */

/* operator offset location */
static WORD BCODE gw4OpOffset[NUM4VOICES][NUMOPS] = {
	{0x000,0x003,0x008,0x00b}, {0x001,0x004,0x009,0x00c},
	{0x002,0x005,0x00a,0x00d}, {0x100,0x103,0x108,0x10b},
	{0x101,0x104,0x109,0x10c}, {0x102,0x105,0x10a,0x10d}};
static WORD BCODE gw2OpOffset[NUM2VOICES][2] = {
	{0x010,0x013}, {0x011,0x014}, {0x012,0x015},
	{0x110,0x113}, {0x111,0x114}, {0x112,0x115}
	};

/* voice offset location */
static WORD BCODE gw4VoiceOffset[NUM4VOICES] = {
	0x000, 0x001, 0x002, 0x100, 0x101, 0x102};
static WORD BCODE gw2VoiceOffset[NUM2VOICES] = {
	0x006, 0x007, 0x008, 0x106, 0x107, 0x108};

/* pitch values, from middle c, to octave above it */
static DWORD BCODE gdwPitch[12] = {
	PITCH(C), PITCH(CSHARP), PITCH(D), PITCH(DSHARP),
	PITCH(E), PITCH(F), PITCH(FSHARP), PITCH(G),
	PITCH(GSHARP), PITCH(A), PITCH(ASHARP), PITCH(B)};



/* --- internal functions -------------------------------------- */


/***************************************************************
Opl3_FMNote - This turns on a FM-synthesizer note.

inputs
	WORD    wNote - the note number from 0 to NUMVOICES.
		If wNote < NUM4VOICES then dealing with 4 operator synth,
		else using 2-operator synth.
	noteStruct FAR * lpSN - structure containing information about
		what is to be played.
returns
	none
*/
VOID NEAR PASCAL Opl3_FMNote (WORD wNote, noteStruct FAR * lpSN)
{
    WORD    i;
    WORD    wOffset;
    operStruct FAR * lpOS;

    // D1 ("\nOpl3_FMNote");
    /* write out a note off, just to make sure */
    MidiSendFM (AD_BLOCK +
    ((wNote < NUM4VOICES) ? gw4VoiceOffset[wNote] : gw2VoiceOffset[wNote - NUM4VOICES]),
    (BYTE)0);

    /* write out information specifying if we are
    to use 2 or 4 operators */
    if (wNote < NUM4VOICES) {
	if (lpSN->bOp == PATCH_1_4OP)
		gbCur4opReg |= (1 << wNote);
	else
		gbCur4opReg &= (~(1 << wNote));
	MidiSendFM (AD_CONNECTION, gbCur4opReg);
    };
    /* else send out nothing */

    /* writing the operator information */
    for (i = 0; i < (WORD)((wNote < NUM4VOICES) ? NUMOPS : 2); i++) {
	wOffset = (wNote < NUM4VOICES) ?
		gw4OpOffset[wNote][i] : gw2OpOffset[wNote - NUM4VOICES][i];
	lpOS = &lpSN->op[i];
	MidiSendFM (0x20 + wOffset, lpOS->bAt20);
	MidiSendFM (0x40 + wOffset, lpOS->bAt40);
	MidiSendFM (0x60 + wOffset, lpOS->bAt60);
	MidiSendFM (0x80 + wOffset, lpOS->bAt80);
	MidiSendFM (0xE0 + wOffset, lpOS->bAtE0);
    };
    /* write out the voice information */
    wOffset = (wNote < NUM4VOICES) ?
	gw4VoiceOffset[wNote] : gw2VoiceOffset[wNote - NUM4VOICES];
    MidiSendFM (0xa0 + wOffset, lpSN->bAtA0[0]);
    MidiSendFM (0xc0 + wOffset, lpSN->bAtC0[0]);
    if (wNote < NUM4VOICES) {
	MidiSendFM (0xc3 + wOffset, lpSN->bAtC0[1]);
	MidiSendFM (0xa3 + wOffset, lpSN->bAtA0[1]);
	MidiSendFM (0xb3 + wOffset, (BYTE)(lpSN->bAtB0[1] | 0x20)  /* note on */);
    };
    MidiSendFM (0xb0 + wOffset, (BYTE)(lpSN->bAtB0[0] | 0x20)  /* note on */);

    /* done */
}

/***************************************************************
Opl3_FindEmptySlot - This finds an empty note-slot for a MIDI voice.
	If there are no empty slots then this looks for the oldest
	off note. It this doesnt work then it looks\
	for the oldest on-note of the same patch. If all notes are still on then this
	finds the oldest turned-on-note.

inputs
	BYTE    bPatch - MIDI patch that will replace it
	BYTE    b4Op - if TRUE then looking through 4-operator voices,
				else looking through 2-operator voices
returns
	WORD - note slot #
*/
WORD NEAR PASCAL Opl3_FindEmptySlot (BYTE bPatch, BYTE b4Op)
{
    WORD    i, found;
    DWORD   dwOldest;

    // D1 ("\nOpl3_FindEmptySlot");
    /* first, look for a slot with a time == 0 */
    for (i = (b4Op ? 0 : NUM4VOICES); i < (WORD)(b4Op ? NUM4VOICES : NUMVOICES); i++)
	if (!gVoice[i].dwTime)
	    return i;

    /* now, look for a slot of the oldest off-note */
    dwOldest = 0xffffffff;
    found = 0xffff;
    for (i = (b4Op ? 0 : NUM4VOICES); i < (WORD)(b4Op ? NUM4VOICES : NUMVOICES); i++)
	if (!gVoice[i].bOn && (gVoice[i].dwTime < dwOldest)) {

	    dwOldest = gVoice[i].dwTime;
	    found = i;
	};
    if (found != 0xffff)
	return found;

    /* now, look for a slot of the oldest note with the same patch */
    dwOldest = 0xffffffff;
    found = 0xffff;
    for (i = (b4Op ? 0 : NUM4VOICES); i < (WORD)(b4Op ? NUM4VOICES : NUMVOICES); i++)
	if ((gVoice[i].bPatch == bPatch) &&
	    (gVoice[i].dwTime < dwOldest)) {

		dwOldest = gVoice[i].dwTime;
		found = i;
	};
    if (found != 0xffff)
	return found;

    /* now, just look for the oldest voice */
    found = (b4Op ? 0 : NUM4VOICES);
    dwOldest = gVoice[found].dwTime;
    for (i = (found + 1); i < (WORD)(b4Op ? NUM4VOICES : NUMVOICES); i++)
	if (gVoice[i].dwTime < dwOldest) {

		dwOldest = gVoice[i].dwTime;
		found = i;
	};

    return found;
}


/***************************************************************
Opl3_FindFullSlot - This finds a slot with a specific note,
	and channel. If it is not found then 0xffff is
	returned.

inputs
	BYTE    bNote - MIDI note number
	BYTE    bChannel - MIDI channel #
returns
	WORD - note slot #, or 0xffff if cant find
*/
WORD NEAR PASCAL Opl3_FindFullSlot (
	BYTE bNote, BYTE bChannel)
{
    WORD    i;

    // D1("\nOpl3_FindFullSlot");
    for (i = 0; i < NUMVOICES; i++)
	if ((bChannel == gVoice[i].bChannel) &&
		(bNote == gVoice[i].bNote) && (gVoice[i].bOn))
	    return i;

    /* couldnt find it */
    return 0xffff;
}

/**************************************************************
Opl3_CalcBend - This calculates the effects of pitch bend
	on an original value.

inputs
	DWORD   dwOrig - original frequency
	short   iBend - from -32768 to 32768, -2 half steps to +2
returns
	DWORD - new frequency
*/
DWORD NEAR PASCAL Opl3_CalcBend (DWORD dwOrig, short iBend)
{
    DWORD   dw;
    // D1 ("\nOpl3_CalcBend");

    /* do different things depending upon positive or
	negative bend */
    if (iBend > 0)
    {
	dw = (DWORD)((iBend * (LONG)(256.0 * (EQUAL * EQUAL - 1.0))) >> 8);
	dwOrig += (DWORD)(AsULMUL(dw, dwOrig) >> 15);
    }
    else if (iBend < 0)
    {
	dw = (DWORD)(((-iBend) * (LONG)(256.0 * (1.0 - 1.0 / EQUAL / EQUAL))) >> 8);
	dwOrig -= (DWORD)(AsULMUL(dw, dwOrig) >> 15);
    }

    return dwOrig;
}


/*****************************************************************
Opl3_CalcFAndB - Calculates the FNumber and Block given
	a frequency.

inputs
	DWORD   dwPitch - pitch
returns
	WORD - High byte contains the 0xb0 section of the
			block and fNum, and the low byte contains the
			0xa0 section of the fNumber
*/
WORD NEAR PASCAL Opl3_CalcFAndB (DWORD dwPitch)
{
    BYTE    bBlock;

    // D1("\nOpl3_CalcFAndB");
    /* bBlock is like an exponential to dwPitch (or FNumber) */
    for (bBlock = 1; dwPitch >= 0x400; dwPitch >>= 1, bBlock++)
	;

    if (bBlock > 0x07)
	bBlock = 0x07;  /* we cant do anything about this */

    /* put in high two bits of F-num into bBlock */
    return ((WORD) bBlock << 10) | (WORD) dwPitch;
}


/**************************************************************
Opl3_CalcVolume - This calculates the attenuation for an operator.

inputs
	BYTE	bOrigAtten - original attenuation in 0.75 dB units
	BYTE	bChannel - MIDI channel
	BYTE	bVelocity - velocity of the note
	BYTE	bOper - operator number (from 0 to 3)
	BYTE	bMode - voice mode (from 0 through 7 for
				modulator/carrier selection)
returns
	BYTE - new attenuation in 0.75 dB units, maxing out at 0x3f.
*/
BYTE NEAR PASCAL Opl3_CalcVolume (BYTE bOrigAtten, BYTE bChannel,
	BYTE bVelocity, BYTE bOper, BYTE bMode)
{
    BYTE	bVolume;
    WORD	wTemp;
    WORD	wMin;

    switch (bMode) {
	case 0:
		bVolume = (BYTE)(bOper == 3);
		break;
	case 1:
		bVolume = (BYTE)((bOper == 1) || (bOper == 3));
		break;
	case 2:
		bVolume = (BYTE)((bOper == 0) || (bOper == 3));
		break;
	case 3:
		bVolume = (BYTE)(bOper != 1);
		break;
	case 4:
		bVolume = (BYTE)((bOper == 1) || (bOper == 3));
		break;
	case 5:
		bVolume = (BYTE)(bOper >= 1);
		break;
	case 6:
		bVolume = (BYTE)(bOper <= 2);
		break;
	case 7:
		bVolume = TRUE;
		break;
	};
    if (!bVolume)
	return bOrigAtten; /* this is a modulator wave */

    wMin =(wSynthAttenL < wSynthAttenR) ? wSynthAttenL : wSynthAttenR;
    wTemp = bOrigAtten + ((wMin << 1) +
	gbChanAtten[bChannel] + gbVelocityAtten[bVelocity >> 2]);
    return (wTemp > 0x3f) ? (BYTE) 0x3f : (BYTE) wTemp;
}

/**************************************************************
Opl3_CalcStereoMask - This calculates the stereo mask.

inputs
	BYTE	bChannel - MIDI channel
returns
	BYTE - mask (for register 0xc0-c8) for eliminating the
		left/right/both channels
*/
BYTE NEAR PASCAL Opl3_CalcStereoMask (BYTE bChannel)
{
    WORD	wLeft, wRight;

    /* figure out the basic levels of the 2 channels */
    wLeft = (wSynthAttenL << 1) + gbChanAtten[bChannel];
    wRight = (wSynthAttenR << 1) + gbChanAtten[bChannel];

    /* if both are too quiet then mask to nothing */
    if ((wLeft > 0x3f) && (wRight > 0x3f))
	return 0xcf;

    /* if one channel is significantly quieter than the other than
	eliminate it */
    if ((wLeft + 8) < wRight)
	return (BYTE)(0xef & gbStereoMask[bChannel]);	/* right is too quiet so eliminate */
    else if ((wRight + 8) < wLeft)
	return (BYTE)(0xdf & gbStereoMask[bChannel]);	/* left too quiet so eliminate */
    else
	return (BYTE)(gbStereoMask[bChannel]);	/* use both channels */
}



/*
 * opl3_setvolume
 *
 * set the volume on channel bChannel (all channels if 0xff).
 */
static VOID Opl3_setvolume(BYTE bChannel)
{
    WORD    i, j, wTemp, wOffset;
    noteStruct FAR * lpPS;
    BYTE	b4Op, bMode, bStereo;

    /* loop through all the notes looking for the right
     *	channel. Anything with the right channel gets its vol. changed
     */
    for (i = 0; i < NUMVOICES; i++) {
	if ((gVoice[i].bChannel == bChannel) || (bChannel == 0xff)) {

		/* get a pointer to the patch*/
		lpPS = &(glpPatch + gVoice[i].bPatch)->note;
		b4Op = (BYTE)(lpPS->bOp != PATCH_1_2OP);

		/* moify level for each operator, but only if they
			are carrier waves*/
		if (b4Op) {
			bMode = (BYTE)((lpPS->bAtC0[0] & 0x01) * 2 |
				(lpPS->bAtC0[1] & 0x01));
			if (lpPS->bOp == PATCH_2_2OP)
				bMode += 4;
			}
		else
			bMode = (BYTE) ( (lpPS->bAtC0[0] & 0x01) * 2 + 4);

		for (j = 0; j < (WORD)(b4Op ? NUMOPS : 2); j++) {
			wTemp = (BYTE) Opl3_CalcVolume (
				(BYTE)(lpPS->op[j].bAt40 & (BYTE) 0x3f), gVoice[i].bChannel,
				gVoice[i].bVelocity, (BYTE) j, bMode);

			/* write the new value out */
			wOffset = (i < NUM4VOICES) ?
				gw4OpOffset[i][j] : gw2OpOffset[i - NUM4VOICES][j];
			MidiSendFM (0x40 + wOffset,
				(BYTE) ((lpPS->op[j].bAt40 & (BYTE)0xc0) | (BYTE) wTemp));
			};

		/* do stereo panning, but cutting off a left or right
			channel if necessary */
		bStereo = Opl3_CalcStereoMask(gVoice[i].bChannel);
		wOffset = (i < NUM4VOICES) ?
			gw4VoiceOffset[i] : gw2VoiceOffset[i - NUM4VOICES];
		MidiSendFM (0xc0 + wOffset,
			(BYTE)(lpPS->bAtC0[0] & bStereo));
		if (b4Op)
			MidiSendFM (0xc3 + wOffset,
				(BYTE) (lpPS->bAtC0[1] & bStereo));
	}
    }
}

/* --- externally called functions ---------------------------- */

/*
 * Opl3_NoteOn - This turns a note on. (Including drums, with
 *	a patch # of the drum Note + 128)
 *
 * inputs
 *      BYTE    bPatch - MIDI patch number
 *	BYTE    bNote - MIDI note number
 *      BYTE    bChannel - MIDI channel #
 *	BYTE    bVelocity - Velocity #
 *	short   iBend - current pitch bend from -32768, to 32767
 * returns
 *	none
 */
VOID NEAR PASCAL Opl3_NoteOn (BYTE bPatch,
	BYTE bNote, BYTE bChannel, BYTE bVelocity,
	short iBend)
{
    WORD    wTemp, i, j;
    BYTE    bTemp, bMode, bStereo;
    patchStruct FAR * lpPS;
    DWORD   dwBasicPitch, dwPitch[2];
    noteStruct NS;
    BYTE    b4Op;   /* use a 4-operator voice */

    /* get a pointer to the patch*/
    lpPS = glpPatch + bPatch;

    /* find out the basic pitch according to our
	note. This may be adjusted because of
	pitch bends or special qualities for the note */
    dwBasicPitch = gdwPitch[bNote % 12];
    bTemp = bNote / (BYTE)12;
    if (bTemp > (BYTE) (60 / 12))
	dwBasicPitch = AsLSHL(dwBasicPitch, (BYTE)(bTemp - (BYTE)(60/12)));
    else if (bTemp < (BYTE) (60/12))
	dwBasicPitch = AsULSHR(dwBasicPitch, (BYTE)((BYTE) (60/12) - bTemp));

    /* copy the note information over and modify
	the total level and pitch according to
	the velocity, midi volume, and tuning */
    AsMemCopy ((LPSTR) &NS, (LPSTR) &lpPS->note, sizeof(noteStruct));
    b4Op = (BYTE)(NS.bOp != PATCH_1_2OP);

    for (j = 0; j < (WORD)(b4Op ? 2 : 1); j++) {
	/* modify pitch */
	dwPitch[j] = dwBasicPitch;
	bTemp = (BYTE)((NS.bAtB0[j] >> 2) & 0x07);
	if (bTemp > 4)
		dwPitch[j] = AsLSHL(dwPitch[j], (BYTE)(bTemp - (BYTE)4));
	else if (bTemp < 4)
		dwPitch[j] = AsULSHR(dwPitch[j], (BYTE)((BYTE)4 - bTemp));

	wTemp = Opl3_CalcFAndB (Opl3_CalcBend (dwPitch[j], iBend));
	NS.bAtA0[j] = (BYTE) wTemp;
	NS.bAtB0[j] = (BYTE)0x20 | (BYTE) (wTemp >> 8);
    };

    /* modify level for each operator, but only if they
	are carrier waves*/
    if (b4Op) {
	bMode = (BYTE)((NS.bAtC0[0] & 0x01) * 2 | (NS.bAtC0[1] & 0x01));
	if (NS.bOp == PATCH_2_2OP)
		bMode += 4;
	}
    else
	bMode = (BYTE) ( (NS.bAtC0[0] & 0x01) * 2 + 4);

    for (i = 0; i < (WORD)(b4Op ? NUMOPS : 2); i++) {
	wTemp = (BYTE) Opl3_CalcVolume (
		(BYTE)(NS.op[i].bAt40 & (BYTE) 0x3f), bChannel,
		bVelocity, (BYTE) i, bMode);
	NS.op[i].bAt40 = (NS.op[i].bAt40 & (BYTE)0xc0) | (BYTE) wTemp;
	};

    /* do stereo panning, but cutting off a left or right
       channel if necessary */
    bStereo = Opl3_CalcStereoMask(bChannel);
    NS.bAtC0[0] &= bStereo;
    if (b4Op)
	NS.bAtC0[1] &= bStereo;

    /* find an empty slot, and use it */
    wTemp = Opl3_FindEmptySlot (bPatch, b4Op);
    Opl3_FMNote (wTemp, &NS);
    gVoice[wTemp].bNote = bNote;
    gVoice[wTemp].bChannel = bChannel;
    gVoice[wTemp].bPatch = bPatch;
    gVoice[wTemp].bVelocity = bVelocity;
    gVoice[wTemp].bOn = TRUE;
    gVoice[wTemp].dwTime = gdwCurTime++;
    gVoice[wTemp].dwOrigPitch[0] = dwPitch[0];    /* not including bend */
    gVoice[wTemp].dwOrigPitch[1] = dwPitch[1];    /* not including bend */
    gVoice[wTemp].bBlock[0] = NS.bAtB0[0];
    gVoice[wTemp].bBlock[1] = NS.bAtB0[1];

    return;
}


/* Opl3_NoteOff - This turns a note off. (Including drums,
 *	with a patch # of the drum note + 128)
 *
 * inputs
 *	BYTE    bPatch - MIDI patch #
 *	BYTE    bNote - MIDI note number
 *	BYTE    bChannel - MIDI channel #
 * returns
 *	none
 */
VOID FAR PASCAL Opl3_NoteOff (BYTE bPatch,
	BYTE bNote, BYTE bChannel)
{
    WORD    wTemp;

    // D1("\nOpl3_NoteOff");

    /* find the note slot */
    wTemp = Opl3_FindFullSlot (bNote, bChannel);
    if (wTemp != 0xffff) {

	/* shut off the note portion */
	/* we have the note slot. turn it off */
	if (wTemp < NUM4VOICES) {
		MidiSendFM (AD_BLOCK + gw4VoiceOffset[wTemp],
			(BYTE)(gVoice[wTemp].bBlock[0] & 0x1f));
		MidiSendFM (AD_BLOCK + gw4VoiceOffset[wTemp] + 3,
			(BYTE)(gVoice[wTemp].bBlock[1] & 0x1f));
		}
	else
		MidiSendFM (AD_BLOCK + gw2VoiceOffset[wTemp - NUM4VOICES],
			(BYTE)(gVoice[wTemp].bBlock[0] & 0x1f));

	/* note this */
	gVoice[wTemp].bOn = FALSE;
	gVoice[wTemp].bBlock[0] &= 0x1f;
	gVoice[wTemp].bBlock[1] &= 0x1f;
	gVoice[wTemp].dwTime = gdwCurTime;
    };

}

/*
 * Opl3_AllNotesOff - turn off all notes
 *
 */
VOID Opl3_AllNotesOff(void)
{
    BYTE i;

    for (i = 0; i < NUMVOICES; i++) {
        Opl3_NoteOff (gVoice[i].bPatch, gVoice[i].bNote, gVoice[i].bChannel);
    }
}



/* Opl3_NewVolume - This should be called if a volume level
 *	has changed. This will adjust the levels of all the playing
 *	voices.
 *
 * inputs
 *	WORD	wLeft	- left attenuation (1.5 db units)
 *	WORD	wRight  - right attenuation (ignore if mono)
 * returns
 *	none
 */
VOID FAR PASCAL Opl3_NewVolume (WORD wLeft, WORD wRight)
{

    /* make sure that we are actually open */
    if (!glpPatch)
	return;

    wSynthAttenL = wLeft;
    wSynthAttenR = wRight;

    Opl3_setvolume(0xff);

}



/* Opl3_ChannelVolume - set the volume level for an individual channel.
 *
 * inputs
 * 	BYTE	bChannel - channel number to change
 *	WORD	wAtten	- attenuation in 1.5 db units
 *
 * returns
 *	none
 */
VOID FAR PASCAL Opl3_ChannelVolume(BYTE bChannel, WORD wAtten)
{
    gbChanAtten[bChannel] = wAtten;

    Opl3_setvolume(bChannel);
}
	


/* Opl3_SetPan - set the left-right pan position.
 *
 * inputs
 *      BYTE    bChannel  - channel number to alter
 *	BYTE	bPan   - 0 for left, 127 for right or somewhere in the middle.
 *
 * returns - none
 */
VOID FAR PASCAL Opl3_SetPan(BYTE bChannel, BYTE bPan)
{
    /* change the pan level */
    if (bPan > (64 + 16))
	    gbStereoMask[bChannel] = 0xef;	/* let only right channel through */
    else if (bPan < (64 - 16))
	    gbStereoMask[bChannel] = 0xdf;	/* let only left channel through */
    else
	    gbStereoMask[bChannel] = 0xff;	/* let both channels */

    /* change any curently playing patches */
    Opl3_setvolume(bChannel);
}


/* Opl3_PitchBend - This pitch bends a channel.
 *
 * inputs
 *	BYTE    bChannel - channel
 *	short   iBend - Values from -32768 to 32767, being
 *			-2 to +2 half steps
 * returns
 *	none
 */
VOID NEAR PASCAL Opl3_PitchBend (BYTE bChannel, short iBend)
{
    WORD    i, wTemp[2], j;
    DWORD   dwNew;

    /* loop through all the notes looking for the right
     * channel. Anything with the right channel gets its pitch bent
     */
    for (i = 0; i < NUMVOICES; i++) {
	if (gVoice[i].bChannel == bChannel) {
	    for (j = 0; j < (WORD)((i < NUM4VOICES) ? 2 : 1); j++) {
		dwNew = Opl3_CalcBend (gVoice[i].dwOrigPitch[j], iBend);
		wTemp[j] = Opl3_CalcFAndB (dwNew);
		gVoice[i].bBlock[j] = (gVoice[i].bBlock[j] & (BYTE)0xe0) |
			(BYTE) (wTemp[j] >> 8);
	    }

	    if (i < NUM4VOICES) {
		MidiSendFM (AD_BLOCK + gw4VoiceOffset[i],
			gVoice[i].bBlock[0]);
		MidiSendFM (AD_BLOCK + gw4VoiceOffset[i] + 3,
			gVoice[i].bBlock[1]);
		MidiSendFM (AD_FNUMBER + gw4VoiceOffset[i],
			(BYTE) wTemp[0]);
		MidiSendFM (AD_FNUMBER + gw4VoiceOffset[i] + 3,
			(BYTE) wTemp[1]);
	    } else {
		MidiSendFM (AD_BLOCK + gw2VoiceOffset[i - NUM4VOICES],
			gVoice[i].bBlock[0]);
		MidiSendFM (AD_FNUMBER + gw2VoiceOffset[i - NUM4VOICES],
			(BYTE) wTemp[0]);
	    }
	}
    }
}

static TCHAR BCODE gszDefPatchLib[]          = TEXT("SYNTH.PAT");
static TCHAR BCODE gszIniKeyPatchLib[]       = INI_STR_PATCHLIB;
static TCHAR BCODE gszIniDrvSection[]        = INI_DRIVER;
static TCHAR BCODE gszIniDrvFile[]           = INI_SOUND;
static TCHAR BCODE gszSysIniSection[]        = TEXT("synth.dll");
static TCHAR BCODE gszSysIniFile[]           = TEXT("System.Ini");

/** static DWORD NEAR PASCAL DrvGetProfileString(LPSTR szKeyName, LPSTR szDef, LPSTR szBuf, UINT wBufLen)
 *
 *  DESCRIPTION:
 *
 *
 *  ARGUMENTS:
 *      (LPSTR szKeyName, LPSTR szDef, LPSTR szBuf, WORD wBufLen)
 *		HINT	wSystem - if TRUE write/read to system.ini
 *
 *  RETURN (static DWORD NEAR PASCAL):
 *
 *
 *  NOTES:
 *
 ** cjp */

static DWORD NEAR PASCAL DrvGetProfileString(LPTSTR szKeyName, LPTSTR szDef, LPTSTR szBuf, UINT wBufLen,
	UINT wSystem)
{
    return GetPrivateProfileString(wSystem ? gszSysIniSection : gszIniDrvSection, szKeyName, szDef,
    		szBuf, wBufLen, wSystem ? gszSysIniFile : gszIniDrvFile);
} /* DrvGetProfileString() */

/* Opl3_BoardInit - initialise board and load patches as necessary.
 *
 * inputs - none
 * returns - 0 for success or the error code
 */
WORD Opl3_BoardInit(void)
{
    HMMIO       hmmio;
    MMCKINFO    mmckinfo, mm2;
    TCHAR       szPatchLib[STRINGLEN];     /* patch libarary */

    D1 ("\nOpl3_Init");

    /* Check we haven't already initialized */
    if (glpPatch != NULL) {
        return 0;
    }

    /* should the load patches be moved to the init section? */
    DrvGetProfileString(gszIniKeyPatchLib, gszDefPatchLib,
	szPatchLib, sizeof(szPatchLib), FALSE);


    /* allocate the memory, and fill it up from the patch
     * library. The name of the library has been set previously
     * and written into szPatchLib
     */
    glpPatch = (patchStruct FAR *)GlobalLock(GlobalAlloc(GMEM_MOVEABLE|GMEM_SHARE|GMEM_ZEROINIT, sizeof(patchStruct) * NUMPATCHES));
    if (!glpPatch) {
	D1 ("Opl3_Init: could not allocate patch container memory!");
	return ERR_OUTOFMEMORY;
    }

    hmmio = mmioOpen (szPatchLib, NULL, MMIO_READ);
    if (hmmio) {
	mmioDescend (hmmio, &mmckinfo, NULL, 0);
	if ((mmckinfo.ckid == FOURCC_RIFF) &&
            (mmckinfo.fccType == RIFF_PATCH)) {

            mm2.ckid = RIFF_FM4;
            if (!mmioDescend (hmmio, &mm2, &mmckinfo, MMIO_FINDCHUNK)) {
                    /* we have found the synthesis chunk */
                    if (mm2.cksize > (sizeof(patchStruct)*NUMPATCHES))
                            mm2.cksize = sizeof(patchStruct)*NUMPATCHES;
                    mmioRead (hmmio, (LPSTR) glpPatch, mm2.cksize);
            } else {
                    D1("\nBad mmioDescend2");
            };
	} else {
            D1("\nBad mmioDescend1");
        };

	mmioClose (hmmio, 0);
    } else {

	TCHAR   szAlert[50];
	TCHAR   szErrorBuffer[255];

	LoadString(ghModule, SR_ALERT, szAlert, sizeof(szAlert));
	LoadString(ghModule, SR_ALERT_NOPATCH, szErrorBuffer, sizeof(szErrorBuffer));
	MessageBox(NULL, szErrorBuffer, szAlert, MB_OK|MB_ICONHAND);
	D1 ("\nBad mmioOpen");
    };


    return 0;       /* done */
}


/*
 * Opl3_BoardReset - silence the board and set all voices off.
 */
VOID Opl3_BoardReset(void)
{

    BYTE i;

    /* make sure all notes turned off */
    Opl3_AllNotesOff();


    /* ---- silence the chip -------- */

    /* tell the FM chip to use 4-operator mode, and
    fill in any other random variables */
    MidiSendFM (AD_NEW, 0x01);
    MidiSendFM (AD_MASK, 0x60);
    MidiSendFM (AD_CONNECTION, 0x3f);
    MidiSendFM (AD_NTS, 0x00);


    /* turn off the drums, and use high vibrato/modulation */
    MidiSendFM (AD_DRUM, 0xc0);

    /* turn off all the oscillators */
    for (i = 0; i < 0x15; i++) {
	MidiSendFM (AD_LEVEL + i, 0x3f);
	MidiSendFM (AD_LEVEL2 + i, 0x3f);
    };

    /* turn off all the voices */
    for (i = 0; i < 0x08; i++) {
	MidiSendFM (AD_BLOCK + i, 0x00);
	MidiSendFM (AD_BLOCK2 + i, 0x00);
    };


    /* clear all of the voice slots to empty */
    for (i = 0; i < NUMVOICES; i++)
            gVoice[i].dwTime = 0;

    /* start attenuations at -3 dB, which is 90 MIDI level */
    for (i = 0; i < NUMCHANNELS; i++) {
            gbChanAtten[i] = 4;
            gbStereoMask[i] = 0xff;
    };

}




unix.superglobalmegacorp.com

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