- Added Gez's HMI/XMI division fixes, and partially the XMI tempo fix (not currently used).

SVN r3377 (trunk)
This commit is contained in:
Randy Heit 2012-02-21 20:19:25 +00:00
parent 91ba2ec404
commit 170284ad57
3 changed files with 47 additions and 7 deletions

View file

@ -637,6 +637,7 @@ protected:
int FindXMIDforms(const BYTE *chunk, int len, TrackInfo *songs) const; int FindXMIDforms(const BYTE *chunk, int len, TrackInfo *songs) const;
void FoundXMID(const BYTE *chunk, int len, TrackInfo *song) const; void FoundXMID(const BYTE *chunk, int len, TrackInfo *song) const;
void ScanForTempo(const TrackInfo *song);
bool SetMIDISubsong(int subsong); bool SetMIDISubsong(int subsong);
void DoInitialSetup(); void DoInitialSetup();
void DoRestart(); void DoRestart();

View file

@ -1,5 +1,5 @@
/* /*
** music_midi_midiout.cpp ** music_hmi_midiout.cpp
** Code to let ZDoom play HMI MIDI music through the MIDI streaming API. ** Code to let ZDoom play HMI MIDI music through the MIDI streaming API.
** **
**--------------------------------------------------------------------------- **---------------------------------------------------------------------------
@ -54,7 +54,7 @@
} }
// In song header // In song header
#define HMI_DIVISION_OFFSET 0xD2 #define HMI_DIVISION_OFFSET 0xD4
#define HMI_TRACK_COUNT_OFFSET 0xE4 #define HMI_TRACK_COUNT_OFFSET 0xE4
#define HMI_TRACK_DIR_PTR_OFFSET 0xE8 #define HMI_TRACK_DIR_PTR_OFFSET 0xE8
@ -202,7 +202,10 @@ void HMISong::SetupForHMI(int len)
} }
// The division is the number of pulses per quarter note (PPQN). // The division is the number of pulses per quarter note (PPQN).
Division = GetShort(MusHeader + HMI_DIVISION_OFFSET); // HMI files have two values here, a full value and a quarter value. Some games,
// notably Quarantines, have identical values for some reason, so it's safer to
// use the quarter value and multiply it by four than to trust the full value.
Division = GetShort(MusHeader + HMI_DIVISION_OFFSET) << 2;
InitialTempo = 4000000; InitialTempo = 4000000;
Tracks = new TrackInfo[NumTracks + 1]; Tracks = new TrackInfo[NumTracks + 1];

View file

@ -134,14 +134,16 @@ XMISong::XMISong (FILE *file, BYTE *musiccache, int len, EMidiDevice type)
{ {
return; return;
} }
// The division is the number of pulses per quarter note (PPQN).
// It is set by a specific MIDI event, but just in case these aren't used,
// set a default value. Specs indicate it should be 120.
Division = 120;
Songs = new TrackInfo[NumSongs]; Songs = new TrackInfo[NumSongs];
memset(Songs, 0, sizeof(*Songs) * NumSongs); memset(Songs, 0, sizeof(*Songs) * NumSongs);
FindXMIDforms(MusHeader, len, Songs); FindXMIDforms(MusHeader, len, Songs);
CurrSong = Songs; CurrSong = Songs;
DPrintf("XMI song count: %d\n", NumSongs); DPrintf("XMI song count: %d\n", NumSongs);
DPrintf("Divisions: %d\n", Division);
// The division is the number of pulses per quarter note (PPQN).
Division = 60;
} }
//========================================================================== //==========================================================================
@ -198,6 +200,8 @@ int XMISong::FindXMIDforms(const BYTE *chunk, int len, TrackInfo *songs) const
// IFF chunks are padded to even byte boundaries to avoid // IFF chunks are padded to even byte boundaries to avoid
// unaligned reads on 68k processors. // unaligned reads on 68k processors.
p += 8 + chunklen + (chunklen & 1); p += 8 + chunklen + (chunklen & 1);
// Avoid crashes from corrupt chunks which indicate a negative size.
if (chunklen < 0) p = len;
} }
return count; return count;
} }
@ -233,6 +237,37 @@ void XMISong::FoundXMID(const BYTE *chunk, int len, TrackInfo *song) const
} }
} }
//==========================================================================
//
// XMISong :: ScanForTempo
//
// Look through events chunk to find one that sets the tempo.
//
//==========================================================================
void XMISong::ScanForTempo(const TrackInfo *song)
{
const BYTE *chunk = song->EventChunk;
for (size_t q = 0; q < song->EventLen - 3; )
{
int r = q;
int evmark = chunk[r];
int evtype = chunk[r + 1];
int evlen = chunk[r + 2];
// Is it a tempo event? If so, length should be 3.
if (evmark == MIDI_META && evtype == MIDI_META_TEMPO && evlen == 3)
{
// Tempo is given in microseconds per quarter notes, so for a base of
// 120 per second, we have to divide by a million a multiply by 120 to
// get the PPQN. This is simplified by 40 to manipulate smaller values.
Tempo = (chunk[r + 3] << 16) | (chunk[r + 4] << 8) | chunk[r + 5];
Division = Tempo * 3 / 25000;
}
q += 3 + evlen;
}
}
//========================================================================== //==========================================================================
// //
// XMISong :: SetMIDISubsong // XMISong :: SetMIDISubsong
@ -304,7 +339,7 @@ bool XMISong::CheckDone()
// //
// XMISong :: MakeEvents // XMISong :: MakeEvents
// //
// Copies MIDI events from the SMF and puts them into a MIDI stream // Copies MIDI events from the XMI and puts them into a MIDI stream
// buffer. Returns the new position in the buffer. // buffer. Returns the new position in the buffer.
// //
//========================================================================== //==========================================================================
@ -542,6 +577,7 @@ DWORD *XMISong::SendCommand (DWORD *events, EventSource due, DWORD delay)
events[1] = 0; events[1] = 0;
events[2] = (MEVT_TEMPO << 24) | Tempo; events[2] = (MEVT_TEMPO << 24) | Tempo;
events += 3; events += 3;
Division = Tempo * 3 / 25000;
break; break;
} }
track->EventP += len; track->EventP += len;