diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/adlmidi.cpp | 20 | ||||
-rw-r--r-- | src/adlmidi_load.cpp | 50 | ||||
-rw-r--r-- | src/adlmidi_midiplay.cpp | 415 | ||||
-rw-r--r-- | src/adlmidi_private.hpp | 109 |
4 files changed, 180 insertions, 414 deletions
diff --git a/src/adlmidi.cpp b/src/adlmidi.cpp index bbd80e4..a7bab22 100644 --- a/src/adlmidi.cpp +++ b/src/adlmidi.cpp @@ -303,6 +303,20 @@ ADLMIDI_EXPORT double adl_totalTimeLength(ADL_MIDIPlayer *device) return reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->timeLength(); } +ADLMIDI_EXPORT double adl_loopStartTime(struct ADL_MIDIPlayer *device) +{ + if(!device) + return -1.0; + return reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->getLoopStart(); +} + +ADLMIDI_EXPORT double adl_loopEndTime(struct ADL_MIDIPlayer *device) +{ + if(!device) + return -1.0; + return reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->getLoopEnd(); +} + ADLMIDI_EXPORT double adl_positionTell(struct ADL_MIDIPlayer *device) { if(!device) @@ -324,6 +338,12 @@ ADLMIDI_EXPORT void adl_positionRewind(struct ADL_MIDIPlayer *device) reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->rewind(); } +ADLMIDI_EXPORT void adl_setTempo(struct ADL_MIDIPlayer *device, double tempo) +{ + if(!device || (tempo <= 0.0)) + return; + reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->setTempo(tempo); +} #ifdef ADLMIDI_USE_DOSBOX_OPL diff --git a/src/adlmidi_load.cpp b/src/adlmidi_load.cpp index 5bc2ba0..08892a5 100644 --- a/src/adlmidi_load.cpp +++ b/src/adlmidi_load.cpp @@ -48,53 +48,6 @@ uint64_t MIDIplay::ReadLEint(const void *buffer, size_t nbytes) return result; } -//uint64_t MIDIplay::ReadVarLenEx(size_t tk, bool &ok) -//{ -// uint64_t result = 0; -// ok = false; - -// for(;;) -// { -// if(tk >= TrackData.size()) -// return 1; - -// if(tk >= CurrentPosition.track.size()) -// return 2; - -// size_t ptr = CurrentPosition.track[tk].ptr; - -// if(ptr >= TrackData[tk].size()) -// return 3; - -// unsigned char byte = TrackData[tk][CurrentPosition.track[tk].ptr++]; -// result = (result << 7) + (byte & 0x7F); - -// if(!(byte & 0x80)) break; -// } - -// ok = true; -// return result; -//} - -uint64_t MIDIplay::ReadVarLenEx(uint8_t **ptr, uint8_t *end, bool &ok) -{ - uint64_t result = 0; - ok = false; - - for(;;) - { - if(*ptr >= end) - return 2; - unsigned char byte = *((*ptr)++); - result = (result << 7) + (byte & 0x7F); - if(!(byte & 0x80)) break; - } - - ok = true; - return result; -} - - bool MIDIplay::LoadBank(const std::string &filename) { fileReader file; @@ -388,6 +341,7 @@ bool MIDIplay::LoadMIDI(MIDIplay::fileReader &fr) ADL_UNUSED(fsize); //! Temp buffer for conversion AdlMIDI_CPtr<uint8_t> cvt_buf; + errorString.clear(); #ifdef DISABLE_EMBEDDED_BANKS if((opl.AdlBank != ~0u) || (opl.dynamic_metainstruments.size() < 256)) @@ -792,7 +746,7 @@ InvFmt: //Build new MIDI events table (ALPHA!!!) if(!buildTrackData()) { - ADLMIDI_ErrorString = fr._fileName + ": MIDI data parsing error has occouped!"; + ADLMIDI_ErrorString = fr._fileName + ": MIDI data parsing error has occouped!\n" + errorString; return false; } diff --git a/src/adlmidi_midiplay.cpp b/src/adlmidi_midiplay.cpp index 44f6d68..7b9094a 100644 --- a/src/adlmidi_midiplay.cpp +++ b/src/adlmidi_midiplay.cpp @@ -189,17 +189,23 @@ void MIDIplay::MidiTrackRow::sortEvents() bool MIDIplay::buildTrackData() { fullSongTimeLength = 0.0; + loopStartTime = -1.0; + loopEndTime = -1.0; musTitle.clear(); trackDataNew.clear(); - trackDataNewStatus.clear(); const size_t trackCount = TrackData.size(); trackDataNew.resize(trackCount, MidiTrackQueue()); - trackDataNewStatus.resize(trackCount, 0); invalidLoop = false; bool gotLoopStart = false, gotLoopEnd = false, gotLoopEventInThisRow = false; + //! Tick position of loop start tag uint64_t loopStartTicks = 0; + //! Tick position of loop end tag uint64_t loopEndTicks = 0; + //! Full length of song in ticks + uint64_t ticksSongLength = 0; + //! Cache for error message strign + char error[150]; CurrentPositionNew.track.clear(); CurrentPositionNew.track.resize(trackCount); @@ -226,7 +232,9 @@ bool MIDIplay::buildTrackData() evtPos.delay = ReadVarLenEx(&trackPtr, end, ok); if(!ok) { - //TODO: Implement file parsing error here + int len = std::snprintf(error, 150, "buildTrackData: Can't read variable-length value at begin of track %d.\n", (int)tk); + if((len > 0) && (len < 150)) + errorString += std::string(error, (size_t)len); return false; } CurrentPositionNew.wait = evtPos.delay; @@ -241,7 +249,9 @@ bool MIDIplay::buildTrackData() event = parseEvent(&trackPtr, end, status); if(!event.isValid) { - //TODO: Implement file parsing error here + int len = std::snprintf(error, 150, "buildTrackData: Fail to parse event in the track %d.\n", (int)tk); + if((len > 0) && (len < 150)) + errorString += std::string(error, (size_t)len); return false; } evtPos.events.push_back(event); @@ -296,7 +306,9 @@ bool MIDIplay::buildTrackData() evtPos.delay = ReadVarLenEx(&trackPtr, end, ok); if(!ok) { - //TODO: Implement file parsing error here + int len = std::snprintf(error, 150, "buildTrackData: Can't read variable-length value in the track %d.\n", (int)tk); + if((len > 0) && (len < 150)) + errorString += std::string(error, (size_t)len); return false; } } @@ -312,11 +324,19 @@ bool MIDIplay::buildTrackData() } } while((trackPtr <= end) && (event.subtype != MidiEvent::ST_ENDTRACK)); + if(ticksSongLength < abs_position) + ticksSongLength = abs_position; //Set the chain of events begin if(trackDataNew[tk].size() > 0) CurrentPositionNew.track[tk].pos = trackDataNew[tk].begin(); } + if(gotLoopStart && !gotLoopEnd) + { + gotLoopEnd = true; + loopEndTicks = ticksSongLength; + } + //loopStart must be located before loopEnd! if(loopStartTicks >= loopEndTicks) invalidLoop = true; @@ -409,6 +429,16 @@ bool MIDIplay::buildTrackData() pos.time = time; time += pos.timeDelay; + if(!invalidLoop) + { + // Set loop points times + if(loopStartTicks == pos.absPos) + loopStartTime = pos.time; + else + if(loopEndTicks == pos.absPos) + loopEndTime = pos.time; + } + #ifdef DEBUG_TIME_CALCULATION std::fprintf(stdout, "= %10" PRId64 " = %10f%s\n", pos.absPos, pos.time, tempoChanged ? " <----TEMPO CHANGED" : ""); std::fflush(stdout); @@ -432,6 +462,9 @@ MIDIplay::MIDIplay(): cmf_percussion_mode(false), fullSongTimeLength(0.0), postSongWaitDelay(1.0), + loopStartTime(-1.0), + loopEndTime(-1.0), + tempoMultiplier(1.0), trackStart(false), atEnd(false), loopStart(false), @@ -478,22 +511,28 @@ uint64_t MIDIplay::ReadVarLen(uint8_t **ptr) return result; } -//uint64_t MIDIplay::ReadVarLen(size_t tk) -//{ -// uint64_t result = 0; -// for(;;) -// { -// uint8_t byte = TrackData[tk][CurrentPosition.track[tk].ptr++]; -// result = (result << 7) + (byte & 0x7F); -// if(!(byte & 0x80)) -// break; -// } -// return result; -//} +uint64_t MIDIplay::ReadVarLenEx(uint8_t **ptr, uint8_t *end, bool &ok) +{ + uint64_t result = 0; + ok = false; + + for(;;) + { + if(*ptr >= end) + return 2; + unsigned char byte = *((*ptr)++); + result = (result << 7) + (byte & 0x7F); + if(!(byte & 0x80)) break; + } + + ok = true; + return result; +} double MIDIplay::Tick(double s, double granularity) { + s *= tempoMultiplier; //if(CurrentPositionNew.began) CurrentPositionNew.wait -= s; CurrentPositionNew.absTimePosition += s; @@ -522,31 +561,6 @@ double MIDIplay::Tick(double s, double granularity) return 0.0; return CurrentPositionNew.wait; - -// if(CurrentPosition.began) -// CurrentPosition.wait -= s; - -// int antiFreezeCounter = 10000;//Limit 10000 loops to avoid freezing -// while((CurrentPosition.wait <= granularity * 0.5) && (antiFreezeCounter > 0)) -// { -// //std::fprintf(stderr, "wait = %g...\n", CurrentPosition.wait); -// if(!ProcessEvents()) -// break; -// if(CurrentPosition.wait <= 0.0) -// antiFreezeCounter--; -// } - -// if(antiFreezeCounter <= 0) -// CurrentPosition.wait += 1.0;/* Add extra 1 second when over 10000 events -// with zero delay are been detected */ - -// for(uint16_t c = 0; c < opl.NumChannels; ++c) -// ch[c].AddAge(static_cast<int64_t>(s * 1000.0)); - -// UpdateVibrato(s); -// UpdateArpeggio(s); - - // return CurrentPosition.wait; } void MIDIplay::seek(double seconds) @@ -613,11 +627,20 @@ double MIDIplay::timeLength() return fullSongTimeLength; } +double MIDIplay::getLoopStart() +{ + return loopStartTime; +} + +double MIDIplay::getLoopEnd() +{ + return loopEndTime; +} + void MIDIplay::rewind() { Panic(); KillSustainingNotes(-1, -1); - //CurrentPosition = trackBeginPosition; CurrentPositionNew = trackBeginPositionNew; trackStart = true; atEnd = false; @@ -625,6 +648,11 @@ void MIDIplay::rewind() invalidLoop = false; } +void MIDIplay::setTempo(double tempo) +{ + tempoMultiplier = tempo; +} + void MIDIplay::realTime_ResetState() { for(size_t ch = 0; ch < Ch.size(); ch++) @@ -1245,103 +1273,6 @@ void MIDIplay::NoteUpdate(uint16_t MidCh, } -//bool MIDIplay::ProcessEvents() -//{ -// if(TrackData.size() == 0) -// atEnd = true;//No MIDI track data to play -// if(atEnd) -// return false;//No more events in the queue - -// loopEnd = false; -// const size_t TrackCount = TrackData.size(); -// const Position RowBeginPosition(CurrentPosition); - -// for(size_t tk = 0; tk < TrackCount; ++tk) -// { -// if(CurrentPosition.track[tk].status >= 0 -// && CurrentPosition.track[tk].delay <= 0) -// { -// // Handle event -// HandleEvent(tk); - -// // Read next event time (unless the track just ended) -// if(CurrentPosition.track[tk].ptr >= TrackData[tk].size()) -// CurrentPosition.track[tk].status = -1; - -// if(CurrentPosition.track[tk].status >= 0) -// CurrentPosition.track[tk].delay += ReadVarLen(tk); -// } -// } - -// // Find shortest delay from all track -// uint64_t shortest = 0; -// bool shortest_no = true; - -// for(size_t tk = 0; tk < TrackCount; ++tk) -// if((CurrentPosition.track[tk].status >= 0) && (shortest_no || CurrentPosition.track[tk].delay < shortest)) -// { -// shortest = CurrentPosition.track[tk].delay; -// shortest_no = false; -// } - -// //if(shortest > 0) UI.PrintLn("shortest: %ld", shortest); - -// // Schedule the next playevent to be processed after that delay -// for(size_t tk = 0; tk < TrackCount; ++tk) -// CurrentPosition.track[tk].delay -= shortest; - -// fraction<uint64_t> t = shortest * Tempo; - -// if(CurrentPosition.began) -// CurrentPosition.wait += t.valuel(); - -// //if(shortest > 0) UI.PrintLn("Delay %ld (%g)", shortest, (double)t.valuel()); -// /* -// if(CurrentPosition.track[0].ptr > 8119) -// loopEnd = true; -// // ^HACK: CHRONO TRIGGER LOOP -// */ - -// if(loopStart_hit && (loopStart || loopEnd)) //Avoid invalid loops -// { -// invalidLoop = true; -// loopStart = false; -// loopEnd = false; -// LoopBeginPosition = trackBeginPosition; -// } -// else -// loopStart_hit = false; - -// if(loopStart) -// { -// if(trackStart) -// { -// trackBeginPosition = RowBeginPosition; -// trackStart = false; -// atEnd = false; -// } -// LoopBeginPosition = RowBeginPosition; -// loopStart = false; -// loopStart_hit = true; -// } - -// if(shortest_no || loopEnd) -// { -// //Loop if song end or loop end point has reached -// loopEnd = false; -// shortest = 0; -// if(!m_setup.loopingIsEnabled) -// { -// atEnd = true; //Don't handle events anymore -// CurrentPosition.wait += 1.0;//One second delay until stop playing -// return true;//We have caugh end here! -// } -// CurrentPosition = LoopBeginPosition; -// } - -// return true;//Has events in queue -//} - bool MIDIplay::ProcessEventsNew(bool isSeek) { if(CurrentPositionNew.track.size() == 0) @@ -1421,33 +1352,11 @@ bool MIDIplay::ProcessEventsNew(bool isSeek) fraction<uint64_t> t = shortest * Tempo; //if(CurrentPositionNew.began) - CurrentPositionNew.wait += t.valuel(); + CurrentPositionNew.wait += t.value(); //if(shortest > 0) UI.PrintLn("Delay %ld (%g)", shortest, (double)t.valuel()); - /* - if(CurrentPosition.track[0].ptr > 8119) - loopEnd = true; - // ^HACK: CHRONO TRIGGER LOOP - */ - - //if(loopStart_hit && (loopStart || loopEnd)) //Avoid invalid loops - //{ - // invalidLoop = true; - // loopStart = false; - // loopEnd = false; - // LoopBeginPositionNew = trackBeginPositionNew; - //} - //else - // loopStart_hit = false; - if(loopStart) { - //if(trackStart) - //{ - // trackBeginPositionNew = RowBeginPosition; - // trackStart = false; - // atEnd = false; - //} LoopBeginPositionNew = RowBeginPosition; loopStart = false; } @@ -1476,6 +1385,7 @@ MIDIplay::MidiEvent MIDIplay::parseEvent(uint8_t**pptr, uint8_t *end, int &statu if(ptr + 1 > end) { + errorString += "parseEvent: Can't read event type byte - Unexpected end of track data.\n"; evt.isValid = 0; return evt; } @@ -1488,6 +1398,7 @@ MIDIplay::MidiEvent MIDIplay::parseEvent(uint8_t**pptr, uint8_t *end, int &statu uint64_t length = ReadVarLenEx(pptr, end, ok); if(!ok || (ptr + length > end)) { + errorString += "parseEvent: Can't read SysEx event - Unexpected end of track data.\n"; evt.isValid = 0; return evt; } @@ -1502,6 +1413,7 @@ MIDIplay::MidiEvent MIDIplay::parseEvent(uint8_t**pptr, uint8_t *end, int &statu uint64_t length = ReadVarLenEx(pptr, end, ok); if(!ok || (ptr + length > end)) { + errorString += "parseEvent: Can't read Special event - Unexpected end of track data.\n"; evt.isValid = 0; return evt; } @@ -1584,6 +1496,7 @@ MIDIplay::MidiEvent MIDIplay::parseEvent(uint8_t**pptr, uint8_t *end, int &statu { if(ptr + 1 > end) { + errorString += "parseEvent: Can't read System Command Song Select event - Unexpected end of track data.\n"; evt.isValid = 0; return evt; } @@ -1597,6 +1510,7 @@ MIDIplay::MidiEvent MIDIplay::parseEvent(uint8_t**pptr, uint8_t *end, int &statu { if(ptr + 2 > end) { + errorString += "parseEvent: Can't read System Command Position Pointer event - Unexpected end of track data.\n"; evt.isValid = 0; return evt; } @@ -1620,6 +1534,7 @@ MIDIplay::MidiEvent MIDIplay::parseEvent(uint8_t**pptr, uint8_t *end, int &statu case MidiEvent::T_WHEEL: if(ptr + 2 > end) { + errorString += "parseEvent: Can't read regular 2-byte event - Unexpected end of track data.\n"; evt.isValid = 0; return evt; } @@ -1641,6 +1556,7 @@ MIDIplay::MidiEvent MIDIplay::parseEvent(uint8_t**pptr, uint8_t *end, int &statu case MidiEvent::T_CHANAFTTOUCH: if(ptr + 1 > end) { + errorString += "parseEvent: Can't read regular 1-byte event - Unexpected end of track data.\n"; evt.isValid = 0; return evt; } @@ -1651,179 +1567,6 @@ MIDIplay::MidiEvent MIDIplay::parseEvent(uint8_t**pptr, uint8_t *end, int &statu return evt; } -//void MIDIplay::HandleEvent(size_t tk) -//{ -// unsigned char byte = TrackData[tk][CurrentPosition.track[tk].ptr++]; - -// if(byte == 0xF7 || byte == 0xF0) // Ignore SysEx -// { -// uint64_t length = ReadVarLen(tk); -// //std::string data( length?(const char*) &TrackData[tk][CurrentPosition.track[tk].ptr]:0, length ); -// CurrentPosition.track[tk].ptr += (size_t)length; -// //UI.PrintLn("SysEx %02X: %u bytes", byte, length/*, data.c_str()*/); -// return; -// } - -// if(byte == 0xFF) -// { -// // Special event FF -// uint8_t evtype = TrackData[tk][CurrentPosition.track[tk].ptr++]; -// uint64_t length = ReadVarLen(tk); -// std::string data(length ? (const char *) &TrackData[tk][CurrentPosition.track[tk].ptr] : 0, (size_t)length); -// CurrentPosition.track[tk].ptr += (size_t)length; - -// if(evtype == 0x2F)//End Of Track -// { -// CurrentPosition.track[tk].status = -1; -// return; -// } - -// if(evtype == 0x51)//Tempo change -// { -// Tempo = InvDeltaTicks * fraction<uint64_t>(ReadBEint(data.data(), data.size())); -// return; -// } - -// if(evtype == 6)//Meta event -// { -// //Turn on/off Loop handling when loop is disabled -// if(m_setup.loopingIsEnabled) -// { -// /* Move this away from events handler */ -// for(size_t i = 0; i < data.size(); i++) -// { -// if(data[i] <= 'Z' && data[i] >= 'A') -// data[i] = data[i] - ('Z' - 'z'); -// } - -// if((data == "loopstart") && (!invalidLoop)) -// { -// loopStart = true; -// loopStart_passed = true; -// } - -// if((data == "loopend") && (!invalidLoop)) -// { -// if((loopStart_passed) && (!loopStart)) -// loopEnd = true; -// else -// invalidLoop = true; -// } -// } -// } - -// if(evtype == 9) -// current_device[tk] = ChooseDevice(data); - -// //if(evtype >= 1 && evtype <= 6) -// // UI.PrintLn("Meta %d: %s", evtype, data.c_str()); - -// if(evtype == 0xE3) // Special non-spec ADLMIDI special for IMF playback: Direct poke to AdLib -// { -// uint8_t i = static_cast<uint8_t>(data[0]), v = static_cast<uint8_t>(data[1]); - -// if((i & 0xF0) == 0xC0) -// v |= 0x30; - -// //std::printf("OPL poke %02X, %02X\n", i, v); -// //std::fflush(stdout); -// opl.PokeN(0, i, v); -// } - -// return; -// } - -// // Any normal event (80..EF) -// if(byte < 0x80) -// { -// byte = static_cast<uint8_t>(CurrentPosition.track[tk].status | 0x80); -// CurrentPosition.track[tk].ptr--; -// } - -// if(byte == 0xF3) -// { -// CurrentPosition.track[tk].ptr += 1; -// return; -// } - -// if(byte == 0xF2) -// { -// CurrentPosition.track[tk].ptr += 2; -// return; -// } - -// /*UI.PrintLn("@%X Track %u: %02X %02X", -// CurrentPosition.track[tk].ptr-1, (unsigned)tk, byte, -// TrackData[tk][CurrentPosition.track[tk].ptr]);*/ -// uint8_t MidCh = byte & 0x0F, EvType = byte >> 4; -// MidCh += (uint8_t)current_device[tk]; -// CurrentPosition.track[tk].status = byte; - -// switch(EvType) -// { -// case 0x8: // Note off -// { -// uint8_t note = TrackData[tk][CurrentPosition.track[tk].ptr++]; -// /*uint8_t vol=*/TrackData[tk][CurrentPosition.track[tk].ptr++]; -// //if(MidCh != 9) note -= 12; // HACK -// realTime_NoteOff(MidCh, note); -// break; -// } -// case 0x9: // Note on -// { -// uint8_t note = TrackData[tk][CurrentPosition.track[tk].ptr++]; -// uint8_t vol = TrackData[tk][CurrentPosition.track[tk].ptr++]; -// //if(MidCh != 9) note -= 12; // HACK -// if(realTime_NoteOn(MidCh, note, vol)) -// CurrentPosition.began = true; -// break; -// } - -// case 0xA: // Note touch -// { -// uint8_t note = TrackData[tk][CurrentPosition.track[tk].ptr++]; -// uint8_t vol = TrackData[tk][CurrentPosition.track[tk].ptr++]; -// realTime_NoteAfterTouch(MidCh, note, vol); -// break; -// } - -// case 0xB: // Controller change -// { -// uint8_t ctrlno = TrackData[tk][CurrentPosition.track[tk].ptr++]; -// uint8_t value = TrackData[tk][CurrentPosition.track[tk].ptr++]; - -// if((m_setup.loopingIsEnabled) && (ctrlno == 111) && !invalidLoop) -// { -// loopStart = true; -// loopStart_passed = true; -// break; -// } - -// realTime_Controller(MidCh, ctrlno, value); -// break; -// } - -// case 0xC: // Patch change -// realTime_PatchChange(MidCh, TrackData[tk][CurrentPosition.track[tk].ptr++]); -// break; - -// case 0xD: // Channel after-touch -// { -// // TODO: Verify, is this correct action? -// uint8_t vol = TrackData[tk][CurrentPosition.track[tk].ptr++]; -// realTime_ChannelAfterTouch(MidCh, vol); -// break; -// } - -// case 0xE: // Wheel/pitch bend -// { -// uint8_t a = TrackData[tk][CurrentPosition.track[tk].ptr++]; -// uint8_t b = TrackData[tk][CurrentPosition.track[tk].ptr++]; -// realTime_PitchBend(MidCh, b, a); -// break; -// } -// } -//} void MIDIplay::HandleEvent(size_t tk, MIDIplay::MidiEvent &evt, int &status) { diff --git a/src/adlmidi_private.hpp b/src/adlmidi_private.hpp index 92cb1df..fdd7d32 100644 --- a/src/adlmidi_private.hpp +++ b/src/adlmidi_private.hpp @@ -187,25 +187,6 @@ public: class MIDIplay { - // Information about each track - //struct Position - //{ - // bool began; - // char padding[7]; - // double wait; - // struct TrackInfo - // { - // size_t ptr; - // uint64_t delay; - // int status; - // char padding2[4]; - // TrackInfo(): ptr(0), delay(0), status(0) {} - // }; - // std::vector<TrackInfo> track; - - // Position(): began(false), wait(0.0l), track() { } - //} CurrentPosition, LoopBeginPosition, trackBeginPosition; - std::map<std::string, uint64_t> devices; std::map<uint64_t /*track*/, uint64_t /*channel begin index*/> current_device; @@ -294,9 +275,10 @@ class MIDIplay AdlChannel(): users(), koff_time_until_neglible(0) { } void AddAge(int64_t ms); }; -public: + + //Padding to fix CLanc code model's warning char ____padding[7]; -private: + std::vector<AdlChannel> ch; std::vector<std::vector<uint8_t> > TrackData; @@ -430,9 +412,29 @@ private: //! Delay after song playd before rejecting the output stream requests double postSongWaitDelay; + //! Loop start time + double loopStartTime; + //! Loop end time + double loopEndTime; + //! Local error string + std::string errorString; + + //! Pre-processed track data storage std::vector<MidiTrackQueue > trackDataNew; - std::vector<int> trackDataNewStatus; + + /** + * @brief Build MIDI track data from the raw track data storage + * @return true if everything successfully processed, or false on any error + */ bool buildTrackData(); + + /** + * @brief Parse one event from raw MIDI track stream + * @param [_inout] ptr pointer to pointer to current position on the raw data track + * @param [_in] end address to end of raw track data, needed to validate position and size + * @param [_inout] status status of the track processing + * @return Parsed MIDI event entry + */ MidiEvent parseEvent(uint8_t **ptr, uint8_t *end, int &status); public: @@ -442,6 +444,8 @@ public: std::string musTitle; fraction<uint64_t> InvDeltaTicks, Tempo; + //! Tempo multiplier + double tempoMultiplier; bool trackStart, atEnd, loopStart, @@ -481,18 +485,27 @@ public: unsigned long PCM_RATE; } m_setup; -public: static uint64_t ReadBEint(const void *buffer, size_t nbytes); static uint64_t ReadLEint(const void *buffer, size_t nbytes); + /** + * @brief Standard MIDI Variable-Length numeric value parser without of validation + * @param [_inout] ptr Pointer to memory block that contains begin of variable-length value + * @return Unsigned integer that conains parsed variable-length value + */ uint64_t ReadVarLen(uint8_t **ptr); - //uint64_t ReadVarLen(size_t tk); - //uint64_t ReadVarLenEx(size_t tk, bool &ok); + /** + * @brief Secure Standard MIDI Variable-Length numeric value parser with anti-out-of-range protection + * @param [_inout] ptr Pointer to memory block that contains begin of variable-length value, will be iterated forward + * @param [_in end Pointer to end of memory block where variable-length value is stored (after end of track) + * @param [_out] ok Reference to boolean which takes result of variable-length value parsing + * @return Unsigned integer that conains parsed variable-length value + */ uint64_t ReadVarLenEx(uint8_t **ptr, uint8_t *end, bool &ok); /* * A little class gives able to read filedata from disk and also from a memory segment - */ + */ class fileReader { public: @@ -650,19 +663,55 @@ public: bool LoadMIDI(void *data, unsigned long size); bool LoadMIDI(fileReader &fr); - /* Periodic tick handler. - * Input: s = seconds since last call - * Input: granularity = don't expect intervals smaller than this, in seconds - * Output: desired number of seconds until next call + /** + * @brief Periodic tick handler. + * @param s seconds since last call + * @param granularity don't expect intervals smaller than this, in seconds + * @return desired number of seconds until next call */ double Tick(double s, double granularity); + /** + * @brief Change current position to specified time position in seconds + * @param seconds Absolute time position in seconds + */ void seek(double seconds); + + /** + * @brief Gives current time position in seconds + * @return Current time position in seconds + */ double tell(); + + /** + * @brief Gives time length of current song in seconds + * @return Time length of current song in seconds + */ double timeLength(); + /** + * @brief Gives loop start time position in seconds + * @return Loop start time position in seconds or -1 if song has no loop points + */ + double getLoopStart(); + + /** + * @brief Gives loop end time position in seconds + * @return Loop end time position in seconds or -1 if song has no loop points + */ + double getLoopEnd(); + + /** + * @brief Return to begin of current song + */ void rewind(); + /** + * @brief Set tempo multiplier + * @param tempo Tempo multiplier: 1.0 - original tempo. >1 - faster, <1 - slower + */ + void setTempo(double tempo); + /* RealTime event triggers */ void realTime_ResetState(); |