diff options
author | Wohlstand <admin@wohlnet.ru> | 2017-10-23 23:42:19 +0300 |
---|---|---|
committer | Wohlstand <admin@wohlnet.ru> | 2017-10-23 23:42:19 +0300 |
commit | 70b7fd4c17864c6ce5f5d883fe2b42b6efe1c100 (patch) | |
tree | fa99930753f3abc683cbdd0f1f74a663963c684b | |
parent | a8dde37099558dc805dfa07eb870c2baf66f4fec (diff) | |
download | libADLMIDI-70b7fd4c17864c6ce5f5d883fe2b42b6efe1c100.tar.gz libADLMIDI-70b7fd4c17864c6ce5f5d883fe2b42b6efe1c100.tar.bz2 libADLMIDI-70b7fd4c17864c6ce5f5d883fe2b42b6efe1c100.zip |
Experimental time position calculation based on in-track tempo events
-rw-r--r-- | src/adlmidi_midiplay.cpp | 101 | ||||
-rw-r--r-- | src/adlmidi_private.hpp | 5 |
2 files changed, 93 insertions, 13 deletions
diff --git a/src/adlmidi_midiplay.cpp b/src/adlmidi_midiplay.cpp index f536f4e..69a9e81 100644 --- a/src/adlmidi_midiplay.cpp +++ b/src/adlmidi_midiplay.cpp @@ -132,13 +132,15 @@ void MIDIplay::AdlChannel::AddAge(int64_t ms) MIDIplay::MidiEvent::MidiEvent() : type(T_UNKNOWN), subtype(T_UNKNOWN), - channel(0) + channel(0), + absPosition(0) {} MIDIplay::MidiTrackPos::MidiTrackPos() : time(0.0), delay(0), + absPos(0), timeDelay(0.0), next(NULL) {} @@ -147,6 +149,7 @@ void MIDIplay::MidiTrackPos::reset() { time = 0.0; delay = 0; + absPos = 0; timeDelay = 0.0; events.clear(); next = NULL; @@ -201,10 +204,14 @@ void MIDIplay::buildTrackData() * will cause time desynchronization between tracks. * Also, seconds calculation is incorrect */ + //Tempo change events + std::vector<MidiEvent> tempos; + for(size_t tk = 0; tk < trackCount; ++tk) { - fraction<uint64_t> currentTempo = Tempo; - double time = 0.0; + //fraction<uint64_t> currentTempo = Tempo; + uint64_t abs_position = 0; + //double time = 0.0; int status = 0; std::vector<MidiTrackPos> posEvents; MidiEvent event; @@ -213,10 +220,13 @@ void MIDIplay::buildTrackData() { MidiTrackPos evtPos; evtPos.delay = ReadVarLen(&trackPtr); - fraction<uint64_t> t = evtPos.delay * currentTempo; CurrentPositionNew.wait = evtPos.delay; - evtPos.timeDelay = t.value(); - time += evtPos.timeDelay; + evtPos.absPos = abs_position; + abs_position += evtPos.delay; + //fraction<uint64_t> t = evtPos.delay * currentTempo; + //evtPos.timeDelay = t.value(); + //evtPos.time = time; + //time += evtPos.timeDelay; trackDataNew[tk].push_back(evtPos); } @@ -226,14 +236,21 @@ void MIDIplay::buildTrackData() event = parseEvent(&trackPtr, status); evtPos.events.push_back(event); if(event.type == MidiEvent::T_SPECIAL && event.subtype == MidiEvent::ST_TEMPOCHANGE) - currentTempo = InvDeltaTicks * fraction<uint64_t>(ReadBEint(event.data.data(), event.data.size())); + { + event.absPosition = abs_position; + tempos.push_back(event); + //currentTempo = InvDeltaTicks * fraction<uint64_t>(ReadBEint(event.data.data(), event.data.size())); + } + evtPos.delay = ReadVarLen(&trackPtr); if((evtPos.delay > 0) || (event.subtype == MidiEvent::ST_ENDTRACK)) { - fraction<uint64_t> t = evtPos.delay * currentTempo; - evtPos.timeDelay = t.value() ; - evtPos.time = time; - time += evtPos.timeDelay; + evtPos.absPos = abs_position; + abs_position += evtPos.delay; + //fraction<uint64_t> t = evtPos.delay * currentTempo; + //evtPos.timeDelay = t.value() ; + //evtPos.time = time; + //time += evtPos.timeDelay; evtPos.sortEvents(); trackDataNew[tk].push_back(evtPos); evtPos.reset(); @@ -248,6 +265,66 @@ void MIDIplay::buildTrackData() CurrentPositionNew.track[tk].pos = &trackDataNew[tk][0]; } + //! Calculate time basing on collected tempo events + for(size_t tk = 0; tk < trackCount; ++tk) + { + fraction<uint64_t> currentTempo = Tempo; + double time = 0.0; + uint8_t abs_position = 0; + size_t tempo_change_index = 0; + std::vector<MidiTrackPos> &track = trackDataNew[tk]; + if(track.empty()) + continue;//Empty track is useless! + MidiTrackPos *posPrev = &track[0];//First element + for(std::vector<MidiTrackPos>::iterator it = track.begin(); it != track.end(); it++) + { + MidiTrackPos &pos = *it; + if( (posPrev != &pos) && //Skip first event + (!tempos.empty()) && //Only when in-track tempo events are available + (tempo_change_index < tempos.size()) + ) + { + MidiEvent &tempo = tempos[tempo_change_index]; + if(tempo.absPosition <= abs_position) + { + /* If tempo event appears between of two events, + * calculate pre-delay and post-delay are going + * between each event and tempo event between of them */ + uint64_t preDelay = 0, postDelay = 0; + + //Delay between left event and tempo + preDelay = (tempo.absPosition - posPrev->absPos); + //Delay between tempo and right event + postDelay = (pos.absPos - tempo.absPosition); + + //Time delay between left event and tempo + fraction<uint64_t> t = preDelay * currentTempo; + pos.timeDelay = t.value(); + + //Apply new tempo + currentTempo = InvDeltaTicks * fraction<uint64_t>(ReadBEint(tempo.data.data(), tempo.data.size())); + + //Time delay between tempo and right event + t = postDelay * currentTempo; + pos.timeDelay += t.value(); + + //Store Common time delay + pos.time = time; + time += pos.timeDelay; + tempo_change_index++; + } + } + else + { + fraction<uint64_t> t = pos.delay * currentTempo; + pos.timeDelay = t.value(); + pos.time = time; + time += pos.timeDelay; + } + abs_position += pos.delay; + posPrev = &pos; + } + } } MIDIplay::MIDIplay(): @@ -1098,7 +1175,7 @@ bool MIDIplay::ProcessEventsNew() break;//Stop event handling on catching loopEnd event! } - std::fprintf(stdout, "Time: %f\r", CurrentPositionNew.track[tk].pos->time); + std::fprintf(stdout, "Time: %10f\r", CurrentPositionNew.track[tk].pos->time); std::fflush(stdout); // Read next event time (unless the track just ended) diff --git a/src/adlmidi_private.hpp b/src/adlmidi_private.hpp index a6ea6ae..d7065f8 100644 --- a/src/adlmidi_private.hpp +++ b/src/adlmidi_private.hpp @@ -340,9 +340,10 @@ private: uint8_t subtype; //! Targeted MIDI channel uint8_t channel; - //! Reserved 5 bytes padding uint8_t __padding[5]; + //! Absolute tick position (Used for the tempo calculation only) + uint64_t absPosition; //! Raw data of this event std::vector<uint8_t> data; }; @@ -362,6 +363,8 @@ private: double time; //! Delay to next event in ticks uint64_t delay; + //! Absolute position in ticks + uint64_t absPos; //! Delay to next event in seconds double timeDelay; std::vector<MidiEvent> events; |