system events shouldn't reset the running status (fixes crash on MIDI string events)
add handling of time signatures
This commit is contained in:
@@ -137,6 +137,12 @@ public:
|
|||||||
double tickSeconds;
|
double tickSeconds;
|
||||||
};
|
};
|
||||||
std::vector<TempoChange> tempoEvents_;
|
std::vector<TempoChange> tempoEvents_;
|
||||||
|
struct TimeSignatureChange {
|
||||||
|
unsigned long count;
|
||||||
|
unsigned long numerator;
|
||||||
|
unsigned long denominator;
|
||||||
|
};
|
||||||
|
std::vector<TimeSignatureChange> timeSignatureEvents_;
|
||||||
std::vector<unsigned long> trackCounters_;
|
std::vector<unsigned long> trackCounters_;
|
||||||
std::vector<unsigned int> trackTempoIndex_;
|
std::vector<unsigned int> trackTempoIndex_;
|
||||||
|
|
||||||
@@ -176,9 +182,9 @@ void MidiFileIn :: Load( RString fileName )
|
|||||||
char chunkType[4];
|
char chunkType[4];
|
||||||
int32_t length;
|
int32_t length;
|
||||||
if( !file_.Read( chunkType, 4 ) )
|
if( !file_.Read( chunkType, 4 ) )
|
||||||
goto error;
|
FAIL_M( "NotesLoaderMidi" );
|
||||||
if( !file_.Read( &length, 4 ) )
|
if( !file_.Read( &length, 4 ) )
|
||||||
goto error;
|
FAIL_M( "NotesLoaderMidi" );
|
||||||
length = Swap32BE( length );
|
length = Swap32BE( length );
|
||||||
if( strncmp( chunkType, "MThd", 4 ) || ( length != 6 ) )
|
if( strncmp( chunkType, "MThd", 4 ) || ( length != 6 ) )
|
||||||
{
|
{
|
||||||
@@ -188,7 +194,7 @@ void MidiFileIn :: Load( RString fileName )
|
|||||||
// Read the MIDI file format.
|
// Read the MIDI file format.
|
||||||
int16_t data;
|
int16_t data;
|
||||||
if( !file_.Read( &data, 2 ) )
|
if( !file_.Read( &data, 2 ) )
|
||||||
goto error;
|
FAIL_M( "NotesLoaderMidi" );
|
||||||
data = Swap16BE( data );
|
data = Swap16BE( data );
|
||||||
if( data < 0 || data > 2 )
|
if( data < 0 || data > 2 )
|
||||||
{
|
{
|
||||||
@@ -198,7 +204,7 @@ void MidiFileIn :: Load( RString fileName )
|
|||||||
|
|
||||||
// Read the number of tracks.
|
// Read the number of tracks.
|
||||||
if( !file_.Read( &data, 2 ) )
|
if( !file_.Read( &data, 2 ) )
|
||||||
goto error;
|
FAIL_M( "NotesLoaderMidi" );
|
||||||
data = Swap16BE( data );
|
data = Swap16BE( data );
|
||||||
if( format_ == 0 && data != 1 )
|
if( format_ == 0 && data != 1 )
|
||||||
{
|
{
|
||||||
@@ -208,7 +214,7 @@ void MidiFileIn :: Load( RString fileName )
|
|||||||
|
|
||||||
// Read the beat division.
|
// Read the beat division.
|
||||||
if( !file_.Read( &data, 2 ) )
|
if( !file_.Read( &data, 2 ) )
|
||||||
goto error;
|
FAIL_M( "NotesLoaderMidi" );
|
||||||
data = Swap16BE( data );
|
data = Swap16BE( data );
|
||||||
division_ = (int)data;
|
division_ = (int)data;
|
||||||
double tickrate;
|
double tickrate;
|
||||||
@@ -235,11 +241,11 @@ void MidiFileIn :: Load( RString fileName )
|
|||||||
for( unsigned int i=0; i<nTracks_; i++ )
|
for( unsigned int i=0; i<nTracks_; i++ )
|
||||||
{
|
{
|
||||||
if( !file_.Read( chunkType, 4 ) )
|
if( !file_.Read( chunkType, 4 ) )
|
||||||
goto error;
|
FAIL_M( "NotesLoaderMidi" );
|
||||||
if( strncmp( chunkType, "MTrk", 4 ) )
|
if( strncmp( chunkType, "MTrk", 4 ) )
|
||||||
goto error;
|
FAIL_M( "NotesLoaderMidi" );
|
||||||
if( !file_.Read( &length, 4 ) )
|
if( !file_.Read( &length, 4 ) )
|
||||||
goto error;
|
FAIL_M( "NotesLoaderMidi" );
|
||||||
length = Swap32BE( length );
|
length = Swap32BE( length );
|
||||||
trackLengths_.push_back( length );
|
trackLengths_.push_back( length );
|
||||||
trackOffsets_.push_back( (long) file_.Tell() );
|
trackOffsets_.push_back( (long) file_.Tell() );
|
||||||
@@ -253,10 +259,21 @@ void MidiFileIn :: Load( RString fileName )
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Save the initial tickSeconds parameter.
|
// Save the initial tickSeconds parameter.
|
||||||
TempoChange tempoEvent;
|
{
|
||||||
tempoEvent.count = 0;
|
TempoChange tempoEvent;
|
||||||
tempoEvent.tickSeconds = tickSeconds_[0];
|
tempoEvent.count = 0;
|
||||||
tempoEvents_.push_back( tempoEvent );
|
tempoEvent.tickSeconds = tickSeconds_[0];
|
||||||
|
tempoEvents_.push_back( tempoEvent );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save the initial tickSeconds parameter.
|
||||||
|
{
|
||||||
|
TimeSignatureChange timeSignatureEvent;
|
||||||
|
timeSignatureEvent.count = 0;
|
||||||
|
timeSignatureEvent.numerator = 4;
|
||||||
|
timeSignatureEvent.denominator = 4;
|
||||||
|
timeSignatureEvents_.push_back( timeSignatureEvent );
|
||||||
|
}
|
||||||
|
|
||||||
// If format 1 and not using time code, parse and save the tempo map
|
// If format 1 and not using time code, parse and save the tempo map
|
||||||
// on track 0.
|
// on track 0.
|
||||||
@@ -274,15 +291,32 @@ void MidiFileIn :: Load( RString fileName )
|
|||||||
if( ( event.size() == 6 ) && ( event[0] == 0xff ) &&
|
if( ( event.size() == 6 ) && ( event[0] == 0xff ) &&
|
||||||
( event[1] == 0x51 ) && ( event[2] == 0x03 ) )
|
( event[1] == 0x51 ) && ( event[2] == 0x03 ) )
|
||||||
{
|
{
|
||||||
tempoEvent.count = count;
|
TempoChange tempoEvent;
|
||||||
unsigned long value = ( event[3] << 16 ) + ( event[4] << 8 ) + event[5];
|
tempoEvent.count = count;
|
||||||
tempoEvent.tickSeconds = (double) (0.000001 * value / tickrate);
|
unsigned long value = ( event[3] << 16 ) + ( event[4] << 8 ) + event[5];
|
||||||
if( count > tempoEvents_.back().count )
|
tempoEvent.tickSeconds = (double) (0.000001 * value / tickrate);
|
||||||
tempoEvents_.push_back( tempoEvent );
|
if( count > tempoEvents_.back().count )
|
||||||
else
|
tempoEvents_.push_back( tempoEvent );
|
||||||
tempoEvents_.back() = tempoEvent;
|
else
|
||||||
}
|
tempoEvents_.back() = tempoEvent;
|
||||||
count += getNextEvent( &event, 0 );
|
}
|
||||||
|
|
||||||
|
if( ( event.size() == 7 ) && ( event[0] == 0xff ) &&
|
||||||
|
( event[1] == 0x58 ) && ( event[2] == 0x04 ) )
|
||||||
|
{
|
||||||
|
TimeSignatureChange timeSignatureEvent;
|
||||||
|
timeSignatureEvent.count = count;
|
||||||
|
timeSignatureEvent.numerator = event[3];
|
||||||
|
timeSignatureEvent.denominator = (int)pow( 2, (int)event[4] );
|
||||||
|
//unsigned long metronome = event[5]; // not currently used
|
||||||
|
//unsigned long u32nds = event[6]; // not currently used
|
||||||
|
if( count > timeSignatureEvents_.back().count )
|
||||||
|
timeSignatureEvents_.push_back( timeSignatureEvent );
|
||||||
|
else
|
||||||
|
timeSignatureEvents_.back() = timeSignatureEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
count += getNextEvent( &event, 0 );
|
||||||
}
|
}
|
||||||
rewindTrack( 0 );
|
rewindTrack( 0 );
|
||||||
for( unsigned int i=0; i<nTracks_; i++ )
|
for( unsigned int i=0; i<nTracks_; i++ )
|
||||||
@@ -295,9 +329,6 @@ void MidiFileIn :: Load( RString fileName )
|
|||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
error:
|
|
||||||
FAIL_M( "MidiFileIn: error reading from file (" + fileName + ")." );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MidiFileIn :: ~MidiFileIn()
|
MidiFileIn :: ~MidiFileIn()
|
||||||
@@ -378,26 +409,28 @@ unsigned long MidiFileIn :: getNextEvent( std::vector<unsigned char> *event, uns
|
|||||||
// Read the event delta time.
|
// Read the event delta time.
|
||||||
file_.Seek( trackPointers_[track] );
|
file_.Seek( trackPointers_[track] );
|
||||||
if( !readVariableLength( &ticks ) )
|
if( !readVariableLength( &ticks ) )
|
||||||
goto error;
|
FAIL_M( "NotesLoaderMidi" );
|
||||||
|
|
||||||
// Parse the event stream to determine the event length.
|
// Parse the event stream to determine the event length.
|
||||||
unsigned char c;
|
unsigned char c;
|
||||||
if( !file_.Read( (char *)&c, 1 ) )
|
if( !file_.Read( (char *)&c, 1 ) )
|
||||||
goto error;
|
FAIL_M( "NotesLoaderMidi" );
|
||||||
switch( c )
|
switch( c )
|
||||||
{
|
{
|
||||||
case 0xFF: // A Meta-Event
|
case 0xFF: // A Meta-Event
|
||||||
unsigned long position;
|
unsigned long position;
|
||||||
trackStatus_[track] = 0;
|
|
||||||
|
// RealTime Category messages (ie, Status of 0xF8 to 0xFF) do not affect running status
|
||||||
|
|
||||||
event->push_back( c );
|
event->push_back( c );
|
||||||
if( !file_.Read( (char *)&c, 1 ) )
|
if( !file_.Read( (char *)&c, 1 ) )
|
||||||
goto error;
|
FAIL_M( "NotesLoaderMidi" );
|
||||||
event->push_back( c );
|
event->push_back( c );
|
||||||
if( format_ != 1 && ( c == 0x51 ) )
|
if( format_ != 1 && ( c == 0x51 ) )
|
||||||
isTempoEvent = true;
|
isTempoEvent = true;
|
||||||
position = file_.Tell();
|
position = file_.Tell();
|
||||||
if( !readVariableLength( &bytes ) )
|
if( !readVariableLength( &bytes ) )
|
||||||
goto error;
|
FAIL_M( "NotesLoaderMidi" );
|
||||||
bytes += ( (unsigned long)file_.Tell() - position );
|
bytes += ( (unsigned long)file_.Tell() - position );
|
||||||
file_.Seek( position );
|
file_.Seek( position );
|
||||||
break;
|
break;
|
||||||
@@ -408,7 +441,7 @@ unsigned long MidiFileIn :: getNextEvent( std::vector<unsigned char> *event, uns
|
|||||||
event->push_back( c );
|
event->push_back( c );
|
||||||
position = file_.Tell();
|
position = file_.Tell();
|
||||||
if( !readVariableLength( &bytes ) )
|
if( !readVariableLength( &bytes ) )
|
||||||
goto error;
|
FAIL_M( "NotesLoaderMidi" );
|
||||||
bytes += ( (unsigned long)file_.Tell() - position );
|
bytes += ( (unsigned long)file_.Tell() - position );
|
||||||
file_.Seek( position );
|
file_.Seek( position );
|
||||||
break;
|
break;
|
||||||
@@ -417,7 +450,7 @@ unsigned long MidiFileIn :: getNextEvent( std::vector<unsigned char> *event, uns
|
|||||||
if( c & 0x80 ) // MIDI status byte
|
if( c & 0x80 ) // MIDI status byte
|
||||||
{
|
{
|
||||||
if( c > 0xF0 )
|
if( c > 0xF0 )
|
||||||
goto error;
|
FAIL_M( "NotesLoaderMidi" );
|
||||||
trackStatus_[track] = c;
|
trackStatus_[track] = c;
|
||||||
event->push_back( c );
|
event->push_back( c );
|
||||||
c &= 0xF0;
|
c &= 0xF0;
|
||||||
@@ -436,7 +469,7 @@ unsigned long MidiFileIn :: getNextEvent( std::vector<unsigned char> *event, uns
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
goto error;
|
FAIL_M( "NotesLoaderMidi" );
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -446,7 +479,7 @@ unsigned long MidiFileIn :: getNextEvent( std::vector<unsigned char> *event, uns
|
|||||||
for( unsigned long i=0; i<bytes; i++ )
|
for( unsigned long i=0; i<bytes; i++ )
|
||||||
{
|
{
|
||||||
if( !file_.Read( (char *)&c, 1 ) )
|
if( !file_.Read( (char *)&c, 1 ) )
|
||||||
goto error;
|
FAIL_M( "NotesLoaderMidi" );
|
||||||
event->push_back( c );
|
event->push_back( c );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -477,10 +510,6 @@ unsigned long MidiFileIn :: getNextEvent( std::vector<unsigned char> *event, uns
|
|||||||
trackPointers_[track] = file_.Tell();
|
trackPointers_[track] = file_.Tell();
|
||||||
|
|
||||||
return ticks;
|
return ticks;
|
||||||
|
|
||||||
error:
|
|
||||||
FAIL_M( "MidiFileIn::getNextEvent: file read error!" );
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned long MidiFileIn :: getNextMidiEvent( std::vector<unsigned char> *midiEvent, unsigned int track )
|
unsigned long MidiFileIn :: getNextMidiEvent( std::vector<unsigned char> *midiEvent, unsigned int track )
|
||||||
@@ -632,7 +661,17 @@ static bool LoadFromMidi( const RString &sPath, Song &songOut )
|
|||||||
double fSecondsPerBeat = (iter->tickSeconds * GUITAR_MIDI_COUNTS_PER_BEAT);
|
double fSecondsPerBeat = (iter->tickSeconds * GUITAR_MIDI_COUNTS_PER_BEAT);
|
||||||
bpmSeg.m_fBPS = 1 / fSecondsPerBeat;
|
bpmSeg.m_fBPS = 1 / fSecondsPerBeat;
|
||||||
|
|
||||||
songOut.AddBPMSegment( bpmSeg );
|
songOut.m_Timing.AddBPMSegment( bpmSeg );
|
||||||
|
}
|
||||||
|
|
||||||
|
FOREACH_CONST( MidiFileIn::TimeSignatureChange, midi.timeSignatureEvents_, iter )
|
||||||
|
{
|
||||||
|
TimeSignatureSegment seg;
|
||||||
|
seg.m_iStartRow = MidiCountToNoteRow( iter->count );
|
||||||
|
seg.m_iNumerator = iter->numerator;
|
||||||
|
seg.m_iDenominator = iter->denominator;
|
||||||
|
|
||||||
|
songOut.m_Timing.AddTimeSignatureSegment( seg );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -644,7 +683,7 @@ static bool LoadFromMidi( const RString &sPath, Song &songOut )
|
|||||||
|
|
||||||
{
|
{
|
||||||
std::vector<unsigned char> event;
|
std::vector<unsigned char> event;
|
||||||
int iTrack = 1; // this track should be named "T1 GEMS"
|
int iTrack = 1; // this track should be named "T1 GEMS" or "PART GUITAR"
|
||||||
unsigned long count = midi.getNextEvent( &event, iTrack );
|
unsigned long count = midi.getNextEvent( &event, iTrack );
|
||||||
double fSeconds = midi.getTickSeconds( iTrack );
|
double fSeconds = midi.getTickSeconds( iTrack );
|
||||||
while( event.size() )
|
while( event.size() )
|
||||||
@@ -671,6 +710,7 @@ static bool LoadFromMidi( const RString &sPath, Song &songOut )
|
|||||||
if( NoteNumberToDifficultyAndNoteNumberType( uNoteNumber, gd, nnt ) )
|
if( NoteNumberToDifficultyAndNoteNumberType( uNoteNumber, gd, nnt ) )
|
||||||
{
|
{
|
||||||
MidiEvent event = { count, midiEventType };
|
MidiEvent event = { count, midiEventType };
|
||||||
|
//float fBeat = NoteRowToBeat( MidiCountToNoteRow(count) );
|
||||||
vMidiEvent[gd][nnt].push_back( event );
|
vMidiEvent[gd][nnt].push_back( event );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -723,6 +763,7 @@ static bool LoadFromMidi( const RString &sPath, Song &songOut )
|
|||||||
*/
|
*/
|
||||||
MidiEventType midiEventType = iter->midiEventType;
|
MidiEventType midiEventType = iter->midiEventType;
|
||||||
long count = iter->count;
|
long count = iter->count;
|
||||||
|
//float fBeat = NoteRowToBeat( MidiCountToNoteRow(count) );
|
||||||
bool bNoteHandled = false;
|
bool bNoteHandled = false;
|
||||||
long length = count - countOfLastNote;
|
long length = count - countOfLastNote;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user