📄 mid2strm.c
字号:
#endif
pInTrack->pTrackPointer = pInTrack->pTrackData;
pInTrack->fdwTrack = 0;
pInTrack->bRunningStatus = 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;
}
}
ots.tkTrack = 0;
ots.pFirst = NULL;
ots.pLast = NULL;
fRet = TRUE;
Init_Cleanup:
if (!fRet)
Cleanup();
return fRet;
}
// -------------
// GetInFileData
//
// Gets the requested number of UBYTEs of data from the input file and returns
// a pointer to them.
//
// Returns a pointer to the data or NULL if we'd read more than is
// there.
// -------------
static UBYTE* GetInFileData(LONG iBytesToGet)
{
UBYTE* pRet;
if (ifs.iBytesLeft < iBytesToGet) // requested more UBYTEs than there are left
return NULL;
pRet = ifs.pFilePointer; // pointer to requested data
ifs.iBytesLeft -= iBytesToGet;
ifs.pFilePointer += iBytesToGet;
return pRet;
}
// -------
// Cleanup
//
// Free anything we ever allocated
// -------
static void Cleanup(void)
{
PSTREAMBUF pCurr;
PSTREAMBUF pNext;
if (hInFile != INVALID_HANDLE_VALUE)
CloseHandle(hInFile);
if (hOutFile != INVALID_HANDLE_VALUE)
CloseHandle(hOutFile);
if (ifs.pFile)
GlobalFreePtr(ifs.pFile);
/* faB: made pTracks static
if (ifs.pTracks)
GlobalFreePtr(ifs.pTracks);
*/
pCurr = ots.pFirst;
while (pCurr)
{
pNext = pCurr->pNext;
GlobalFreePtr(pCurr);
pCurr = pNext;
}
}
// --------------
// BuildNewTracks
//
// This is where the actual work gets done.
//
// Until all tracks are done,
// Scan the tracks to find the next due event
// Figure out where the event belongs in the new mapping
// Put it there
// Add end of track metas to all new tracks that now have any data
//
// Return TRUE on success
// Prints its own error message if something goes wrong
// --------------
static BOOL BuildNewTracks(void)
{
INTRACKSTATE* pInTrack;
INTRACKSTATE* pInTrackFound;
UINT idx;
DWORD tkNext;
TEMPEVENT me;
for(;;)
{
// Find nearest event due
//
pInTrackFound = NULL;
tkNext = 0xFFFFFFFFL;
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
//
if (!pInTrackFound)
break;
// Ok, get the event header from that track
//
if (!GetTrackEvent(pInTrackFound, &me))
{
CONS_Printf ( "MIDI file is corrupt!\n");
return FALSE;
}
// Don't add end of track event 'til we're done
//
if (me.abEvent[0] == MIDI_META && me.abEvent[1] == MIDI_META_EOT)
continue;
if (!AddEventToStream(&me))
{
CONS_Printf ( "Out of memory building tracks.\n");
return FALSE;
}
}
return TRUE;
}
//
// WriteStreamBuffers
//
// Write stream buffers into an MDS file (RIFF MIDS format)
//
// Return TRUE on success
// Prints its own error message if something goes wrong
//
#define FOURCC_MIDS mmioFOURCC('M','I','D','S')
#define FOURCC_fmt mmioFOURCC('f','m','t',' ')
#define FOURCC_data mmioFOURCC('d','a','t','a')
static BOOL WriteStreamBuffers(void)
{
LONG cbFmt;
LONG cbData;
LONG cbRiff;
PSTREAMBUF psb;
FOURCC fcc;
FOURCC fcc2;
MIDSFMT fmt;
MIDSBUFFER data;
LONG cb;
LONG cBuffers;
// Walk buffer list to find entire size of data chunk
//
cbData = sizeof(cBuffers);
cBuffers = 0;
for (psb = ots.pFirst; psb; psb = psb->pNext, ++cBuffers)
cbData += sizeof(MIDSBUFFER) + (CB_STREAMBUF - psb->iBytesLeft);
cbFmt = sizeof(fmt);
// Figure size of entire RIFF chunk
//
cbRiff =
sizeof(FOURCC) + // RIFF form type ('MIDS')
sizeof(FOURCC) + // Format chunk type ('fmt ')
sizeof(LONG) + // Format chunk size
sizeof(MIDSFMT) + // Format chunk contents
sizeof(FOURCC) + // Data chunk type ('data')
sizeof(LONG) + // Data chunk size
cbData; // Data chunk contents
fcc = FOURCC_RIFF;
fcc2 = FOURCC_MIDS;
if ((!WriteFile(hOutFile, &fcc, sizeof(fcc), &cb, NULL)) ||
(!WriteFile(hOutFile, &cbRiff, sizeof(cbRiff), &cb, NULL)) ||
(!WriteFile(hOutFile, &fcc2, sizeof(fcc2), &cb, NULL)))
return FALSE;
fmt.dwTimeFormat = ifs.dwTimeDivision;
fmt.cbMaxBuffer = CB_STREAMBUF;
fmt.dwFlags = 0;
if (fCompress)
fmt.dwFlags |= MDS_F_NOSTREAMID;
fcc = FOURCC_fmt;
if ((!WriteFile(hOutFile, &fcc, sizeof(fcc), &cb, NULL)) ||
(!WriteFile(hOutFile, &cbFmt, sizeof(cbFmt), &cb, NULL)) ||
(!WriteFile(hOutFile, &fmt, sizeof(fmt), &cb, NULL)))
return FALSE;
fcc = FOURCC_data;
if ((!WriteFile(hOutFile, &fcc, sizeof(fcc), &cb, NULL)) ||
(!WriteFile(hOutFile, &cbData, sizeof(cbData), &cb, NULL)) ||
(!WriteFile(hOutFile, &cBuffers, sizeof(cBuffers), &cb, NULL)))
return FALSE;
for (psb = ots.pFirst; psb; psb = psb->pNext)
{
data.tkStart = psb->tkStart;
data.cbBuffer = CB_STREAMBUF - psb->iBytesLeft;
if ((!WriteFile(hOutFile, &data, sizeof(data), &cb, NULL)) ||
(!WriteFile(hOutFile, psb->pBuffer, data.cbBuffer, &cb, NULL)))
return FALSE;
}
return TRUE;
}
// --------------
// GetTrackVDWord
// Attempts to parse a variable length LONG from the given track. A VDWord
// in a MIDI file
// (a) is in lo-hi format
// (b) has the high bit set on every UBYTE except the last
// Returns the LONG in *lpdw and TRUE on success; else
// FALSE if we hit end of track first. Sets ITS_F_ENDOFTRK
// if we hit end of track.
// --------------
static BOOL GetTrackVDWord(INTRACKSTATE* pInTrack, ULONG* lpdw)
{
BYTE b;
LONG dw = 0;
if (pInTrack->fdwTrack & ITS_F_ENDOFTRK)
return FALSE;
do
{
if (!pInTrack->iBytesLeft)
{
pInTrack->fdwTrack |= ITS_F_ENDOFTRK;
return FALSE;
}
b = *pInTrack->pTrackPointer++;
--pInTrack->iBytesLeft;
dw = (dw << 7) | (b & 0x7F);
} while (b & 0x80);
*lpdw = dw;
return TRUE;
}
// -------------
// GetTrackEvent
//
// Fills in the event struct with the next event from the track
//
// pMe->tkEvent will contain the absolute tick time of the event
// pMe->abEvent[0] will contain
// MIDI_META if the event is a meta event;
// in this case pMe->abEvent[1] will contain the meta class
// MIDI_SYSEX or MIDI_SYSEXEND if the event is a SysEx event
// Otherwise, the event is a channel message and pMe->abEvent[1]
// and pMe->abEvent[2] will contain the rest of the event.
//
// pMe->dwEventLength will contain
// The total length of the channel message in pMe->abEvent if
// the event is a channel message
// The total length of the paramter data pointed to by
// pMe->pEvent otherwise
//
// pMe->pEvent will point at any additional paramters if the
// event is a SysEx or meta event with non-zero length; else
// it will contain NULL
//
// Returns TRUE on success or FALSE on any kind of parse error
// Prints its own error message ONLY in the debug version
//
// Maintains the state of the input track (i.e. pInTrack->iBytesLeft,
// pInTrack->pTrackPointers, and pInTrack->bRunningStatus).
// -------------
static BOOL GetTrackEvent(INTRACKSTATE* pInTrack, TEMPEVENT *pMe)
{
BYTE b;
LONG dwEventLength;
// Clear out the temporary event structure to get rid of old data...
ZeroMemory( pMe, sizeof(TEMPEVENT));
// Already at end of track? There's nothing to read.
//
if ((pInTrack->fdwTrack & ITS_F_ENDOFTRK) || !pInTrack->iBytesLeft)
return FALSE;
// Get the first UBYTE, which determines the type of event.
//
b = *pInTrack->pTrackPointer++;
--pInTrack->iBytesLeft;
// If the high bit is not set, then this is a channel message
// which uses the status UBYTE from the last channel message
// we saw. NOTE: We do not clear running status across SysEx or
// meta events even though the spec says to because there are
// actually files out there which contain that sequence of data.
//
if (!(b & 0x80))
{
// No previous status UBYTE? We're hosed.
//
if (!pInTrack->bRunningStatus)
{
TRACKERR(pInTrack, gteBadRunStat);
return FALSE;
}
//faB: the last midi command issued on that track
pMe->abEvent[0] = pInTrack->bRunningStatus;
pMe->abEvent[1] = b; // the data !
b = pMe->abEvent[0] & 0xF0;
pMe->dwEventLength = 2; //2 data bytes
// Only program change and channel pressure events are 2 UBYTEs long;
// the rest are 3 and need another UBYTE
//
if ((b != MIDI_PRGMCHANGE) && (b != MIDI_CHANPRESS))
{
if (!pInTrack->iBytesLeft)
{
TRACKERR(pInTrack, gteRunStatMsgTrunc);
pInTrack->fdwTrack |= ITS_F_ENDOFTRK;
return FALSE;
}
pMe->abEvent[2] = *pInTrack->pTrackPointer++;
--pInTrack->iBytesLeft;
++pMe->dwEventLength;
}
}
else if ((b & 0xF0) != MIDI_SYSEX)
{
// Not running status, not in SysEx range - must be
// normal channel message (0x80-0xEF)
//
pMe->abEvent[0] = b;
pInTrack->bRunningStatus = b;
// Strip off channel and just keep message type
//
b &= 0xF0;
dwEventLength = (b == MIDI_PRGMCHANGE || b == MIDI_CHANPRESS) ? 1 : 2;
pMe->dwEventLength = dwEventLength + 1;
if (pInTrack->iBytesLeft < dwEventLength)
{
TRACKERR(pInTrack, gteChanMsgTrunc);
pInTrack->fdwTrack |= ITS_F_ENDOFTRK;
return FALSE;
}
pMe->abEvent[1] = *pInTrack->pTrackPointer++;
if (dwEventLength == 2)
pMe->abEvent[2] = *pInTrack->pTrackPointer++;
pInTrack->iBytesLeft -= dwEventLength;
}
else if (b == MIDI_SYSEX || b == MIDI_SYSEXEND)
{
// One of the SysEx types. (They are the same as far as we're concerned;
// there is only a semantic difference in how the data would actually
// get sent when the file is played. We must take care to put the correct
// event type back on the output track, however.)
//
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -