File:  [WindowsNT SDKs] / ntddk / src / mmedia / synth / dll / midi.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
 *
 */

/*
 * midi.c
 *
 * Midi FM Synthesis routines. converts midi messages into calls to
 * FM Synthesis functions  - currently supports base adlib (in adlib.c)
 * and opl3 synthesisers (in opl3.c).
 *
 * 15 Dec 92 Geraint Davies - based on a combination of the adlib
 *			      and WSS midi drivers.
 */


#include <windows.h>
#include <mmsystem.h>

#include "mmddk.h"
#include "driver.h"

#include "adlib.h"
#include "opl3.h"


/***********************************************************
global memory */


PORTALLOC gMidiInClient;     // input client information structure
DWORD dwRefTime;             // time when midi input was opened

DWORD dwMsgTime;             // timestamp (in ms) of current msg
DWORD dwMsg = 0L;            // short midi message
BYTE  bBytesLeft = 0;        // number of bytes needed to complete message
BYTE  bBytePos = 0;          // position in short message buffer
DWORD dwCurData = 0L;        // position in long message buffer
BOOL  fSysex = FALSE;        // are we in sysex mode?
BYTE  status = 0;
BYTE    fMidiInStarted = 0;     /* has the midi been started */
LPMIDIHDR      lpMIQueue = NULL;
BYTE	gbMidiInUse = 0;		/* if MIDI is in use */

static WORD  wMidiOutEntered = 0;    // reentrancy check
static PORTALLOC gMidiOutClient;     // client information

/* transformation of linear velocity value to
	logarithmic attenuation */
BYTE gbVelocityAtten[32] = {
	40, 36, 32, 28, 23, 21, 19, 17,
	15, 14, 13, 12, 11, 10, 9, 8,
	7, 6, 5, 5, 4, 4, 3, 3,
	2, 2, 1, 1, 1, 0, 0, 0 };

short   giBend[NUMCHANNELS];    /* bend for each channel */
BYTE    gbPatch[NUMCHANNELS];   /* patch number mapped to */

/* --- interface functions ---------------------------------- */

/*
 * the functions in this section call out to adlib.c or opl3.c
 * depending on which device we have installed.
 */


/***************************************************************
MidiNoteOn - 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
	WORD - note slot #, or 0xffff if its inaudible
*/
VOID NEAR PASCAL MidiNoteOn (BYTE bPatch,
	BYTE bNote, BYTE bChannel, BYTE bVelocity,
	short iBend)
{
    switch(gMidiType) {
    case TYPE_OPL3:
	Opl3_NoteOn(bPatch, bNote, bChannel, bVelocity, iBend);
	break;

    case TYPE_ADLIB:
	Adlib_NoteOn(bPatch, bNote, bChannel, bVelocity, iBend);
    	break;
    }
    return;
}


/**************************************************************
MidiNoteOff - 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 MidiNoteOff (BYTE bPatch,
	BYTE bNote, BYTE bChannel)
{
    switch (gMidiType) {
    case TYPE_OPL3:
	Opl3_NoteOff(bPatch, bNote, bChannel);
	break;

    case TYPE_ADLIB:
	Adlib_NoteOff(bPatch, bNote, bChannel);
	break;
    }
}

/**************************************************************
MidiAllNotesOff - switch off all active voices.

inputs - none
returns - none
*/
VOID MidiAllNotesOff(void)
{
    switch (gMidiType) {
    case TYPE_OPL3:
	Opl3_AllNotesOff();
	break;

    case TYPE_ADLIB:
	Adlib_AllNotesOff();
	break;
    }
}


/**************************************************************
MidiNewVolume - 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 MidiNewVolume (WORD wLeft, WORD wRight)
{
    switch (gMidiType) {
    case TYPE_OPL3:
	Opl3_NewVolume(wLeft, wRight);
	break;

    case TYPE_ADLIB:
	Adlib_NewVolume(wLeft, wRight);
	break;
    }

}

/***************************************************************
MidiChannelVolume - 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 MidiChannelVolume(BYTE bChannel, WORD wAtten)
{

    switch (gMidiType) {
    case TYPE_OPL3:
	Opl3_ChannelVolume(bChannel, wAtten);
	break;

    case TYPE_ADLIB:
	Adlib_ChannelVolume(bChannel, wAtten);
	break;
    }

}
	


/***************************************************************
MidiSetPan - 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 MidiSetPan(BYTE bChannel, BYTE bPan)
{
    switch (gMidiType) {
    case TYPE_OPL3:
	Opl3_SetPan(bChannel, bPan);
	break;

    case TYPE_ADLIB:
	Adlib_SetPan(bChannel, bPan);
	break;
    }

}

/***************************************************************
MidiPitchBend - 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 MidiPitchBend (BYTE bChannel,
	short iBend)
{
    switch (gMidiType) {
    case TYPE_OPL3:
	Opl3_PitchBend(bChannel, iBend);
	break;

    case TYPE_ADLIB:
	Adlib_PitchBend(bChannel, iBend);
	break;
    }

}

/***************************************************************
MidiBoardInit - initialise board and load patches as necessary.

* inputs - none
* returns - 0 for success or the error code
*/
WORD MidiBoardInit(void)
{
    /*
     * load patch tables and reset board
     */

    switch (gMidiType) {
    case TYPE_OPL3:
	return( Opl3_BoardInit());
	break;

    case TYPE_ADLIB:
	return (Adlib_BoardInit());
	break;
    }
    return(MMSYSERR_ERROR);
}

/*
 * MidiBoardReset - silence the board and set all voices off.
 */
VOID MidiBoardReset(void)
{
    BYTE i;

    /*
     * switch off pitch bend (we own this, not the opl3/adlib code)
     */
    for (i = 0; i < NUMCHANNELS; i++)
	giBend[i] = 0;

    /*
     * set all voices off, set channel atten to default,
     * & silence board.
     */
    switch (gMidiType) {
    case TYPE_OPL3:
	Opl3_BoardReset();
	break;

    case TYPE_ADLIB:
	Adlib_BoardReset();
	break;
    }
}



/* --- midi interpretation -------------------------------------*/


/***************************************************************
MidiMessage - This handles a MIDI message. This
	does not do running status.

inputs
	DWORD dwData - up to 4 bytes of MIDI data
		depending upon the message.
returns
	none
*/
VOID NEAR PASCAL MidiMessage (DWORD dwData)
{
    BYTE    bChannel, bVelocity, bNote;
    WORD    wTemp;

    // D1("\nMidiMessage");
    bChannel = (BYTE) dwData & (BYTE)0x0f;
    bVelocity = (BYTE) (dwData >> 16) & (BYTE)0x7f;
    bNote = (BYTE) ((WORD) dwData >> 8) & (BYTE)0x7f;

    switch ((BYTE)dwData & 0xf0) {
	case 0x90:
#ifdef DEBUG
	{
		char szTemp[4];
		szTemp[0] = "0123456789abcdef"[bNote >> 4];
		szTemp[1] = "0123456789abcdef"[bNote & 0x0f];
		szTemp[2] = ' ';
		szTemp[3] = 0;
		if ((bChannel == 9) && bVelocity) D1(szTemp);
	}
#endif
		/* turn key on, or key off if volume == 0 */
		if (bVelocity) {
			MidiNoteOn (
				(BYTE) ((bChannel == DRUMCHANNEL) ?
					(BYTE) (bNote + 128) : (BYTE) gbPatch[bChannel]),
				bNote, bChannel, bVelocity, (short) giBend[bChannel]);
			break;
		};

		/* else, continue through and turn key off */
	case 0x80:
		/* turn key off */
		MidiNoteOff (
			(BYTE) ((bChannel == DRUMCHANNEL) ?
				(BYTE) (bNote + 128) : (BYTE) gbPatch[bChannel]),
				bNote, bChannel);
		break;

	case 0xb0:
		// D1("\nChangeControl");
		/* change control */
		switch (bNote) {
			case 7:
    				/* change channel volume */
				MidiChannelVolume(
				    gbVelocityAtten[(bVelocity & 0x7f) >> 2],
				    bChannel);

				break;
			case 8:
			case 10:
				/* change the pan level */
				MidiSetPan(bChannel, bVelocity);
				break;
			};
		break;

	case 0xc0:
    		/* program change */

#if DBG
		if (wDebugLevel > 1) {
		    char szTemp[64];

		    wsprintfA(szTemp, "0x%x: patch chan %d>%d\n",dwData, bChannel, bNote);
		    OutputDebugStringA(szTemp);

		}
#endif

		/* change patch */
		gbPatch[bChannel] = bNote;
		break;

	case 0xe0:
		// D1("\nBend");
		/* pitch bend */
		wTemp = ((WORD) bVelocity << 9) | ((WORD) bNote << 2);
		giBend[bChannel] = (short) (WORD) (wTemp + 0x8000);
		MidiPitchBend (bChannel, giBend[bChannel]);

		break;
    };

    return;
}

/****************************************************************************
 * @doc INTERNAL
 *
 * @api void | midiCallback | This calls DriverCallback for a midi device.
 *
 * @parm NPPORTALLOC| pPort | Pointer to the PORTALLOC.
 *
 * @parm WORD | msg | The message to send.
 *
 * @parm DWORD | dw1 | Message-dependent parameter.
 *
 * @parm DWORD | dw2 | Message-dependent parameter.
 *
 * @rdesc There is no return value.
 ***************************************************************************/
void NEAR PASCAL midiCallback(NPPORTALLOC pPort, WORD msg, DWORD dw1, DWORD dw2)
{

    // invoke the callback function, if it exists.  dwFlags contains driver-
    // specific flags in the LOWORD and generic driver flags in the HIWORD
    if (pPort->dwCallback)
	DriverCallback(pPort->dwCallback,       // client's callback DWORD
	    HIWORD(pPort->dwFlags) | DCB_NOSWITCH,  // callback flags
	    pPort->hMidi,            // handle to the wave device
	    msg,                     // the message
	    pPort->dwInstance,       // client's instance data
	    dw1,                     // first DWORD
	    dw2);                    // second DWORD
}

/****************************************************************************
 * @doc INTERNAL
 *
 * @api void | midBufferWrite | This function writes a byte into the long
 *     message buffer.  If the buffer is full or a SYSEX_ERROR or
 *     end-of-sysex byte is received, the buffer is marked as 'done' and
 *     it's owner is called back.
 *
 * @parm BYTE | byte | The byte received.
 *
 * @rdesc There is no return value
 ***************************************************************************/
static void NEAR PASCAL midBufferWrite(BYTE byte)
{
    LPMIDIHDR  lpmh;
    WORD       msg;

    // if no buffers, nothing happens
    if (lpMIQueue == NULL)
	return;

    lpmh = lpMIQueue;

    if (byte == SYSEX_ERROR) {
	D2("sysexerror");
	msg = MIM_LONGERROR;
	}
    else {
	D2("bufferwrite");
	msg = MIM_LONGDATA;
	*((HPSTR)(lpmh->lpData) + dwCurData++) = byte;
	}

    // if end of sysex, buffer full or error, send them back the buffer
    if ((byte == SYSEX_ERROR) || (byte == 0xF7) || (dwCurData >= lpmh->dwBufferLength)) {
	D2("bufferdone");
	lpMIQueue = lpMIQueue->lpNext;
	lpmh->dwBytesRecorded = dwCurData;
	dwCurData = 0L;
	lpmh->dwFlags |= MHDR_DONE;
	lpmh->dwFlags &= ~MHDR_INQUEUE;
	midiCallback(&gMidiInClient, msg, (DWORD)lpmh, dwMsgTime);
    }

    return;
}



/****************************************************************************

    This function conforms to the standard MIDI output driver message proc
    modMessage, which is documented in mmddk.d.

 ***************************************************************************/
DWORD FAR PASCAL _loadds modMessage(UINT id,
	UINT msg, DWORD dwUser, DWORD dwParam1, DWORD dwParam2)
{
    LPMIDIHDR    lpHdr;
#ifdef FOGHORN
    WORD            wTemp1, wTemp2;
#endif
    LPSTR           lpBuf;          /* current spot in the long msg buf */
    DWORD           dwBytesRead;    /* how far are we in the buffer */
    DWORD           dwMsg = 0;      /* short midi message sent to synth */
    BYTE            bBytePos=0;     /* shift current byte by dwBytePos*s */
    BYTE            bBytesLeft = 0; /* how many dat bytes needed */
    BYTE            curByte;        /* current byte in long buffer */
    UINT            mRc;            /* Return code */

#ifdef WIN16
    if (!fEnabled || gfMidiSuspended) {
	D1("modMessage called while disabled");
    } else {
	if (msg == MODM_GETNUMDEVS)
	else
	    return MMSYSERR_NOTENABLED;
    }
#endif // WIN16


    // this driver only supports one device
    if (id != 0) {
	D1("invalid midi device id");
	return MMSYSERR_BADDEVICEID;
    }

    switch (msg) {
	case MODM_GETNUMDEVS:
	    D1("MODM_GETNUMDEVS");
#ifdef WIN16
        mRc = 1L;
#else
        //
	// Check if the kernel driver got loaded OK
	//
	{
	    HANDLE hDevice;
	    if (MidiOpenDevice(&hDevice, FALSE) == MMSYSERR_NOERROR) {
                CloseHandle(hDevice);
    	        mRc = 1L;
    	    } else {
    		mRc = 0L;
    	    }
	}
#endif // WIN16
		break;

	case MODM_GETDEVCAPS:
	    D1("MODM_GETDEVCAPS");
	    modGetDevCaps((LPBYTE)dwParam1, (WORD)dwParam2);
	    mRc = 0L;
	    break;

	case MODM_OPEN:
	    D1("MODM_OPEN");

	    /* open the midi */
	    if (MidiOpen())
		return MMSYSERR_ALLOCATED;

	    /* call up the volume control */
#ifdef WIN16
	    VolSendMsg (FH_MIDI_BEGIN);
#endif // WIN16

	    // save client information
	    gMidiOutClient.dwCallback = ((LPMIDIOPENDESC)dwParam1)->dwCallback;
	    gMidiOutClient.dwInstance = ((LPMIDIOPENDESC)dwParam1)->dwInstance;
	    gMidiOutClient.hMidi      = ((LPMIDIOPENDESC)dwParam1)->hMidi;
	    gMidiOutClient.dwFlags    = dwParam2;

	    // notify client
	    midiCallback(&gMidiOutClient, MOM_OPEN, 0L, 0L);

	    /* were in use */
	    gbMidiInUse = TRUE;

	    mRc = 0L;
		break;

	case MODM_CLOSE:
	    D1("MODM_CLOSE");

	    /* shut up the FM synthesizer */
	    MidiClose();

	    /* tell volume about this */
#ifdef WIN16
	    VolSendMsg (FH_MIDI_END);
#endif // WIN16

	    // notify client
	    midiCallback(&gMidiOutClient, MOM_CLOSE, 0L, 0L);

	    /* were not used any more */
	    gbMidiInUse = FALSE;

	    mRc = 0L;
		break;

	case MODM_RESET:
	    D1("MODM_RESET");

            //
            //  turn off FM synthesis
            //
            //  note that we increment our 're-entered' counter so that a
            //  background interrupt handler doesn't mess up our resetting
            //  of the synth by calling midiOut[Short|Long]Msg.. just
            //  practicing safe midi. NOTE: this should never be necessary
            //  if a midi app is PROPERLY written!
            //
	    wMidiOutEntered++;
            {
                if (wMidiOutEntered == 1)
                {
                    MidiReset();
                    dwParam1 = 0L;
                }
                else
                {
                    D1("MODM_RESET reentered!");
                    dwParam1 = MIDIERR_NOTREADY;
                }
            }
	    wMidiOutEntered--;
	    mRc = (dwParam1);
		break;

	case MODM_DATA:                    // message is in dwParam1
#ifndef WIN16
		MidiCheckVolume();			   // See if the volume has changed
#endif // WIN16

	    // make sure we're not being reentered
	    wMidiOutEntered++;
	    if (wMidiOutEntered > 1) {
			D1("MODM_DATA reentered!");
			wMidiOutEntered--;
			return MIDIERR_NOTREADY;
		    }

	    /* if have repeated messages */
	    if (dwParam1 & 0x00000080)  /* status byte */
			status = LOBYTE(LOWORD(dwParam1));
	    else
			dwParam1 = (dwParam1 << 8) | ((DWORD) status);

	    /* if not, have an FM synthesis message */
	    MidiMessage (dwParam1);

	    wMidiOutEntered--;
	    mRc = 0L;
		break;

	case MODM_LONGDATA:      // far pointer to header in dwParam1

#ifndef WIN16
		MidiCheckVolume();			   // See if the volume has changed
#endif // WIN16

	    // make sure we're not being reentered
	    wMidiOutEntered++;
	    if (wMidiOutEntered > 1) {
			D1("MODM_LONGDATA reentered!");
			wMidiOutEntered--;
			return MIDIERR_NOTREADY;
			}

	    // check if it's been prepared
	    lpHdr = (LPMIDIHDR)dwParam1;
	    if (!(lpHdr->dwFlags & MHDR_PREPARED)) {
			wMidiOutEntered--;
			return MIDIERR_UNPREPARED;
			}

	    lpBuf = lpHdr->lpData;
	    dwBytesRead = 0;
	    curByte = *lpBuf;

	    while (TRUE) {
			/* if its a system realtime message send it and continue
				this does not affect the running status */

			if (curByte >= 0xf8)
				MidiMessage (0x000000ff & curByte);
			else if (curByte >= 0xf0) {
				status = 0;     /* kill running status */
				dwMsg = 0L;     /* throw away any incomplete data */
				bBytePos = 0;   /* start at beginning of message */

				switch (curByte) {
					case 0xf0:      /* sysex - ignore */
					case 0xf7:
						break;
					case 0xf4:      /* system common, no data */
					case 0xf5:
					case 0xf6:
						MidiMessage (0x000000ff & curByte);
						break;
					case 0xf1:      /* system common, one data byte */
					case 0xf3:
						dwMsg |= curByte;
						bBytesLeft = 1;
						bBytePos = 1;
						break;
					case 0xf2:      /* system common, 2 data bytes */
						dwMsg |= curByte;
						bBytesLeft = 2;
						bBytePos = 1;
						break;
					};
				}
		    /* else its a channel message */
		    else if (curByte >= 0x80) {
				status = curByte;
				dwMsg = 0L;

				switch (curByte & 0xf0) {
					case 0xc0:      /* channel message, one data */
					case 0xd0:
						dwMsg |= curByte;
						bBytesLeft = 1;
						bBytePos = 1;
						break;
					case 0x80:      /* two bytes */
					case 0x90:
					case 0xa0:
					case 0xb0:
					case 0xe0:
						dwMsg |= curByte;
						bBytesLeft = 2;
						bBytePos = 1;
						break;
					};
				}

			/* else if its an expected data byte */
		    else if (bBytePos != 0) {
				dwMsg |= ((DWORD)curByte) << (bBytePos++ * 8);
				if (--bBytesLeft == 0) {

						MidiMessage (dwMsg);

					if (status) {
						dwMsg |= status;
						bBytesLeft = bBytePos - (BYTE)1;
						bBytePos = 1;
						}
					else {
						dwMsg = 0L;
						bBytePos = 0;
						};
					};
				};

	    /* read the next byte if there is one */
	    /* remember we have already read and processed one byte that
	     * we have not yet counted- so we need to pre-inc, not post-inc
	     */
	    if (++dwBytesRead >= lpHdr->dwBufferLength) break;
		curByte = *++lpBuf;
	    };      /* while TRUE */

	    /* return buffer to client */
	    lpHdr->dwFlags |= MHDR_DONE;
	    midiCallback (&gMidiOutClient, MOM_DONE, dwParam1, 0L);

	    wMidiOutEntered--;
	    mRc = 0L;
		break;

#ifdef WIN16
	case MODM_SETVOLUME:
		gwLinVol[VOL_MIDI][VOL_LEFT] = LOWORD(dwParam1);
		gwLinVol[VOL_MIDI][VOL_RIGHT] = HIWORD(dwParam1);
		VolSetMidiVolume (VolLinearToLog ((WORD)dwParam1),
			VolLinearToLog ((WORD) (dwParam1 >> 16) ));
		mRc = 0L;
		mRc = MidiSetVolume(LOWORD(dwParam1) << 16, HIWORD(dwParam1) << 16);
		break;

	case MODM_GETVOLUME:
		*((DWORD FAR *)dwParam1) =
			MAKELONG (
				gwLinVol[VOL_MIDI][VOL_LEFT],
				gwLinVol[VOL_MIDI][VOL_RIGHT]);
		mRc = 0L;
		break;
#else
	case MODM_SETVOLUME:
		mRc = MidiSetVolume(LOWORD(dwParam1) << 16, HIWORD(dwParam1) << 16);
		break;

	case MODM_GETVOLUME:
		mRc = MidiGetVolume((LPDWORD)dwParam1);
		break;
#endif // WIN16


#ifdef FOGHORN
	case MODM_VUMETER:
		/* there is a duplicate in auxil.c which must be
			kept up to date */
		/* if we're out of range then 0 volume */
		if (dwParam1 >= 16) {
			*((DWORD FAR *)dwParam2) = 0;
			return 0;
			};

		if (!gbMidiInUse) {
			*((DWORD FAR *) dwParam2) = 0;
			return MMSYSERR_ALLOCATED;
			};

		/* find out what the values are */
		wTemp1 = (WORD) gbLeftLevel[dwParam1] << 9;
		wTemp2 = (WORD) gbRightLevel[dwParam1] << 9;
		gbLeftLevel[dwParam1] = gbRightLevel[dwParam1] = 0;
		*((DWORD FAR *) dwParam2) = ((DWORD) wTemp2 << 16) | wTemp1;
		mRc = 0L;
		break;

	case MODM_SETPATCH:
		/* there is a duplicate in auxil.c which must be
			kept up to date */
		wTemp1 = (LOWORD(dwParam2));
		if (wTemp1 >= 256)
			wTemp1 = 0;
		MidiSetPatch ((BYTE) wTemp1, (LPSTR) dwParam1, HIWORD(dwParam2));
		mRc = 0L;
		break;

	case MODM_GETPATCH:
		/* there is a duplicate in auxil.c which must be
			kept up to date */
		wTemp1 = (LOWORD(dwParam2));
		if (wTemp1 >= 256)
			wTemp1 = 0;
		MidiGetPatch ((BYTE) wTemp1, (LPSTR) dwParam1, HIWORD(dwParam2));
		mRc = 0L;
		break;
#endif
	default:
	    return MMSYSERR_NOTSUPPORTED;
    }
#ifndef WIN16
	MidiFlush();
#endif // WIN16

	return mRc;


// should never get here...
return MMSYSERR_NOTSUPPORTED;
}


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");




/****************************************************************
MidiInit - Initializes the FM synthesis chip and internal
        variables. This assumes that HwInit() has been called
        and that a card location has been found. This loads in
        the patch information.

inputs
        none
returns
        WORD - 0 if successful, else error
*/
WORD FAR PASCAL MidiInit (VOID)
{
//    WORD        i;

    D1 ("\nMidiInit");

// don't reset the patch map - it will be initialised at loadtime to 0
// (because its static data) and we should not change it after that
// since the mci sequencer will not re-send patch change messages.
//
//
//    /* reset all channels to patch 0 */
//    for (i = 0; i < NUMCHANNELS; i++) {
//	gbPatch[i] = 0;
//    }

    /* initialise the h/w specific patch tables */
    return MidiBoardInit();
}


/*****************************************************************
MidiOpen - This should be called when a midi file is opened.
        It initializes some variables and locks the patch global
        memories.

inputs
        none
returns
        UINT - 0 if succedes, else error
*/
UINT FAR PASCAL MidiOpen (VOID)
{
    MMRESULT mRc;

    D1("\nMidiOpen");


    //
    // For 32-bit we must open our kernel device
    //

    mRc = MidiOpenDevice(&MidiDeviceHandle, TRUE);

    if (mRc != MMSYSERR_NOERROR) {
	    return mRc;
    }


    /*
     * reset the device (set default channel attenuation etc)
     */
    MidiBoardReset();

    return 0;
}

/***************************************************************
MidiClose - This kills the playing midi voices and closes the kernel driver

inputs
        none
returns
        none
*/
VOID FAR PASCAL MidiClose (VOID)
{

    D1("\nMidiClose");

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

    MidiCloseDevice(MidiDeviceHandle);
}

/** void FAR PASCAL MidiReset(void)
 *
 *  DESCRIPTION:
 *
 *
 *  ARGUMENTS:
 *      (void)
 *
 *  RETURN (void FAR PASCAL):
 *
 *
 *  NOTES:
 *
 ** cjp */

void FAR PASCAL MidiReset(void)
{

    D1("\nMidiReset");

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

    /* silence the board and reset board-specific variables */
    MidiBoardReset();


} /* MidiReset() */


/*****************************************************************************
 * @doc INTERNAL
 *
 * @api void | modGetDevCaps | Get the capabilities of the port.
 *
 * @parm LPBYTE | lpCaps | Far pointer to a MIDIOUTCAPS structure.
 *
 * @parm UINT | wSize | Size of the MIDIOUTCAPS structure.
 *
 * @rdesc There is no return value.
 ****************************************************************************/
void FAR PASCAL modGetDevCaps(LPBYTE lpCaps, UINT wSize)
{
    MIDIOUTCAPS mc;

    mc.wMid = MM_MICROSOFT;
    mc.wPid = MM_ADLIB;
    mc.wTechnology = MOD_FMSYNTH;
    mc.wVoices = 128;

    mc.wChannelMask = 0xffff;                       // all channels
    mc.vDriverVersion = 0x100;
    if (gMidiType == TYPE_OPL3) {
	mc.wNotes = 12;
	mc.dwSupport = MIDICAPS_VOLUME | MIDICAPS_LRVOLUME;
    } else {
	mc.wNotes = 11;
	mc.dwSupport = MIDICAPS_VOLUME;
    }
    LoadString(ghModule, SR_STR_DRIVERMIDIOUT, mc.szPname, sizeof(mc.szPname));

    AsMemCopy(lpCaps, &mc, min(wSize,sizeof(mc)));
}


unix.superglobalmegacorp.com

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