📄 mid2strm.c
字号:
#endif
/* faB: made static
ifs.pTracks = (INTRACKSTATE*)GlobalAllocPtr(GPTR, ifs.nTracks*sizeof(INTRACKSTATE));
if (ifs.pTracks==NULL)
{
CONS_Printf ( "Out of memory.\n");
goto Init_Cleanup;
}
*/
// faB: made it static, but don't quit if there are more tracks, just skip'em
if (ifs.nTracks > MAX_MIDI_IN_TRACKS)
ifs.nTracks = MAX_MIDI_IN_TRACKS;
for (iTrack = 0, pInTrack = ifs.pTracks; iTrack < ifs.nTracks; ++iTrack, ++pInTrack)
{
if ((pChunkID = (ULONG*)GetInFileData(sizeof(*pChunkID))) == NULL ||
*pChunkID!= MTrk ||
(pChunkSize = (ULONG*)GetInFileData(sizeof(*pChunkSize))) == NULL)
{
CONS_Printf ( "Read error on track header.\n");
goto Init_Cleanup;
}
iChunkSize = LONGSWAP(*pChunkSize);
pInTrack->iTrackLen = iChunkSize; // Total track length
pInTrack->iBytesLeft = iChunkSize;
pInTrack->pTrackData = GetInFileData(iChunkSize);
if (pInTrack->pTrackData == NULL)
{
CONS_Printf ( "Read error while reading track data.\n");
goto Init_Cleanup;
}
#ifdef DEBUGMIDISTREAM
CONS_Printf ("Track %d : length %d bytes\n", iTrack, iChunkSize);
pInTrack->nTrack = iTrack;
#endif
// Setup pointer to the current position in the track
pInTrack->pTrackPointer = pInTrack->pTrackData;
pInTrack->fdwTrack = 0;
pInTrack->bRunningStatus = BAD_MIDI_FIX;
pInTrack->tkNextEventDue = 0;
// Handle bozo MIDI files which contain empty track chunks
if (!pInTrack->iBytesLeft)
{
pInTrack->fdwTrack |= ITS_F_ENDOFTRK;
continue;
}
// We always preread the time from each track so the mixer code can
// determine which track has the next event with a minimum of work
if (!GetTrackVDWord(pInTrack, &pInTrack->tkNextEventDue))
{
CONS_Printf ( "Read error while reading first delta time of track.\n");
goto Init_Cleanup;
}
}
// End of track initialization code
fRet = FALSE;
Init_Cleanup:
if( fRet )
Mid2StreamConverterCleanup();
return( fRet );
}
// ----------------------
// AddEventToStreamBuffer
//
// Put the given event into the given stream buffer at the given location
// pMe must point to an event filled out in accordance with the
// description given in GetTrackEvent
//
// Returns FALSE on sucess or TRUE on an error condition
// Handles its own error notification by displaying to the appropriate
// output device (either our debugging window, or the screen).
// ----------------------
static int AddEventToStreamBuffer( PTEMPEVENT pMe, CONVERTINFO *lpciInfo )
{
DWORD tkNow, tkDelta;
MIDIEVENT *pmeEvent;
pmeEvent = (MIDIEVENT *)( lpciInfo->mhBuffer.lpData
+ lpciInfo->dwStartOffset
+ lpciInfo->dwBytesRecorded );
// When we see a new, empty buffer, set the start time on it...
if( !lpciInfo->dwBytesRecorded )
lpciInfo->tkStart = tkCurrentTime;
// Use the above set start time to figure out how much longer we should fill
// this buffer before officially declaring it as "full"
if( tkCurrentTime - lpciInfo->tkStart > dwBufferTickLength )
{
if( lpciInfo->bTimesUp )
{
lpciInfo->bTimesUp = FALSE;
return( CONVERTERR_BUFFERFULL );
}
else
lpciInfo->bTimesUp = TRUE;
}
tkNow = tkCurrentTime;
// Delta time is absolute event time minus absolute time
// already gone by on this track
tkDelta = pMe->tkEvent - tkCurrentTime;
// Event time is now current time on this track
tkCurrentTime = pMe->tkEvent;
if( pMe->abEvent[0] < MIDI_SYSEX )
{
// Channel message. We know how long it is, just copy it.
// Need 3 DWORD's: delta-t, stream-ID, event
if( lpciInfo->dwMaxLength-lpciInfo->dwBytesRecorded < 3*sizeof(DWORD))
{
// Cleanup from our write operation
return( CONVERTERR_BUFFERFULL );
}
pmeEvent->dwDeltaTime = tkDelta;
pmeEvent->dwStreamID = 0;
pmeEvent->dwEvent = ( pMe->abEvent[0] )
| (((DWORD)pMe->abEvent[1] ) << 8 )
| (((DWORD)pMe->abEvent[2] ) << 16 )
| MEVT_F_SHORT;
//faB: control the volume with our own volume percentage
if((( pMe->abEvent[0] & 0xF0) == MIDI_CTRLCHANGE ) &&
( pMe->abEvent[1] == MIDICTRL_VOLUME ))
{
// If this is a volume change, generate a callback so we can grab
// the new volume for our cache
// BP: this seems like this is useless and cause many many many bugs !
//pmeEvent->dwEvent |= MEVT_F_CALLBACK;
}
lpciInfo->dwBytesRecorded += 3 *sizeof(DWORD);
}
else if(( pMe->abEvent[0] == MIDI_SYSEX ) ||
( pMe->abEvent[0] == MIDI_SYSEXEND ))
{
CONS_Printf ( "NOTE: Ignoring SysEx for now.\n");
}
else
{
DWORD dwCurrentTempo;
// Better be a meta event.
// BYTE byEvent
// BYTE byEventType
// VDWORD dwEventLength
// BYTE pLongEventData[dwEventLength]
//
if (!(pMe->abEvent[0] == MIDI_META))
{
I_Error ("AddEventToStreamBuffer: not a META event\n");
}
// The only meta-event we care about is change tempo
//
if (pMe->abEvent[1] != MIDI_META_TEMPO)
return( CONVERTERR_METASKIP );
// We should have three bytes of parameter data...
if (!(pMe->dwEventLength == 3))
{
I_Error ("AddEventToStreamBuffer: dwEventLength <> 3\n");
}
// Need 3 DWORD's: delta-t, stream-ID, event data
if( lpciInfo->dwMaxLength - lpciInfo->dwBytesRecorded < 3 *sizeof(DWORD))
{
return( CONVERTERR_BUFFERFULL );
}
pmeEvent->dwDeltaTime = tkDelta;
pmeEvent->dwStreamID = 0;
// Note: this is backwards from above because we're converting a single
// data value from hi-lo to lo-hi format...
pmeEvent->dwEvent = ( pMe->pEvent[2] )
| (((DWORD)pMe->pEvent[1] ) << 8 )
| (((DWORD)pMe->pEvent[0] ) << 16 );
dwCurrentTempo = pmeEvent->dwEvent;
pmeEvent->dwEvent |= (((DWORD)MEVT_TEMPO ) << 24 ) | MEVT_F_SHORT;
dwBufferTickLength = ( ifs.dwTimeDivision * 1000 * BUFFER_TIME_LENGTH ) / dwCurrentTempo;
#ifdef DEBUGMIDISTREAM
CONS_Printf("dwBufferTickLength = %lu", dwBufferTickLength );
#endif
lpciInfo->dwBytesRecorded += 3 *sizeof(DWORD);
}
return( FALSE );
}
// -------------------------
// Mid2StreamRewindConverter
// This little function is an adaptation of the ConverterInit() code which
// resets the tracks without closing and opening the file, thus reducing the
// time it takes to loop back to the beginning when looping.
// -------------------------
static BOOL Mid2StreamRewindConverter( void )
{
DWORD iTrack;
PINTRACKSTATE pInTrack;
tkCurrentTime = 0;
// reset to start of midi file
ifs.iBytesLeft = ifs.FileSize;
ifs.pFilePointer = ifs.pFile;
for (iTrack = 0, pInTrack = ifs.pTracks; iTrack < ifs.nTracks; ++iTrack, ++pInTrack)
{
pInTrack->iBytesLeft = pInTrack->iTrackLen;
// Setup pointer to the current position in the track
pInTrack->pTrackPointer = pInTrack->pTrackData;
pInTrack->fdwTrack = 0;
pInTrack->bRunningStatus = BAD_MIDI_FIX;
pInTrack->tkNextEventDue = 0;
// Handle bozo MIDI files which contain empty track chunks
if (!pInTrack->iBytesLeft)
{
pInTrack->fdwTrack |= ITS_F_ENDOFTRK;
continue;
}
// We always preread the time from each track so the mixer code can
// determine which track has the next event with a minimum of work
if (!GetTrackVDWord(pInTrack, &pInTrack->tkNextEventDue))
{
CONS_Printf ( "Read error while reading first delta time of track.\n");
return TRUE;
}
}
// End of track initialization code
return FALSE;
}
// -------------------------
// Mid2StreamConvertToBuffer
//
// This function converts MIDI data from the track buffers setup by a previous
// call to Mid2StreamConverterInit(). It will convert data until an error is
// encountered or the output buffer has been filled with as much event data
// as possible, not to exceed dwMaxLength. This function can take a couple
// bit flags, passed through dwFlags. Information about the success/failure
// of this operation and the number of output bytes actually converted will
// be returned in the CONVERTINFO structure pointed at by lpciInfo.
// -------------------------
int Mid2StreamConvertToBuffer( DWORD dwFlags, LPCONVERTINFO lpciInfo )
{
static INTRACKSTATE *pInTrack, *pInTrackFound;
static DWORD dwStatus;
static DWORD tkNext;
static TEMPEVENT teTemp;
int nChkErr;
DWORD idx;
lpciInfo->dwBytesRecorded = 0;
if( dwFlags & CONVERTF_RESET )
{
dwProgressBytes = 0;
dwStatus = 0;
ZeroMemory( &teTemp, sizeof(TEMPEVENT));
pInTrack = pInTrackFound = NULL;
}
// If we were already done, then return with a warning...
if( dwStatus & CONVERTF_STATUS_DONE )
{
if( bMidiLooped )
{
Mid2StreamRewindConverter();
dwProgressBytes = 0;
dwStatus = 0;
}
else
return( CONVERTERR_DONE );
}
// The caller is asking us to continue, but we're already hosed because we
// previously identified something as corrupt, so complain louder this time.
else if( dwStatus & CONVERTF_STATUS_STUCK )
{
return( CONVERTERR_STUCK );
}
else if( dwStatus & CONVERTF_STATUS_GOTEVENT )
{
// Turn off this bit flag
dwStatus ^= CONVERTF_STATUS_GOTEVENT;
// Don't add end of track event 'til we're done
//
if( teTemp.abEvent[0] == MIDI_META &&
teTemp.abEvent[1] == MIDI_META_EOT )
{
}
else if(( nChkErr = AddEventToStreamBuffer( &teTemp, lpciInfo ))
!= CONVERTERR_NOERROR )
{
if( nChkErr == CONVERTERR_BUFFERFULL )
{
// Do some processing and tell caller that this buffer's full
dwStatus |= CONVERTF_STATUS_GOTEVENT;
return( CONVERTERR_NOERROR );
}
else if( nChkErr == CONVERTERR_METASKIP )
{
// We skip by all meta events that aren't tempo changes...
}
else
{
I_Error ( "Unable to add event to stream buffer." );
}
}
}
for( ; ; )
{
pInTrackFound = NULL;
tkNext = 0xFFFFFFFFL;
// Find nearest event due
//
for (idx = 0, pInTrack = ifs.pTracks; idx < ifs.nTracks; ++idx, ++pInTrack) {
if ( (!(pInTrack->fdwTrack & ITS_F_ENDOFTRK)) && (pInTrack->tkNextEventDue < tkNext))
{
tkNext = pInTrack->tkNextEventDue;
pInTrackFound = pInTrack;
}
}
// None found? We must be done, so return to the caller with a smile.
//
if (!pInTrackFound)
{
dwStatus |= CONVERTF_STATUS_DONE;
// Need to set return buffer members properly
return( CONVERTERR_NOERROR );
}
// Ok, get the event header from that track
//
if( !GetTrackEvent( pInTrackFound, &teTemp ))
{
// Warn future calls that this converter is stuck at a corrupt spot
// and can't continue
dwStatus |= CONVERTF_STATUS_STUCK;
return( CONVERTERR_CORRUPT );
}
// Don't add end of track event 'til we're done
//
if( teTemp.abEvent[0] == MIDI_META &&
teTemp.abEvent[1] == MIDI_META_EOT )
continue;
if(( nChkErr = AddEventToStreamBuffer( &teTemp, lpciInfo ))
!= CONVERTERR_NOERROR )
{
if( nChkErr == CONVERTERR_BUFFERFULL )
{
// Do some processing and tell somebody this buffer is full...
dwStatus |= CONVERTF_STATUS_GOTEVENT;
return( CONVERTERR_NOERROR );
}
else if( nChkErr == CONVERTERR_METASKIP )
{
// We skip by all meta events that aren't tempo changes...
}
else
{
I_Error( "Unable to add event to stream buffer." );
}
}
}
return( CONVERTERR_NOERROR );
}
#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -