aboutsummaryrefslogtreecommitdiff
path: root/src/midi_sequencer_impl.hpp
diff options
context:
space:
mode:
authorWohlstand <admin@wohlnet.ru>2021-10-25 05:26:48 +0300
committerWohlstand <admin@wohlnet.ru>2021-10-25 05:26:48 +0300
commit57a8d528e877d3810deaa1ce24545512e1a46ac9 (patch)
tree437057856291966b8b813d6e8b12e3e581d2fea5 /src/midi_sequencer_impl.hpp
parent87ed51791a8f486b48eed5aaa8f58ee8c30c8bdf (diff)
downloadlibADLMIDI-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.hpp156
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();
}