911 lines
26 KiB
C
911 lines
26 KiB
C
/*
|
||
*
|
||
* Copyright (c) 1992-1995 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 };
|
||
|
||
BYTE BCODE gbPercMap[53][2] =
|
||
{
|
||
{ 0, 35 },
|
||
{ 0, 35 },
|
||
{ 2, 52 },
|
||
{ 3, 48 },
|
||
{ 4, 58 },
|
||
{ 5, 60 },
|
||
{ 6, 47 },
|
||
{ 7, 43 },
|
||
{ 6, 49 },
|
||
{ 9, 43 },
|
||
{ 6, 51 },
|
||
{ 11, 43 },
|
||
{ 6, 54 },
|
||
{ 6, 57 },
|
||
{ 14, 72 },
|
||
{ 6, 60 },
|
||
{ 16, 76 },
|
||
{ 17, 84 },
|
||
{ 18, 36 },
|
||
{ 19, 76 },
|
||
{ 20, 84 },
|
||
{ 21, 83 },
|
||
{ 22, 84 },
|
||
{ 23, 24 },
|
||
{ 16, 77 },
|
||
{ 25, 60 },
|
||
{ 26, 65 },
|
||
{ 27, 59 },
|
||
{ 28, 51 },
|
||
{ 29, 45 },
|
||
{ 30, 71 },
|
||
{ 31, 60 },
|
||
{ 32, 58 },
|
||
{ 33, 53 },
|
||
{ 34, 64 },
|
||
{ 35, 71 },
|
||
{ 36, 61 },
|
||
{ 37, 61 },
|
||
{ 38, 48 },
|
||
{ 39, 48 },
|
||
{ 40, 69 },
|
||
{ 41, 68 },
|
||
{ 42, 63 },
|
||
{ 43, 74 },
|
||
{ 44, 60 },
|
||
{ 45, 80 },
|
||
{ 46, 64 },
|
||
{ 47, 69 },
|
||
{ 48, 73 },
|
||
{ 49, 75 },
|
||
{ 50, 68 },
|
||
{ 51, 48 },
|
||
{ 52, 53 }
|
||
} ;
|
||
|
||
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.
|
||
*/
|
||
|
||
|
||
/**************************************************************
|
||
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) {
|
||
switch(gMidiType) {
|
||
case TYPE_OPL3:
|
||
if (bChannel == DRUMCHANNEL)
|
||
{
|
||
if (bNote >= 35 && bNote <= 87)
|
||
{
|
||
#ifdef DEBUG
|
||
char szDebug[ 80 ] ;
|
||
|
||
wsprintf( szDebug, "bChannel = %d, bNote = %d",
|
||
bChannel, bNote ) ;
|
||
D1( szDebug ) ;
|
||
#endif
|
||
|
||
Opl3_NoteOn( (BYTE) (gbPercMap[ bNote - 35 ][ 0 ] + 0x80),
|
||
gbPercMap[ bNote - 35 ][ 1 ],
|
||
bChannel, bVelocity,
|
||
(short) giBend[ bChannel ] ) ;
|
||
}
|
||
}
|
||
else
|
||
Opl3_NoteOn( (BYTE) gbPatch[ bChannel ], bNote, bChannel,
|
||
bVelocity, (short) giBend[ bChannel ] ) ;
|
||
break;
|
||
|
||
case TYPE_ADLIB:
|
||
Adlib_NoteOn (
|
||
(BYTE) ((bChannel == DRUMCHANNEL) ?
|
||
(BYTE) (bNote + 128) : (BYTE) gbPatch[bChannel]),
|
||
bNote, bChannel, bVelocity, (short) giBend[bChannel]);
|
||
break;
|
||
}
|
||
break;
|
||
}
|
||
|
||
/* else, continue through and turn key off */
|
||
case 0x80:
|
||
/* turn key off */
|
||
switch (gMidiType) {
|
||
case TYPE_OPL3:
|
||
if (bChannel == DRUMCHANNEL)
|
||
{
|
||
if (bNote >= 35 && bNote <= 87)
|
||
Opl3_NoteOff( (BYTE) (gbPercMap[ bNote - 35 ][ 0 ] + 0x80),
|
||
gbPercMap[ bNote - 35 ][ 1 ], bChannel ) ;
|
||
}
|
||
else
|
||
Opl3_NoteOff( (BYTE) gbPatch[bChannel], bNote, bChannel ) ;
|
||
break;
|
||
|
||
case TYPE_ADLIB:
|
||
Adlib_NoteOff (
|
||
(BYTE) ((bChannel == DRUMCHANNEL) ?
|
||
(BYTE) (bNote + 128) : (BYTE) gbPatch[bChannel]),
|
||
bNote, bChannel);
|
||
break;
|
||
}
|
||
break;
|
||
|
||
case 0xb0:
|
||
// D1("\nChangeControl");
|
||
/* change control */
|
||
switch (bNote) {
|
||
case 7:
|
||
/* change channel volume */
|
||
MidiChannelVolume(
|
||
bChannel,
|
||
gbVelocityAtten[(bVelocity & 0x7f) >> 2]);
|
||
|
||
break;
|
||
case 8:
|
||
case 10:
|
||
/* change the pan level */
|
||
MidiSetPan(bChannel, bVelocity);
|
||
break;
|
||
};
|
||
break;
|
||
|
||
case 0xc0:
|
||
if (bChannel != DRUMCHANNEL)
|
||
{
|
||
int i ;
|
||
|
||
// Turn off all active notes for this channel...
|
||
|
||
if (gMidiType == TYPE_OPL3) {
|
||
Opl3_ChannelNotesOff(bChannel);
|
||
}
|
||
|
||
gbPatch[ bChannel ] = bNote ;
|
||
|
||
}
|
||
break;
|
||
|
||
case 0xe0:
|
||
// D1("\nBend");
|
||
/* pitch bend */
|
||
wTemp = ((WORD) bVelocity << 9) | ((WORD) bNote << 2);
|
||
giBend[bChannel] = (short) (WORD) (wTemp + 0x7FFF);
|
||
MidiPitchBend (bChannel, giBend[bChannel]);
|
||
|
||
break;
|
||
};
|
||
|
||
return;
|
||
}
|
||
|
||
/****************************************************************************
|
||
* @doc INTERNAL
|
||
*
|
||
* @api void | midiSynthCallback | 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 midiSynthCallback(NPPORTALLOC pPort, WORD msg, DWORD_PTR dw1, DWORD_PTR 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
|
||
(HDRVR)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;
|
||
midiSynthCallback(&gMidiInClient, msg, (DWORD_PTR)lpmh, dwMsgTime);
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
|
||
/****************************************************************************
|
||
|
||
This function conforms to the standard MIDI output driver message proc
|
||
modMessage, which is documented in mmddk.d.
|
||
|
||
***************************************************************************/
|
||
DWORD APIENTRY modSynthMessage(UINT id,
|
||
UINT msg, DWORD_PTR dwUser, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
|
||
{
|
||
LPMIDIHDR lpHdr;
|
||
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 */
|
||
|
||
|
||
// 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"));
|
||
//
|
||
// Check if the kernel driver got loaded OK
|
||
//
|
||
mRc = sndGetNumDevs(SYNTH_DEVICE);
|
||
break;
|
||
|
||
case MODM_GETDEVCAPS:
|
||
D1(("MODM_GETDEVCAPS"));
|
||
mRc = midiGetDevCaps(0, SYNTH_DEVICE, (LPBYTE)dwParam1, (WORD)dwParam2);
|
||
break;
|
||
|
||
case MODM_OPEN:
|
||
D1(("MODM_OPEN"));
|
||
|
||
/* open the midi */
|
||
if (MidiOpen())
|
||
return MMSYSERR_ALLOCATED;
|
||
|
||
// save client information
|
||
gMidiOutClient.dwCallback = ((LPMIDIOPENDESC)dwParam1)->dwCallback;
|
||
gMidiOutClient.dwInstance = ((LPMIDIOPENDESC)dwParam1)->dwInstance;
|
||
gMidiOutClient.hMidi = (HMIDIOUT)((LPMIDIOPENDESC)dwParam1)->hMidi;
|
||
gMidiOutClient.dwFlags = (DWORD)dwParam2;
|
||
|
||
// notify client
|
||
midiSynthCallback(&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();
|
||
|
||
// notify client
|
||
midiSynthCallback(&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 = (DWORD)dwParam1;
|
||
break;
|
||
|
||
case MODM_DATA: // message is in dwParam1
|
||
MidiCheckVolume(); // See if the volume has changed
|
||
|
||
// 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 ((DWORD)dwParam1);
|
||
|
||
wMidiOutEntered--;
|
||
mRc = 0L;
|
||
break;
|
||
|
||
case MODM_LONGDATA: // far pointer to header in dwParam1
|
||
|
||
MidiCheckVolume(); // See if the volume has changed
|
||
|
||
// 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;
|
||
midiSynthCallback (&gMidiOutClient, MOM_DONE, dwParam1, 0L);
|
||
|
||
wMidiOutEntered--;
|
||
mRc = 0L;
|
||
break;
|
||
|
||
case MODM_SETVOLUME:
|
||
mRc = MidiSetVolume(LOWORD(dwParam1) << 16, HIWORD(dwParam1) << 16);
|
||
break;
|
||
|
||
case MODM_GETVOLUME:
|
||
mRc = MidiGetVolume((LPDWORD)dwParam1);
|
||
break;
|
||
|
||
default:
|
||
return MMSYSERR_NOTSUPPORTED;
|
||
}
|
||
MidiFlush();
|
||
|
||
return mRc;
|
||
|
||
|
||
// should never get here...
|
||
return MMSYSERR_NOTSUPPORTED;
|
||
}
|
||
|
||
/****************************************************************
|
||
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(("MidiOpen"));
|
||
|
||
|
||
//
|
||
// 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(("MidiClose"));
|
||
|
||
if (MidiDeviceHandle == NULL) {
|
||
return;
|
||
}
|
||
|
||
/* make sure all notes turned off */
|
||
MidiAllNotesOff();
|
||
|
||
MidiCloseDevice(MidiDeviceHandle);
|
||
|
||
MidiDeviceHandle = NULL;
|
||
}
|
||
|
||
/** void FAR PASCAL MidiReset(void)
|
||
*
|
||
* DESCRIPTION:
|
||
*
|
||
*
|
||
* ARGUMENTS:
|
||
* (void)
|
||
*
|
||
* RETURN (void FAR PASCAL):
|
||
*
|
||
*
|
||
* NOTES:
|
||
*
|
||
** cjp */
|
||
|
||
void FAR PASCAL MidiReset(void)
|
||
{
|
||
|
||
D1(("MidiReset"));
|
||
|
||
/* make sure all notes turned off */
|
||
MidiAllNotesOff();
|
||
|
||
/* silence the board and reset board-specific variables */
|
||
MidiBoardReset();
|
||
|
||
|
||
} /* MidiReset() */
|
||
|
||
|
||
|