diff options
author | Wohlstand <admin@wohlnet.ru> | 2021-10-25 05:26:48 +0300 |
---|---|---|
committer | Wohlstand <admin@wohlnet.ru> | 2021-10-25 05:26:48 +0300 |
commit | 57a8d528e877d3810deaa1ce24545512e1a46ac9 (patch) | |
tree | 437057856291966b8b813d6e8b12e3e581d2fea5 /src/midi_sequencer_impl.hpp | |
parent | 87ed51791a8f486b48eed5aaa8f58ee8c30c8bdf (diff) | |
download | libADLMIDI-57a8d528e877d3810deaa1ce24545512e1a46ac9.tar.gz libADLMIDI-57a8d528e877d3810deaa1ce24545512e1a46ac9.tar.bz2 libADLMIDI-57a8d528e877d3810deaa1ce24545512e1a46ac9.zip |
Updated the midi sequencer code
Diffstat (limited to 'src/midi_sequencer_impl.hpp')
-rw-r--r-- | src/midi_sequencer_impl.hpp | 156 |
1 files changed, 151 insertions, 5 deletions
diff --git a/src/midi_sequencer_impl.hpp b/src/midi_sequencer_impl.hpp index f13768f..dac8b12 100644 --- a/src/midi_sequencer_impl.hpp +++ b/src/midi_sequencer_impl.hpp @@ -314,6 +314,7 @@ BW_MidiSequencer::BW_MidiSequencer() : m_loopEndTime(-1.0), m_tempoMultiplier(1.0), m_atEnd(false), + m_loopCount(-1), m_trackSolo(~static_cast<size_t>(0)), m_triggerHandler(NULL), m_triggerUserData(NULL) @@ -417,6 +418,33 @@ bool BW_MidiSequencer::setTrackEnabled(size_t track, bool enable) return true; } +bool BW_MidiSequencer::setChannelEnabled(size_t channel, bool enable) +{ + if(channel >= 16) + return false; + + if(!enable && m_channelDisable[channel] != !enable) + { + uint8_t ch = static_cast<uint8_t>(channel); + + // Releae all pedals + m_interface->rt_controllerChange(m_interface->rtUserData, ch, 64, 0); + m_interface->rt_controllerChange(m_interface->rtUserData, ch, 66, 0); + + // Release all notes on the channel now + for(int i = 0; i < 127; ++i) + { + if(m_interface->rt_noteOff) + m_interface->rt_noteOff(m_interface->rtUserData, ch, i); + if(m_interface->rt_noteOffVel) + m_interface->rt_noteOffVel(m_interface->rtUserData, ch, i, 0); + } + } + + m_channelDisable[channel] = !enable; + return true; +} + void BW_MidiSequencer::setSoloTrack(size_t track) { m_trackSolo = track; @@ -448,6 +476,18 @@ void BW_MidiSequencer::setLoopEnabled(bool enabled) m_loopEnabled = enabled; } +int BW_MidiSequencer::getLoopsCount() +{ + return m_loopCount >= 0 ? (m_loopCount + 1) : m_loopCount; +} + +void BW_MidiSequencer::setLoopsCount(int loops) +{ + if(loops >= 1) + loops -= 1; // Internally, loops count has the 0 base + m_loopCount = loops; +} + void BW_MidiSequencer::setLoopHooksOnly(bool enabled) { m_loopHooksOnly = enabled; @@ -491,6 +531,7 @@ void BW_MidiSequencer::buildSmfSetupReset(size_t trackCount) m_loopEndTime = -1.0; m_loopFormat = Loop_Default; m_trackDisable.clear(); + std::memset(m_channelDisable, 0, sizeof(m_channelDisable)); m_trackSolo = ~(size_t)0; m_musTitle.clear(); m_musCopyright.clear(); @@ -903,6 +944,85 @@ void BW_MidiSequencer::buildTimeLine(const std::vector<MidiEvent> &tempos, // Set lowest level of the loop stack m_loop.stackLevel = -1; + // Set the count of loops + m_loop.loopsCount = m_loopCount; + m_loop.loopsLeft = m_loopCount; + + /********************************************************************************/ + // Find and set proper loop points + /********************************************************************************/ + if(!m_loop.invalidLoop && !m_currentPosition.track.empty()) + { + unsigned caughLoopStart = 0; + bool scanDone = false; + const size_t trackCount = m_currentPosition.track.size(); + Position rowPosition(m_currentPosition); + + while(!scanDone) + { + const Position rowBeginPosition(rowPosition); + + for(size_t tk = 0; tk < trackCount; ++tk) + { + Position::TrackInfo &track = rowPosition.track[tk]; + if((track.lastHandledEvent >= 0) && (track.delay <= 0)) + { + // Check is an end of track has been reached + if(track.pos == m_trackData[tk].end()) + { + track.lastHandledEvent = -1; + continue; + } + + for(size_t i = 0; i < track.pos->events.size(); i++) + { + const MidiEvent &evt = track.pos->events[i]; + if(evt.type == MidiEvent::T_SPECIAL && evt.subtype == MidiEvent::ST_LOOPSTART) + { + caughLoopStart++; + scanDone = true; + break; + } + } + + if(track.lastHandledEvent >= 0) + { + track.delay += track.pos->delay; + track.pos++; + } + } + } + + // Find a shortest delay from all track + uint64_t shortestDelay = 0; + bool shortestDelayNotFound = true; + + for(size_t tk = 0; tk < trackCount; ++tk) + { + Position::TrackInfo &track = rowPosition.track[tk]; + if((track.lastHandledEvent >= 0) && (shortestDelayNotFound || track.delay < shortestDelay)) + { + shortestDelay = track.delay; + shortestDelayNotFound = false; + } + } + + // Schedule the next playevent to be processed after that delay + for(size_t tk = 0; tk < trackCount; ++tk) + rowPosition.track[tk].delay -= shortestDelay; + + if(caughLoopStart > 0) + { + m_loopBeginPosition = rowBeginPosition; + m_loopBeginPosition.absTimePosition = m_loopStartTime; + scanDone = true; + } + + if(shortestDelayNotFound) + break; + } + } + /********************************************************************************/ // Resolve "hell of all times" of too short drum notes: // move too short percussion note-offs far far away as possible @@ -1154,7 +1274,7 @@ bool BW_MidiSequencer::processEvents(bool isSeek) #endif m_currentPosition.wait += t.value(); - if(caughLoopStart > 0) + if(caughLoopStart > 0 && m_loopBeginPosition.absTimePosition <= 0.0) m_loopBeginPosition = rowBeginPosition; if(caughLoopStackStart > 0) @@ -1238,13 +1358,24 @@ bool BW_MidiSequencer::processEvents(bool isSeek) m_loop.caughtEnd = false; shortestDelay = 0; - if(!m_loopEnabled || m_loopHooksOnly) + if(!m_loopEnabled || (shortestDelayNotFound && m_loop.loopsCount >= 0 && m_loop.loopsLeft < 1) || m_loopHooksOnly) { m_atEnd = true; // Don't handle events anymore m_currentPosition.wait += m_postSongWaitDelay; // One second delay until stop playing return true; // We have caugh end here! } - m_currentPosition = m_loopBeginPosition; + + if(m_loop.temporaryBroken) + { + m_currentPosition = m_trackBeginPosition; + m_loop.temporaryBroken = false; + } + else if(m_loop.loopsCount < 0 || m_loop.loopsLeft >= 1) + { + m_currentPosition = m_loopBeginPosition; + if(m_loop.loopsCount >= 1) + m_loop.loopsLeft--; + } } return true; // Has events in queue @@ -1797,6 +1928,8 @@ void BW_MidiSequencer::handleEvent(size_t track, const BW_MidiSequencer::MidiEve { case MidiEvent::T_NOTEOFF: // Note off { + if(midCh < 16 && m_channelDisable[midCh]) + break; // Disabled channel uint8_t note = evt.data[0]; uint8_t vol = evt.data[1]; if(m_interface->rt_noteOff) @@ -1808,6 +1941,8 @@ void BW_MidiSequencer::handleEvent(size_t track, const BW_MidiSequencer::MidiEve case MidiEvent::T_NOTEON: // Note on { + if(midCh < 16 && m_channelDisable[midCh]) + break; // Disabled channel uint8_t note = evt.data[0]; uint8_t vol = evt.data[1]; m_interface->rt_noteOn(m_interface->rtUserData, static_cast<uint8_t>(midCh), note, vol); @@ -1897,7 +2032,7 @@ double BW_MidiSequencer::seek(double seconds, const double granularity) /* Attempt to go away out of song end must rewind position to begin */ if(seconds > m_fullSongTimeLength) { - rewind(); + this->rewind(); return 0.0; } @@ -1912,7 +2047,7 @@ double BW_MidiSequencer::seek(double seconds, const double granularity) * - All sustaining notes must be killed * - Ignore Note-On events */ - rewind(); + this->rewind(); /* * Set "loop Start" to false to prevent overwrite of loopStart position with @@ -1922,6 +2057,8 @@ double BW_MidiSequencer::seek(double seconds, const double granularity) */ m_loop.caughtStart = false; + m_loop.temporaryBroken = (seconds >= m_loopEndTime); + while((m_currentPosition.absTimePosition < seconds) && (m_currentPosition.absTimePosition < m_fullSongTimeLength)) { @@ -1951,6 +2088,13 @@ double BW_MidiSequencer::seek(double seconds, const double granularity) if(m_currentPosition.wait < 0.0) m_currentPosition.wait = 0.0; + if(m_atEnd) + { + this->rewind(); + m_loopEnabled = loopFlagState; + return 0.0; + } + m_time.reset(); m_time.delay = m_currentPosition.wait; @@ -1983,8 +2127,10 @@ void BW_MidiSequencer::rewind() m_currentPosition = m_trackBeginPosition; m_atEnd = false; + m_loop.loopsCount = m_loopCount; m_loop.reset(); m_loop.caughtStart = true; + m_loop.temporaryBroken = false; m_time.reset(); } |