1194 lines
39 KiB
C
1194 lines
39 KiB
C
/*
|
|
* $Header: /H3/game/mstrconv.c 1 2/27/97 4:04p Rjohnson $
|
|
*/
|
|
|
|
#include <windows.h>
|
|
#include <windowsx.h>
|
|
|
|
#include <mmsystem.h>
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
|
|
#include "midi.h"
|
|
#include "midstuff.h"
|
|
|
|
// Global stuff which is defined in the main module
|
|
//
|
|
BOOL bInsertTempo = FALSE;
|
|
|
|
// A few global variables used by this module only
|
|
//
|
|
//static HANDLE hInFile = INVALID_HANDLE_VALUE;
|
|
INFILESTATE ifs;
|
|
static DWORD tkCurrentTime;
|
|
byte *MidiData;
|
|
int MidiOffset,MidiSize;
|
|
|
|
// Tracks how many malloc blocks exist. If there are any and we decide to shut
|
|
// down, we must scan for them and free them. Malloc blocks are only created as
|
|
// temporary storgae blocks for extra parameter data associated with MIDI_SYSEX,
|
|
// MIDI_SYSEXEND, and MIDI_META events.
|
|
static DWORD dwMallocBlocks = 0;
|
|
|
|
extern DWORD dwBufferTickLength, dwTempoMultiplier, dwCurrentTempo;
|
|
extern DWORD dwProgressBytes, dwVolumePercent;
|
|
extern BOOL bLooped;
|
|
|
|
// Messages
|
|
//
|
|
static char szInitErrMem[] = "Out of memory.\n";
|
|
static char szInitErrInFile[] = "Read error on input file or file is corrupt.\n";
|
|
static char szNoTrackBuffMem[] = "Insufficient memory for track buffer allocation\n";
|
|
|
|
#ifdef DEBUG
|
|
static char gteBadRunStat[] = "Reference to missing running status.";
|
|
static char gteRunStatMsgTrunc[]= "Running status message truncated";
|
|
static char gteChanMsgTrunc[] = "Channel message truncated";
|
|
static char gteSysExLenTrunc[] = "SysEx event truncated (length)";
|
|
static char gteSysExTrunc[] = "SysEx event truncated";
|
|
static char gteMetaNoClass[] = "Meta event truncated (no class byte)";
|
|
static char gteMetaLenTrunc[] = "Meta event truncated (length)";
|
|
static char gteMetaTrunc[] = "Meta event truncated";
|
|
static char gteNoMem[] = "Out of memory during malloc call";
|
|
#endif
|
|
|
|
// Prototypes
|
|
//
|
|
static int AddEventToStreamBuffer( PTEMPEVENT pteTemp, LPCONVERTINFO );
|
|
static BOOL GetInFileData( LPVOID lpDest, DWORD cbToGet );
|
|
static BOOL GetTrackByte( PINTRACKSTATE ptsTrack, LPBYTE lpbyByte );
|
|
static BOOL GetTrackEvent( PINTRACKSTATE ptsTrack, PTEMPEVENT pteTemp );
|
|
static BOOL GetTrackVDWord( PINTRACKSTATE ptsTrack, LPDWORD lpdw );
|
|
static BOOL RefillTrackBuffer( PINTRACKSTATE ptsTrack );
|
|
static BOOL RewindConverter( void );
|
|
|
|
#ifdef DEBUG
|
|
static void ShowTrackError( PINTRACKSTATE ptsTrack, char* szErr );
|
|
#endif
|
|
|
|
int SetFilePointer2(LONG lDistanceToMove, PLONG lpDistanceToMoveHigh, DWORD dwMoveMethod)
|
|
{
|
|
int SaveMidi;
|
|
|
|
SaveMidi = MidiOffset;
|
|
if (dwMoveMethod == FILE_BEGIN)
|
|
{
|
|
MidiOffset = lDistanceToMove;
|
|
}
|
|
else MidiOffset += lDistanceToMove;
|
|
|
|
if (MidiOffset >= MidiSize)
|
|
{
|
|
MidiOffset = SaveMidi;
|
|
return -1;
|
|
}
|
|
return MidiOffset;
|
|
}
|
|
|
|
|
|
BOOL ReadFile2(LPVOID lpBuffer, DWORD nNumberOfBytesToRead, LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped)
|
|
{
|
|
if (MidiOffset+nNumberOfBytesToRead > MidiSize)
|
|
{
|
|
// Con_Printf("Bad Read (%d+%d>=%d)\n",MidiOffset,nNumberOfBytesToRead,MidiSize);
|
|
return FALSE;
|
|
}
|
|
|
|
memcpy(lpBuffer,MidiData+MidiOffset,nNumberOfBytesToRead);
|
|
MidiOffset += nNumberOfBytesToRead;
|
|
*lpNumberOfBytesRead = nNumberOfBytesToRead;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// ConverterInit
|
|
//
|
|
// Open the input file
|
|
// Allocate and read the entire input file into memory
|
|
// Validate the input file structure
|
|
// Allocate the input track structures and initialize them
|
|
// Initialize the output track structures
|
|
//
|
|
// Return TRUE on success
|
|
// Prints its own error message if something goes wrong
|
|
//
|
|
BOOL ConverterInit( LPSTR szInFile )
|
|
{
|
|
BOOL fRet = TRUE;
|
|
DWORD cbRead, dwTag, cbHeader, dwToRead;
|
|
MIDIFILEHDR Header;
|
|
PINTRACKSTATE ptsTrack;
|
|
UINT idx;
|
|
|
|
tkCurrentTime = 0;
|
|
|
|
// Initialize things we'll try to free later if we fail
|
|
//
|
|
memset( &ifs, 0, sizeof(INFILESTATE));
|
|
ifs.cbFileLength = 0;
|
|
ifs.pitsTracks = NULL;
|
|
|
|
// Attempt to open the input and output files
|
|
//
|
|
MidiData = (byte *)COM_LoadHunkFile2((char *)szInFile, (int *)&ifs.cbFileLength);
|
|
if (!MidiData)
|
|
{
|
|
goto Init_Cleanup;
|
|
}
|
|
MidiOffset = 0;
|
|
MidiSize = ifs.cbFileLength;
|
|
|
|
/* hInFile = CreateFile( szInFile, GENERIC_READ,
|
|
FILE_SHARE_READ, NULL, OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL, NULL);
|
|
if( hInFile == INVALID_HANDLE_VALUE )
|
|
{
|
|
wsprintf( szTemp, "Could not open \"%s\" for read.\n", szInFile );
|
|
MessageBox( GetActiveWindow(), szTemp,
|
|
"TEST", MB_OK | MB_ICONEXCLAMATION );
|
|
goto Init_Cleanup;
|
|
}
|
|
*/
|
|
|
|
// Figure out how big the input file is.
|
|
/* if((( ifs.cbFileLength = GetFileSize( hInFile, NULL )) == (UINT)-1 ))
|
|
{
|
|
MessageBox( GetActiveWindow(), "File system error on input file.\n",
|
|
"TEST", MB_OK | MB_ICONEXCLAMATION );
|
|
goto Init_Cleanup;
|
|
}*/
|
|
|
|
// Set up to read from the memory buffer. Read and validate
|
|
// - MThd header
|
|
// - size of file header chunk
|
|
// - file header itself
|
|
//
|
|
if( GetInFileData( &dwTag, sizeof(DWORD))
|
|
|| ( dwTag != MThd )
|
|
|| GetInFileData( &cbHeader, sizeof(DWORD))
|
|
|| (( cbHeader = DWORDSWAP( cbHeader )) < sizeof(MIDIFILEHDR))
|
|
|| GetInFileData( &Header, cbHeader ) )
|
|
{
|
|
Con_Printf("MIDI: %s\n",szInitErrInFile);
|
|
goto Init_Cleanup;
|
|
}
|
|
|
|
// File header is stored in hi-lo order. Swap this into Intel order and save
|
|
// parameters in our native int size (32 bits)
|
|
//
|
|
ifs.dwFormat = (DWORD)WORDSWAP( Header.wFormat );
|
|
ifs.dwTrackCount = (DWORD)WORDSWAP( Header.wTrackCount );
|
|
ifs.dwTimeDivision = (DWORD)WORDSWAP( Header.wTimeDivision );
|
|
|
|
// We know how many tracks there are; allocate the structures for them and parse
|
|
// them. The parse merely looks at the MTrk signature and track chunk length
|
|
// in order to skip to the next track header.
|
|
//
|
|
ifs.pitsTracks = (PINTRACKSTATE)GlobalAllocPtr( GPTR,
|
|
ifs.dwTrackCount * sizeof(INTRACKSTATE));
|
|
if( ifs.pitsTracks == NULL )
|
|
{
|
|
Con_Printf("MIDI: %s\n",szInitErrMem);
|
|
goto Init_Cleanup;
|
|
}
|
|
|
|
for( idx = 0, ptsTrack = ifs.pitsTracks; idx < ifs.dwTrackCount;
|
|
++idx, ++ptsTrack )
|
|
{
|
|
if(( ptsTrack->pTrackStart
|
|
= GlobalAllocPtr( GHND, TRACK_BUFFER_SIZE )) == NULL )
|
|
{
|
|
Con_Printf("MIDI: %s\n", szNoTrackBuffMem);
|
|
goto Init_Cleanup;
|
|
}
|
|
|
|
if( GetInFileData( &dwTag, sizeof(dwTag)) || ( dwTag != MTrk )
|
|
|| GetInFileData( &cbHeader, sizeof(cbHeader)))
|
|
{
|
|
Con_Printf("MIDI: %s\n", szInitErrInFile);
|
|
goto Init_Cleanup;
|
|
}
|
|
|
|
cbHeader = DWORDSWAP( cbHeader );
|
|
ptsTrack->dwTrackLength = cbHeader; // Total track length
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Here we need to determine if all track data will fit into a single one of
|
|
// our track buffers. If not, we need to read in a buffer full and come back
|
|
// for more later, saving the file offset to continue from and the amount left
|
|
// to read in the track structure.
|
|
|
|
// Save the file offset of the beginning of this track
|
|
/* ptsTrack->foTrackStart = SetFilePointer( hInFile, 0, NULL,
|
|
FILE_CURRENT );*/
|
|
ptsTrack->foTrackStart = SetFilePointer2( 0, NULL,
|
|
FILE_CURRENT );
|
|
|
|
if( ptsTrack->dwTrackLength > TRACK_BUFFER_SIZE )
|
|
dwToRead = TRACK_BUFFER_SIZE;
|
|
else
|
|
dwToRead = ptsTrack->dwTrackLength;
|
|
/* if( !ReadFile( hInFile, ptsTrack->pTrackStart, dwToRead, &cbRead, NULL )
|
|
|| ( cbRead != dwToRead ))
|
|
{
|
|
MessageBox( GetActiveWindow(), szInitErrInFile,
|
|
"TEST", MB_OK | MB_ICONEXCLAMATION );
|
|
goto Init_Cleanup;
|
|
}*/
|
|
if( !ReadFile2( ptsTrack->pTrackStart, dwToRead, &cbRead, NULL )
|
|
|| ( cbRead != dwToRead ))
|
|
{
|
|
Con_Printf("MIDI: %s\n", szInitErrInFile);
|
|
goto Init_Cleanup;
|
|
}
|
|
// Save the number of bytes that didn't make it into the buffer
|
|
ptsTrack->dwLeftOnDisk = ptsTrack->dwTrackLength - cbRead;
|
|
ptsTrack->dwLeftInBuffer = cbRead;
|
|
// Save the current file offset so we can seek to it later
|
|
/* ptsTrack->foNextReadStart = SetFilePointer( hInFile, 0,
|
|
NULL, FILE_CURRENT );*/
|
|
ptsTrack->foNextReadStart = SetFilePointer2( 0,
|
|
NULL, FILE_CURRENT );
|
|
|
|
// Setup pointer to the current position in the track
|
|
ptsTrack->pTrackCurrent = ptsTrack->pTrackStart;
|
|
ptsTrack->fdwTrack = 0;
|
|
ptsTrack->byRunningStatus = 0;
|
|
ptsTrack->tkNextEventDue = 0;
|
|
|
|
// Handle bozo MIDI files which contain empty track chunks
|
|
//
|
|
if( !ptsTrack->dwLeftInBuffer && !ptsTrack->dwLeftOnDisk )
|
|
{
|
|
ptsTrack->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( ptsTrack, &ptsTrack->tkNextEventDue ))
|
|
{
|
|
Con_Printf("MIDI: %s\n", szInitErrInFile);
|
|
goto Init_Cleanup;
|
|
}
|
|
// Step over any unread data, advancing to the beginning of the next
|
|
// track's data
|
|
/* SetFilePointer( hInFile, ptsTrack->foTrackStart + ptsTrack->dwTrackLength,
|
|
NULL, FILE_BEGIN );*/
|
|
SetFilePointer2( ptsTrack->foTrackStart + ptsTrack->dwTrackLength,
|
|
NULL, FILE_BEGIN );
|
|
|
|
} // End of track initialization code
|
|
|
|
fRet = FALSE;
|
|
|
|
Init_Cleanup:
|
|
if( fRet )
|
|
ConverterCleanup();
|
|
|
|
return( fRet );
|
|
}
|
|
|
|
//
|
|
// GetInFileData
|
|
//
|
|
// Gets the requested number of bytes 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 BOOL GetInFileData( LPVOID lpDest, DWORD cbToGet )
|
|
{
|
|
DWORD cbRead;
|
|
|
|
/* if( !ReadFile( hInFile, lpDest, cbToGet, &cbRead, NULL )
|
|
|| ( cbRead != cbToGet ))
|
|
{
|
|
return( TRUE );
|
|
}*/
|
|
if( !ReadFile2( lpDest, cbToGet, &cbRead, NULL )
|
|
|| ( cbRead != cbToGet ))
|
|
{
|
|
return( TRUE );
|
|
}
|
|
|
|
return( FALSE );
|
|
}
|
|
|
|
|
|
//
|
|
// ConverterCleanup
|
|
//
|
|
// Free anything we ever allocated
|
|
//
|
|
void ConverterCleanup( void )
|
|
{
|
|
DWORD idx;
|
|
|
|
/* if( hInFile != INVALID_HANDLE_VALUE )
|
|
{
|
|
CloseHandle( hInFile );
|
|
hInFile = INVALID_HANDLE_VALUE;
|
|
}*/
|
|
|
|
if( ifs.pitsTracks )
|
|
{
|
|
// De-allocate all our track buffers
|
|
for( idx = 0; idx < ifs.dwTrackCount; idx++ )
|
|
if( ifs.pitsTracks[idx].pTrackStart )
|
|
GlobalFreePtr( ifs.pitsTracks[idx].pTrackStart );
|
|
|
|
GlobalFreePtr( ifs.pitsTracks );
|
|
ifs.pitsTracks = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
/*****************************************************************************/
|
|
/* RewindConverter() */
|
|
/* */
|
|
/* 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 RewindConverter( void )
|
|
{
|
|
DWORD dwToRead, cbRead, idx;
|
|
BOOL fRet;
|
|
|
|
PINTRACKSTATE ptsTrack;
|
|
|
|
tkCurrentTime = 0;
|
|
|
|
for( idx = 0, ptsTrack = ifs.pitsTracks; idx < ifs.dwTrackCount;
|
|
++idx, ++ptsTrack )
|
|
{
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Here we need to determine if all track data will fit into a single one of
|
|
// our track buffers. If not, we need to read in a buffer full and come back
|
|
// for more later, saving the file offset to continue from and the amount left
|
|
// to read in the track structure.
|
|
|
|
// SetFilePointer( hInFile, ptsTrack->foTrackStart, NULL, FILE_BEGIN );
|
|
SetFilePointer2( ptsTrack->foTrackStart, NULL, FILE_BEGIN );
|
|
|
|
if( ptsTrack->dwTrackLength > TRACK_BUFFER_SIZE )
|
|
dwToRead = TRACK_BUFFER_SIZE;
|
|
else
|
|
dwToRead = ptsTrack->dwTrackLength;
|
|
/* if( !ReadFile( hInFile, ptsTrack->pTrackStart, dwToRead, &cbRead, NULL )
|
|
|| ( cbRead != dwToRead ))
|
|
{
|
|
MessageBox( GetActiveWindow(), szInitErrInFile,
|
|
"TEST", MB_OK | MB_ICONEXCLAMATION );
|
|
goto Rewind_Cleanup;
|
|
}*/
|
|
if( !ReadFile2( ptsTrack->pTrackStart, dwToRead, &cbRead, NULL )
|
|
|| ( cbRead != dwToRead ))
|
|
{
|
|
Con_Printf("MIDI: %s\n", szInitErrInFile);
|
|
goto Rewind_Cleanup;
|
|
}
|
|
// Save the number of bytes that didn't make it into the buffer
|
|
ptsTrack->dwLeftOnDisk = ptsTrack->dwTrackLength - cbRead;
|
|
ptsTrack->dwLeftInBuffer = cbRead;
|
|
// Save the current file offset so we can seek to it later
|
|
/* ptsTrack->foNextReadStart = SetFilePointer( hInFile, 0,
|
|
NULL, FILE_CURRENT );*/
|
|
ptsTrack->foNextReadStart = SetFilePointer2( 0,
|
|
NULL, FILE_CURRENT );
|
|
|
|
// Setup pointer to the current position in the track
|
|
ptsTrack->pTrackCurrent = ptsTrack->pTrackStart;
|
|
ptsTrack->fdwTrack = 0;
|
|
ptsTrack->byRunningStatus = 0;
|
|
ptsTrack->tkNextEventDue = 0;
|
|
|
|
|
|
// Handle bozo MIDI files which contain empty track chunks
|
|
//
|
|
if( !ptsTrack->dwLeftInBuffer && !ptsTrack->dwLeftOnDisk )
|
|
{
|
|
ptsTrack->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( ptsTrack, &ptsTrack->tkNextEventDue ))
|
|
{
|
|
Con_Printf("MIDI: %s\n", szInitErrInFile);
|
|
goto Rewind_Cleanup;
|
|
}
|
|
// Step over any unread data, advancing to the beginning of the next
|
|
// track's data
|
|
/* SetFilePointer( hInFile, ptsTrack->foTrackStart + ptsTrack->dwTrackLength,
|
|
NULL, FILE_BEGIN );*/
|
|
SetFilePointer2( ptsTrack->foTrackStart + ptsTrack->dwTrackLength,
|
|
NULL, FILE_BEGIN );
|
|
} // End of track initialization code
|
|
|
|
fRet = FALSE;
|
|
|
|
Rewind_Cleanup:
|
|
|
|
if( fRet )
|
|
return( TRUE );
|
|
|
|
return( FALSE );
|
|
}
|
|
|
|
|
|
/*****************************************************************************/
|
|
/* ConvertToBuffer() */
|
|
/* */
|
|
/* This function converts MIDI data from the track buffers setup by a */
|
|
/* previous call to ConverterInit(). 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 ConvertToBuffer( DWORD dwFlags, LPCONVERTINFO lpciInfo )
|
|
{
|
|
static INTRACKSTATE *ptsTrack, *ptsFound;
|
|
static DWORD dwStatus;
|
|
static DWORD tkNext;
|
|
static TEMPEVENT teTemp;
|
|
|
|
int nChkErr;
|
|
DWORD idx;
|
|
|
|
lpciInfo->dwBytesRecorded = 0;
|
|
|
|
if( dwFlags & CONVERTF_RESET )
|
|
{
|
|
dwProgressBytes = 0;
|
|
dwStatus = 0;
|
|
memset( &teTemp, 0, sizeof(TEMPEVENT));
|
|
ptsTrack = ptsFound = NULL;
|
|
}
|
|
// If we were already done, then return with a warning...
|
|
if( dwStatus & CONVERTF_STATUS_DONE )
|
|
{
|
|
if( bLooped )
|
|
{
|
|
RewindConverter();
|
|
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;
|
|
|
|
/*
|
|
* The following code for this case is duplicated from below, and is
|
|
* designed to handle a "straggler" event, should we have one left over
|
|
* from previous processing the last time this function was called.
|
|
*/
|
|
|
|
// Don't add end of track event 'til we're done
|
|
//
|
|
if( teTemp.byShortData[0] == MIDI_META
|
|
&& teTemp.byShortData[1] == MIDI_META_EOT )
|
|
{
|
|
if( dwMallocBlocks )
|
|
{
|
|
free( teTemp.pLongData );
|
|
dwMallocBlocks--;
|
|
}
|
|
}
|
|
|
|
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
|
|
{
|
|
DebugPrint( "Unable to add event to stream buffer." );
|
|
if( dwMallocBlocks )
|
|
{
|
|
free( teTemp.pLongData );
|
|
dwMallocBlocks--;
|
|
}
|
|
return( TRUE );
|
|
}
|
|
}
|
|
}
|
|
|
|
for( ; ; )
|
|
{
|
|
ptsFound = NULL;
|
|
tkNext = 0xFFFFFFFFL;
|
|
// Find nearest event due
|
|
//
|
|
for( idx = 0, ptsTrack = ifs.pitsTracks; idx < ifs.dwTrackCount;
|
|
++idx, ++ptsTrack )
|
|
{
|
|
if(( !( ptsTrack->fdwTrack & ITS_F_ENDOFTRK ))
|
|
&& ( ptsTrack->tkNextEventDue < tkNext ))
|
|
{
|
|
tkNext = ptsTrack->tkNextEventDue;
|
|
ptsFound = ptsTrack;
|
|
}
|
|
}
|
|
|
|
// None found? We must be done, so return to the caller with a smile.
|
|
//
|
|
if( !ptsFound )
|
|
{
|
|
dwStatus |= CONVERTF_STATUS_DONE;
|
|
// Need to set return buffer members properly
|
|
return( CONVERTERR_NOERROR );
|
|
}
|
|
|
|
// Ok, get the event header from that track
|
|
//
|
|
if( GetTrackEvent( ptsFound, &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.byShortData[0] == MIDI_META
|
|
&& teTemp.byShortData[1] == MIDI_META_EOT )
|
|
{
|
|
if( dwMallocBlocks )
|
|
{
|
|
free( teTemp.pLongData );
|
|
dwMallocBlocks--;
|
|
}
|
|
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
|
|
{
|
|
DebugPrint( "Unable to add event to stream buffer." );
|
|
if( dwMallocBlocks )
|
|
{
|
|
free( teTemp.pLongData );
|
|
dwMallocBlocks--;
|
|
}
|
|
return( TRUE );
|
|
}
|
|
}
|
|
}
|
|
|
|
return( CONVERTERR_NOERROR );
|
|
}
|
|
|
|
|
|
//
|
|
// GetTrackVDWord
|
|
//
|
|
// Attempts to parse a variable length DWORD from the given track. A VDWord
|
|
// in a MIDI file
|
|
// (a) is in lo-hi format
|
|
// (b) has the high bit set on every byte except the last
|
|
//
|
|
// Returns the DWORD 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( PINTRACKSTATE ptsTrack, LPDWORD lpdw )
|
|
{
|
|
BYTE byByte;
|
|
DWORD dw = 0;
|
|
|
|
if( ptsTrack->fdwTrack & ITS_F_ENDOFTRK )
|
|
return( TRUE );
|
|
|
|
do
|
|
{
|
|
if( !ptsTrack->dwLeftInBuffer && !ptsTrack->dwLeftOnDisk )
|
|
{
|
|
ptsTrack->fdwTrack |= ITS_F_ENDOFTRK;
|
|
return( TRUE );
|
|
}
|
|
|
|
if( GetTrackByte( ptsTrack, &byByte ))
|
|
return( TRUE );
|
|
|
|
dw = ( dw << 7 ) | ( byByte & 0x7F );
|
|
} while( byByte & 0x80 );
|
|
|
|
*lpdw = dw;
|
|
|
|
return( FALSE );
|
|
}
|
|
|
|
|
|
//
|
|
// GetTrackEvent
|
|
//
|
|
// Fills in the event struct with the next event from the track
|
|
//
|
|
// pteTemp->tkEvent will contain the absolute tick time of the event
|
|
// pteTemp->byShortData[0] will contain
|
|
// MIDI_META if the event is a meta event;
|
|
// in this case pteTemp->byShortData[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 pteTemp->byShortData[1]
|
|
// and pteTemp->byShortData[2] will contain the rest of the event.
|
|
//
|
|
// pteTemp->dwEventLength will contain
|
|
// The total length of the channel message in pteTemp->byShortData if
|
|
// the event is a channel message
|
|
// The total length of the paramter data pointed to by
|
|
// pteTemp->pLongData otherwise
|
|
//
|
|
// pteTemp->pLongData 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 FALSE on success or TRUE 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. ptsTrack->dwLeftInBuffer,
|
|
// ptsTrack->pTrackPointers, and ptsTrack->byRunningStatus).
|
|
//
|
|
static BOOL GetTrackEvent( INTRACKSTATE *ptsTrack, PTEMPEVENT pteTemp )
|
|
{
|
|
DWORD idx;
|
|
BYTE byByte;
|
|
UINT dwEventLength;
|
|
|
|
// Clear out the temporary event structure to get rid of old data...
|
|
memset( pteTemp, 0, sizeof(TEMPEVENT));
|
|
|
|
// Already at end of track? There's nothing to read.
|
|
//
|
|
if(( ptsTrack->fdwTrack & ITS_F_ENDOFTRK )
|
|
|| ( !ptsTrack->dwLeftInBuffer && !ptsTrack->dwLeftOnDisk ))
|
|
return( TRUE );
|
|
|
|
// Get the first byte, which determines the type of event.
|
|
//
|
|
if( GetTrackByte( ptsTrack, &byByte ))
|
|
return( TRUE );
|
|
|
|
// If the high bit is not set, then this is a channel message
|
|
// which uses the status byte 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( !( byByte & 0x80 ))
|
|
{
|
|
// No previous status byte? We're hosed.
|
|
if( !ptsTrack->byRunningStatus )
|
|
{
|
|
TRACKERR(ptsTrack, gteBadRunStat);
|
|
return( TRUE );
|
|
}
|
|
|
|
pteTemp->byShortData[0] = ptsTrack->byRunningStatus;
|
|
pteTemp->byShortData[1] = byByte;
|
|
|
|
byByte = pteTemp->byShortData[0] & 0xF0;
|
|
pteTemp->dwEventLength = 2;
|
|
|
|
// Only program change and channel pressure events are 2 bytes long;
|
|
// the rest are 3 and need another byte
|
|
//
|
|
if(( byByte != MIDI_PRGMCHANGE ) && ( byByte != MIDI_CHANPRESS ))
|
|
{
|
|
if( !ptsTrack->dwLeftInBuffer && !ptsTrack->dwLeftOnDisk )
|
|
{
|
|
TRACKERR( ptsTrack, gteRunStatMsgTrunc );
|
|
ptsTrack->fdwTrack |= ITS_F_ENDOFTRK;
|
|
return( TRUE );
|
|
}
|
|
|
|
if( GetTrackByte( ptsTrack, &pteTemp->byShortData[2] ))
|
|
return( TRUE );
|
|
++pteTemp->dwEventLength;
|
|
}
|
|
}
|
|
else if(( byByte & 0xF0 ) != MIDI_SYSEX )
|
|
{
|
|
// Not running status, not in SysEx range - must be
|
|
// normal channel message (0x80-0xEF)
|
|
//
|
|
pteTemp->byShortData[0] = byByte;
|
|
ptsTrack->byRunningStatus = byByte;
|
|
|
|
// Strip off channel and just keep message type
|
|
//
|
|
byByte &= 0xF0;
|
|
|
|
dwEventLength = ( byByte == MIDI_PRGMCHANGE || byByte == MIDI_CHANPRESS ) ? 1 : 2;
|
|
pteTemp->dwEventLength = dwEventLength + 1;
|
|
|
|
if(( ptsTrack->dwLeftInBuffer + ptsTrack->dwLeftOnDisk ) < dwEventLength )
|
|
{
|
|
TRACKERR( ptsTrack, gteChanMsgTrunc );
|
|
ptsTrack->fdwTrack |= ITS_F_ENDOFTRK;
|
|
return( TRUE );
|
|
}
|
|
|
|
if( GetTrackByte( ptsTrack, &pteTemp->byShortData[1] ))
|
|
return( TRUE );
|
|
if( dwEventLength == 2 )
|
|
if( GetTrackByte( ptsTrack, &pteTemp->byShortData[2] ))
|
|
return( TRUE );
|
|
}
|
|
else if(( byByte == MIDI_SYSEX ) || ( byByte == 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 proper
|
|
// event type back on the output track, however.)
|
|
//
|
|
// Parse the general format of:
|
|
// BYTE bEvent (MIDI_SYSEX or MIDI_SYSEXEND)
|
|
// VDWORD cbParms
|
|
// BYTE abParms[cbParms]
|
|
//
|
|
pteTemp->byShortData[0] = byByte;
|
|
if( GetTrackVDWord( ptsTrack, &pteTemp->dwEventLength ))
|
|
{
|
|
TRACKERR( ptsTrack, gteSysExLenTrunc );
|
|
return( TRUE );
|
|
}
|
|
|
|
if(( ptsTrack->dwLeftInBuffer + ptsTrack->dwLeftOnDisk )
|
|
< pteTemp->dwEventLength )
|
|
{
|
|
TRACKERR( ptsTrack, gteSysExTrunc );
|
|
ptsTrack->fdwTrack |= ITS_F_ENDOFTRK;
|
|
return( TRUE );
|
|
}
|
|
|
|
// Malloc a temporary memory block to hold the parameter data
|
|
if(( pteTemp->pLongData = malloc( pteTemp->dwEventLength )) == NULL )
|
|
{
|
|
TRACKERR( ptsTrack, gteNoMem );
|
|
return( TRUE );
|
|
}
|
|
// Copy from the input buffer to the parameter data buffer
|
|
for( idx = 0; idx < pteTemp->dwEventLength; idx++ )
|
|
if( GetTrackByte( ptsTrack, pteTemp->pLongData + idx ))
|
|
{
|
|
TRACKERR( ptsTrack, gteSysExTrunc );
|
|
return( TRUE );
|
|
}
|
|
// Increment our counter, which tells the program to look around for
|
|
// a malloc block to free, should it need to exit or reset before the
|
|
// block would normally be freed
|
|
dwMallocBlocks++;
|
|
}
|
|
else if( byByte == MIDI_META )
|
|
{
|
|
// It's a meta event. Parse the general form:
|
|
// BYTE bEvent (MIDI_META)
|
|
// BYTE bClass
|
|
// VDWORD cbParms
|
|
// BYTE abParms[cbParms]
|
|
//
|
|
pteTemp->byShortData[0] = byByte;
|
|
|
|
if( !ptsTrack->dwLeftInBuffer && !ptsTrack->dwLeftOnDisk )
|
|
{
|
|
TRACKERR(ptsTrack, gteMetaNoClass );
|
|
ptsTrack->fdwTrack |= ITS_F_ENDOFTRK;
|
|
return( TRUE );
|
|
}
|
|
|
|
if( GetTrackByte( ptsTrack, &pteTemp->byShortData[1] ))
|
|
return( TRUE );
|
|
|
|
if( GetTrackVDWord( ptsTrack, &pteTemp->dwEventLength ))
|
|
{
|
|
TRACKERR( ptsTrack, gteMetaLenTrunc );
|
|
return( TRUE );
|
|
}
|
|
|
|
// NOTE: It's perfectly valid to have a meta with no data
|
|
// In this case, dwEventLength == 0 and pLongData == NULL
|
|
//
|
|
if( pteTemp->dwEventLength )
|
|
{
|
|
if(( ptsTrack->dwLeftInBuffer + ptsTrack->dwLeftOnDisk )
|
|
< pteTemp->dwEventLength )
|
|
{
|
|
TRACKERR( ptsTrack, gteMetaTrunc );
|
|
ptsTrack->fdwTrack |= ITS_F_ENDOFTRK;
|
|
return( TRUE );
|
|
}
|
|
|
|
// Malloc a temporary memory block to hold the parameter data
|
|
if(( pteTemp->pLongData = malloc( pteTemp->dwEventLength ))
|
|
== NULL )
|
|
{
|
|
TRACKERR( ptsTrack, gteNoMem );
|
|
return( TRUE );
|
|
}
|
|
// Copy from the input buffer to the parameter data buffer
|
|
for( idx = 0; idx < pteTemp->dwEventLength; idx++ )
|
|
if( GetTrackByte( ptsTrack, pteTemp->pLongData + idx ))
|
|
{
|
|
TRACKERR( ptsTrack, gteMetaTrunc );
|
|
return( TRUE );
|
|
}
|
|
// Increment our counter, which tells the program to look around for
|
|
// a malloc block to free, should it need to exit or reset before the
|
|
// block would normally be freed
|
|
dwMallocBlocks++;
|
|
}
|
|
|
|
if( pteTemp->byShortData[1] == MIDI_META_EOT )
|
|
ptsTrack->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 either misparsed or the
|
|
// authoring software is stupid.
|
|
//
|
|
return( TRUE );
|
|
}
|
|
|
|
// Event time was already stored as the current track time
|
|
//
|
|
pteTemp->tkEvent = ptsTrack->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. NOTE: This code is a continuation of the track event
|
|
// time pre-read which is done at the end of track initialization.
|
|
//
|
|
if( !( ptsTrack->fdwTrack & ITS_F_ENDOFTRK ))
|
|
{
|
|
DWORD tkDelta;
|
|
|
|
if( GetTrackVDWord( ptsTrack, &tkDelta ))
|
|
return( TRUE );
|
|
|
|
ptsTrack->tkNextEventDue += tkDelta;
|
|
}
|
|
|
|
return( FALSE );
|
|
}
|
|
|
|
|
|
//
|
|
// GetTrackByte
|
|
//
|
|
// Retrieve the next byte from the track buffer, refilling the buffer from
|
|
// disk if necessary.
|
|
//
|
|
static BOOL GetTrackByte( PINTRACKSTATE ptsTrack, LPBYTE lpbyByte )
|
|
{
|
|
if( !ptsTrack->dwLeftInBuffer )
|
|
{
|
|
if( RefillTrackBuffer( ptsTrack ))
|
|
return( TRUE );
|
|
}
|
|
|
|
*lpbyByte = *ptsTrack->pTrackCurrent++;
|
|
ptsTrack->dwLeftInBuffer--;
|
|
return( FALSE );
|
|
}
|
|
|
|
|
|
//
|
|
// RefillTrackBuffer()
|
|
//
|
|
// This function attempts to read in a buffer-full of data for a MIDI track.
|
|
//
|
|
BOOL RefillTrackBuffer( PINTRACKSTATE ptsTrack )
|
|
{
|
|
DWORD dwBytesRead, dwResult;
|
|
BOOL bResult;
|
|
|
|
if( ptsTrack->dwLeftOnDisk )
|
|
{
|
|
ptsTrack->pTrackCurrent = ptsTrack->pTrackStart;
|
|
|
|
// Seek to the proper place in the file, indicated by
|
|
// ptsTrack->foNextReadStart and read in the remaining data,
|
|
// up to a maximum of the buffer size.
|
|
|
|
/* if(( dwResult = SetFilePointer( hInFile,
|
|
(long)ptsTrack->foNextReadStart,
|
|
0L, FILE_BEGIN )) == 0xFFFFFFFF )
|
|
{
|
|
MessageBox( GetActiveWindow(),
|
|
"Unable to seek to track buffer location in RefillTrackBuffer()!!",
|
|
"TEST", MB_OK | MB_ICONEXCLAMATION );
|
|
return( TRUE );
|
|
}*/
|
|
if(( dwResult = SetFilePointer2(
|
|
(long)ptsTrack->foNextReadStart,
|
|
0L, FILE_BEGIN )) == 0xFFFFFFFF )
|
|
{
|
|
Con_Printf("MIDI: Unable to seek to track buffer location in RefillTrackBuffer()!!\n");
|
|
return( TRUE );
|
|
}
|
|
|
|
if( ptsTrack->dwLeftOnDisk > TRACK_BUFFER_SIZE )
|
|
ptsTrack->dwLeftInBuffer = TRACK_BUFFER_SIZE;
|
|
else
|
|
ptsTrack->dwLeftInBuffer = ptsTrack->dwLeftOnDisk;
|
|
/* bResult = ReadFile( hInFile, ptsTrack->pTrackStart,
|
|
ptsTrack->dwLeftInBuffer,
|
|
&dwBytesRead, NULL );*/
|
|
bResult = ReadFile2( ptsTrack->pTrackStart,
|
|
ptsTrack->dwLeftInBuffer,
|
|
&dwBytesRead, NULL );
|
|
|
|
ptsTrack->dwLeftOnDisk -= dwBytesRead;
|
|
ptsTrack->foNextReadStart = dwResult + dwBytesRead;
|
|
ptsTrack->dwLeftInBuffer = dwBytesRead;
|
|
|
|
if( !bResult || ( bResult && !dwBytesRead )
|
|
|| ( bResult && dwBytesRead != ptsTrack->dwLeftInBuffer ))
|
|
{
|
|
Con_Printf("MIDI: Read operation failed prematurely!!\n");
|
|
ptsTrack->dwLeftInBuffer = dwBytesRead;
|
|
return( TRUE );
|
|
}
|
|
else
|
|
return( FALSE );
|
|
}
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
|
|
//
|
|
// AddEventToStreamBuffer
|
|
//
|
|
// Put the given event into the given stream buffer at the given location
|
|
// pteTemp 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 pteTemp, CONVERTINFO *lpciInfo )
|
|
{
|
|
DWORD tkNow, tkDelta;
|
|
MIDIEVENT *pmeEvent;
|
|
char szTemp[256];
|
|
|
|
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 = pteTemp->tkEvent - tkCurrentTime;
|
|
|
|
// Event time is now current time on this track
|
|
tkCurrentTime = pteTemp->tkEvent;
|
|
|
|
if( bInsertTempo )
|
|
{
|
|
bInsertTempo = FALSE;
|
|
|
|
if( lpciInfo->dwMaxLength-lpciInfo->dwBytesRecorded < 3*sizeof(DWORD))
|
|
{
|
|
// Cleanup from our write operation
|
|
return( CONVERTERR_BUFFERFULL );
|
|
}
|
|
if( dwCurrentTempo )
|
|
{
|
|
pmeEvent->dwDeltaTime = 0;
|
|
pmeEvent->dwStreamID = 0;
|
|
pmeEvent->dwEvent = ( dwCurrentTempo * 100 ) / dwTempoMultiplier;
|
|
pmeEvent->dwEvent |= (((DWORD)MEVT_TEMPO ) << 24 ) | MEVT_F_SHORT;
|
|
|
|
lpciInfo->dwBytesRecorded += 3 * sizeof(DWORD);
|
|
pmeEvent += 3 * sizeof(DWORD);
|
|
}
|
|
}
|
|
|
|
if( pteTemp->byShortData[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 = ( pteTemp->byShortData[0] )
|
|
| (((DWORD)pteTemp->byShortData[1] ) << 8 )
|
|
| (((DWORD)pteTemp->byShortData[2] ) << 16 )
|
|
| MEVT_F_SHORT;
|
|
|
|
if((( pteTemp->byShortData[0] & 0xF0) == MIDI_CTRLCHANGE )
|
|
&& ( pteTemp->byShortData[1] == MIDICTRL_VOLUME ))
|
|
{
|
|
// If this is a volume change, generate a callback so we can grab
|
|
// the new volume for our cache
|
|
pmeEvent->dwEvent |= MEVT_F_CALLBACK;
|
|
}
|
|
lpciInfo->dwBytesRecorded += 3 *sizeof(DWORD);
|
|
}
|
|
else if(( pteTemp->byShortData[0] == MIDI_SYSEX )
|
|
|| ( pteTemp->byShortData[0] == MIDI_SYSEXEND ))
|
|
{
|
|
DebugPrint( "AddEventToStreamBuffer: Ignoring SysEx event." );
|
|
if( dwMallocBlocks )
|
|
{
|
|
free( pteTemp->pLongData );
|
|
dwMallocBlocks--;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Better be a meta event.
|
|
// BYTE byEvent
|
|
// BYTE byEventType
|
|
// VDWORD dwEventLength
|
|
// BYTE pLongEventData[dwEventLength]
|
|
//
|
|
assert( pteTemp->byShortData[0] == MIDI_META );
|
|
|
|
// The only meta-event we care about is change tempo
|
|
//
|
|
if( pteTemp->byShortData[1] != MIDI_META_TEMPO )
|
|
{
|
|
if( dwMallocBlocks )
|
|
{
|
|
free( pteTemp->pLongData );
|
|
dwMallocBlocks--;
|
|
}
|
|
return( CONVERTERR_METASKIP );
|
|
}
|
|
|
|
// We should have three bytes of parameter data...
|
|
assert( pteTemp->dwEventLength == 3 );
|
|
|
|
// Need 3 DWORD's: delta-t, stream-ID, event data
|
|
if( lpciInfo->dwMaxLength - lpciInfo->dwBytesRecorded < 3 *sizeof(DWORD))
|
|
{
|
|
// Cleanup the temporary event if necessary and return
|
|
if( dwMallocBlocks )
|
|
{
|
|
free( pteTemp->pLongData );
|
|
dwMallocBlocks--;
|
|
}
|
|
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 = ( pteTemp->pLongData[2] )
|
|
| (((DWORD)pteTemp->pLongData[1] ) << 8 )
|
|
| (((DWORD)pteTemp->pLongData[0] ) << 16 );
|
|
|
|
/* This next step has absolutely nothing to do with the conversion of a
|
|
* MIDI file to a stream, it's simply put here to add the functionality
|
|
* of the tempo slider. If you don't need this, be sure to remove the
|
|
* next two lines.
|
|
*/
|
|
dwCurrentTempo = pmeEvent->dwEvent;
|
|
pmeEvent->dwEvent = (pmeEvent->dwEvent * 100 ) / dwTempoMultiplier;
|
|
|
|
pmeEvent->dwEvent |= (((DWORD)MEVT_TEMPO ) << 24 ) | MEVT_F_SHORT;
|
|
|
|
dwBufferTickLength = ( ifs.dwTimeDivision * 1000 * BUFFER_TIME_LENGTH ) / dwCurrentTempo;
|
|
wsprintf( szTemp, "dwBufferTickLength = %lu", dwBufferTickLength );
|
|
DebugPrint( szTemp );
|
|
|
|
if( dwMallocBlocks )
|
|
{
|
|
free( pteTemp->pLongData );
|
|
dwMallocBlocks--;
|
|
}
|
|
lpciInfo->dwBytesRecorded += 3 *sizeof(DWORD);
|
|
}
|
|
|
|
return( FALSE );
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
static void ShowTrackError( PINTRACKSTATE ptsTrack, LPSTR lpszErr )
|
|
{
|
|
wsprintf( szTemp, "Track buffer offset %lu",
|
|
(DWORD)(ptsTrack->pTrackCurrent - ptsTrack->pTrackStart));
|
|
DebugPrint( szTemp );
|
|
wsprintf( szTemp, "Track total %lu Track left %lu",
|
|
ptsTrack->dwTrackLength, ptsTrack->dwLeftInBuffer );
|
|
DebugPrint( szTemp );
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
/*
|
|
* $Log: /H3/game/mstrconv.c $
|
|
*
|
|
* 1 2/27/97 4:04p Rjohnson
|
|
* Initial Revision
|
|
*/
|