diff options
author | Vitaly Novichkov <admin@wohlnet.ru> | 2018-07-08 06:39:42 +0300 |
---|---|---|
committer | Vitaly Novichkov <admin@wohlnet.ru> | 2018-07-08 06:39:42 +0300 |
commit | 75dda861e1ea98c4dbc0123fc20516d0ddf70463 (patch) | |
tree | 671129787379ea4de41906f544c6a5a3b1475a81 /src/midi_sequencer_impl.hpp | |
parent | 4664f286e238e97d0d6b3bc4dff5f0640a277e7f (diff) | |
download | libADLMIDI-75dda861e1ea98c4dbc0123fc20516d0ddf70463.tar.gz libADLMIDI-75dda861e1ea98c4dbc0123fc20516d0ddf70463.tar.bz2 libADLMIDI-75dda861e1ea98c4dbc0123fc20516d0ddf70463.zip |
[Experimental] Complex loop support
Diffstat (limited to 'src/midi_sequencer_impl.hpp')
-rw-r--r-- | src/midi_sequencer_impl.hpp | 398 |
1 files changed, 343 insertions, 55 deletions
diff --git a/src/midi_sequencer_impl.hpp b/src/midi_sequencer_impl.hpp index a0830f0..9785331 100644 --- a/src/midi_sequencer_impl.hpp +++ b/src/midi_sequencer_impl.hpp @@ -173,8 +173,15 @@ void BW_MidiSequencer::MidiTrackRow::sortEvents(bool *noteStates) controllers.reserve(events.size()); controllers.push_back(events[i]); } - else if((events[i].type == MidiEvent::T_SPECIAL) - && ((events[i].subtype == MidiEvent::ST_MARKER) || (events[i].subtype == MidiEvent::ST_DEVICESWITCH))) + else if((events[i].type == MidiEvent::T_SPECIAL) && ( + (events[i].subtype == MidiEvent::ST_MARKER) || + (events[i].subtype == MidiEvent::ST_DEVICESWITCH) || + (events[i].subtype == MidiEvent::ST_LOOPSTART) || + (events[i].subtype == MidiEvent::ST_LOOPEND) || + (events[i].subtype == MidiEvent::ST_LOOPSTACK_BEGIN) || + (events[i].subtype == MidiEvent::ST_LOOPSTACK_END) || + (events[i].subtype == MidiEvent::ST_LOOPSTACK_BREAK) + )) { if(metas.capacity() == 0) metas.reserve(events.size()); @@ -270,11 +277,11 @@ BW_MidiSequencer::BW_MidiSequencer() : m_loopEndTime(-1.0), m_tempoMultiplier(1.0), m_atEnd(false), - m_loopStart(false), - m_loopEnd(false), - m_invalidLoop(false), m_trackSolo(~(size_t)0) -{} +{ + m_loop.reset(); + m_loop.invalidLoop = false; +} BW_MidiSequencer::~BW_MidiSequencer() {} @@ -394,8 +401,13 @@ bool BW_MidiSequencer::buildTrackData(const std::vector<std::vector<uint8_t> > & m_trackData.resize(trackCount, MidiTrackQueue()); m_trackDisable.resize(trackCount); - m_invalidLoop = false; - bool gotLoopStart = false, gotLoopEnd = false, gotLoopEventInThisRow = false; + m_loop.reset(); + m_loop.invalidLoop = false; + + bool gotGlobalLoopStart = false, + gotGlobalLoopEnd = false, + gotStackLoopStart = false, + gotLoopEventInThisRow = false; //! Tick position of loop start tag uint64_t loopStartTicks = 0; //! Tick position of loop end tag @@ -484,24 +496,24 @@ bool BW_MidiSequencer::buildTrackData(const std::vector<std::vector<uint8_t> > & event.absPosition = abs_position; tempos.push_back(event); } - else if(!m_invalidLoop && (event.subtype == MidiEvent::ST_LOOPSTART)) + else if(!m_loop.invalidLoop && (event.subtype == MidiEvent::ST_LOOPSTART)) { /* * loopStart is invalid when: * - starts together with loopEnd * - appears more than one time in same MIDI file */ - if(gotLoopStart || gotLoopEventInThisRow) - m_invalidLoop = true; + if(gotGlobalLoopStart || gotLoopEventInThisRow) + m_loop.invalidLoop = true; else { - gotLoopStart = true; + gotGlobalLoopStart = true; loopStartTicks = abs_position; } //In this row we got loop event, register this! gotLoopEventInThisRow = true; } - else if(!m_invalidLoop && (event.subtype == MidiEvent::ST_LOOPEND)) + else if(!m_loop.invalidLoop && (event.subtype == MidiEvent::ST_LOOPEND)) { /* * loopEnd is invalid when: @@ -509,16 +521,71 @@ bool BW_MidiSequencer::buildTrackData(const std::vector<std::vector<uint8_t> > & * - starts together with loopStart * - appars more than one time in same MIDI file */ - if(gotLoopEnd || gotLoopEventInThisRow) - m_invalidLoop = true; + if(gotGlobalLoopEnd || gotLoopEventInThisRow) + { + m_loop.invalidLoop = true; + if(m_interface->onDebugMessage) + { + m_interface->onDebugMessage( + m_interface->onDebugMessage_userData, + "== Invalid loop detected! %s %s ==", + (gotGlobalLoopEnd ? "[Caught more than 1 loopEnd!]" : ""), + (gotLoopEventInThisRow ? "[loopEnd in same row as loopStart!]" : "") + ); + } + } else { - gotLoopEnd = true; + gotGlobalLoopEnd = true; loopEndTicks = abs_position; } - //In this row we got loop event, register this! + // In this row we got loop event, register this! gotLoopEventInThisRow = true; } + else if(!m_loop.invalidLoop && (event.subtype == MidiEvent::ST_LOOPSTACK_BEGIN)) + { + if(!gotStackLoopStart) + { + if(!gotGlobalLoopStart) + loopStartTicks = abs_position; + gotStackLoopStart = true; + } + + m_loop.stackUp(); + if(m_loop.stackLevel >= static_cast<int>(m_loop.stack.size())) + { + LoopStackEntry e; + e.loops = event.data[0]; + e.infinity = (event.data[0] == 0); + e.start = abs_position; + e.end = abs_position; + m_loop.stack.push_back(e); + } + } + else if(!m_loop.invalidLoop && + ((event.subtype == MidiEvent::ST_LOOPSTACK_END) || + (event.subtype == MidiEvent::ST_LOOPSTACK_BREAK)) + ) + { + if(m_loop.stackLevel <= -1) + { + m_loop.invalidLoop = true; // Caught loop end without of loop start! + if(m_interface->onDebugMessage) + { + m_interface->onDebugMessage( + m_interface->onDebugMessage_userData, + "== Invalid loop detected! [Caught loop end without of loop start] ==" + ); + } + } + else + { + if(loopEndTicks < abs_position) + loopEndTicks = abs_position; + m_loop.getCurStack().end = abs_position; + m_loop.stackDown(); + } + } } if(event.subtype != MidiEvent::ST_ENDTRACK)//Don't try to read delta after EndOfTrack event! @@ -551,15 +618,24 @@ bool BW_MidiSequencer::buildTrackData(const std::vector<std::vector<uint8_t> > & m_currentPosition.track[tk].pos = m_trackData[tk].begin(); } - if(gotLoopStart && !gotLoopEnd) + if(gotGlobalLoopStart && !gotGlobalLoopEnd) { - gotLoopEnd = true; + gotGlobalLoopEnd = true; loopEndTicks = ticksSongLength; } //loopStart must be located before loopEnd! if(loopStartTicks >= loopEndTicks) - m_invalidLoop = true; + { + m_loop.invalidLoop = true; + if(m_interface->onDebugMessage) + { + m_interface->onDebugMessage( + m_interface->onDebugMessage_userData, + "== Invalid loop detected! [loopEnd is going before loopStart] ==" + ); + } + } /********************************************************************************/ //Calculate time basing on collected tempo events @@ -666,7 +742,7 @@ bool BW_MidiSequencer::buildTrackData(const std::vector<std::vector<uint8_t> > & } //Capture loop points time positions - if(!m_invalidLoop) + if(!m_loop.invalidLoop) { // Set loop points times if(loopStartTicks == pos.absPos) @@ -822,9 +898,14 @@ bool BW_MidiSequencer::processEvents(bool isSeek) if(m_atEnd) return false;//No more events in the queue - m_loopEnd = false; + m_loop.caughtEnd = false; const size_t TrackCount = m_currentPosition.track.size(); - const Position RowBeginPosition(m_currentPosition); + const Position rowBeginPosition(m_currentPosition); + bool doLoopJump = false; + unsigned caughLoopStart = 0; + unsigned caughLoopStackStart = 0; + unsigned caughLoopStackEnds = 0; + unsigned caughLoopStackBreaks = 0; #ifdef DEBUG_TIME_CALCULATION double maxTime = 0.0; @@ -847,14 +928,41 @@ bool BW_MidiSequencer::processEvents(bool isSeek) { const MidiEvent &evt = track.pos->events[i]; #ifdef ENABLE_BEGIN_SILENCE_SKIPPING - if(!CurrentPositionNew.began && (evt.type == MidiEvent::T_NOTEON)) - CurrentPositionNew.began = true; + if(!m_currentPosition.began && (evt.type == MidiEvent::T_NOTEON)) + m_currentPosition.began = true; #endif if(isSeek && (evt.type == MidiEvent::T_NOTEON)) continue; handleEvent(tk, evt, track.lastHandledEvent); - if(m_loopEnd) + + if(m_loop.caughtStart) + { + caughLoopStart++; + m_loop.caughtStart = false; + } + + if(m_loop.caughtStackStart) + { + caughLoopStackStart++; + m_loop.caughtStackStart = false; + } + + if(m_loop.caughtStackBreak) + { + caughLoopStackBreaks++; + m_loop.caughtStackBreak = false; + } + + if(m_loop.caughtEnd || m_loop.isStackEnd()) + { + if(m_loop.caughtStackEnd) + { + m_loop.caughtStackEnd = false; + caughLoopStackEnds++; + } + doLoopJump = true; break;//Stop event handling on catching loopEnd event! + } } #ifdef DEBUG_TIME_CALCULATION @@ -867,6 +975,9 @@ bool BW_MidiSequencer::processEvents(bool isSeek) track.delay += track.pos->delay; track.pos++; } + + if(doLoopJump) + break; } } @@ -899,21 +1010,81 @@ bool BW_MidiSequencer::processEvents(bool isSeek) fraction<uint64_t> t = shortest * m_tempo; #ifdef ENABLE_BEGIN_SILENCE_SKIPPING - if(CurrentPositionNew.began) + if(m_currentPosition.began) #endif m_currentPosition.wait += t.value(); //if(shortest > 0) UI.PrintLn("Delay %ld (%g)", shortest, (double)t.valuel()); - if(m_loopStart) + if(caughLoopStart > 0) + m_loopBeginPosition = rowBeginPosition; + + if(caughLoopStackStart > 0) { - m_loopBeginPosition = RowBeginPosition; - m_loopStart = false; + while(caughLoopStackStart > 0) + { + m_loop.stackUp(); + LoopStackEntry &s = m_loop.getCurStack(); + s.startPosition = rowBeginPosition; + caughLoopStackStart--; + } + return true; + } + + if(caughLoopStackBreaks > 0) + { + while(caughLoopStackBreaks > 0) + { + LoopStackEntry &s = m_loop.getCurStack(); + s.loops = 0; + s.infinity = false; + // Quit the loop + m_loop.stackDown(); + caughLoopStackBreaks--; + } } - if(shortest_no || m_loopEnd) + if(caughLoopStackEnds > 0) + { + while(caughLoopStackEnds > 0) + { + LoopStackEntry &s = m_loop.getCurStack(); + if(s.infinity) + { + m_currentPosition = s.startPosition; + m_loop.skipStackStart = true; + return true; + } + else + if(s.loops >= 0) + { + s.loops--; + if(s.loops > 0) + { + m_currentPosition = s.startPosition; + m_loop.skipStackStart = true; + return true; + } + else + { + // Quit the loop + m_loop.stackDown(); + } + } + else + { + // Quit the loop + m_loop.stackDown(); + } + caughLoopStackEnds--; + } + + return true; + } + + if(shortest_no || m_loop.caughtEnd) { //Loop if song end or loop end point has reached - m_loopEnd = false; + m_loop.caughtEnd = false; shortest = 0; if(!m_loopEnabled) { @@ -1050,6 +1221,46 @@ BW_MidiSequencer::MidiEvent BW_MidiSequencer::parseEvent(const uint8_t **pptr, c evt.data.clear();//Data is not needed return evt; } + + if(!data.compare(0, 10, "loopstart=")) + { + evt.type = MidiEvent::T_SPECIAL; + evt.subtype = MidiEvent::ST_LOOPSTACK_BEGIN; + uint8_t loops = static_cast<uint8_t>(atoi(data.substr(10).c_str())); + evt.data.clear(); + evt.data.push_back(loops); + + if(m_interface->onDebugMessage) + { + m_interface->onDebugMessage( + m_interface->onDebugMessage_userData, + "Stack Marker Loop Start at %d to %d level with %d loops", + m_loop.stackLevel, + m_loop.stackLevel + 1, + loops + ); + } + return evt; + } + + if(!data.compare(0, 8, "loopend=")) + { + evt.type = MidiEvent::T_SPECIAL; + evt.subtype = MidiEvent::ST_LOOPSTACK_END; + evt.data.clear(); + + if(m_interface->onDebugMessage) + { + m_interface->onDebugMessage( + m_interface->onDebugMessage_userData, + "Stack Marker Loop %s at %d to %d level", + (evt.subtype == MidiEvent::ST_LOOPSTACK_END ? "End" : "Break"), + m_loop.stackLevel, + m_loop.stackLevel - 1 + ); + } + return evt; + } } if(evtype == MidiEvent::ST_ENDTRACK) @@ -1119,13 +1330,60 @@ BW_MidiSequencer::MidiEvent BW_MidiSequencer::parseEvent(const uint8_t **pptr, c if((evType == MidiEvent::T_NOTEON) && (evt.data[1] == 0)) { evt.type = MidiEvent::T_NOTEOFF; // Note ON with zero velocity is Note OFF! - } //111'th loopStart controller (RPG Maker and others) - else if((evType == MidiEvent::T_CTRLCHANGE) && (evt.data[0] == 111)) + } + else + if(evType == MidiEvent::T_CTRLCHANGE) { - //Change event type to custom Loop Start event and clear data - evt.type = MidiEvent::T_SPECIAL; - evt.subtype = MidiEvent::ST_LOOPSTART; - evt.data.clear(); + //111'th loopStart controller (RPG Maker and others) + if((m_format == Format_MIDI) && (evt.data[0] == 111)) + { + //Change event type to custom Loop Start event and clear data + evt.type = MidiEvent::T_SPECIAL; + evt.subtype = MidiEvent::ST_LOOPSTART; + evt.data.clear(); + } + + if(m_format == Format_XMIDI) + { + if(evt.data[0] == 116) + { + evt.type = MidiEvent::T_SPECIAL; + evt.subtype = MidiEvent::ST_LOOPSTACK_BEGIN; + evt.data[0] = evt.data[1]; + evt.data.pop_back(); + + if(m_interface->onDebugMessage) + { + m_interface->onDebugMessage( + m_interface->onDebugMessage_userData, + "Stack XMI Loop Start at %d to %d level with %d loops", + m_loop.stackLevel, + m_loop.stackLevel + 1, + evt.data[0] + ); + } + } + + if(evt.data[0] == 117) + { + evt.type = MidiEvent::T_SPECIAL; + evt.subtype = evt.data[1] < 64 ? + MidiEvent::ST_LOOPSTACK_BREAK : + MidiEvent::ST_LOOPSTACK_END; + evt.data.clear(); + + if(m_interface->onDebugMessage) + { + m_interface->onDebugMessage( + m_interface->onDebugMessage_userData, + "Stack XMI Loop %s at %d to %d level", + (evt.subtype == MidiEvent::ST_LOOPSTACK_END ? "End" : "Break"), + m_loop.stackLevel, + m_loop.stackLevel - 1 + ); + } + } + } } return evt; @@ -1186,7 +1444,7 @@ void BW_MidiSequencer::handleEvent(size_t track, const BW_MidiSequencer::MidiEve // Special event FF uint8_t evtype = evt.subtype; uint64_t length = (uint64_t)evt.data.size(); - std::string data(length ? (const char *)evt.data.data() : 0, (size_t)length); + const char *data(length ? (const char *)evt.data.data() : ""); if(evtype == MidiEvent::ST_ENDTRACK)//End Of Track { @@ -1209,9 +1467,9 @@ void BW_MidiSequencer::handleEvent(size_t track, const BW_MidiSequencer::MidiEve if(evtype == MidiEvent::ST_DEVICESWITCH) { if(m_interface->onDebugMessage) - m_interface->onDebugMessage(m_interface->onDebugMessage_userData, "Switching another device: %s", data.c_str()); + m_interface->onDebugMessage(m_interface->onDebugMessage_userData, "Switching another device: %s", data); if(m_interface->rt_deviceSwitch) - m_interface->rt_deviceSwitch(m_interface->rtUserData, track, data.c_str(), data.size()); + m_interface->rt_deviceSwitch(m_interface->rtUserData, track, data, length); return; } @@ -1219,17 +1477,43 @@ void BW_MidiSequencer::handleEvent(size_t track, const BW_MidiSequencer::MidiEve // UI.PrintLn("Meta %d: %s", evtype, data.c_str()); //Turn on Loop handling when loop is enabled - if(m_loopEnabled && !m_invalidLoop) + if(m_loopEnabled && !m_loop.invalidLoop) { if(evtype == MidiEvent::ST_LOOPSTART) // Special non-spec MIDI loop Start point { - m_loopStart = true; + m_loop.caughtStart = true; return; } if(evtype == MidiEvent::ST_LOOPEND) // Special non-spec MIDI loop End point { - m_loopEnd = true; + m_loop.caughtEnd = true; + return; + } + + if(evtype == MidiEvent::ST_LOOPSTACK_BEGIN) + { + if(m_loop.skipStackStart) + { + m_loop.skipStackStart = false; + return; + } + LoopStackEntry &s = m_loop.stack[(m_loop.stackLevel + 1)]; + s.loops = (int)data[0]; + s.infinity = (data[0] == 0); + m_loop.caughtStackStart = true; + return; + } + + if(evtype == MidiEvent::ST_LOOPSTACK_END) + { + m_loop.caughtStackEnd = true; + return; + } + + if(evtype == MidiEvent::ST_LOOPSTACK_BREAK) + { + m_loop.caughtStackBreak = true; return; } } @@ -1384,7 +1668,7 @@ double BW_MidiSequencer::seek(double seconds, const double granularity) * * TODO: Detect & set loopStart position on load time to don't break loop while seeking */ - m_loopStart = false; + m_loop.caughtStart = false; while((m_currentPosition.absTimePosition < seconds) && (m_currentPosition.absTimePosition < m_fullSongTimeLength)) @@ -1442,9 +1726,10 @@ double BW_MidiSequencer::getLoopEnd() void BW_MidiSequencer::rewind() { m_currentPosition = m_trackBeginPosition; - m_atEnd = false; - m_loopStart = true; - m_loopEnd = false; + m_atEnd = false; + + m_loop.reset(); + m_loop.caughtStart = true; } void BW_MidiSequencer::setTempo(double tempo) @@ -1510,8 +1795,8 @@ bool BW_MidiSequencer::loadMIDI(FileAndMemReader &fr) } m_atEnd = false; - m_loopStart = true; - m_invalidLoop = false; + m_loop.fullReset(); + m_loop.caughtStart = true; m_format = Format_MIDI; @@ -1523,7 +1808,7 @@ bool BW_MidiSequencer::loadMIDI(FileAndMemReader &fr) const size_t headerSize = 4 + 4 + 2 + 2 + 2; // 14 char headerBuf[headerSize] = ""; size_t DeltaTicks = 192, TrackCount = 1; - unsigned Fmt = 0; + unsigned smfFormat = 0; riffskip: fsize = fr.read(headerBuf, 1, headerSize); @@ -1630,6 +1915,8 @@ riffskip: cvt_buf.set(mid); //Open converted MIDI file fr.openData(mid, static_cast<size_t>(mid_len)); + //Set format as XMIDI + m_format = Format_XMIDI; //Re-Read header again! goto riffskip; } @@ -1752,12 +2039,12 @@ riffskip: return false; } - Fmt = (unsigned)readBEint(headerBuf + 8, 2); + smfFormat = (unsigned)readBEint(headerBuf + 8, 2); TrackCount = (size_t)readBEint(headerBuf + 10, 2); DeltaTicks = (size_t)readBEint(headerBuf + 12, 2); - if(Fmt > 2) - Fmt = 1; + if(smfFormat > 2) + smfFormat = 1; } } @@ -1875,7 +2162,8 @@ riffskip: return false; } - m_smfFormat = Fmt; + m_smfFormat = smfFormat; + m_loop.stackLevel = -1; return true; } |