📄 win_snd.c
字号:
#ifdef DEBUGMIDISTREAM
CONS_Printf("I_RegisterSong: \n");
#endif
if (!memcmp(data,"MUS",3))
{
// convert mus to mid with a wonderful function
// thanks to S.Bacquet for the sources of qmus2mid
// convert mus to mid and load it in memory
if((iErrorCode = qmus2mid((char *)data,pMus2MidData,89,64,0,len,MIDBUFFERSIZE,
&iMus2MidSize))!=0)
{
CONS_Printf("Cannot convert mus to mid, converterror :%d\n",iErrorCode);
return 0;
}
pMidiFileData = pMus2MidData;
}
else
// support mid file in WAD !!! (no conversion needed)
if (!memcmp(data,"MThd",4))
{
pMidiFileData = data;
}
else
{
CONS_Printf ("Music lump is not MID or MUS music format\n");
return 0;
}
if (pMidiFileData == NULL)
{
CONS_Printf ("Not a valid MIDI file : %d\n",iErrorCode);
return 0;
}
#ifdef DEBUGMIDISTREAM //EVENMORE
else
{
I_SaveMemToFile (pMidiFileData, iMus2MidSize, "c:/temp/debug.mid");
}
#endif
// setup midi stream buffer
if (StreamBufferSetup(pMidiFileData, iMus2MidSize)) {
Mid2StreamConverterCleanup();
I_Error ("I_RegisterSong: StreamBufferSetup FAILED");
}
return 1;
}
// -----------------
// StreamBufferSetup
// This function uses the filename stored in the global character array to
// open a MIDI file. Then it goes about converting at least the first part of
// that file into a midiStream buffer for playback.
// -----------------
//mid2strm.c - returns TRUE if an error occurs
BOOL Mid2StreamConverterInit( unsigned char* pMidiData, ULONG iMidiSize );
void Mid2StreamConverterCleanup( void );
// -----------------
// StreamBufferSetup
// - returns TRUE if a problem occurs
// -----------------
static BOOL StreamBufferSetup( unsigned char* pMidiData, int iMidiSize )
{
MMRESULT mmrRetVal;
MIDIPROPTIMEDIV mptd;
BOOL bFoundEnd = FALSE;
int dwConvertFlag;
int nChkErr;
int idx;
#ifdef DEBUGMIDISTREAM
if (hStream == NULL)
I_Error ("StreamBufferSetup: hStream is NULL!");
#endif
// pause midi stream before manipulate there buffers
midiStreamPause(hStream);
// allocate the stream buffers (only once)
for (idx = 0; idx < NUM_STREAM_BUFFERS; idx++ )
{
ciStreamBuffers[idx].mhBuffer.dwBufferLength = OUT_BUFFER_SIZE;
if( ciStreamBuffers[idx].mhBuffer.lpData == NULL ) {
if(( ciStreamBuffers[idx].mhBuffer.lpData = GlobalAllocPtr( GHND, OUT_BUFFER_SIZE )) == NULL )
{
return (FALSE);
}
}
}
// returns TRUE in case of conversion error
if (Mid2StreamConverterInit( pMidiData, iMidiSize ))
return( TRUE );
// Initialize the volume cache array to some pre-defined value
for( idx = 0; idx < MAX_MIDI_IN_TRACKS; idx++ )
dwVolCache[idx] = VOL_CACHE_INIT;
mptd.cbStruct = sizeof(mptd);
mptd.dwTimeDiv = ifs.dwTimeDivision;
if(( mmrRetVal = midiStreamProperty(hStream,(LPBYTE)&mptd,
MIDIPROP_SET | MIDIPROP_TIMEDIV ))
!= MMSYSERR_NOERROR )
{
MidiErrorMessageBox( mmrRetVal );
return( TRUE );
}
nEmptyBuffers = 0;
dwConvertFlag = CONVERTF_RESET;
for( nCurrentBuffer = 0; nCurrentBuffer < NUM_STREAM_BUFFERS; nCurrentBuffer++ )
{
// Tell the converter to convert up to one entire buffer's length of output
// data. Also, set a flag so it knows to reset any saved state variables it
// may keep from call to call.
ciStreamBuffers[nCurrentBuffer].dwStartOffset = 0;
ciStreamBuffers[nCurrentBuffer].dwMaxLength = OUT_BUFFER_SIZE;
ciStreamBuffers[nCurrentBuffer].tkStart = 0;
ciStreamBuffers[nCurrentBuffer].bTimesUp = FALSE;
if(( nChkErr = Mid2StreamConvertToBuffer( dwConvertFlag,
&ciStreamBuffers[nCurrentBuffer] ))
!= CONVERTERR_NOERROR )
{
if( nChkErr == CONVERTERR_DONE )
{
bFoundEnd = TRUE;
}
else
{
CONS_Printf( "StreamBufferSetup: initial conversion pass failed" );
return( TRUE );
}
}
ciStreamBuffers[nCurrentBuffer].mhBuffer.dwBytesRecorded
= ciStreamBuffers[nCurrentBuffer].dwBytesRecorded;
if( !bBuffersPrepared ) {
if(( mmrRetVal = midiOutPrepareHeader( (HMIDIOUT)hStream,
&ciStreamBuffers[nCurrentBuffer].mhBuffer,
sizeof(MIDIHDR))) != MMSYSERR_NOERROR )
{
MidiErrorMessageBox( mmrRetVal );
return( TRUE );
}
}
if(( mmrRetVal = midiStreamOut( hStream,
&ciStreamBuffers[nCurrentBuffer].mhBuffer,
sizeof(MIDIHDR))) != MMSYSERR_NOERROR )
{
MidiErrorMessageBox( mmrRetVal );
break;
}
dwConvertFlag = 0;
if( bFoundEnd )
break;
}
bBuffersPrepared = TRUE;
nCurrentBuffer = 0;
// MIDI volume
dwVolumePercent = (cv_musicvolume.value * 1000) / 32;
if( hStream )
SetAllChannelVolumes( dwVolumePercent );
return( FALSE );
}
// ----------------
// SetChannelVolume
// Call here delayed by MIDI stream callback, to adapt the volume event of the
// midi stream to our own set volume percentage.
// ----------------
void I_SetMidiChannelVolume( DWORD dwChannel, DWORD dwVolumePercent )
{
DWORD dwEvent, dwVol;
MMRESULT mmrRetVal;
if( !bMidiPlaying )
return;
dwVol = ( dwVolCache[dwChannel] * dwVolumePercent ) / 1000;
//CONS_Printf ("setvolchannel %d vol %d\n", dwChannel, dwVol);
dwEvent = MIDI_CTRLCHANGE | dwChannel | ((DWORD)MIDICTRL_VOLUME << 8)
| ((DWORD)dwVol << 16);
if(( mmrRetVal = midiOutShortMsg( (HMIDIOUT)hStream, dwEvent ))
!= MMSYSERR_NOERROR )
{
#ifdef DEBUGMIDISTREAM
MidiErrorMessageBox( mmrRetVal );
#endif
return;
}
}
// ------------------
// MidiStreamCallback
// This is the callback handler which continually refills MIDI data buffers
// as they're returned to us from the audio subsystem.
// ------------------
static void CALLBACK MidiStreamCallback (HMIDIIN hMidi, UINT uMsg, DWORD dwInstance,
DWORD dwParam1, DWORD dwParam2 )
{
//static int nWaitingBuffers = 0;
MMRESULT mmrRetVal;
int nChkErr;
MIDIEVENT *pme;
MIDIHDR *pmh;
switch( uMsg )
{
case MOM_DONE:
//faB: dwParam1 is LPMIDIHDR
if( uCallbackStatus == STATUS_CALLBACKDEAD )
return;
nEmptyBuffers++;
//faB: we reached end of song, but we wait until all the buffers are returned
if( uCallbackStatus == STATUS_WAITINGFOREND )
{
if( nEmptyBuffers < NUM_STREAM_BUFFERS )
{
return;
}
else
{
// stop the song when end reached (was not looping)
uCallbackStatus = STATUS_CALLBACKDEAD;
SetEvent( hBufferReturnEvent );
I_StopSong(0);
return;
}
}
// This flag is set whenever the callback is waiting for all buffers to
// come back.
if( uCallbackStatus == STATUS_KILLCALLBACK )
{
// Count NUM_STREAM_BUFFERS-1 being returned for the last time
if( nEmptyBuffers < NUM_STREAM_BUFFERS )
{
return;
}
// Then send a stop message when we get the last buffer back...
else
{
// Change the status to callback dead
uCallbackStatus = STATUS_CALLBACKDEAD;
SetEvent( hBufferReturnEvent );
return;
}
}
dwProgressBytes += ciStreamBuffers[nCurrentBuffer].mhBuffer.dwBytesRecorded;
// -------------------------------------------------
// Fill an available buffer with audio data again...
// -------------------------------------------------
if( bMidiPlaying && nEmptyBuffers )
{
ciStreamBuffers[nCurrentBuffer].dwStartOffset = 0;
ciStreamBuffers[nCurrentBuffer].dwMaxLength = OUT_BUFFER_SIZE;
ciStreamBuffers[nCurrentBuffer].tkStart = 0;
ciStreamBuffers[nCurrentBuffer].dwBytesRecorded = 0;
ciStreamBuffers[nCurrentBuffer].bTimesUp = FALSE;
if(( nChkErr = Mid2StreamConvertToBuffer( 0, &ciStreamBuffers[nCurrentBuffer] ))
!= CONVERTERR_NOERROR )
{
if( nChkErr == CONVERTERR_DONE )
{
// Don't include this one in the count
//nWaitingBuffers = NUM_STREAM_BUFFERS - 1;
uCallbackStatus = STATUS_WAITINGFOREND;
return;
}
else
{
//faB: we're not in the main thread so we can't call I_Error() now
// log the error message out, and post exit message.
CONS_Printf( "MidiStreamCallback(): conversion pass failed!" );
PostMessage(hWndMain, WM_CLOSE, 0, 0);
return;
}
}
ciStreamBuffers[nCurrentBuffer].mhBuffer.dwBytesRecorded
= ciStreamBuffers[nCurrentBuffer].dwBytesRecorded;
if(( mmrRetVal = midiStreamOut( hStream,
&ciStreamBuffers[nCurrentBuffer].mhBuffer,
sizeof(MIDIHDR))) != MMSYSERR_NOERROR )
{
MidiErrorMessageBox( mmrRetVal );
Mid2StreamConverterCleanup();
return;
}
nCurrentBuffer = ( nCurrentBuffer + 1 ) % NUM_STREAM_BUFFERS;
nEmptyBuffers--;
}
break;
case MOM_POSITIONCB:
pmh = (MIDIHDR *)dwParam1;
pme = (MIDIEVENT *)(pmh->lpData + pmh->dwOffset);
if( MIDIEVENT_TYPE( pme->dwEvent ) == MIDI_CTRLCHANGE )
{
#ifdef DEBUGMIDISTREAM
if( MIDIEVENT_DATA1( pme->dwEvent ) == MIDICTRL_VOLUME_LSB )
{
CONS_Printf ( "Got an LSB volume event" );
PostMessage (hWndMain, WM_CLOSE, 0, 0); //faB: can't I_Error() here
break;
}
#endif
// this is meant to respond to our own intention, from mid2strm.c
if( MIDIEVENT_DATA1( pme->dwEvent ) != MIDICTRL_VOLUME )
break;
// Mask off the channel number and cache the volume data byte
//CONS_Printf ( "dwvolcache %d = %d\n", MIDIEVENT_CHANNEL( pme->dwEvent ), MIDIEVENT_VOLUME( pme->dwEvent ));
dwVolCache[ MIDIEVENT_CHANNEL( pme->dwEvent )] = MIDIEVENT_VOLUME( pme->dwEvent );
// call SetChannelVolume() later to adjust MIDI volume control message to our
// own current volume level.
PostMessage( hWndMain, WM_MSTREAM_UPDATEVOLUME,
MIDIEVENT_CHANNEL( pme->dwEvent ), 0L );
}
break;
default:
break;
}
return;
}
// ---------------------
// Mid2StreamFreeBuffers
// This function unprepares and frees all our buffers -- something we must
// do to work around a bug in MMYSYSTEM that prevents a device from playing
// back properly unless it is closed and reopened after each stop.
// ---------------------
static void Mid2StreamFreeBuffers( void )
{
DWORD idx;
MMRESULT mmrRetVal;
if( bBuffersPrepared )
{
for( idx = 0; idx < NUM_STREAM_BUFFERS; idx++ ) {
if(( mmrRetVal = midiOutUnprepareHeader( (HMIDIOUT)hStream,
&ciStreamBuffers[idx].mhBuffer,
sizeof(MIDIHDR)))
!= MMSYSERR_NOERROR )
{
MidiErrorMessageBox( mmrRetVal );
}
}
bBuffersPrepared = FALSE;
}
//faB: I don't free the stream buffers here, but rather allocate them
// once at startup, and free'em at shutdown
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -