449 lines
11 KiB
C++
449 lines
11 KiB
C++
#include <common/math.hpp>
|
|
#include <midiseq/miditrck.hpp>
|
|
|
|
bool MidiTrack::readTrack(void)
|
|
{
|
|
mBytesRead=0;
|
|
if(PureHeader::Unset==mTimingMethod)return false;
|
|
if(!mMidiFile.read(mTrack,sizeof(mTrack)))return false;
|
|
if(::strncmp(mTrack,"MTrk",sizeof(mTrack)))return false;
|
|
if(!mMidiFile.read(mLengthData))return false;
|
|
if(!mLengthData)return false;
|
|
readTime();
|
|
|
|
while(mBytesRead<mLengthData)
|
|
{
|
|
if(!mMidiFile.read(mEventType))return true;
|
|
mBytesRead++;
|
|
if(!handleEvent())return true;
|
|
if(mBytesRead>=mLengthData)break;
|
|
readTime();
|
|
if(NullEvent==mRunningEvent)mLastEventType=mEventType;
|
|
else mLastEventType=mRunningEvent;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void MidiTrack::readTime(void)
|
|
{
|
|
switch(mTimingMethod)
|
|
{
|
|
case PureHeader::DeltaTime :
|
|
case PureHeader::TimeCode :
|
|
mDeltaTime=readVarLength();
|
|
break;
|
|
}
|
|
}
|
|
|
|
WORD MidiTrack::handleEvent(void)
|
|
{
|
|
WORD returnCode;
|
|
|
|
switch(mEventType)
|
|
{
|
|
case MetaEvent :
|
|
mRunningEvent=NullEvent;
|
|
returnCode=handleMetaEvent();
|
|
break;
|
|
case SystemExclusiveOne :
|
|
case SystemExclusiveTwo :
|
|
mRunningEvent=NullEvent;
|
|
returnCode=handleSystemExclusiveEvent();
|
|
break;
|
|
default :
|
|
returnCode=handleMIDIChannelMessage();
|
|
break;
|
|
}
|
|
return returnCode;
|
|
}
|
|
|
|
WORD MidiTrack::isChannelMessage(BYTE eventType)const
|
|
{
|
|
BYTE status((eventType>>4)&0x0F);
|
|
|
|
if((status<=0x07)||(0x0F==status))
|
|
{
|
|
outDebug("[isChannelMessage] FAIL",eventType);
|
|
return false;
|
|
}
|
|
if((status>=0x08&&status<=0x0B)||(status==0x0E))return 2;
|
|
return 1;
|
|
}
|
|
|
|
WORD MidiTrack::handleMIDIChannelMessage(void)
|
|
{
|
|
WORD returnCode;
|
|
|
|
if(isRunningStatus(mEventType))
|
|
{
|
|
if(NullEvent==mRunningEvent)mRunningEvent=mLastEventType;
|
|
mLastData=mEventType;
|
|
mEventType=mLastEventType;
|
|
}
|
|
else mRunningEvent=NullEvent;
|
|
if(isChannelMessage(mEventType))returnCode=processMIDIChannelMessage();
|
|
else returnCode=FALSE;
|
|
return returnCode;
|
|
}
|
|
|
|
WORD MidiTrack::handleMetaEvent(void)
|
|
{
|
|
BYTE entryType;
|
|
WORD returnCode(TRUE);
|
|
|
|
if(!mMidiFile.read(entryType))return FALSE;
|
|
mBytesRead++;
|
|
switch(entryType)
|
|
{
|
|
case SequenceNumberEvent :
|
|
outDebug("[handleMetaEvent]SequenceNumberEvent");
|
|
returnCode=handleMetaSequenceNumberEvent();
|
|
break;
|
|
case TextEvent :
|
|
outDebug("[handleMetaEvent]TextEvent");
|
|
returnCode=handleMetaTextEvent();
|
|
break;
|
|
case CopyrightNotice :
|
|
outDebug("[handleMetaEvent]CopyrightNotice");
|
|
returnCode=handleMetaTextEvent();
|
|
break;
|
|
case SequenceTrackName :
|
|
outDebug("[handleMetaEvent]SequenceTrackName");
|
|
handleGenericMetaEvent();
|
|
break;
|
|
case InstrumentName :
|
|
outDebug("[handleMetaEvent]InstrumentName");
|
|
handleGenericMetaEvent();
|
|
break;
|
|
case Lyric :
|
|
outDebug("[handleMetaEvent]Lyric");
|
|
handleGenericMetaEvent();
|
|
break;
|
|
case Marker :
|
|
outDebug("[handleMetaEvent]Marker");
|
|
handleGenericMetaEvent();
|
|
break;
|
|
case CuePoint :
|
|
outDebug("[handleMetaEvent]CuePoint");
|
|
handleGenericMetaEvent();
|
|
break;
|
|
case ChannelPrefix :
|
|
outDebug("[handleMetaEvent]ChannelPrefix");
|
|
handleChannelPrefix();
|
|
break;
|
|
case EndOfTrack :
|
|
outDebug("[handleMetaEvent]EndOfTrack");
|
|
handleGenericMetaEvent();
|
|
endOfTrack();
|
|
returnCode=FALSE;
|
|
break;
|
|
case SetTempo :
|
|
outDebug("[handleMetaEvent]SetTempo");
|
|
handleSetTempoEvent();
|
|
break;
|
|
case SMPTEFormatSpec :
|
|
outDebug("[handleMetaEvent]SMTPFormatSpec");
|
|
handleSMPTEFormatEvent();
|
|
break;
|
|
case TimeSignature :
|
|
outDebug("[handleMetaEvent]TimeSignature");
|
|
handleTimeSignatureEvent();
|
|
break;
|
|
case KeySignature :
|
|
outDebug("[handleMetaEvent]KeySignature");
|
|
handleGenericMetaEvent();
|
|
break;
|
|
case SequencerSpecificMetaEvent :
|
|
outDebug("[handleMetaEvent]SequencerSpecificMetaEvent");
|
|
handleGenericMetaEvent();
|
|
break;
|
|
default :
|
|
handleGenericMetaEvent();
|
|
break;
|
|
}
|
|
return returnCode;
|
|
}
|
|
|
|
WORD MidiTrack::handleSystemExclusiveEvent(void)
|
|
{
|
|
return handleGenericMetaEvent();
|
|
}
|
|
|
|
WORD MidiTrack::handleMetaSequenceNumberEvent(void)
|
|
{
|
|
WORD sequenceNumber;
|
|
|
|
if(!mMidiFile.read(sequenceNumber))return FALSE;
|
|
mBytesRead++;
|
|
return TRUE;
|
|
}
|
|
|
|
WORD MidiTrack::handleMetaTextEvent(void)
|
|
{
|
|
BYTE textLength;
|
|
BYTE textByte;
|
|
String strText;
|
|
|
|
if(!mMidiFile.read(textLength))return FALSE;
|
|
mBytesRead++;
|
|
for(short index=0;index<textLength;index++)
|
|
{
|
|
if(!mMidiFile.read(textByte))return FALSE;
|
|
strText+=textByte;
|
|
mBytesRead++;
|
|
}
|
|
textMessage(strText);
|
|
return TRUE;
|
|
}
|
|
|
|
WORD MidiTrack::handleSetTempoEvent(void)
|
|
{
|
|
DWORD microsecsPerQtrNote(0L);
|
|
BYTE tempoBytes[TempoEventLength];
|
|
short entryLength;
|
|
|
|
entryLength=(short)readVarLength();
|
|
if(sizeof(tempoBytes)!=entryLength)return FALSE;
|
|
if(!mMidiFile.read(tempoBytes,sizeof(tempoBytes)))return FALSE;
|
|
microsecsPerQtrNote+=(DWORD)tempoBytes[0]*65536L;
|
|
microsecsPerQtrNote+=(DWORD)tempoBytes[1]*256L;
|
|
microsecsPerQtrNote+=(DWORD)tempoBytes[2];
|
|
outDebug(String("[MidiTrack::handleSetTempoEvent] microsecsPerQtrNote=")+String().fromInt(microsecsPerQtrNote));
|
|
setTempo(microsecsPerQtrNote);
|
|
return TRUE;
|
|
}
|
|
|
|
WORD MidiTrack::handleTimeSignatureEvent(void)
|
|
{
|
|
BYTE tsBytes[TimeInfo::LengthTimeInfo];
|
|
|
|
if(TimeInfo::LengthTimeInfo!=readVarLength())return FALSE;
|
|
if(!mMidiFile.read(tsBytes,sizeof(tsBytes)))return FALSE;
|
|
TimeInfo midiTimeSignature(tsBytes[0],Math::power(2,tsBytes[1]),tsBytes[2],tsBytes[3]);
|
|
timeSignature(midiTimeSignature);
|
|
outDebug("[handleTimeSignatureEvent]"+midiTimeSignature.toString());
|
|
return TRUE;
|
|
}
|
|
|
|
WORD MidiTrack::handleSMPTEFormatEvent(void)
|
|
{
|
|
SMPTEFormat smpteFormatInfo;
|
|
if(SMPTEFormat::SMPTEFormatLength!=readVarLength())
|
|
{
|
|
outDebug("[handleSMPTEFormatEvent] Unexpected SMPTEFormatLength");
|
|
return FALSE;
|
|
}
|
|
smpteFormatInfo<<mMidiFile;
|
|
smpteFormat(smpteFormatInfo);
|
|
outDebug("[handleSMPTEFormatEvent]"+smpteFormatInfo.toString());
|
|
return TRUE;
|
|
}
|
|
|
|
WORD MidiTrack::handleChannelPrefix(void)
|
|
{
|
|
BYTE charByte;
|
|
short entryLength;
|
|
|
|
entryLength=readVarLength();
|
|
for(short index=0;index<entryLength;index++){if(!mMidiFile.read(charByte))return FALSE;mBytesRead++;}
|
|
return TRUE;
|
|
}
|
|
|
|
WORD MidiTrack::handleGenericMetaEvent(void)
|
|
{
|
|
BYTE charByte;
|
|
short entryLength;
|
|
|
|
entryLength=(short)readVarLength();
|
|
for(short index=0;index<entryLength;index++){if(!mMidiFile.read(charByte))return FALSE;mBytesRead++;}
|
|
return TRUE;
|
|
}
|
|
|
|
WORD MidiTrack::processMIDIChannelMessage(void)
|
|
{
|
|
WORD returnCode(TRUE);
|
|
BYTE channel;
|
|
BYTE messageDataOne;
|
|
BYTE messageDataTwo;
|
|
BYTE requiredDataBytes;
|
|
|
|
channel=mEventType&0x0F;
|
|
requiredDataBytes=isChannelMessage(mEventType);
|
|
switch(mEventType&0xF0)
|
|
{
|
|
case MIDIProgramChange :
|
|
if(NullEvent==mRunningEvent)mMidiFile.read(messageDataOne);
|
|
else messageDataOne=mLastData;
|
|
messageDataOne&=0x7F;
|
|
midiChannelMessage(PureEvent(mEventType,mDeltaTime,channel,messageDataOne,0));
|
|
if(NullEvent!=mRunningEvent)mBytesRead++;
|
|
break;
|
|
case MIDINoteOff :
|
|
if(PureNote::PureNoteLength!=requiredDataBytes){returnCode=FALSE;break;}
|
|
if(NullEvent==mRunningEvent)mNoteOff<<mMidiFile;
|
|
else
|
|
{
|
|
mMidiFile.read(messageDataOne);
|
|
mNoteOff.pitch(mLastData);
|
|
mNoteOff.velocity(messageDataOne);
|
|
channel=mLastChannel;
|
|
}
|
|
mNoteOff.pitch(mNoteOff.pitch()&0x7F);
|
|
mNoteOff.velocity(mNoteOff.velocity()&0x7F);
|
|
midiChannelMessage(PureEvent(mEventType,mDeltaTime,channel,mNoteOff.pitch(),mNoteOff.velocity()));
|
|
if(NullEvent==mRunningEvent)mBytesRead+=PureNote::PureNoteLength;
|
|
else mBytesRead+=PureNote::PureNoteLength-1;
|
|
break;
|
|
case MIDINoteOn :
|
|
if(PureNote::PureNoteLength!=requiredDataBytes){returnCode=FALSE;break;}
|
|
if(NullEvent==mRunningEvent)mNoteOn<<mMidiFile;
|
|
else
|
|
{
|
|
mMidiFile.read(messageDataOne);
|
|
mNoteOn.pitch(mLastData);
|
|
mNoteOn.velocity(messageDataOne);
|
|
channel=mLastChannel;
|
|
}
|
|
mNoteOn.pitch(mNoteOn.pitch()&0x7F);
|
|
mNoteOn.velocity(mNoteOn.velocity()&0x7F);
|
|
midiChannelMessage(PureEvent(mEventType,mDeltaTime,channel,mNoteOn.pitch(),mNoteOn.velocity()));
|
|
if(NullEvent==mRunningEvent)mBytesRead+=PureNote::PureNoteLength;
|
|
else mBytesRead+=PureNote::PureNoteLength-1;
|
|
break;
|
|
case MIDIKeyPressure :
|
|
if(2!=requiredDataBytes){returnCode=FALSE;break;}
|
|
if(NullEvent==mRunningEvent)
|
|
{
|
|
mMidiFile.read(messageDataOne);
|
|
mMidiFile.read(messageDataTwo);
|
|
}
|
|
else
|
|
{
|
|
messageDataOne=mLastData;
|
|
mMidiFile.read(messageDataTwo);
|
|
}
|
|
messageDataOne&=0x7F;
|
|
messageDataTwo&=0x7F;
|
|
midiChannelMessage(PureEvent(mEventType,mDeltaTime,channel,messageDataOne,messageDataTwo));
|
|
if(NullEvent==mRunningEvent)mBytesRead+=requiredDataBytes;
|
|
else mBytesRead++;
|
|
break;
|
|
case MIDIParameter :
|
|
if(2!=requiredDataBytes){returnCode=FALSE;break;}
|
|
if(NullEvent==mRunningEvent)
|
|
{
|
|
mMidiFile.read(messageDataOne);
|
|
mMidiFile.read(messageDataTwo);
|
|
}
|
|
else
|
|
{
|
|
messageDataOne=mLastData;
|
|
mMidiFile.read(messageDataTwo);
|
|
}
|
|
messageDataOne&=0x7F;
|
|
messageDataTwo&=0x7F;
|
|
midiChannelMessage(PureEvent(mEventType,mDeltaTime,channel,messageDataOne,messageDataTwo));
|
|
if(NullEvent==mRunningEvent)mBytesRead+=requiredDataBytes;
|
|
else mBytesRead++;
|
|
break;
|
|
case MIDIPitchBend :
|
|
if(2!=requiredDataBytes){returnCode=FALSE;break;}
|
|
if(NullEvent==mRunningEvent)
|
|
{
|
|
mMidiFile.read(messageDataOne);
|
|
mMidiFile.read(messageDataTwo);
|
|
}
|
|
else
|
|
{
|
|
messageDataOne=mLastData;
|
|
mMidiFile.read(messageDataTwo);
|
|
}
|
|
messageDataOne&=0x7F;
|
|
messageDataTwo&=0x7F;
|
|
midiChannelMessage(PureEvent(mEventType,mDeltaTime,channel,messageDataOne,messageDataTwo));
|
|
if(NullEvent==mRunningEvent)mBytesRead+=requiredDataBytes;
|
|
else mBytesRead++;
|
|
break;
|
|
case MIDIChannelPressure :
|
|
if(1!=requiredDataBytes){returnCode=FALSE;break;}
|
|
if(NullEvent==mRunningEvent)mMidiFile.read(messageDataOne);
|
|
else messageDataOne=mLastData;
|
|
messageDataOne&=0x7F;
|
|
midiChannelMessage(PureEvent(mEventType,mDeltaTime,channel,messageDataOne,0));
|
|
if(NullEvent==mRunningEvent)mBytesRead++;
|
|
break;
|
|
default :
|
|
returnCode=FALSE;
|
|
break;
|
|
}
|
|
mLastChannel=channel;
|
|
return returnCode;
|
|
}
|
|
|
|
DWORD MidiTrack::readVarLength(void)
|
|
{
|
|
DWORD valueData(0L);
|
|
BYTE byteValue;
|
|
|
|
while(TRUE)
|
|
{
|
|
mMidiFile.read(byteValue);
|
|
mBytesRead++;
|
|
valueData=(valueData*0x80)+(byteValue&0x7F);
|
|
if(!(byteValue&0x80))break;
|
|
}
|
|
return valueData;
|
|
}
|
|
|
|
void MidiTrack::outDebug(const String &message,BYTE data)const
|
|
{
|
|
if(!mIsDebug)return;
|
|
String strData;
|
|
String strHeader=String("[MidiTrack]");
|
|
::sprintf(strData.str(),"%d (0x%08lx)",data,data);
|
|
::OutputDebugString(strHeader+message+String(" DATA=")+strData+String("\n"));
|
|
}
|
|
|
|
void MidiTrack::outDebug(const String &message)const
|
|
{
|
|
if(!mIsDebug)return;
|
|
String strHeader=String("[MidiTrack]");
|
|
::OutputDebugString(strHeader+message+String("\n"));
|
|
}
|
|
|
|
// virtuals
|
|
|
|
void MidiTrack::setTempo(DWORD /*microsecondsPerQuarterNote*/)
|
|
{
|
|
return;
|
|
}
|
|
|
|
void MidiTrack::midiChannelMessage(PureEvent &/*channelEvent*/)
|
|
{
|
|
return;
|
|
}
|
|
|
|
void MidiTrack::timeSignature(const TimeInfo &/*someTimeInfo*/)
|
|
{
|
|
return;
|
|
}
|
|
|
|
void MidiTrack::smpteFormat(const SMPTEFormat &/*someSMPTEFormat*/)
|
|
{
|
|
return;
|
|
}
|
|
|
|
void MidiTrack::textMessage(const String &/*strText*/)
|
|
{
|
|
return;
|
|
}
|
|
|
|
void MidiTrack::endOfTrack(void)
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
|