📄 mid2strm.c
字号:
// Parse the general format of:
// BYTE bEvent (MIDI_SYSEX or MIDI_SYSEXEND)
// VLONG cbParms
// BYTE abParms[cbParms]
//
pMe->abEvent[0] = b;
if (!GetTrackVDWord(pInTrack, &pMe->dwEventLength))
{
TRACKERR(pInTrack, gteSysExLenTrunc);
return FALSE;
}
if (pInTrack->iBytesLeft < pMe->dwEventLength)
{
TRACKERR(pInTrack, gteSysExTrunc);
pInTrack->fdwTrack |= ITS_F_ENDOFTRK;
return FALSE;
}
pMe->pEvent = pInTrack->pTrackPointer;
pInTrack->pTrackPointer += pMe->dwEventLength;
pInTrack->iBytesLeft -= pMe->dwEventLength;
}
else if (b == MIDI_META)
{
// It's a meta event. Parse the general form:
// BYTE bEvent (MIDI_META)
// BYTE bClass
// VLONG cbParms
// BYTE abParms[cbParms]
//
pMe->abEvent[0] = b;
if (!pInTrack->iBytesLeft)
{
TRACKERR(pInTrack, gteMetaNoClass);
pInTrack->fdwTrack |= ITS_F_ENDOFTRK;
return FALSE;
}
pMe->abEvent[1] = *pInTrack->pTrackPointer++;
--pInTrack->iBytesLeft;
if (!GetTrackVDWord(pInTrack, &pMe->dwEventLength))
{
TRACKERR(pInTrack, gteMetaLenTrunc);
return FALSE;
}
// NOTE: Perfectly valid to have a meta with no data
// In this case, dwEventLength == 0 and pEvent == NULL
//
if (pMe->dwEventLength)
{
if (pInTrack->iBytesLeft < pMe->dwEventLength)
{
TRACKERR(pInTrack, gteMetaTrunc);
pInTrack->fdwTrack |= ITS_F_ENDOFTRK;
return FALSE;
}
pMe->pEvent = pInTrack->pTrackPointer;
pInTrack->pTrackPointer += pMe->dwEventLength;
pInTrack->iBytesLeft -= pMe->dwEventLength;
}
if (pMe->abEvent[1] == MIDI_META_EOT)
pInTrack->fdwTrack |= ITS_F_ENDOFTRK;
}
else
{
// Messages in this range are system messages and aren't supposed to
// be in a normal MIDI file. If they are, we've misparsed or the
// authoring software is stpuid.
//
#ifdef DEBUGMIDISTREAM
CONS_Printf ("System message not supposed to be in MIDI file..\n");
#endif
return FALSE;
}
// Event time was already stored as the current track time
//
pMe->tkEvent = pInTrack->tkNextEventDue;
// Now update to the next event time. The code above MUST properly
// maintain the end of track flag in case the end of track meta is
// missing.
//
if (!(pInTrack->fdwTrack & ITS_F_ENDOFTRK))
{
LONG tkDelta;
if (!GetTrackVDWord(pInTrack, &tkDelta))
return FALSE;
pInTrack->tkNextEventDue += tkDelta;
}
return TRUE;
}
// ----------------
// AddEventToStream
//
// Put the given event onto the given output track.
// pMe must point to an event filled out in accordance with the
// description given in GetTrackEvent
//
// Returns TRUE on sucess or FALSE if we're out of memory
// ----------------
static BOOL AddEventToStream(TEMPEVENT *pMe)
{
PLONG pdw;
LONG tkNow, tkDelta;
UINT cdw;
tkNow = ots.tkTrack;
// Delta time is absolute event time minus absolute time
// already gone by on this track
//
tkDelta = pMe->tkEvent - ots.tkTrack;
// Event time is now current time on this track
//
ots.tkTrack = pMe->tkEvent;
if (pMe->abEvent[0] < MIDI_SYSEX)
{
// Channel message. We know how long it is, just copy it. Need 3 LONG's: delta-t,
// stream-ID, event
//
// TODO: Compress with running status
//
cdw = (fCompress ? 2 : 3);
if (NULL == (pdw = (PLONG)GetOutStreamBytes(tkNow, cdw * sizeof(LONG), 3 * sizeof(LONG))))
return FALSE;
*pdw++ = tkDelta;
if (!fCompress)
*pdw++ = 0;
*pdw = (pMe->abEvent[0]) |
(((LONG)pMe->abEvent[1]) << 8) |
(((LONG)pMe->abEvent[2]) << 16) |
MIDS_SHORTMSG;
}
else if (pMe->abEvent[0] == MIDI_SYSEX || pMe->abEvent[0] == MIDI_SYSEXEND)
{
CONS_Printf ( "NOTE: Ignoring SysEx for now.\n");
}
else
{
// Better be a meta event.
// BYTE bEvent
// BYTE bClass
// VLONG cbParms
// BYTE abParms[cbParms]
//
if (!(pMe->abEvent[0] == MIDI_META))
{
CONS_Printf ("Mid2Stream: error1\n");
return FALSE;
}
// The only meta-event we care about is change tempo
//
if (pMe->abEvent[1] != MIDI_META_TEMPO)
return TRUE;
if (!(pMe->dwEventLength == 3))
{
CONS_Printf ("Mid2Stream: error2\n");
return FALSE;
}
cdw = (fCompress ? 2 : 3);
pdw = (PLONG)GetOutStreamBytes(tkNow, cdw * sizeof(LONG), 3 * sizeof(LONG));
if (NULL == pdw)
return FALSE;
*pdw++ = tkDelta;
if (!fCompress)
*pdw++ = (LONG)-1;
*pdw = (pMe->pEvent[2]) |
(((LONG)pMe->pEvent[1]) << 8) |
(((LONG)pMe->pEvent[0]) << 16) |
MIDS_TEMPO;
}
return TRUE;
}
// -----------------
// GetOutStreamBytes
//
// This function performs the memory management and pseudo-file I/O for output
// tracks.
//
// We build a linked list of stream buffers as they would exist if they were
// about to be played. Each buffer is CB_STREAMBUF UBYTEs long maximum. They are
// filled as full as possible; events are not allowed to cross buffers.
//
// Returns a pointer to the number of requested UBYTEs or NULL if we're out of memory
// -----------------
static UBYTE* GetOutStreamBytes(LONG tkNow, LONG cbNeeded, LONG cbUncompressed)
{
UBYTE* pb;
// Round request up to the next LONG boundry. This aligns the final output buffer correctly
// and allows the above routines to deal with UBYTE-aligned data
//
cbNeeded = (cbNeeded + 3) & ~3;
cbUncompressed = (cbUncompressed + 3) & ~3;
if (!(cbUncompressed >= cbNeeded))
{
CONS_Printf ("GetOutStreamBytes: error\n");
return NULL;
}
if (NULL == ots.pLast || cbUncompressed > ots.pLast->iBytesLeftUncompressed)
{
PSTREAMBUF pNew;
pNew = GlobalAllocPtr(GHND, sizeof(*pNew) + CB_STREAMBUF);
if (NULL == pNew)
return NULL;
pNew->pBuffer = (UBYTE*)(pNew + 1); //skip PSTRAMBUF struct header..
pNew->tkStart = tkNow;
pNew->pbNextEvent = pNew->pBuffer;
pNew->iBytesLeft = CB_STREAMBUF;
pNew->iBytesLeftUncompressed = CB_STREAMBUF;
pNew->pNext = NULL;
if (!ots.pLast)
{
ots.pFirst = pNew;
ots.pLast = pNew;
}
else
{
ots.pLast->pNext = pNew;
ots.pLast = pNew;
}
}
// If there's STILL not enough room for the requested block, then an event is bigger than
// the buffer size -- this is unacceptable.
//
if (cbNeeded > ots.pLast->iBytesLeft)
{
CONS_Printf ( "NOTE: An event requested %lu UBYTEs of memory; the\n", cbNeeded);
CONS_Printf ( " maximum configured buffer size is %lu.\n", (LONG)CB_STREAMBUF);
return NULL;
}
pb = ots.pLast->pbNextEvent;
ots.pLast->pbNextEvent += cbNeeded;
ots.pLast->iBytesLeft -= cbNeeded;
ots.pLast->iBytesLeftUncompressed -= cbUncompressed;
return pb;
}
#ifdef DEBUGMIDISTREAM
static void ShowTrackError(INTRACKSTATE* pInTrack, char* szErr)
{
unsigned char* data;
int i;
CONS_Printf ( "Track %u: %s\n", pInTrack->nTrack, szErr);
CONS_Printf ( "Track offset %lu\n", (LONG)(pInTrack->pTrackPointer - pInTrack->pTrackData));
CONS_Printf ( "Track total %lu Track left %lu\n", pInTrack->iTrackLen, pInTrack->iBytesLeft);
CONS_Printf (" Midi header: ");
data = ifs.pFile;
for (i = 0; i < 6; i++)
{
CONS_Printf ("%02x ", data[i]);
}
CONS_Printf ( "Track data: ");
data = pInTrack->pTrackData;
for (i = 0; i < 512; i++)
{
CONS_Printf ("%02x ", data[i]);
}
}
#endif
#ifndef LEGACY
// if LEGACY is not defined, the stand-alone version will print out error messages to stderr
void CONS_Printf (char *fmt, ...)
{
va_list argptr;
char txt[512];
va_start (argptr,fmt);
vsprintf (txt,fmt,argptr);
va_end (argptr);
fprintf (stderr,"%s", txt);
}
#endif
// ==========================================================================
// MIDI STREAM PLAYBACK (called by win_snd.c)
// ==========================================================================
// the following code is used with Legacy (not in stand-alone)
#ifdef LEGACY
static DWORD dwBufferTickLength;
DWORD dwProgressBytes;
// win_snd.c
extern BOOL bMidiLooped;
// --------------------------
// Mid2StreamConverterCleanup
// Free whatever was allocated before exiting program
// --------------------------
void Mid2StreamConverterCleanup (void)
{
// hmm.. nothing to clean up.. since I made the INTRACKSTATE's static
/* faB: made pTracks static
if (ifs.pTracks)
GlobalFreePtr(ifs.pTracks);
*/
}
// -----------------------
// Mid2StreamConverterInit
//
// Validate the input file structure
// Allocate the input track structures and initialize them (faB: now STATIC)
// Initialize the output track structures
//
// Return TRUE on success
// -----------------------
BOOL Mid2StreamConverterInit( UBYTE* pMidiData, ULONG iMidiSize )
{
BOOL fRet = TRUE;
ULONG* pChunkID;
ULONG* pChunkSize;
LONG iChunkSize;
MIDIFILEHDR* pHeader;
INTRACKSTATE* pInTrack;
UINT iTrack;
tkCurrentTime = 0;
// Initialize things we'll try to free later if we fail
ZeroMemory( &ifs, sizeof(INFILESTATE));
//ifs.pTracks = NULL; //faB: now static
// Set up to read from the memory buffer. Read and validate
// - MThd header
// - size of file header chunk
// - file header itself
//
ifs.FileSize = iMidiSize;
ifs.pFile = pMidiData;
ifs.iBytesLeft = ifs.FileSize;
ifs.pFilePointer = ifs.pFile;
#ifdef DEBUGMIDISTREAM
CONS_Printf ("Midi file size: %d\n", iMidiSize);
#endif
// note: midi header size should always be 6
if ((pChunkID = (ULONG*)GetInFileData(sizeof(*pChunkID))) == NULL ||
*pChunkID != MThd ||
(pChunkSize = (ULONG*)GetInFileData(sizeof(*pChunkSize))) == NULL ||
(iChunkSize = LONGSWAP(*pChunkSize)) < sizeof(MIDIFILEHDR) ||
(pHeader = (MIDIFILEHDR*)GetInFileData(iChunkSize)) == NULL )
{
CONS_Printf ( "Read error on MIDI header.\n");
goto Init_Cleanup;
}
ifs.dwFormat = (LONG)WORDSWAP(pHeader->wFormat);
ifs.nTracks = (LONG)WORDSWAP(pHeader->nTracks);
ifs.dwTimeDivision = (LONG)WORDSWAP(pHeader->wTimeDivision);
#ifdef DEBUGMIDISTREAM
CONS_Printf ("MIDI Header:\n"
"------------\n"
"format: %d\n"
"number of tracks: %d\n"
"time division: %d\n", ifs.dwFormat, ifs.nTracks, ifs.dwTimeDivision);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -