aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorWohlstand <admin@wohlnet.ru>2017-10-26 03:17:15 +0300
committerWohlstand <admin@wohlnet.ru>2017-10-26 03:17:15 +0300
commitd9a000cc0a6a8aae37f34c4cc6e23797a1ceb8c9 (patch)
tree7fc7899b2a68250666b939bc151ae797814e5f8f /src
parent650d6b913504f37ed84c5f855a85aa00d9dc34d8 (diff)
downloadlibADLMIDI-d9a000cc0a6a8aae37f34c4cc6e23797a1ceb8c9.tar.gz
libADLMIDI-d9a000cc0a6a8aae37f34c4cc6e23797a1ceb8c9.tar.bz2
libADLMIDI-d9a000cc0a6a8aae37f34c4cc6e23797a1ceb8c9.zip
Continue works on loop points and some clean-up
- Added more detailed error information while parsing MIDI file - Added abiltiy to retrieve loop points time positions - Added ability to change playing tempo by giving multiplier - Removed old commented code TODO: - implement meta-information store and a way to retreive it - implement hook-ability for every MIDI event - implement ability to mute/solo every channel
Diffstat (limited to 'src')
-rw-r--r--src/adlmidi.cpp20
-rw-r--r--src/adlmidi_load.cpp50
-rw-r--r--src/adlmidi_midiplay.cpp415
-rw-r--r--src/adlmidi_private.hpp109
4 files changed, 180 insertions, 414 deletions
diff --git a/src/adlmidi.cpp b/src/adlmidi.cpp
index bbd80e4..a7bab22 100644
--- a/src/adlmidi.cpp
+++ b/src/adlmidi.cpp
@@ -303,6 +303,20 @@ ADLMIDI_EXPORT double adl_totalTimeLength(ADL_MIDIPlayer *device)
return reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->timeLength();
}
+ADLMIDI_EXPORT double adl_loopStartTime(struct ADL_MIDIPlayer *device)
+{
+ if(!device)
+ return -1.0;
+ return reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->getLoopStart();
+}
+
+ADLMIDI_EXPORT double adl_loopEndTime(struct ADL_MIDIPlayer *device)
+{
+ if(!device)
+ return -1.0;
+ return reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->getLoopEnd();
+}
+
ADLMIDI_EXPORT double adl_positionTell(struct ADL_MIDIPlayer *device)
{
if(!device)
@@ -324,6 +338,12 @@ ADLMIDI_EXPORT void adl_positionRewind(struct ADL_MIDIPlayer *device)
reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->rewind();
}
+ADLMIDI_EXPORT void adl_setTempo(struct ADL_MIDIPlayer *device, double tempo)
+{
+ if(!device || (tempo <= 0.0))
+ return;
+ reinterpret_cast<MIDIplay *>(device->adl_midiPlayer)->setTempo(tempo);
+}
#ifdef ADLMIDI_USE_DOSBOX_OPL
diff --git a/src/adlmidi_load.cpp b/src/adlmidi_load.cpp
index 5bc2ba0..08892a5 100644
--- a/src/adlmidi_load.cpp
+++ b/src/adlmidi_load.cpp
@@ -48,53 +48,6 @@ uint64_t MIDIplay::ReadLEint(const void *buffer, size_t nbytes)
return result;
}
-//uint64_t MIDIplay::ReadVarLenEx(size_t tk, bool &ok)
-//{
-// uint64_t result = 0;
-// ok = false;
-
-// for(;;)
-// {
-// if(tk >= TrackData.size())
-// return 1;
-
-// if(tk >= CurrentPosition.track.size())
-// return 2;
-
-// size_t ptr = CurrentPosition.track[tk].ptr;
-
-// if(ptr >= TrackData[tk].size())
-// return 3;
-
-// unsigned char byte = TrackData[tk][CurrentPosition.track[tk].ptr++];
-// result = (result << 7) + (byte & 0x7F);
-
-// if(!(byte & 0x80)) break;
-// }
-
-// ok = true;
-// return result;
-//}
-
-uint64_t MIDIplay::ReadVarLenEx(uint8_t **ptr, uint8_t *end, bool &ok)
-{
- uint64_t result = 0;
- ok = false;
-
- for(;;)
- {
- if(*ptr >= end)
- return 2;
- unsigned char byte = *((*ptr)++);
- result = (result << 7) + (byte & 0x7F);
- if(!(byte & 0x80)) break;
- }
-
- ok = true;
- return result;
-}
-
-
bool MIDIplay::LoadBank(const std::string &filename)
{
fileReader file;
@@ -388,6 +341,7 @@ bool MIDIplay::LoadMIDI(MIDIplay::fileReader &fr)
ADL_UNUSED(fsize);
//! Temp buffer for conversion
AdlMIDI_CPtr<uint8_t> cvt_buf;
+ errorString.clear();
#ifdef DISABLE_EMBEDDED_BANKS
if((opl.AdlBank != ~0u) || (opl.dynamic_metainstruments.size() < 256))
@@ -792,7 +746,7 @@ InvFmt:
//Build new MIDI events table (ALPHA!!!)
if(!buildTrackData())
{
- ADLMIDI_ErrorString = fr._fileName + ": MIDI data parsing error has occouped!";
+ ADLMIDI_ErrorString = fr._fileName + ": MIDI data parsing error has occouped!\n" + errorString;
return false;
}
diff --git a/src/adlmidi_midiplay.cpp b/src/adlmidi_midiplay.cpp
index 44f6d68..7b9094a 100644
--- a/src/adlmidi_midiplay.cpp
+++ b/src/adlmidi_midiplay.cpp
@@ -189,17 +189,23 @@ void MIDIplay::MidiTrackRow::sortEvents()
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, MidiTrackQueue());
- trackDataNewStatus.resize(trackCount, 0);
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);
@@ -226,7 +232,9 @@ bool MIDIplay::buildTrackData()
evtPos.delay = ReadVarLenEx(&trackPtr, end, ok);
if(!ok)
{
- //TODO: Implement file parsing error here
+ 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;
@@ -241,7 +249,9 @@ bool MIDIplay::buildTrackData()
event = parseEvent(&trackPtr, end, status);
if(!event.isValid)
{
- //TODO: Implement file parsing error here
+ 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);
@@ -296,7 +306,9 @@ bool MIDIplay::buildTrackData()
evtPos.delay = ReadVarLenEx(&trackPtr, end, ok);
if(!ok)
{
- //TODO: Implement file parsing error here
+ 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;
}
}
@@ -312,11 +324,19 @@ bool MIDIplay::buildTrackData()
}
} 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].begin();
}
+ if(gotLoopStart && !gotLoopEnd)
+ {
+ gotLoopEnd = true;
+ loopEndTicks = ticksSongLength;
+ }
+
//loopStart must be located before loopEnd!
if(loopStartTicks >= loopEndTicks)
invalidLoop = true;
@@ -409,6 +429,16 @@ bool MIDIplay::buildTrackData()
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);
@@ -432,6 +462,9 @@ MIDIplay::MIDIplay():
cmf_percussion_mode(false),
fullSongTimeLength(0.0),
postSongWaitDelay(1.0),
+ loopStartTime(-1.0),
+ loopEndTime(-1.0),
+ tempoMultiplier(1.0),
trackStart(false),
atEnd(false),
loopStart(false),
@@ -478,22 +511,28 @@ uint64_t MIDIplay::ReadVarLen(uint8_t **ptr)
return result;
}
-//uint64_t MIDIplay::ReadVarLen(size_t tk)
-//{
-// uint64_t result = 0;
-// for(;;)
-// {
-// uint8_t byte = TrackData[tk][CurrentPosition.track[tk].ptr++];
-// result = (result << 7) + (byte & 0x7F);
-// if(!(byte & 0x80))
-// break;
-// }
-// return result;
-//}
+uint64_t MIDIplay::ReadVarLenEx(uint8_t **ptr, uint8_t *end, bool &ok)
+{
+ uint64_t result = 0;
+ ok = false;
+
+ for(;;)
+ {
+ if(*ptr >= end)
+ return 2;
+ unsigned char byte = *((*ptr)++);
+ result = (result << 7) + (byte & 0x7F);
+ if(!(byte & 0x80)) break;
+ }
+
+ ok = true;
+ return result;
+}
double MIDIplay::Tick(double s, double granularity)
{
+ s *= tempoMultiplier;
//if(CurrentPositionNew.began)
CurrentPositionNew.wait -= s;
CurrentPositionNew.absTimePosition += s;
@@ -522,31 +561,6 @@ double MIDIplay::Tick(double s, double granularity)
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--;
-// }
-
-// if(antiFreezeCounter <= 0)
-// CurrentPosition.wait += 1.0;/* Add extra 1 second when over 10000 events
-// with zero delay are been detected */
-
-// for(uint16_t c = 0; c < opl.NumChannels; ++c)
-// ch[c].AddAge(static_cast<int64_t>(s * 1000.0));
-
-// UpdateVibrato(s);
-// UpdateArpeggio(s);
-
- // return CurrentPosition.wait;
}
void MIDIplay::seek(double seconds)
@@ -613,11 +627,20 @@ double MIDIplay::timeLength()
return fullSongTimeLength;
}
+double MIDIplay::getLoopStart()
+{
+ return loopStartTime;
+}
+
+double MIDIplay::getLoopEnd()
+{
+ return loopEndTime;
+}
+
void MIDIplay::rewind()
{
Panic();
KillSustainingNotes(-1, -1);
- //CurrentPosition = trackBeginPosition;
CurrentPositionNew = trackBeginPositionNew;
trackStart = true;
atEnd = false;
@@ -625,6 +648,11 @@ void MIDIplay::rewind()
invalidLoop = false;
}
+void MIDIplay::setTempo(double tempo)
+{
+ tempoMultiplier = tempo;
+}
+
void MIDIplay::realTime_ResetState()
{
for(size_t ch = 0; ch < Ch.size(); ch++)
@@ -1245,103 +1273,6 @@ void MIDIplay::NoteUpdate(uint16_t MidCh,
}
-//bool MIDIplay::ProcessEvents()
-//{
-// 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 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(!m_setup.loopingIsEnabled)
-// {
-// 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(bool isSeek)
{
if(CurrentPositionNew.track.size() == 0)
@@ -1421,33 +1352,11 @@ bool MIDIplay::ProcessEventsNew(bool isSeek)
fraction<uint64_t> t = shortest * Tempo;
//if(CurrentPositionNew.began)
- CurrentPositionNew.wait += t.valuel();
+ CurrentPositionNew.wait += t.value();
//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;
- // LoopBeginPositionNew = trackBeginPositionNew;
- //}
- //else
- // loopStart_hit = false;
-
if(loopStart)
{
- //if(trackStart)
- //{
- // trackBeginPositionNew = RowBeginPosition;
- // trackStart = false;
- // atEnd = false;
- //}
LoopBeginPositionNew = RowBeginPosition;
loopStart = false;
}
@@ -1476,6 +1385,7 @@ MIDIplay::MidiEvent MIDIplay::parseEvent(uint8_t**pptr, uint8_t *end, int &statu
if(ptr + 1 > end)
{
+ errorString += "parseEvent: Can't read event type byte - Unexpected end of track data.\n";
evt.isValid = 0;
return evt;
}
@@ -1488,6 +1398,7 @@ MIDIplay::MidiEvent MIDIplay::parseEvent(uint8_t**pptr, uint8_t *end, int &statu
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;
}
@@ -1502,6 +1413,7 @@ MIDIplay::MidiEvent MIDIplay::parseEvent(uint8_t**pptr, uint8_t *end, int &statu
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;
}
@@ -1584,6 +1496,7 @@ MIDIplay::MidiEvent MIDIplay::parseEvent(uint8_t**pptr, uint8_t *end, int &statu
{
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;
}
@@ -1597,6 +1510,7 @@ MIDIplay::MidiEvent MIDIplay::parseEvent(uint8_t**pptr, uint8_t *end, int &statu
{
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;
}
@@ -1620,6 +1534,7 @@ MIDIplay::MidiEvent MIDIplay::parseEvent(uint8_t**pptr, uint8_t *end, int &statu
case MidiEvent::T_WHEEL:
if(ptr + 2 > end)
{
+ errorString += "parseEvent: Can't read regular 2-byte event - Unexpected end of track data.\n";
evt.isValid = 0;
return evt;
}
@@ -1641,6 +1556,7 @@ MIDIplay::MidiEvent MIDIplay::parseEvent(uint8_t**pptr, uint8_t *end, int &statu
case MidiEvent::T_CHANAFTTOUCH:
if(ptr + 1 > end)
{
+ errorString += "parseEvent: Can't read regular 1-byte event - Unexpected end of track data.\n";
evt.isValid = 0;
return evt;
}
@@ -1651,179 +1567,6 @@ MIDIplay::MidiEvent MIDIplay::parseEvent(uint8_t**pptr, uint8_t *end, int &statu
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;
-// }
-
-// if(byte == 0xFF)
-// {
-// // 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
-// {
-// Tempo = InvDeltaTicks * fraction<uint64_t>(ReadBEint(data.data(), data.size()));
-// return;
-// }
-
-// if(evtype == 6)//Meta event
-// {
-// //Turn on/off Loop handling when loop is disabled
-// if(m_setup.loopingIsEnabled)
-// {
-// /* 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());
-
-// if(evtype == 0xE3) // 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;
-// }
-
-// // 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((m_setup.loopingIsEnabled) && (ctrlno == 111) && !invalidLoop)
-// {
-// loopStart = true;
-// loopStart_passed = true;
-// break;
-// }
-
-// 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;
-// }
-
-// 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;
-// }
-// }
-//}
void MIDIplay::HandleEvent(size_t tk, MIDIplay::MidiEvent &evt, int &status)
{
diff --git a/src/adlmidi_private.hpp b/src/adlmidi_private.hpp
index 92cb1df..fdd7d32 100644
--- a/src/adlmidi_private.hpp
+++ b/src/adlmidi_private.hpp
@@ -187,25 +187,6 @@ public:
class MIDIplay
{
- // Information about each track
- //struct Position
- //{
- // bool began;
- // char padding[7];
- // double wait;
- // struct TrackInfo
- // {
- // size_t ptr;
- // uint64_t delay;
- // int status;
- // char padding2[4];
- // TrackInfo(): ptr(0), delay(0), status(0) {}
- // };
- // std::vector<TrackInfo> track;
-
- // Position(): began(false), wait(0.0l), track() { }
- //} CurrentPosition, LoopBeginPosition, trackBeginPosition;
-
std::map<std::string, uint64_t> devices;
std::map<uint64_t /*track*/, uint64_t /*channel begin index*/> current_device;
@@ -294,9 +275,10 @@ class MIDIplay
AdlChannel(): users(), koff_time_until_neglible(0) { }
void AddAge(int64_t ms);
};
-public:
+
+ //Padding to fix CLanc code model's warning
char ____padding[7];
-private:
+
std::vector<AdlChannel> ch;
std::vector<std::vector<uint8_t> > TrackData;
@@ -430,9 +412,29 @@ private:
//! Delay after song playd before rejecting the output stream requests
double postSongWaitDelay;
+ //! Loop start time
+ double loopStartTime;
+ //! Loop end time
+ double loopEndTime;
+ //! Local error string
+ std::string errorString;
+
+ //! Pre-processed track data storage
std::vector<MidiTrackQueue > trackDataNew;
- std::vector<int> trackDataNewStatus;
+
+ /**
+ * @brief Build MIDI track data from the raw track data storage
+ * @return true if everything successfully processed, or false on any error
+ */
bool buildTrackData();
+
+ /**
+ * @brief Parse one event from raw MIDI track stream
+ * @param [_inout] ptr pointer to pointer to current position on the raw data track
+ * @param [_in] end address to end of raw track data, needed to validate position and size
+ * @param [_inout] status status of the track processing
+ * @return Parsed MIDI event entry
+ */
MidiEvent parseEvent(uint8_t **ptr, uint8_t *end, int &status);
public:
@@ -442,6 +444,8 @@ public:
std::string musTitle;
fraction<uint64_t> InvDeltaTicks, Tempo;
+ //! Tempo multiplier
+ double tempoMultiplier;
bool trackStart,
atEnd,
loopStart,
@@ -481,18 +485,27 @@ public:
unsigned long PCM_RATE;
} m_setup;
-public:
static uint64_t ReadBEint(const void *buffer, size_t nbytes);
static uint64_t ReadLEint(const void *buffer, size_t nbytes);
+ /**
+ * @brief Standard MIDI Variable-Length numeric value parser without of validation
+ * @param [_inout] ptr Pointer to memory block that contains begin of variable-length value
+ * @return Unsigned integer that conains parsed variable-length value
+ */
uint64_t ReadVarLen(uint8_t **ptr);
- //uint64_t ReadVarLen(size_t tk);
- //uint64_t ReadVarLenEx(size_t tk, bool &ok);
+ /**
+ * @brief Secure Standard MIDI Variable-Length numeric value parser with anti-out-of-range protection
+ * @param [_inout] ptr Pointer to memory block that contains begin of variable-length value, will be iterated forward
+ * @param [_in end Pointer to end of memory block where variable-length value is stored (after end of track)
+ * @param [_out] ok Reference to boolean which takes result of variable-length value parsing
+ * @return Unsigned integer that conains parsed variable-length value
+ */
uint64_t ReadVarLenEx(uint8_t **ptr, uint8_t *end, bool &ok);
/*
* A little class gives able to read filedata from disk and also from a memory segment
- */
+ */
class fileReader
{
public:
@@ -650,19 +663,55 @@ public:
bool LoadMIDI(void *data, unsigned long size);
bool LoadMIDI(fileReader &fr);
- /* Periodic tick handler.
- * Input: s = seconds since last call
- * Input: granularity = don't expect intervals smaller than this, in seconds
- * Output: desired number of seconds until next call
+ /**
+ * @brief Periodic tick handler.
+ * @param s seconds since last call
+ * @param granularity don't expect intervals smaller than this, in seconds
+ * @return desired number of seconds until next call
*/
double Tick(double s, double granularity);
+ /**
+ * @brief Change current position to specified time position in seconds
+ * @param seconds Absolute time position in seconds
+ */
void seek(double seconds);
+
+ /**
+ * @brief Gives current time position in seconds
+ * @return Current time position in seconds
+ */
double tell();
+
+ /**
+ * @brief Gives time length of current song in seconds
+ * @return Time length of current song in seconds
+ */
double timeLength();
+ /**
+ * @brief Gives loop start time position in seconds
+ * @return Loop start time position in seconds or -1 if song has no loop points
+ */
+ double getLoopStart();
+
+ /**
+ * @brief Gives loop end time position in seconds
+ * @return Loop end time position in seconds or -1 if song has no loop points
+ */
+ double getLoopEnd();
+
+ /**
+ * @brief Return to begin of current song
+ */
void rewind();
+ /**
+ * @brief Set tempo multiplier
+ * @param tempo Tempo multiplier: 1.0 - original tempo. >1 - faster, <1 - slower
+ */
+ void setTempo(double tempo);
+
/* RealTime event triggers */
void realTime_ResetState();