void HMISong :: DoRestart() { int i; // Set initial state. FakeTrack = &Tracks[NumTracks]; NoteOffs.Clear(); for (i = 0; i <= NumTracks; ++i) { Tracks[i].TrackP = 0; Tracks[i].Finished = false; Tracks[i].RunningStatus = 0; Tracks[i].PlayedTime = 0; } ProcessInitialMetaEvents (); for (i = 0; i < NumTracks; ++i) { Tracks[i].Delay = ReadVarLen(&Tracks[i]); } Tracks[i].Delay = 0; // for the FakeTrack Tracks[i].Enabled = true; TrackDue = Tracks; TrackDue = FindNextDue(); }
bool PS1Seq::ReadEvent(void) { uint32_t beginOffset = curOffset; uint32_t delta = ReadVarLen(curOffset); if (curOffset >= rawfile->size()) return false; AddTime(delta); uint8_t status_byte = GetByte(curOffset++); //if (status_byte == 0) //Jump Relative //{ // short relOffset = (short)GetShortBE(curOffset); // AddGenericEvent(beginOffset, 4, L"Jump Relative", NULL, BG_CLR_PINK); // curOffset += relOffset; // curOffset += 4; //skip the first 4 bytes (no idea) // SetPPQN(GetShortBE(curOffset)); // curOffset += 2; // AddTempo(curOffset, 3, GetWordBE(curOffset-1) & 0xFFFFFF); // curOffset += 3; // uint8_t numer = GetByte(curOffset++); // uint8_t denom = GetByte(curOffset++); // if (numer == 0 || numer > 32) //sanity check // return false; // AddTimeSig(curOffset-2, 2, numer, 1<<denom, GetPPQN()); // SetEventsOffset(offset() + 0x0F); //} // else if (status_byte <= 0x7F) // Running Status { if (status_byte == 0) // some games were ripped to PSF with the EndTrack event missing, so { if (GetWord(curOffset) == 0) //if we read a sequence of four 0 bytes, then just treat that return false; //as the end of the track } status_byte = runningStatus; curOffset--; } else runningStatus = status_byte; channel = status_byte&0x0F; SetCurTrack(channel); switch (status_byte & 0xF0) { case 0x90 : //note event key = GetByte(curOffset++); vel = GetByte(curOffset++); if (vel > 0) //if the velocity is > 0, it's a note on AddNoteOn(beginOffset, curOffset-beginOffset, key, vel); else //otherwise it's a note off AddNoteOff(beginOffset, curOffset-beginOffset, key); break; case 0xB0 : { uint8_t controlNum = GetByte(curOffset++); uint8_t value = GetByte(curOffset++); switch (controlNum) //control number { case 0 : //bank select AddGenericEvent(beginOffset, curOffset-beginOffset, L"Bank Select", L"", CLR_MISC); AddBankSelectNoItem(value); break; case 6 : //data entry AddGenericEvent(beginOffset, curOffset-beginOffset, L"NRPN Data Entry", L"", CLR_MISC); if (VGMSeq::readMode == READMODE_CONVERT_TO_MIDI) { pMidiTrack->AddControllerEvent(channel, controlNum, value); } break; case 7 : //volume AddVol(beginOffset, curOffset-beginOffset, value); break; case 10 : //pan AddPan(beginOffset, curOffset-beginOffset, value); break; case 11 : //expression AddExpression(beginOffset, curOffset-beginOffset, value); break; case 64 : //damper (hold) AddSustainEvent(beginOffset, curOffset-beginOffset, value); break; case 91 : //reverb depth (_SsContExternal) AddReverb(beginOffset, curOffset-beginOffset, value); break; case 98 : //(0x62) NRPN 1 (LSB) switch (value) { case 20 : AddGenericEvent(beginOffset, curOffset-beginOffset, L"NRPN 1 #20", L"", CLR_MISC); break; case 30 : AddGenericEvent(beginOffset, curOffset-beginOffset, L"NRPN 1 #30", L"", CLR_MISC); break; default: AddGenericEvent(beginOffset, curOffset-beginOffset, L"NRPN 1", L"", CLR_MISC); break; } if (VGMSeq::readMode == READMODE_CONVERT_TO_MIDI) { pMidiTrack->AddControllerEvent(channel, controlNum, value); } break; case 99 : //(0x63) NRPN 2 (MSB) switch (value) { case 20 : AddGenericEvent(beginOffset, curOffset-beginOffset, L"Loop Start", L"", CLR_LOOP); break; case 30 : AddGenericEvent(beginOffset, curOffset-beginOffset, L"Loop End", L"", CLR_LOOP); break; default: AddGenericEvent(beginOffset, curOffset-beginOffset, L"NRPN 2", L"", CLR_MISC); break; } if (VGMSeq::readMode == READMODE_CONVERT_TO_MIDI) { pMidiTrack->AddControllerEvent(channel, controlNum, value); } break; case 100 : //(0x64) RPN 1 (LSB), no effect? AddGenericEvent(beginOffset, curOffset-beginOffset, L"RPN 1", L"", CLR_MISC); if (VGMSeq::readMode == READMODE_CONVERT_TO_MIDI) { pMidiTrack->AddControllerEvent(channel, controlNum, value); } break; case 101 : //(0x65) RPN 2 (MSB), no effect? AddGenericEvent(beginOffset, curOffset-beginOffset, L"RPN 2", L"", CLR_MISC); if (VGMSeq::readMode == READMODE_CONVERT_TO_MIDI) { pMidiTrack->AddControllerEvent(channel, controlNum, value); } break; case 121 : //reset all controllers AddGenericEvent(beginOffset, curOffset-beginOffset, L"Reset All Controllers", L"", CLR_MISC); if (VGMSeq::readMode == READMODE_CONVERT_TO_MIDI) { pMidiTrack->AddControllerEvent(channel, controlNum, value); } break; default: AddGenericEvent(beginOffset, curOffset-beginOffset, L"Control Event", L"", CLR_UNKNOWN); if (VGMSeq::readMode == READMODE_CONVERT_TO_MIDI) { pMidiTrack->AddControllerEvent(channel, controlNum, value); } break; } } break; case 0xC0 : { uint8_t progNum = GetByte(curOffset++); AddProgramChange(beginOffset, curOffset-beginOffset, progNum); } break; case 0xE0 : { uint8_t hi = GetByte(curOffset++); uint8_t lo = GetByte(curOffset++); AddPitchBendMidiFormat(beginOffset, curOffset-beginOffset, hi, lo); } break; case 0xF0 : { if (status_byte == 0xFF) { switch (GetByte(curOffset++)) { case 0x51 : //tempo. This is different from SMF, where we'd expect a 51 then 03. Also, supports //a string of tempo events AddTempo(beginOffset, curOffset+3-beginOffset, (GetShortBE(curOffset) << 8) | GetByte(curOffset + 2)); curOffset += 3; break; case 0x2F : AddEndOfTrack(beginOffset, curOffset-beginOffset); return false; default : AddUnknown(beginOffset, curOffset-beginOffset, L"Meta Event"); return false; } } else { AddUnknown(beginOffset, curOffset-beginOffset); return false; } } break; default: AddUnknown(beginOffset, curOffset-beginOffset); return false; } return true; }
} events += 3; if (ReadVarLen == ReadVarLenHMI && (event & 0x70) == (MIDI_NOTEON & 0x70)) { // HMI note on events include the time until an implied note off event. NoteOffs.AddNoteOff(track->ReadVarLenHMI(), event & 0x0F, data1); } } else { // Skip SysEx events just because I don't want to bother with them. // The old MIDI player ignored them too, so this won't break // anything that played before. if (event == MIDI_SYSEX || event == MIDI_SYSEXEND) { len = ReadVarLen(track); track->TrackP += len; } else if (event == MIDI_META) { // It's a meta-event event = track->TrackBegin[track->TrackP++]; CHECK_FINISHED len = ReadVarLen(track); CHECK_FINISHED if (track->TrackP + len <= track->MaxTrackP) { switch (event) { case MIDI_META_EOT:
static T_BOOL ReadMidiTrackEvents(MIDI_INFO *midiInfo) { long delta = 0; unsigned char command; while(1) { delta += ReadVarLen(midiInfo); command = *midiInfo->midiData; midiInfo->midiData++; /* The followings belong to MIDI Control Events */ if ((command & 0xf0) == 0x80 || (command & 0xf0) == 0x90) { midiInfo->midiEvent[midiInfo->eventNum].delta = delta; midiInfo->midiEvent[midiInfo->eventNum].command = (command & 0xf0); midiInfo->midiEvent[midiInfo->eventNum].channel = (command & 0x0f); midiInfo->midiEvent[midiInfo->eventNum].note = *midiInfo->midiData; midiInfo->midiData++; midiInfo->midiEvent[midiInfo->eventNum].velocity = *midiInfo->midiData; midiInfo->midiData++; midiInfo->eventNum++; delta = 0; } else if (command < 0x80) /* running status: available in MIDI Control Events */ { midiInfo->midiEvent[midiInfo->eventNum].delta = delta; midiInfo->midiEvent[midiInfo->eventNum].command = midiInfo->midiEvent[midiInfo->eventNum-1].command; midiInfo->midiEvent[midiInfo->eventNum].channel = midiInfo->midiEvent[midiInfo->eventNum-1].channel; midiInfo->midiEvent[midiInfo->eventNum].note = command; midiInfo->midiEvent[midiInfo->eventNum].velocity = *midiInfo->midiData; midiInfo->midiData++; midiInfo->eventNum++; delta = 0; } else if ((command & 0xf0) == 0xa0 || (command & 0xf0) == 0xb0 || (command & 0xf0) == 0xe0) { /* skip 2 bytes */ command = *midiInfo->midiData; midiInfo->midiData++; command = *midiInfo->midiData; midiInfo->midiData++; } else if ((command & 0xf0) == 0xc0 || (command & 0xf0) == 0xd0) { /* skip 1 byte */ command = *midiInfo->midiData; midiInfo->midiData++; } /* The followings belong to SysEx Events */ else if (command == 0xf0 || command == 0xf7) { unsigned long length; length = ReadVarLen(midiInfo); while (length--) { command = *midiInfo->midiData; midiInfo->midiData++; } } /* The followings belong to Meta Events */ else if (command == 0xff) { unsigned long length; unsigned char metatype; metatype = *midiInfo->midiData; midiInfo->midiData++; length = ReadVarLen(midiInfo); // length = *midiInfo->midiData; // midiInfo->midiData++; if (metatype == 0x2f) /* end of track */ { // midiInfo->midiEvent[midiInfo->eventNum].delta = delta; // midiInfo->midiEvent[midiInfo->eventNum].command = metatype; // midiInfo->eventNum++; break; } else if (metatype == 0x51) { T_U8 strTemp[10]; memcpy(strTemp, midiInfo->midiData, (T_U16)length); midiInfo->midiData += length; midiInfo->tempo = UniteMultiHex(strTemp, 0, length - 1); } else /* skip several bytes */ { while (length--) { command = *midiInfo->midiData; midiInfo->midiData++; } } } if (midiInfo->eventNum >= MAX_EVENT) return AK_FALSE; } return AK_TRUE; }
void ProcessMusic(void){ u8 c1,c2,tmp,trackVol; s16 vol; u16 uVol,tVol; u8 channel; struct TrackStruct* track; //process patches envelopes & pitch slides for(unsigned char trackNo=0;trackNo<CHANNELS;trackNo++){ track=&tracks[trackNo]; //update envelope if(track->envelopeStep!=0){ vol=track->envelopeVol+track->envelopeStep; if(vol<0){ vol=0; }else if(vol>0xff){ vol=0xff; } track->envelopeVol=vol; } if(track->flags & TRACK_FLAGS_SLIDING){ mixer.channels.all[trackNo].step+=track->slideStep; u16 tStep=pgm_read_word(&(steptable[track->slideNote])); if((track->slideStep>0 && mixer.channels.all[trackNo].step>=tStep) || (track->slideStep<0 && mixer.channels.all[trackNo].step<=tStep)) { mixer.channels.all[trackNo].step = tStep; track->flags &= ~(TRACK_FLAGS_SLIDING); } } } //Process song MIDI notes if(playSong){ #if MUSIC_ENGINE == MIDI //process all simultaneous events while(currDeltaTime==nextDeltaTime){ c1=pgm_read_byte(songPos++); if(c1==0xff){ //META data type event c1=pgm_read_byte(songPos++); if(c1==0x2f){ //end of song playSong=false; break; }else if(c1==0x6){ //marker c1=pgm_read_byte(songPos++); //read len c2=pgm_read_byte(songPos++); //read data if(c2=='S'){ //loop start loopStart=songPos; }else if(c2=='E'){//loop end songPos=loopStart; } } }else{ if(c1&0x80) lastStatus=c1; channel=lastStatus&0x0f; //get next data byte //Note: maybe we should not advance the cursor //in case we receive an unsupported command if(c1&0x80) c1=pgm_read_byte(songPos++); switch(lastStatus&0xf0){ //note-on case 0x90: //c1 = note c2=pgm_read_byte(songPos++)<<1; //get volume if(tracks[channel].flags|TRACK_FLAGS_ALLOCATED){ //allocated==true TriggerNote(channel,tracks[channel].patchNo,c1,c2); } break; //controllers case 0xb0: ///c1 = controller # c2=pgm_read_byte(songPos++); //get controller value if(c1==CONTROLER_VOL){ tracks[channel].trackVol=c2<<1; }else if(c1==CONTROLER_EXPRESSION){ tracks[channel].expressionVol=c2<<1; }else if(c1==CONTROLER_TREMOLO){ tracks[channel].tremoloLevel=c2<<1; }else if(c1==CONTROLER_TREMOLO_RATE){ tracks[channel].tremoloRate=c2<<1; } break; //program change case 0xc0: // c1 = patch # tracks[channel].patchNo=c1; break; }//end switch(c1&0xf0) }//end if(c1==0xff) //read next delta time nextDeltaTime=ReadVarLen(&songPos); currDeltaTime=0; #if SONG_SPEED == 1 if(songSpeed != 0){ uint32_t l = (uint32_t)(nextDeltaTime<<8); if(songSpeed < 0){//slower (uint32_t)(l += (uint32_t)(-songSpeed*(nextDeltaTime<<1))); (uint32_t)(l >>= 8); } else//faster (uint32_t)(l /= (uint32_t)((1<<8)+(songSpeed<<1))); nextDeltaTime = l; }
int CMIDISource::ReadData(int nIndex, void *pData, int nSize) { StreamPos *pPos; unsigned char *pBuffer; MIDITrack *pTrack; int nStatus; int nValue; if (nIndex >= m_nOutputNum) return 0; pPos = m_pStreamPos + nIndex; if (pPos->nHeadPos == 0) //invalid track return 0; if (pPos->nRelPos >= pPos->nSize) return 0; SourceSeekData(pPos->nAbsPos, SEEK_SET); pBuffer = (unsigned char *)pData; pTrack = m_pTracks + nIndex; pTrack->nTicks += ReadVarLen(); //delta time nStatus = SourceReadData(); if (nStatus >= 0xF0 && nStatus < 0xFF) //system exclusive event { pBuffer[0] = nStatus; nValue = ReadVarLen(); SourceReadData(pBuffer + 1, nValue); assert(nSize >= 1 + nValue); if (nSize > 1 + nValue) nSize = 1 + nValue; } else { if ((nStatus & 0x80) == 0) //running status { pBuffer[0] = pTrack->nStatus; pBuffer[1] = nStatus; } else //MIDI event or meta event { pTrack->nStatus = nStatus; pBuffer[0] = nStatus; pBuffer[1] = SourceReadData(); } switch (pBuffer[0] & 0xF0) { case 0x80: case 0x90: case 0xA0: case 0xB0: case 0xE0: pBuffer[2] = SourceReadData(); nSize = 3; break; case 0xC0: case 0xD0: nSize = 2; break; case 0xF0: nValue = ReadVarLen(); //data length SourceReadData(pBuffer + 2, nValue); assert(nSize >= 2 + nValue); if (nSize > 2 + nValue) nSize = 2 + nValue; break; default: nSize = 0; break; } //switch } //else pPos->nAbsPos = SourceGetPosition(); pPos->nRelPos = pPos->nAbsPos - pPos->nHeadPos; pPos->nTime = TicksToTime(pTrack->nTicks); if (pPos->nTime < m_nSeekTime) //event before seektime { pPos->nTime = 0; //render immediately if (pBuffer[0] >= 0x80 && pBuffer[0] <= 0x9F) //note on or note off pBuffer[0] = 0x00; //invalid event, should not be renderered } return nSize; }
bool HeartBeatPS1Seq::ReadEvent(void) { uint32_t beginOffset = curOffset; // in this format, end of track (FF 2F 00) comes without delta-time. // so handle that crazy sequence the first. if (curOffset + 3 <= rawfile->size()) { if (GetByte(curOffset) == 0xff && GetByte(curOffset + 1) == 0x2f && GetByte(curOffset + 2) == 0x00) { curOffset += 3; AddEndOfTrack(beginOffset, curOffset-beginOffset); return false; } } uint32_t delta = ReadVarLen(curOffset); if (curOffset >= rawfile->size()) return false; AddTime(delta); uint8_t status_byte = GetByte(curOffset++); if (status_byte <= 0x7F) // Running Status { status_byte = runningStatus; curOffset--; } else runningStatus = status_byte; channel = status_byte&0x0F; SetCurTrack(channel); switch (status_byte & 0xF0) { case 0x80 : //note off key = GetByte(curOffset++); vel = GetByte(curOffset++); AddNoteOff(beginOffset, curOffset-beginOffset, key); break; case 0x90 : //note event key = GetByte(curOffset++); vel = GetByte(curOffset++); if (vel > 0) //if the velocity is > 0, it's a note on AddNoteOn(beginOffset, curOffset-beginOffset, key, vel); else //otherwise it's a note off AddNoteOff(beginOffset, curOffset-beginOffset, key); break; case 0xA0 : AddUnknown(beginOffset, curOffset-beginOffset); return false; case 0xB0 : { uint8_t controlNum = GetByte(curOffset++); uint8_t value = GetByte(curOffset++); switch (controlNum) //control number { case 1: AddGenericEvent(beginOffset, curOffset-beginOffset, L"Modulation", NULL, CLR_UNKNOWN); if (VGMSeq::readMode == READMODE_CONVERT_TO_MIDI) { pMidiTrack->AddControllerEvent(channel, controlNum, value); } break; case 2: // identical to CC#11? AddGenericEvent(beginOffset, curOffset-beginOffset, L"Breath Controller?", NULL, CLR_UNKNOWN); if (VGMSeq::readMode == READMODE_CONVERT_TO_MIDI) { pMidiTrack->AddControllerEvent(channel, controlNum, value); } break; case 4: AddGenericEvent(beginOffset, curOffset-beginOffset, L"Foot Controller?", NULL, CLR_UNKNOWN); if (VGMSeq::readMode == READMODE_CONVERT_TO_MIDI) { pMidiTrack->AddControllerEvent(channel, controlNum, value); } break; case 5: AddGenericEvent(beginOffset, curOffset-beginOffset, L"Portamento Time?", NULL, CLR_UNKNOWN); if (VGMSeq::readMode == READMODE_CONVERT_TO_MIDI) { pMidiTrack->AddControllerEvent(channel, controlNum, value); } break; case 6 : AddGenericEvent(beginOffset, curOffset-beginOffset, L"NRPN Data Entry", NULL, CLR_MISC); if (VGMSeq::readMode == READMODE_CONVERT_TO_MIDI) { pMidiTrack->AddControllerEvent(channel, controlNum, value); } break; case 7 : //volume AddVol(beginOffset, curOffset-beginOffset, value); break; case 9: AddGenericEvent(beginOffset, curOffset-beginOffset, L"Control 9", NULL, CLR_UNKNOWN); if (VGMSeq::readMode == READMODE_CONVERT_TO_MIDI) { pMidiTrack->AddControllerEvent(channel, controlNum, value); } break; case 10 : //pan AddPan(beginOffset, curOffset-beginOffset, value); break; case 11 : //expression AddExpression(beginOffset, curOffset-beginOffset, value); break; case 20: AddGenericEvent(beginOffset, curOffset-beginOffset, L"Control 20", NULL, CLR_UNKNOWN); if (VGMSeq::readMode == READMODE_CONVERT_TO_MIDI) { pMidiTrack->AddControllerEvent(channel, controlNum, value); } break; case 21: AddGenericEvent(beginOffset, curOffset-beginOffset, L"Control 21", NULL, CLR_UNKNOWN); if (VGMSeq::readMode == READMODE_CONVERT_TO_MIDI) { pMidiTrack->AddControllerEvent(channel, controlNum, value); } break; case 22: AddGenericEvent(beginOffset, curOffset-beginOffset, L"Control 22", NULL, CLR_UNKNOWN); if (VGMSeq::readMode == READMODE_CONVERT_TO_MIDI) { pMidiTrack->AddControllerEvent(channel, controlNum, value); } break; case 23: AddGenericEvent(beginOffset, curOffset-beginOffset, L"Control 23", NULL, CLR_UNKNOWN); if (VGMSeq::readMode == READMODE_CONVERT_TO_MIDI) { pMidiTrack->AddControllerEvent(channel, controlNum, value); } break; case 32: AddGenericEvent(beginOffset, curOffset-beginOffset, L"Bank LSB?", NULL, CLR_UNKNOWN); if (VGMSeq::readMode == READMODE_CONVERT_TO_MIDI) { pMidiTrack->AddControllerEvent(channel, controlNum, value); } break; case 52: AddGenericEvent(beginOffset, curOffset-beginOffset, L"Control 52", NULL, CLR_UNKNOWN); if (VGMSeq::readMode == READMODE_CONVERT_TO_MIDI) { pMidiTrack->AddControllerEvent(channel, controlNum, value); } break; case 53: AddGenericEvent(beginOffset, curOffset-beginOffset, L"Control 53", NULL, CLR_UNKNOWN); if (VGMSeq::readMode == READMODE_CONVERT_TO_MIDI) { pMidiTrack->AddControllerEvent(channel, controlNum, value); } break; case 54: AddGenericEvent(beginOffset, curOffset-beginOffset, L"Control 54", NULL, CLR_UNKNOWN); if (VGMSeq::readMode == READMODE_CONVERT_TO_MIDI) { pMidiTrack->AddControllerEvent(channel, controlNum, value); } break; case 55: AddGenericEvent(beginOffset, curOffset-beginOffset, L"Control 55", NULL, CLR_UNKNOWN); if (VGMSeq::readMode == READMODE_CONVERT_TO_MIDI) { pMidiTrack->AddControllerEvent(channel, controlNum, value); } break; case 56: AddGenericEvent(beginOffset, curOffset-beginOffset, L"Control 56", NULL, CLR_UNKNOWN); if (VGMSeq::readMode == READMODE_CONVERT_TO_MIDI) { pMidiTrack->AddControllerEvent(channel, controlNum, value); } break; case 64: AddGenericEvent(beginOffset, curOffset-beginOffset, L"Hold 1?", NULL, CLR_UNKNOWN); if (VGMSeq::readMode == READMODE_CONVERT_TO_MIDI) { pMidiTrack->AddControllerEvent(channel, controlNum, value); } break; case 69: AddGenericEvent(beginOffset, curOffset-beginOffset, L"Hold 2?", NULL, CLR_UNKNOWN); if (VGMSeq::readMode == READMODE_CONVERT_TO_MIDI) { pMidiTrack->AddControllerEvent(channel, controlNum, value); } break; case 71: AddGenericEvent(beginOffset, curOffset-beginOffset, L"Resonance?", NULL, CLR_UNKNOWN); if (VGMSeq::readMode == READMODE_CONVERT_TO_MIDI) { pMidiTrack->AddControllerEvent(channel, controlNum, value); } break; case 72: AddGenericEvent(beginOffset, curOffset-beginOffset, L"Release Time?", NULL, CLR_UNKNOWN); if (VGMSeq::readMode == READMODE_CONVERT_TO_MIDI) { pMidiTrack->AddControllerEvent(channel, controlNum, value); } break; case 73: AddGenericEvent(beginOffset, curOffset-beginOffset, L"Attack Time?", NULL, CLR_UNKNOWN); if (VGMSeq::readMode == READMODE_CONVERT_TO_MIDI) { pMidiTrack->AddControllerEvent(channel, controlNum, value); } break; case 74: AddGenericEvent(beginOffset, curOffset-beginOffset, L"Cut Off Frequency?", NULL, CLR_UNKNOWN); if (VGMSeq::readMode == READMODE_CONVERT_TO_MIDI) { pMidiTrack->AddControllerEvent(channel, controlNum, value); } break; case 75: AddGenericEvent(beginOffset, curOffset-beginOffset, L"Decay Time?", NULL, CLR_UNKNOWN); if (VGMSeq::readMode == READMODE_CONVERT_TO_MIDI) { pMidiTrack->AddControllerEvent(channel, controlNum, value); } break; case 76: AddGenericEvent(beginOffset, curOffset-beginOffset, L"Vibrato Rate?", NULL, CLR_UNKNOWN); if (VGMSeq::readMode == READMODE_CONVERT_TO_MIDI) { pMidiTrack->AddControllerEvent(channel, controlNum, value); } break; case 77: AddGenericEvent(beginOffset, curOffset-beginOffset, L"Vibrato Depth?", NULL, CLR_UNKNOWN); if (VGMSeq::readMode == READMODE_CONVERT_TO_MIDI) { pMidiTrack->AddControllerEvent(channel, controlNum, value); } break; case 78: AddGenericEvent(beginOffset, curOffset-beginOffset, L"Vibrato Delay?", NULL, CLR_UNKNOWN); if (VGMSeq::readMode == READMODE_CONVERT_TO_MIDI) { pMidiTrack->AddControllerEvent(channel, controlNum, value); } break; case 79: AddGenericEvent(beginOffset, curOffset-beginOffset, L"Control 79", NULL, CLR_UNKNOWN); if (VGMSeq::readMode == READMODE_CONVERT_TO_MIDI) { pMidiTrack->AddControllerEvent(channel, controlNum, value); } break; case 91: AddReverb(beginOffset, curOffset-beginOffset, value); break; case 92: AddGenericEvent(beginOffset, curOffset-beginOffset, L"Tremolo Depth?", NULL, CLR_UNKNOWN); if (VGMSeq::readMode == READMODE_CONVERT_TO_MIDI) { pMidiTrack->AddControllerEvent(channel, controlNum, value); } break; case 98: switch (value) { case 20 : AddGenericEvent(beginOffset, curOffset-beginOffset, L"NRPN 1 #20", NULL, CLR_MISC); break; case 30 : AddGenericEvent(beginOffset, curOffset-beginOffset, L"NRPN 1 #30", NULL, CLR_MISC); break; default: AddGenericEvent(beginOffset, curOffset-beginOffset, L"NRPN 1", NULL, CLR_MISC); break; } if (VGMSeq::readMode == READMODE_CONVERT_TO_MIDI) { pMidiTrack->AddControllerEvent(channel, controlNum, value); } break; case 99 : //(0x63) nrpn msb switch (value) { case 20 : AddGenericEvent(beginOffset, curOffset-beginOffset, L"Loop Start", NULL, CLR_LOOP); break; case 30 : AddGenericEvent(beginOffset, curOffset-beginOffset, L"Loop End", NULL, CLR_LOOP); break; default: AddGenericEvent(beginOffset, curOffset-beginOffset, L"NRPN 2", NULL, CLR_MISC); break; } if (VGMSeq::readMode == READMODE_CONVERT_TO_MIDI) { pMidiTrack->AddControllerEvent(channel, controlNum, value); } break; case 121: AddGenericEvent(beginOffset, curOffset-beginOffset, L"Reset All Controllers", NULL, CLR_MISC); if (VGMSeq::readMode == READMODE_CONVERT_TO_MIDI) { pMidiTrack->AddControllerEvent(channel, controlNum, value); } break; case 126: AddGenericEvent(beginOffset, curOffset-beginOffset, L"MONO?", NULL, CLR_UNKNOWN); if (VGMSeq::readMode == READMODE_CONVERT_TO_MIDI) { pMidiTrack->AddControllerEvent(channel, controlNum, value); } break; case 127: AddGenericEvent(beginOffset, curOffset-beginOffset, L"Poly?", NULL, CLR_UNKNOWN); if (VGMSeq::readMode == READMODE_CONVERT_TO_MIDI) { pMidiTrack->AddControllerEvent(channel, controlNum, value); } break; default: AddGenericEvent(beginOffset, curOffset-beginOffset, L"Control Event", NULL, CLR_UNKNOWN); if (VGMSeq::readMode == READMODE_CONVERT_TO_MIDI) { pMidiTrack->AddControllerEvent(channel, controlNum, value); } break; } } break; case 0xC0 : { uint8_t progNum = GetByte(curOffset++); AddProgramChange(beginOffset, curOffset-beginOffset, progNum); } break; case 0xD0 : AddUnknown(beginOffset, curOffset-beginOffset); return false; case 0xE0 : { uint8_t hi = GetByte(curOffset++); uint8_t lo = GetByte(curOffset++); AddPitchBendMidiFormat(beginOffset, curOffset-beginOffset, hi, lo); } break; case 0xF0 : { if (status_byte == 0xFF) { if (curOffset + 1 > rawfile->size()) return false; uint8_t metaNum = GetByte(curOffset++); uint32_t metaLen = ReadVarLen(curOffset); if (curOffset + metaLen > rawfile->size()) return false; switch (metaNum) { case 0x51 : AddTempo(beginOffset, curOffset+metaLen-beginOffset, (GetShortBE(curOffset) << 8) | GetByte(curOffset + 2)); curOffset += metaLen; break; case 0x58 : { uint8_t numer = GetByte(curOffset); uint8_t denom = GetByte(curOffset + 1); AddTimeSig(beginOffset, curOffset+metaLen-beginOffset, numer, 1<<denom, (uint8_t)GetPPQN()); curOffset += metaLen; break; } case 0x2F : // apparently not used, but just in case. AddEndOfTrack(beginOffset, curOffset+metaLen-beginOffset); curOffset += metaLen; return false; default : AddUnknown(beginOffset, curOffset+metaLen-beginOffset); curOffset += metaLen; break; } } else { AddUnknown(beginOffset, curOffset-beginOffset); return false; } } break; } return true; }
/* Loads a mid file in to a MidiFile object * @param pFile - pointer to the start of the midi file * @param size - size of the file in bytes * @returns MidiFile object with the information from the mid file */ MidiFile* ParseMidi(const char *pFile, size_t size) { if(!pFile) return NULL; const char *pOffset = pFile; // "RIFF" is a (useless) wrapper sometimes applied to midis if(CompareToString(pFile, "RIFF")) { pOffset += 8; // if the next bytes are not "RMID", it is not a midi file if(!CompareToString(pFile, "RMID")) { return NULL; } pOffset += 4; } MidiFile::Header_Chunk *pHd = (MidiFile::Header_Chunk*)pOffset; // if the midi header is not found, it is not a midi file if(!CompareToString(pFile, "MThd")) { return NULL; } // Reverse the endian bytes of data FlipEndianness(&pHd->length); FlipEndianness(&pHd->format); FlipEndianness(&pHd->numTracks); FlipEndianness(&pHd->ticksPerBeat); MidiFile *pMidiFile = new MidiFile(); pMidiFile->name[0] = 0; pMidiFile->format = pHd->format; pMidiFile->ticksPerBeat = pHd->ticksPerBeat; pMidiFile->numTracks = pHd->numTracks; pMidiFile->tracks.resize(pHd->numTracks); pOffset += 8 + pHd->length; for(int t = 0; t < pMidiFile->numTracks && pOffset < pFile + size; ++t) { MidiFile::Track_Chunk track; memcpy(&track.signature, pOffset, sizeof(track.signature)); pOffset += sizeof(track.signature); memcpy(&track.length, pOffset, sizeof(track.length)); pOffset += sizeof(track.length); FlipEndianness(&track.length); if(CompareToString(&track.signature, "MTrk")) { const char *pTrk = pOffset; uint32_t tick = 0; // this is most certainly null, but get it just in case MidiFile::MidiEvent *pEv = pMidiFile->tracks[t]; while(pTrk < pOffset + track.length) { uint32_t delta = ReadVarLen(pTrk); tick += delta; uint8_t status = *(uint8_t*)pTrk++; MidiFile::MidiEvent *pEvent = NULL; if(status == MidiFile::MidiEventType::MidiEventType_Meta) { // meta event uint8_t type = *(uint8_t*)pTrk++; uint32_t bytes = ReadVarLen(pTrk); // read event switch(type) { case MidiFile::MidiMeta_SequenceNumber: { static int sequence = 0; MidiFile::MidiEvent_SequenceNumber *pSeq = new MidiFile::MidiEvent_SequenceNumber; pEvent = pSeq; if(!bytes) pSeq->sequence = sequence++; else { uint16_t seq; memcpy(&seq, pTrk, 2); FlipEndianness(&seq); pSeq->sequence = (int)seq; } break; } case MidiFile::MidiMeta_Text: case MidiFile::MidiMeta_Copyright: case MidiFile::MidiMeta_TrackName: case MidiFile::MidiMeta_Instrument: case MidiFile::MidiMeta_Lyric: case MidiFile::MidiMeta_Marker: case MidiFile::MidiMeta_CuePoint: case MidiFile::MidiMeta_PatchName: case MidiFile::MidiMeta_PortName: { MidiFile::MidiEvent_Text *pText = new MidiFile::MidiEvent_Text; pEvent = pText; memcpy(pText->buffer, pTrk, bytes); pText->buffer[bytes] = 0; break; } case MidiFile::MidiMeta_EndOfTrack: { MidiFile::MidiEvent *pE = new MidiFile::MidiEvent; pEvent = pE; pTrk = pOffset + track.length; break; } case MidiFile::MidiMeta_Tempo: { MidiFile::MidiEvent_Tempo *pTempo = new MidiFile::MidiEvent_Tempo; pEvent = pTempo; pTempo->microsecondsPerBeat = ((uint8_t*)pTrk)[0] << 16; pTempo->microsecondsPerBeat |= ((uint8_t*)pTrk)[1] << 8; pTempo->microsecondsPerBeat |= ((uint8_t*)pTrk)[2]; pTempo->BPM = 60000000.0f/(float)pTempo->microsecondsPerBeat; break; } case MidiFile::MidiMeta_SMPTE: { MidiFile::MidiEvent_SMPTE *pSMPTE = new MidiFile::MidiEvent_SMPTE; pEvent = pSMPTE; pSMPTE->hours = ((uint8_t*)pTrk)[0]; pSMPTE->minutes = ((uint8_t*)pTrk)[1]; pSMPTE->seconds = ((uint8_t*)pTrk)[2]; pSMPTE->frames = ((uint8_t*)pTrk)[3]; pSMPTE->subFrames = ((uint8_t*)pTrk)[4]; break; } case MidiFile::MidiMeta_TimeSignature: { MidiFile::MidiEvent_TimeSignature *pTS = new MidiFile::MidiEvent_TimeSignature; pEvent = pTS; pTS->numerator = ((uint8_t*)pTrk)[0]; pTS->denominator = 1 << ((uint8_t*)pTrk)[1]; pTS->clocks = ((uint8_t*)pTrk)[2]; pTS->d = ((uint8_t*)pTrk)[3]; break; } case MidiFile::MidiMeta_KeySignature: { MidiFile::MidiEvent_KeySignature *pKS = new MidiFile::MidiEvent_KeySignature; pEvent = pKS; pKS->sf = ((uint8_t*)pTrk)[0]; pKS->minor = ((uint8_t*)pTrk)[1]; break; } case MidiFile::MidiMeta_Custom: { MidiFile::MidiEvent_Custom *pCustom = new MidiFile::MidiEvent_Custom; pEvent = pCustom; pCustom->pData = pTrk; pCustom->size = bytes; break; } } if(pEvent) pEvent->subType = type; pTrk += bytes; } else if(status == MidiFile::MidiEventType::MidiEventType_SYSEX || status == MidiFile::MidiEventType::MidiEventType_SYSEX2) { uint32_t bytes = ReadVarLen(pTrk); MidiFile::MidiEvent_SYSEX *pSYSEX = new MidiFile::MidiEvent_SYSEX; pEvent = pSYSEX; pSYSEX->pData = pTrk; pSYSEX->size = bytes; pTrk += bytes; } else { static int lastStatus = 0; if(status < 0x80) { --pTrk; status = lastStatus; } lastStatus = status; int eventType = status&0xF0; // some malformed midis cannot be trusted to conform to expectations, do not readvarlen these! int param1 = (*pTrk & 0x7F); pTrk++; int param2 = 0; // param2 will contain useful info, if the event isn't programChange or channelAfterTouch if(eventType != MidiFile::MidiNote_ProgramChange && eventType != MidiFile::MidiNote_ChannelAfterTouch) { param2 = (*pTrk & 0x7F); pTrk++; } switch(eventType) { case MidiFile::MidiNote_NoteOn: case MidiFile::MidiNote_NoteOff: { MidiFile::MidiEvent_Note *pNote = new MidiFile::MidiEvent_Note; pEvent = pNote; pNote->event = status&0xF0; pNote->channel = status&0x0F; pNote->note = param1; pNote->velocity = param2; status &= 0xF0; break; } } if(pEvent) { pEvent->subType = status; status = MidiFile::MidiEventType_Note; } } // append event to track if(pEvent) { pEvent->tick = tick; pEvent->delta = delta; pEvent->type = status; pEvent->pNext = NULL; if(!pEv) { // set the first event pMidiFile->tracks[t] = pEvent; } else { // set the next event pEv->pNext = pEvent; } // point to the most recent event pEv = pEvent; } } } pOffset += track.length; } return pMidiFile; }