aboutsummaryrefslogtreecommitdiff
path: root/src/adlmidi_midiplay.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/adlmidi_midiplay.cpp')
-rw-r--r--src/adlmidi_midiplay.cpp1116
1 files changed, 610 insertions, 506 deletions
diff --git a/src/adlmidi_midiplay.cpp b/src/adlmidi_midiplay.cpp
index f536f4e..577e8b8 100644
--- a/src/adlmidi_midiplay.cpp
+++ b/src/adlmidi_midiplay.cpp
@@ -23,7 +23,6 @@
#include "adlmidi_private.hpp"
-
// Mapping from MIDI volume level to OPL level value.
static const uint32_t DMX_volume_mapping_table[] =
@@ -132,27 +131,29 @@ void MIDIplay::AdlChannel::AddAge(int64_t ms)
MIDIplay::MidiEvent::MidiEvent() :
type(T_UNKNOWN),
subtype(T_UNKNOWN),
- channel(0)
+ channel(0),
+ isValid(1),
+ absPosition(0)
{}
-MIDIplay::MidiTrackPos::MidiTrackPos() :
+MIDIplay::MidiTrackRow::MidiTrackRow() :
time(0.0),
delay(0),
- timeDelay(0.0),
- next(NULL)
+ absPos(0),
+ timeDelay(0.0)
{}
-void MIDIplay::MidiTrackPos::reset()
+void MIDIplay::MidiTrackRow::reset()
{
time = 0.0;
delay = 0;
+ absPos = 0;
timeDelay = 0.0;
events.clear();
- next = NULL;
}
-void MIDIplay::MidiTrackPos::sortEvents()
+void MIDIplay::MidiTrackRow::sortEvents()
{
std::vector<MidiEvent> metas;
std::vector<MidiEvent> noteOffs;
@@ -172,7 +173,7 @@ void MIDIplay::MidiTrackPos::sortEvents()
controllers.push_back(events[i]);
}
else
- if((events[i].type == MidiEvent::T_SPECIAL) && (events[i].subtype == MidiEvent::ST_META))
+ if((events[i].type == MidiEvent::T_SPECIAL) && (events[i].subtype == MidiEvent::ST_MARKER))
metas.push_back(events[i]);
else
anyOther.push_back(events[i]);
@@ -185,83 +186,316 @@ void MIDIplay::MidiTrackPos::sortEvents()
events.insert(events.end(), anyOther.begin(), anyOther.end());
}
-void MIDIplay::buildTrackData()
+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, std::vector<MidiTrackPos>());
- trackDataNewStatus.resize(trackCount, 0);
+ trackDataNew.resize(trackCount, MidiTrackQueue());
+
+ 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);
- /* TODO: Based on tempo changes, make accurate seconds time marking.
- * Current way is inaccurate, because of tempo change at different track
- * will cause time desynchronization between tracks.
- * Also, seconds calculation is incorrect */
+ //Tempo change events
+ std::vector<MidiEvent> tempos;
+
+ /*
+ * TODO: Make this be safer for memory in case of broken input data
+ * which may cause going away of available track data (and then give a crash!)
+ */
for(size_t tk = 0; tk < trackCount; ++tk)
{
- fraction<uint64_t> currentTempo = Tempo;
- double time = 0.0;
+ uint64_t abs_position = 0;
int status = 0;
- std::vector<MidiTrackPos> posEvents;
MidiEvent event;
+ bool ok = false;
+ uint8_t *end = TrackData[tk].data() + TrackData[tk].size();
uint8_t *trackPtr = TrackData[tk].data();
//Time delay that follows the first event in the track
{
- MidiTrackPos evtPos;
- evtPos.delay = ReadVarLen(&trackPtr);
- fraction<uint64_t> t = evtPos.delay * currentTempo;
+ MidiTrackRow evtPos;
+ evtPos.delay = ReadVarLenEx(&trackPtr, end, ok);
+ if(!ok)
+ {
+ 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;
- evtPos.timeDelay = t.value();
- time += evtPos.timeDelay;
+ evtPos.absPos = abs_position;
+ abs_position += evtPos.delay;
trackDataNew[tk].push_back(evtPos);
}
- MidiTrackPos evtPos;
+ MidiTrackRow evtPos;
do
{
- event = parseEvent(&trackPtr, status);
+ event = parseEvent(&trackPtr, end, status);
+ if(!event.isValid)
+ {
+ 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);
- if(event.type == MidiEvent::T_SPECIAL && event.subtype == MidiEvent::ST_TEMPOCHANGE)
- currentTempo = InvDeltaTicks * fraction<uint64_t>(ReadBEint(event.data.data(), event.data.size()));
- evtPos.delay = ReadVarLen(&trackPtr);
+ if(event.type == MidiEvent::T_SPECIAL)
+ {
+ if(event.subtype == MidiEvent::ST_TEMPOCHANGE)
+ {
+ event.absPosition = abs_position;
+ tempos.push_back(event);
+ }
+ else
+ if(!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)
+ invalidLoop = true;
+ else
+ {
+ gotLoopStart = true;
+ loopStartTicks = abs_position;
+ }
+ //In this row we got loop event, register this!
+ gotLoopEventInThisRow = true;
+ }
+ else
+ if(!invalidLoop && (event.subtype == MidiEvent::ST_LOOPEND))
+ {
+ /*
+ * loopEnd is invalid when:
+ * - starts before loopStart
+ * - starts together with loopStart
+ * - appars more than one time in same MIDI file
+ */
+ if(gotLoopEnd || gotLoopEventInThisRow)
+ invalidLoop = true;
+ else
+ {
+ gotLoopEnd = true;
+ loopEndTicks = abs_position;
+ }
+ //In this row we got loop event, register this!
+ gotLoopEventInThisRow = true;
+ }
+ }
+
+ if(event.subtype != MidiEvent::ST_ENDTRACK)//Don't try to read delta after EndOfTrack event!
+ {
+ evtPos.delay = ReadVarLenEx(&trackPtr, end, ok);
+ if(!ok)
+ {
+ 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;
+ }
+ }
+
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;
evtPos.sortEvents();
trackDataNew[tk].push_back(evtPos);
evtPos.reset();
+ gotLoopEventInThisRow = false;
}
- } while(event.subtype != MidiEvent::ST_ENDTRACK);
-
- // Build the chain of events
- for(size_t i = 0, j = 1; i < trackDataNew[tk].size() && j < trackDataNew[tk].size(); i++, j++)
- trackDataNew[tk][i].next = &(trackDataNew[tk][j]);
+ } 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][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;
+
+ //! 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;
+ MidiTrackQueue &track = trackDataNew[tk];
+ if(track.empty())
+ continue;//Empty track is useless!
+
+ #ifdef DEBUG_TIME_CALCULATION
+ std::fprintf(stdout, "\n============Track %" PRIuPTR "=============\n", tk);
+ std::fflush(stdout);
+ #endif
+
+ MidiTrackRow *posPrev = &(*(track.begin()));//First element
+ for(MidiTrackQueue::iterator it = track.begin(); it != track.end(); it++)
+ {
+ #ifdef DEBUG_TIME_CALCULATION
+ bool tempoChanged = false;
+ #endif
+ MidiTrackRow &pos = *it;
+ if( (posPrev != &pos) && //Skip first event
+ (!tempos.empty()) && //Only when in-track tempo events are available
+ (tempo_change_index < tempos.size())
+ )
+ {
+ // If tempo event is going between of current and previous event
+ if(tempos[tempo_change_index].absPosition <= pos.absPos)
+ {
+ //Stop points: begin point and tempo change points are before end point
+ std::vector<TempoChangePoint> points;
+ fraction<uint64_t> t;
+ TempoChangePoint firstPoint = {posPrev->absPos, currentTempo};
+ points.push_back(firstPoint);
+
+ //Collect tempo change points between previous and current events
+ do
+ {
+ TempoChangePoint tempoMarker;
+ MidiEvent &tempoPoint = tempos[tempo_change_index];
+ tempoMarker.absPos = tempoPoint.absPosition;
+ tempoMarker.tempo = InvDeltaTicks * fraction<uint64_t>(ReadBEint(tempoPoint.data.data(), tempoPoint.data.size()));
+ points.push_back(tempoMarker);
+ tempo_change_index++;
+ }
+ while((tempo_change_index < tempos.size()) &&
+ (tempos[tempo_change_index].absPosition <= pos.absPos));
+
+ // Re-calculate time delay of previous event
+ time -= posPrev->timeDelay;
+ posPrev->timeDelay = 0.0;
+
+ for(size_t i = 0, j = 1; j < points.size(); i++, j++)
+ {
+ /* If one or more tempo events are appears between of two events,
+ * calculate delays between each tempo point, begin and end */
+ uint64_t midDelay = 0;
+ //Delay between points
+ midDelay = points[j].absPos - points[i].absPos;
+ //Time delay between points
+ t = midDelay * currentTempo;
+ posPrev->timeDelay += t.value();
+
+ //Apply next tempo
+ currentTempo = points[j].tempo;
+ #ifdef DEBUG_TIME_CALCULATION
+ tempoChanged = true;
+ #endif
+ }
+ //Then calculate time between last tempo change point and end point
+ TempoChangePoint tailTempo = points.back();
+ uint64_t postDelay = pos.absPos - tailTempo.absPos;
+ t = postDelay * currentTempo;
+ posPrev->timeDelay += t.value();
+
+ //Store Common time delay
+ posPrev->time = time;
+ time += posPrev->timeDelay;
+ }
+ }
+
+ fraction<uint64_t> t = pos.delay * currentTempo;
+ pos.timeDelay = t.value();
+ 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);
+ #endif
+
+ abs_position += pos.delay;
+ posPrev = &pos;
+ }
+
+ if(time > fullSongTimeLength)
+ fullSongTimeLength = time;
+ }
+
+ fullSongTimeLength += postSongWaitDelay;
+ trackBeginPositionNew = CurrentPositionNew;
+
+ return true;
}
MIDIplay::MIDIplay():
cmf_percussion_mode(false),
- config(NULL),
+ fullSongTimeLength(0.0),
+ postSongWaitDelay(1.0),
+ loopStartTime(-1.0),
+ loopEndTime(-1.0),
+ tempoMultiplier(1.0),
trackStart(false),
atEnd(false),
loopStart(false),
loopEnd(false),
- loopStart_passed(false),
- invalidLoop(false),
- loopStart_hit(false)
+ invalidLoop(false)
{
devices.clear();
+
+ m_setup.AdlBank = 0;
+ m_setup.NumFourOps = 7;
+ m_setup.NumCards = 2;
+ m_setup.HighTremoloMode = false;
+ m_setup.HighVibratoMode = false;
+ m_setup.AdlPercussionMode = false;
+ m_setup.LogarithmicVolumes = false;
+ m_setup.SkipForward = 0;
+ m_setup.loopingIsEnabled = false;
+ m_setup.ScaleModulators = false;
+ m_setup.delay = 0.0;
+ m_setup.carry = 0.0;
+ m_setup.stored_samples = 0;
+ m_setup.backup_samples_size = 0;
+
+ opl.NumCards = m_setup.NumCards;
+ opl.AdlBank = m_setup.AdlBank;
+ opl.NumFourOps = m_setup.NumFourOps;
+ opl.LogarithmicVolumes = m_setup.LogarithmicVolumes;
+ opl.HighTremoloMode = m_setup.HighTremoloMode;
+ opl.HighVibratoMode = m_setup.HighVibratoMode;
+ opl.AdlPercussionMode = m_setup.AdlPercussionMode;
+ opl.ScaleModulators = m_setup.ScaleModulators;
}
uint64_t MIDIplay::ReadVarLen(uint8_t **ptr)
@@ -277,24 +511,33 @@ uint64_t MIDIplay::ReadVarLen(uint8_t **ptr)
return result;
}
-uint64_t MIDIplay::ReadVarLen(size_t tk)
+uint64_t MIDIplay::ReadVarLenEx(uint8_t **ptr, uint8_t *end, bool &ok)
{
uint64_t result = 0;
+ ok = false;
+
for(;;)
{
- uint8_t byte = TrackData[tk][CurrentPosition.track[tk].ptr++];
+ if(*ptr >= end)
+ return 2;
+ unsigned char byte = *((*ptr)++);
result = (result << 7) + (byte & 0x7F);
- if(!(byte & 0x80))
- break;
+ if(!(byte & 0x80)) break;
}
+
+ ok = true;
return result;
}
double MIDIplay::Tick(double s, double granularity)
{
- //if(CurrentPositionNew.began)
+ s *= tempoMultiplier;
+ #ifdef ENABLE_BEGIN_SILENCE_SKIPPING
+ if(CurrentPositionNew.began)
+ #endif
CurrentPositionNew.wait -= s;
+ CurrentPositionNew.absTimePosition += s;
int antiFreezeCounter = 10000;//Limit 10000 loops to avoid freezing
while((CurrentPositionNew.wait <= granularity * 0.5) && (antiFreezeCounter > 0))
@@ -316,42 +559,100 @@ double MIDIplay::Tick(double s, double granularity)
UpdateVibrato(s);
UpdateArpeggio(s);
+ if(CurrentPositionNew.wait < 0.0)//Avoid negative delay value!
+ 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--;
-// }
+void MIDIplay::seek(double seconds)
+{
+ if(seconds < 0.0)
+ return;//Seeking negative position is forbidden! :-P
+ const double granularity = m_setup.mindelay,
+ granualityHalf = granularity * 0.5,
+ s = seconds;//m_setup.delay < m_setup.maxdelay ? m_setup.delay : m_setup.maxdelay;
+
+ bool loopFlagState = m_setup.loopingIsEnabled;
+ m_setup.loopingIsEnabled = false;
+
+ /*
+ * Seeking search is similar to regular ticking, except of next things:
+ * - We don't processsing arpeggio and vibrato
+ * - To keep correctness of the state after seek, begin every search from begin
+ * - All sustaining notes must be killed
+ * - Ignore Note-On events
+ */
+
+ rewind();
+ while((CurrentPositionNew.absTimePosition < seconds) &&
+ (CurrentPositionNew.absTimePosition < fullSongTimeLength))
+ {
+ CurrentPositionNew.wait -= s;
+ CurrentPositionNew.absTimePosition += s;
+ int antiFreezeCounter = 10000;//Limit 10000 loops to avoid freezing
+ double dstWait = CurrentPositionNew.wait + granualityHalf;
+ while((CurrentPositionNew.wait <= granualityHalf)/*&& (antiFreezeCounter > 0)*/)
+ {
+ //std::fprintf(stderr, "wait = %g...\n", CurrentPosition.wait);
+ if(!ProcessEventsNew(true))
+ break;
+ //Avoid freeze because of no waiting increasing in more than 10000 cycles
+ if(CurrentPositionNew.wait <= dstWait)
+ antiFreezeCounter--;
+ else
+ {
+ dstWait = CurrentPositionNew.wait + granualityHalf;
+ antiFreezeCounter = 10000;
+ }
+ }
+ if(antiFreezeCounter <= 0)
+ CurrentPositionNew.wait += 1.0;/* Add extra 1 second when over 10000 events
+ with zero delay are been detected */
+ }
+
+ if(CurrentPositionNew.wait < 0.0)
+ CurrentPositionNew.wait = 0.0;
-// if(antiFreezeCounter <= 0)
-// CurrentPosition.wait += 1.0;/* Add extra 1 second when over 10000 events
-// with zero delay are been detected */
+ m_setup.loopingIsEnabled = loopFlagState;
+ m_setup.delay = CurrentPositionNew.wait;
+ m_setup.carry = 0.0;
+}
+
+double MIDIplay::tell()
+{
+ return CurrentPositionNew.absTimePosition;
+}
-// for(uint16_t c = 0; c < opl.NumChannels; ++c)
-// ch[c].AddAge(static_cast<int64_t>(s * 1000.0));
+double MIDIplay::timeLength()
+{
+ return fullSongTimeLength;
+}
-// UpdateVibrato(s);
-// UpdateArpeggio(s);
+double MIDIplay::getLoopStart()
+{
+ return loopStartTime;
+}
-// return CurrentPosition.wait;
+double MIDIplay::getLoopEnd()
+{
+ return loopEndTime;
}
void MIDIplay::rewind()
{
- CurrentPosition = trackBeginPosition;
+ Panic();
+ KillSustainingNotes(-1, -1);
+ CurrentPositionNew = trackBeginPositionNew;
trackStart = true;
atEnd = false;
loopStart = true;
- loopStart_passed = false;
invalidLoop = false;
- loopStart_hit = false;
+}
+
+void MIDIplay::setTempo(double tempo)
+{
+ tempoMultiplier = tempo;
}
void MIDIplay::realTime_ResetState()
@@ -562,10 +863,8 @@ bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity)
for(unsigned ccount = 0; ccount < 2; ++ccount)
{
int32_t c = adlchannel[ccount];
-
if(c < 0)
continue;
-
ir.first->second.phys[ static_cast<uint16_t>(adlchannel[ccount]) ] = i[ccount];
}
NoteUpdate(channel, ir.first, Upd_All | Upd_Patch);
@@ -976,153 +1275,77 @@ void MIDIplay::NoteUpdate(uint16_t MidCh,
}
-bool MIDIplay::ProcessEvents()
+bool MIDIplay::ProcessEventsNew(bool isSeek)
{
- if(TrackData.size() == 0)
+ if(CurrentPositionNew.track.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(opl._parent->loopingIsEnabled == 0)
- {
- 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()
-{
- 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 size_t TrackCount = CurrentPositionNew.track.size();
const PositionNew RowBeginPosition(CurrentPositionNew);
+ #ifdef DEBUG_TIME_CALCULATION
+ double maxTime = 0.0;
+ #endif
+
for(size_t tk = 0; tk < TrackCount; ++tk)
{
- if(CurrentPositionNew.track[tk].status >= 0
- && CurrentPositionNew.track[tk].delay <= 0)
+ PositionNew::TrackInfo &track = CurrentPositionNew.track[tk];
+ if((track.status >= 0) && (track.delay <= 0))
{
// Handle event
- for(size_t i = 0; i < CurrentPositionNew.track[tk].pos->events.size(); i++)
+ for(size_t i = 0; i < track.pos->events.size(); i++)
{
- MidiEvent &evt = CurrentPositionNew.track[tk].pos->events[i];
- HandleEvent(tk, evt, CurrentPositionNew.track[tk].status);
+ MidiEvent &evt = track.pos->events[i];
+ #ifdef ENABLE_BEGIN_SILENCE_SKIPPING
+ if(!CurrentPositionNew.began && (evt.type == MidiEvent::T_NOTEON))
+ CurrentPositionNew.began = true;
+ #endif
+ if(isSeek && (evt.type == MidiEvent::T_NOTEON))
+ continue;
+ HandleEvent(tk, evt, track.status);
if(loopEnd)
break;//Stop event handling on catching loopEnd event!
}
- std::fprintf(stdout, "Time: %f\r", CurrentPositionNew.track[tk].pos->time);
- std::fflush(stdout);
+ #ifdef DEBUG_TIME_CALCULATION
+ if(maxTime < track.pos->time)
+ maxTime = track.pos->time;
+ #endif
// Read next event time (unless the track just ended)
- if(CurrentPositionNew.track[tk].pos->next == NULL/* >= TrackData[tk].size()*/)
- CurrentPositionNew.track[tk].status = -1;
+ if(track.pos == trackDataNew[tk].end())
+ track.status = -1;
- if(CurrentPositionNew.track[tk].status >= 0)
- CurrentPositionNew.track[tk].delay += CurrentPositionNew.track[tk].pos->delay;
-
- if(CurrentPositionNew.track[tk].status >= 0)
- CurrentPositionNew.track[tk].pos = CurrentPositionNew.track[tk].pos->next;
+ if(track.status >= 0)
+ {
+ track.delay += track.pos->delay;
+ track.pos++;
+ }
}
}
+ #ifdef DEBUG_TIME_CALCULATION
+ std::fprintf(stdout, " \r");
+ std::fprintf(stdout, "Time: %10f; Audio: %10f\r", maxTime, CurrentPositionNew.absTimePosition);
+ std::fflush(stdout);
+ #endif
+
// Find shortest delay from all track
uint64_t shortest = 0;
bool shortest_no = true;
for(size_t tk = 0; tk < TrackCount; ++tk)
- if((CurrentPositionNew.track[tk].status >= 0) && (shortest_no || CurrentPositionNew.track[tk].delay < shortest))
+ {
+ PositionNew::TrackInfo &track = CurrentPositionNew.track[tk];
+ if((track.status >= 0) && (shortest_no || track.delay < shortest))
{
- shortest = CurrentPositionNew.track[tk].delay;
+ shortest = track.delay;
shortest_no = false;
}
+ }
//if(shortest > 0) UI.PrintLn("shortest: %ld", shortest);
@@ -1132,37 +1355,17 @@ bool MIDIplay::ProcessEventsNew()
fraction<uint64_t> t = shortest * Tempo;
- //if(CurrentPositionNew.began)
- CurrentPositionNew.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
- */
+ #ifdef ENABLE_BEGIN_SILENCE_SKIPPING
+ if(CurrentPositionNew.began)
+ #endif
+ CurrentPositionNew.wait += t.value();
- if(loopStart_hit && (loopStart || loopEnd)) //Avoid invalid loops
- {
- invalidLoop = true;
- loopStart = false;
- loopEnd = false;
- LoopBeginPositionNew = trackBeginPositionNew;
- }
- else
- loopStart_hit = false;
+ //if(shortest > 0) UI.PrintLn("Delay %ld (%g)", shortest, (double)t.valuel());
if(loopStart)
{
- if(trackStart)
- {
- trackBeginPositionNew = RowBeginPosition;
- trackStart = false;
- atEnd = false;
- }
LoopBeginPositionNew = RowBeginPosition;
loopStart = false;
- loopStart_hit = true;
}
if(shortest_no || loopEnd)
@@ -1170,10 +1373,10 @@ bool MIDIplay::ProcessEventsNew()
//Loop if song end or loop end point has reached
loopEnd = false;
shortest = 0;
- if(opl._parent->loopingIsEnabled == 0)
+ if(!m_setup.loopingIsEnabled)
{
atEnd = true; //Don't handle events anymore
- CurrentPositionNew.wait += 1.0;//One second delay until stop playing
+ CurrentPositionNew.wait += postSongWaitDelay;//One second delay until stop playing
return true;//We have caugh end here!
}
CurrentPositionNew = LoopBeginPositionNew;
@@ -1182,24 +1385,45 @@ bool MIDIplay::ProcessEventsNew()
return true;//Has events in queue
}
-MIDIplay::MidiEvent MIDIplay::parseEvent(uint8_t**pptr, int &status)
+MIDIplay::MidiEvent MIDIplay::parseEvent(uint8_t**pptr, uint8_t *end, int &status)
{
uint8_t *&ptr = *pptr;
MIDIplay::MidiEvent evt;
+
+ if(ptr + 1 > end)
+ {
+ errorString += "parseEvent: Can't read event type byte - Unexpected end of track data.\n";
+ evt.isValid = 0;
+ return evt;
+ }
+
unsigned char byte = *(ptr++);
+ bool ok = false;
- if(byte == 0xF7 || byte == 0xF0) // Ignore SysEx
+ if(byte == MidiEvent::T_SYSEX || byte == MidiEvent::T_SYSEX2)// Ignore SysEx
{
- uint64_t length = ReadVarLen(pptr);
+ 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;
+ }
ptr += (size_t)length;
return evt;
}
- if(byte == 0xFF)
+ if(byte == MidiEvent::T_SPECIAL)
{
// Special event FF
uint8_t evtype = *(ptr++);
- uint64_t length = ReadVarLen(pptr);
+ 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;
+ }
std::string data(length ? (const char *)ptr : 0, (size_t)length);
ptr += (size_t)length;
@@ -1207,8 +1431,62 @@ MIDIplay::MidiEvent MIDIplay::parseEvent(uint8_t**pptr, int &status)
evt.subtype = evtype;
evt.data.insert(evt.data.begin(), data.begin(), data.end());
+ /* TODO: Store those meta-strings separately and give ability to read them
+ * by external functions (to display song title and copyright in the player) */
+ if(evt.subtype == MidiEvent::ST_COPYRIGHT)
+ {
+ //TODO: Implement own field for this
+ //TODO: Implement API call to retreive this
+ //TODO: Implement a hook to catch this
+ std::string str((const char*)evt.data.data(), evt.data.size());
+ std::fprintf(stdout, "Copyright: %s\n", str.c_str());
+ std::fflush(stdout);
+ }
+ else
+ if(evt.subtype == MidiEvent::ST_SQTRKTITLE)
+ {
+ //TODO: Implement API call to retreive this
+ //TODO: Implement a hook to catch this
+ if(musTitle.empty())
+ musTitle = std::string((const char*)evt.data.data(), evt.data.size());
+ }
+ else
+ if(evt.subtype == MidiEvent::ST_INSTRTITLE)
+ {
+ //TODO: Implement a hook to catch this
+ std::string str((const char*)evt.data.data(), evt.data.size());
+ std::fprintf(stdout, "Instrument: %s\n", str.c_str());
+ std::fflush(stdout);
+ }
+ else
+ if(evt.subtype == MidiEvent::ST_MARKER)
+ {
+ //To lower
+ 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")
+ {
+ //Return a custom Loop Start event instead of Marker
+ evt.subtype = MidiEvent::ST_LOOPSTART;
+ evt.data.clear();//Data is not needed
+ return evt;
+ }
+
+ if(data == "loopend")
+ {
+ //Return a custom Loop End event instead of Marker
+ evt.subtype = MidiEvent::ST_LOOPEND;
+ evt.data.clear();//Data is not needed
+ return evt;
+ }
+ }
+
if(evtype == MidiEvent::ST_ENDTRACK)
- status = -1;
+ status = -1;//Finalize track
return evt;
}
@@ -1220,313 +1498,151 @@ MIDIplay::MidiEvent MIDIplay::parseEvent(uint8_t**pptr, int &status)
ptr--;
}
- if(byte == 0xF3)
+ //Sys Com Song Select(Song #) [0-127]
+ if(byte == MidiEvent::T_SYSCOMSNGSEL)
{
- ptr += 1;
- return evt;
- }
-
- if(byte == 0xF2)
- {
- ptr += 2;
+ 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;
+ }
+ evt.type = byte;
+ evt.data.push_back(*(ptr++));
return evt;
}
- uint8_t MidCh = byte & 0x0F, EvType = byte >> 4;
- status = byte;
- evt.channel = MidCh;
- evt.type = EvType;
-
- switch(EvType)
- {
- case 0x8: // Note off
- case 0x9: // Note on
- case 0xA: // Note touch
- case 0xB: // Controller change
- case 0xE: // Wheel/pitch bend
+ //Sys Com Song Position Pntr [LSB, MSB]
+ if(byte == MidiEvent::T_SYSCOMSPOSPTR)
{
+ 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;
+ }
+ evt.type = byte;
evt.data.push_back(*(ptr++));
evt.data.push_back(*(ptr++));
return evt;
}
- case 0xC: // Patch change
- case 0xD: // Channel after-touch
- {
- evt.data.push_back(*(ptr++));
- return evt;
- }
- }
-
- 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;
- }
+ uint8_t midCh = byte & 0x0F, evType = (byte >> 4) & 0x0F;
+ status = byte;
+ evt.channel = midCh;
+ evt.type = evType;
- if(byte == 0xFF)
+ switch(evType)
{
- // 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
+ case MidiEvent::T_NOTEOFF://2 byte length
+ case MidiEvent::T_NOTEON:
+ case MidiEvent::T_NOTETOUCH:
+ case MidiEvent::T_CTRLCHANGE:
+ case MidiEvent::T_WHEEL:
+ if(ptr + 2 > end)
{
- Tempo = InvDeltaTicks * fraction<uint64_t>(ReadBEint(data.data(), data.size()));
- return;
+ errorString += "parseEvent: Can't read regular 2-byte event - Unexpected end of track data.\n";
+ evt.isValid = 0;
+ return evt;
}
- if(evtype == 6)//Meta event
- {
- //Turn on/off Loop handling when loop is disabled
- if(opl._parent->loopingIsEnabled != 0)
- {
- /* 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());
+ evt.data.push_back(*(ptr++));
+ evt.data.push_back(*(ptr++));
- if(evtype == 0xE3) // Special non-spec ADLMIDI special for IMF playback: Direct poke to AdLib
+ //111'th loopStart controller (RPG Maker and others)
+ if((evType == MidiEvent::T_CTRLCHANGE) && (evt.data[0] == 111))
{
- 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);
+ //Change event type to custom Loop Start event and clear data
+ evt.type = MidiEvent::T_SPECIAL;
+ evt.subtype = MidiEvent::ST_LOOPSTART;
+ evt.data.clear();
}
- 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((opl._parent->loopingIsEnabled != 0) && (ctrlno == 111) && !invalidLoop)
+ return evt;
+ case MidiEvent::T_PATCHCHANGE://1 byte length
+ case MidiEvent::T_CHANAFTTOUCH:
+ if(ptr + 1 > end)
{
- loopStart = true;
- loopStart_passed = true;
- break;
+ errorString += "parseEvent: Can't read regular 1-byte event - Unexpected end of track data.\n";
+ evt.isValid = 0;
+ return evt;
}
-
- 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;
+ evt.data.push_back(*(ptr++));
+ return evt;
}
- 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;
- }
- }
+ return evt;
}
+
void MIDIplay::HandleEvent(size_t tk, MIDIplay::MidiEvent &evt, int &status)
{
- if(evt.type == 0xF7 || evt.type == 0xF0) // Ignore SysEx
+ if(evt.type == MidiEvent::T_SYSEX || evt.type == MidiEvent::T_SYSEX2) // Ignore SysEx
{
//std::string data( length?(const char*) &TrackData[tk][CurrentPosition.track[tk].ptr]:0, length );
//UI.PrintLn("SysEx %02X: %u bytes", byte, length/*, data.c_str()*/);
return;
}
- if(evt.type == 0xFF)
+ if(evt.type == MidiEvent::T_SPECIAL)
{
// 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);
- if(evtype == 0x2F)//End Of Track
+ if(evtype == MidiEvent::ST_ENDTRACK)//End Of Track
{
status = -1;
return;
}
- if(evtype == 0x51)//Tempo change
+ if(evtype == MidiEvent::ST_TEMPOCHANGE)//Tempo change
{
Tempo = InvDeltaTicks * fraction<uint64_t>(ReadBEint(evt.data.data(), evt.data.size()));
return;
}
- if(evtype == 6)//Meta event
+ if(evtype == MidiEvent::ST_MARKER)//Meta event
{
- //Turn on/off Loop handling when loop is disabled
- if(opl._parent->loopingIsEnabled != 0)
- {
- /* 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;
- }
- }
+ //Do nothing! :-P
+ return;
}
- if(evtype == 9)
+ if(evtype == MidiEvent::ST_DEVICESWITCH)
+ {
current_device[tk] = ChooseDevice(data);
+ return;
+ }
//if(evtype >= 1 && evtype <= 6)
// UI.PrintLn("Meta %d: %s", evtype, data.c_str());
- if(evtype == 0xE1) // Special non-spec ADLMIDI special for IMF playback: Direct poke to AdLib
+ //Turn on Loop handling when loop is enabled
+ if(m_setup.loopingIsEnabled && !invalidLoop)
{
- if(!invalidLoop)
+ if(evtype == MidiEvent::ST_LOOPSTART) // Special non-spec ADLMIDI special for IMF playback: Direct poke to AdLib
{
loopStart = true;
- loopStart_passed = true;
+ return;
}
- }
- if(evtype == 0xE2) // Special non-spec ADLMIDI special for IMF playback: Direct poke to AdLib
- {
- if(!invalidLoop)
+ if(evtype == MidiEvent::ST_LOOPEND) // Special non-spec ADLMIDI special for IMF playback: Direct poke to AdLib
{
- if((loopStart_passed) && (!loopStart))
- loopEnd = true;
- else
- invalidLoop = true;
+ loopEnd = true;
+ return;
}
}
- if(evtype == 0xE3) // Special non-spec ADLMIDI special for IMF playback: Direct poke to AdLib
+ if(evtype == MidiEvent::ST_RAWOPL) // 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;
}
return;
@@ -1539,86 +1655,68 @@ void MIDIplay::HandleEvent(size_t tk, MIDIplay::MidiEvent &evt, int &status)
// CurrentPosition.track[tk].ptr--;
// }
- if(evt.type == 0xF3)
- {
- //CurrentPosition.track[tk].ptr += 1;
- return;
- }
-
- if(evt.type == 0xF2)
- {
- //CurrentPosition.track[tk].ptr += 2;
+ if(evt.type == MidiEvent::T_SYSCOMSNGSEL ||
+ evt.type == MidiEvent::T_SYSCOMSPOSPTR)
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 = evt.channel;//byte & 0x0F, EvType = byte >> 4;
- MidCh += (uint8_t)current_device[tk];
+ uint8_t midCh = evt.channel;//byte & 0x0F, EvType = byte >> 4;
+ midCh += (uint8_t)current_device[tk];
status = evt.type;
switch(evt.type)
{
- case 0x8: // Note off
+ case MidiEvent::T_NOTEOFF: // Note off
{
uint8_t note = evt.data[0];
- /*uint8_t vol=*/ //TrackData[tk][CurrentPosition.track[tk].ptr++];
- //if(MidCh != 9) note -= 12; // HACK
- realTime_NoteOff(MidCh, note);
+ realTime_NoteOff(midCh, note);
break;
}
- case 0x9: // Note on
+
+ case MidiEvent::T_NOTEON: // Note on
{
- uint8_t note = evt.data[0];//TrackData[tk][CurrentPosition.track[tk].ptr++];
- uint8_t vol = evt.data[1];//TrackData[tk][CurrentPosition.track[tk].ptr++];
- //if(MidCh != 9) note -= 12; // HACK
- if(realTime_NoteOn(MidCh, note, vol))
- CurrentPosition.began = true;
+ uint8_t note = evt.data[0];
+ uint8_t vol = evt.data[1];
+ /*if(*/ realTime_NoteOn(midCh, note, vol); /*)*/
+ //CurrentPosition.began = true;
break;
}
- case 0xA: // Note touch
+ case MidiEvent::T_NOTETOUCH: // Note touch
{
- uint8_t note = evt.data[0];//TrackData[tk][CurrentPosition.track[tk].ptr++];
- uint8_t vol = evt.data[1];//TrackData[tk][CurrentPosition.track[tk].ptr++];
- realTime_NoteAfterTouch(MidCh, note, vol);
+ uint8_t note = evt.data[0];
+ uint8_t vol = evt.data[1];
+ realTime_NoteAfterTouch(midCh, note, vol);
break;
}
- case 0xB: // Controller change
+ case MidiEvent::T_CTRLCHANGE: // Controller change
{
- uint8_t ctrlno = evt.data[0];//TrackData[tk][CurrentPosition.track[tk].ptr++];
- uint8_t value = evt.data[1];//TrackData[tk][CurrentPosition.track[tk].ptr++];
-
- if((opl._parent->loopingIsEnabled != 0) && (ctrlno == 111) && !invalidLoop)
- {
- loopStart = true;
- loopStart_passed = true;
- break;
- }
-
- realTime_Controller(MidCh, ctrlno, value);
+ uint8_t ctrlno = evt.data[0];
+ uint8_t value = evt.data[1];
+ realTime_Controller(midCh, ctrlno, value);
break;
}
- case 0xC: // Patch change
- realTime_PatchChange(MidCh, evt.data[0] /*TrackData[tk][CurrentPosition.track[tk].ptr++]*/);
+ case MidiEvent::T_PATCHCHANGE: // Patch change
+ realTime_PatchChange(midCh, evt.data[0]);
break;
- case 0xD: // Channel after-touch
+ case MidiEvent::T_CHANAFTTOUCH: // Channel after-touch
{
// TODO: Verify, is this correct action?
- uint8_t vol = evt.data[0];//TrackData[tk][CurrentPosition.track[tk].ptr++];
- realTime_ChannelAfterTouch(MidCh, vol);
+ uint8_t vol = evt.data[0];
+ realTime_ChannelAfterTouch(midCh, vol);
break;
}
- case 0xE: // Wheel/pitch bend
+ case MidiEvent::T_WHEEL: // Wheel/pitch bend
{
- uint8_t a = evt.data[0];//TrackData[tk][CurrentPosition.track[tk].ptr++];
- uint8_t b = evt.data[1];//TrackData[tk][CurrentPosition.track[tk].ptr++];
- realTime_PitchBend(MidCh, b, a);
+ uint8_t a = evt.data[0];
+ uint8_t b = evt.data[1];
+ realTime_PitchBend(midCh, b, a);
break;
}
}
@@ -1688,11 +1786,8 @@ long MIDIplay::CalculateAdlChannelGoodness(unsigned c, uint16_t ins, uint16_t) c
++m)
{
if(m->second.sustained) continue;
-
if(m->second.vibdelay >= 200) continue;
-
if(m->second.ins != j->second.ins) continue;
-
n_evacuation_stations += 1;
}
}
@@ -1813,6 +1908,15 @@ void MIDIplay::KillOrEvacuate(size_t from_channel, AdlChannel::users_t::iterator
static_cast<int32_t>(from_channel));
}
+void MIDIplay::Panic()
+{
+ for(uint8_t chan = 0; chan < Ch.size(); chan++)
+ {
+ for(uint8_t note = 0; note < 128; note++)
+ realTime_NoteOff(chan, note);
+ }
+}
+
void MIDIplay::KillSustainingNotes(int32_t MidCh, int32_t this_adlchn)
{
uint32_t first = 0, last = opl.NumChannels;