mirror of
https://github.com/AxioDL/amuse.git
synced 2025-12-08 21:17:49 +00:00
Looping SNG support; bug fixes
This commit is contained in:
@@ -75,14 +75,68 @@ static uint32_t DecodeTime(const unsigned char*& data)
|
||||
return ret;
|
||||
}
|
||||
|
||||
void SongState::Header::swapBig()
|
||||
void SongState::Header::swapFromBig()
|
||||
{
|
||||
m_trackIdxOff = SBig(m_trackIdxOff);
|
||||
m_regionIdxOff = SBig(m_regionIdxOff);
|
||||
m_chanMapOff = SBig(m_chanMapOff);
|
||||
m_tempoTableOff = SBig(m_tempoTableOff);
|
||||
m_initialTempo = SBig(m_initialTempo);
|
||||
m_unkOff = SBig(m_unkOff);
|
||||
if (m_initialTempo & 0x80000000)
|
||||
{
|
||||
for (int i = 0; i < 16; ++i)
|
||||
m_loopStartTicks[i] = SBig(m_loopStartTicks[i]);
|
||||
m_chanMapOff2 = SBig(m_chanMapOff2);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_loopStartTicks[0] = SBig(m_loopStartTicks[0]);
|
||||
}
|
||||
}
|
||||
|
||||
void SongState::Header::swapToBig()
|
||||
{
|
||||
m_trackIdxOff = SBig(m_trackIdxOff);
|
||||
m_regionIdxOff = SBig(m_regionIdxOff);
|
||||
m_chanMapOff = SBig(m_chanMapOff);
|
||||
m_tempoTableOff = SBig(m_tempoTableOff);
|
||||
m_initialTempo = SBig(m_initialTempo);
|
||||
if (m_initialTempo & 0x00000080)
|
||||
{
|
||||
for (int i = 0; i < 16; ++i)
|
||||
m_loopStartTicks[i] = SBig(m_loopStartTicks[i]);
|
||||
m_chanMapOff2 = SBig(m_chanMapOff2);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_loopStartTicks[0] = SBig(m_loopStartTicks[0]);
|
||||
}
|
||||
}
|
||||
|
||||
SongState::Header& SongState::Header::operator=(const Header& other)
|
||||
{
|
||||
m_trackIdxOff = other.m_trackIdxOff;
|
||||
m_regionIdxOff = other.m_regionIdxOff;
|
||||
m_chanMapOff = other.m_chanMapOff;
|
||||
m_tempoTableOff = other.m_tempoTableOff;
|
||||
m_initialTempo = other.m_initialTempo;
|
||||
if (SBig(m_initialTempo) & 0x80000000)
|
||||
{
|
||||
for (int i = 0; i < 16; ++i)
|
||||
m_loopStartTicks[i] = other.m_loopStartTicks[i];
|
||||
m_chanMapOff2 = other.m_chanMapOff2;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_loopStartTicks[0] = other.m_loopStartTicks[0];
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool SongState::TrackRegion::indexDone(bool bigEndian, bool loop) const
|
||||
{
|
||||
int16_t idx = (bigEndian ? SBig(m_regionIndex) : m_regionIndex);
|
||||
return loop ? (idx == -1) : (idx < 0);
|
||||
}
|
||||
|
||||
bool SongState::TrackRegion::indexValid(bool bigEndian) const
|
||||
@@ -90,6 +144,13 @@ bool SongState::TrackRegion::indexValid(bool bigEndian) const
|
||||
return (bigEndian ? SBig(m_regionIndex) : m_regionIndex) >= 0;
|
||||
}
|
||||
|
||||
int SongState::TrackRegion::indexLoop(bool bigEndian) const
|
||||
{
|
||||
if ((bigEndian ? SBig(m_regionIndex) : m_regionIndex) != -2)
|
||||
return -1;
|
||||
return (bigEndian ? SBig(m_loopToRegion) : m_loopToRegion);
|
||||
}
|
||||
|
||||
void SongState::TempoChange::swapBig()
|
||||
{
|
||||
m_tick = SBig(m_tick);
|
||||
@@ -103,12 +164,14 @@ void SongState::Track::Header::swapBig()
|
||||
m_modOff = SBig(m_modOff);
|
||||
}
|
||||
|
||||
SongState::Track::Track(SongState& parent, uint8_t midiChan, const TrackRegion* regions)
|
||||
: m_parent(&parent), m_midiChan(midiChan), m_curRegion(nullptr), m_nextRegion(regions)
|
||||
SongState::Track::Track(SongState& parent, uint8_t midiChan, uint32_t loopStart, const TrackRegion* regions, uint32_t tempo)
|
||||
: m_parent(&parent), m_midiChan(midiChan), m_initRegion(regions), m_curRegion(nullptr),
|
||||
m_nextRegion(regions), m_loopStartTick(loopStart), m_tempo(tempo)
|
||||
{
|
||||
resetTempo();
|
||||
}
|
||||
|
||||
void SongState::Track::setRegion(Sequencer* seq, const TrackRegion* region)
|
||||
void SongState::Track::setRegion(const TrackRegion* region)
|
||||
{
|
||||
m_curRegion = region;
|
||||
uint32_t regionIdx = (m_parent->m_bigEndian ? SBig(m_curRegion->m_regionIndex) : m_curRegion->m_regionIndex);
|
||||
@@ -125,13 +188,14 @@ void SongState::Track::setRegion(Sequencer* seq, const TrackRegion* region)
|
||||
m_pitchWheelData = nullptr;
|
||||
m_nextPitchTick = 0x7fffffff;
|
||||
m_nextPitchDelta = 0;
|
||||
m_pitchVal = 0;
|
||||
if (header.m_pitchOff)
|
||||
{
|
||||
m_pitchWheelData = m_parent->m_songData + header.m_pitchOff;
|
||||
if (m_pitchWheelData[0] != 0x80 || m_pitchWheelData[1] != 0x00)
|
||||
{
|
||||
auto delta = DecodeDelta(m_pitchWheelData);
|
||||
m_nextPitchTick = m_parent->m_curTick + delta.first;
|
||||
m_nextPitchTick = m_curTick + delta.first;
|
||||
m_nextPitchDelta = delta.second;
|
||||
}
|
||||
}
|
||||
@@ -139,27 +203,23 @@ void SongState::Track::setRegion(Sequencer* seq, const TrackRegion* region)
|
||||
m_modWheelData = nullptr;
|
||||
m_nextModTick = 0x7fffffff;
|
||||
m_nextModDelta = 0;
|
||||
m_modVal = 0;
|
||||
if (header.m_modOff)
|
||||
{
|
||||
m_modWheelData = m_parent->m_songData + header.m_modOff;
|
||||
if (m_modWheelData[0] != 0x80 || m_modWheelData[1] != 0x00)
|
||||
{
|
||||
auto delta = DecodeDelta(m_modWheelData);
|
||||
m_nextModTick = m_parent->m_curTick + delta.first;
|
||||
m_nextModTick = m_curTick + delta.first;
|
||||
m_nextModDelta = delta.second;
|
||||
}
|
||||
}
|
||||
|
||||
m_eventWaitCountdown = 0;
|
||||
m_pitchVal = 0;
|
||||
m_modVal = 0;
|
||||
if (seq)
|
||||
{
|
||||
seq->setPitchWheel(m_midiChan, clamp(-1.f, m_pitchVal / 32768.f, 1.f));
|
||||
seq->setCtrlValue(m_midiChan, 1, clamp(0, m_modVal * 128 / 16384, 127));
|
||||
}
|
||||
if (m_parent->m_sngVersion == 1)
|
||||
{
|
||||
m_eventWaitCountdown = int32_t(DecodeTime(m_data));
|
||||
}
|
||||
else
|
||||
{
|
||||
int32_t absTick = (m_parent->m_bigEndian ? SBig(*reinterpret_cast<const int32_t*>(m_data))
|
||||
@@ -170,14 +230,14 @@ void SongState::Track::setRegion(Sequencer* seq, const TrackRegion* region)
|
||||
}
|
||||
}
|
||||
|
||||
void SongState::Track::advanceRegion(Sequencer* seq) { setRegion(seq, m_nextRegion); }
|
||||
void SongState::Track::advanceRegion() { setRegion(m_nextRegion); }
|
||||
|
||||
int SongState::DetectVersion(const unsigned char* ptr, bool& isBig)
|
||||
{
|
||||
isBig = ptr[0] == 0;
|
||||
Header header = *reinterpret_cast<const Header*>(ptr);
|
||||
if (isBig)
|
||||
header.swapBig();
|
||||
header.swapFromBig();
|
||||
const uint32_t* trackIdx = reinterpret_cast<const uint32_t*>(ptr + header.m_trackIdxOff);
|
||||
const uint32_t* regionIdxTable = reinterpret_cast<const uint32_t*>(ptr + header.m_regionIdxOff);
|
||||
|
||||
@@ -347,8 +407,9 @@ int SongState::DetectVersion(const unsigned char* ptr, bool& isBig)
|
||||
return v;
|
||||
}
|
||||
|
||||
bool SongState::initialize(const unsigned char* ptr)
|
||||
bool SongState::initialize(const unsigned char* ptr, bool loop)
|
||||
{
|
||||
m_loop = loop;
|
||||
m_sngVersion = DetectVersion(ptr, m_bigEndian);
|
||||
if (m_sngVersion < 0)
|
||||
return false;
|
||||
@@ -356,7 +417,7 @@ bool SongState::initialize(const unsigned char* ptr)
|
||||
m_songData = ptr;
|
||||
m_header = *reinterpret_cast<const Header*>(ptr);
|
||||
if (m_bigEndian)
|
||||
m_header.swapBig();
|
||||
m_header.swapFromBig();
|
||||
const uint32_t* trackIdx = reinterpret_cast<const uint32_t*>(ptr + m_header.m_trackIdxOff);
|
||||
m_regionIdx = reinterpret_cast<const uint32_t*>(ptr + m_header.m_regionIdxOff);
|
||||
const uint8_t* chanMap = reinterpret_cast<const uint8_t*>(ptr + m_header.m_chanMapOff);
|
||||
@@ -368,35 +429,69 @@ bool SongState::initialize(const unsigned char* ptr)
|
||||
{
|
||||
const TrackRegion* region =
|
||||
reinterpret_cast<const TrackRegion*>(ptr + (m_bigEndian ? SBig(trackIdx[i]) : trackIdx[i]));
|
||||
m_tracks[i] = Track(*this, chanMap[i], region);
|
||||
uint8_t chan = chanMap[i];
|
||||
uint32_t loopStart =
|
||||
(m_header.m_initialTempo & 0x80000000) ? m_header.m_loopStartTicks[chan] : m_header.m_loopStartTicks[0];
|
||||
m_tracks[i] = Track(*this, chan, loopStart, region, m_header.m_initialTempo & 0x7fffffff);
|
||||
}
|
||||
else
|
||||
m_tracks[i] = Track();
|
||||
}
|
||||
|
||||
/* Initialize tempo */
|
||||
if (m_header.m_tempoTableOff)
|
||||
m_tempoPtr = reinterpret_cast<const TempoChange*>(ptr + m_header.m_tempoTableOff);
|
||||
else
|
||||
m_tempoPtr = nullptr;
|
||||
|
||||
m_tempo = m_header.m_initialTempo & 0x7fffffff;
|
||||
m_curTick = 0;
|
||||
m_songState = SongPlayState::Playing;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SongState::Track::advance(Sequencer& seq, int32_t ticks)
|
||||
void SongState::Track::resetTempo()
|
||||
{
|
||||
int32_t endTick = m_parent->m_curTick + ticks;
|
||||
if (m_parent->m_header.m_tempoTableOff)
|
||||
m_tempoPtr = reinterpret_cast<const TempoChange*>(m_parent->m_songData + m_parent->m_header.m_tempoTableOff);
|
||||
else
|
||||
m_tempoPtr = nullptr;
|
||||
}
|
||||
|
||||
bool SongState::Track::advance(Sequencer& seq, double dt)
|
||||
{
|
||||
m_remDt += dt;
|
||||
|
||||
/* Compute ticks to compute based on current tempo */
|
||||
double ticksPerSecond = m_tempo * 384 / 60;
|
||||
uint32_t ticks = uint32_t(std::floor(m_remDt * ticksPerSecond));
|
||||
|
||||
/* See if there's an upcoming tempo change in this interval */
|
||||
while (m_tempoPtr && m_tempoPtr->m_tick != 0xffffffff)
|
||||
{
|
||||
TempoChange change = *m_tempoPtr;
|
||||
if (m_parent->m_bigEndian)
|
||||
change.swapBig();
|
||||
|
||||
if (m_curTick + ticks > change.m_tick)
|
||||
ticks = change.m_tick - m_curTick;
|
||||
|
||||
if (ticks <= 0)
|
||||
{
|
||||
/* Turn over tempo */
|
||||
m_tempo = change.m_tempo & 0x7fffffff;
|
||||
ticksPerSecond = m_tempo * 384 / 60;
|
||||
ticks = uint32_t(std::floor(m_remDt * ticksPerSecond));
|
||||
seq.setTempo(m_midiChan, m_tempo * 384 / 60.0);
|
||||
++m_tempoPtr;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
m_remDt -= ticks / ticksPerSecond;
|
||||
uint32_t endTick = m_curTick + ticks;
|
||||
|
||||
/* Advance region if needed */
|
||||
while (m_nextRegion->indexValid(m_parent->m_bigEndian))
|
||||
{
|
||||
uint32_t nextRegTick = (m_parent->m_bigEndian ? SBig(m_nextRegion->m_startTick) : m_nextRegion->m_startTick);
|
||||
uint32_t nextRegTick = (m_parent->m_bigEndian ?
|
||||
SBig(m_nextRegion->m_startTick) : m_nextRegion->m_startTick);
|
||||
if (uint32_t(endTick) > nextRegTick)
|
||||
advanceRegion(&seq);
|
||||
advanceRegion();
|
||||
else
|
||||
break;
|
||||
}
|
||||
@@ -412,184 +507,208 @@ bool SongState::Track::advance(Sequencer& seq, int32_t ticks)
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_data)
|
||||
return !m_nextRegion->indexValid(m_parent->m_bigEndian);
|
||||
|
||||
/* Update continuous pitch data */
|
||||
if (m_pitchWheelData)
|
||||
if (m_data)
|
||||
{
|
||||
int32_t pitchTick = m_parent->m_curTick;
|
||||
int32_t remPitchTicks = ticks;
|
||||
while (pitchTick < endTick)
|
||||
/* Update continuous pitch data */
|
||||
if (m_pitchWheelData)
|
||||
{
|
||||
/* See if there's an upcoming pitch change in this interval */
|
||||
int32_t nextTick = m_nextPitchTick;
|
||||
if (pitchTick + remPitchTicks > nextTick)
|
||||
int32_t pitchTick = m_curTick;
|
||||
int32_t remPitchTicks = ticks;
|
||||
while (pitchTick < endTick)
|
||||
{
|
||||
/* Update pitch */
|
||||
m_pitchVal += m_nextPitchDelta;
|
||||
seq.setPitchWheel(m_midiChan, clamp(-1.f, m_pitchVal / 8191.f, 1.f));
|
||||
if (m_pitchWheelData[0] != 0x80 || m_pitchWheelData[1] != 0x00)
|
||||
/* See if there's an upcoming pitch change in this interval */
|
||||
int32_t nextTick = m_nextPitchTick;
|
||||
if (pitchTick + remPitchTicks > nextTick)
|
||||
{
|
||||
auto delta = DecodeDelta(m_pitchWheelData);
|
||||
m_nextPitchTick += delta.first;
|
||||
m_nextPitchDelta = delta.second;
|
||||
/* Update pitch */
|
||||
m_pitchVal += m_nextPitchDelta;
|
||||
seq.setPitchWheel(m_midiChan, clamp(-1.f, m_pitchVal / 8191.f, 1.f));
|
||||
if (m_pitchWheelData[0] != 0x80 || m_pitchWheelData[1] != 0x00)
|
||||
{
|
||||
auto delta = DecodeDelta(m_pitchWheelData);
|
||||
m_nextPitchTick += delta.first;
|
||||
m_nextPitchDelta = delta.second;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_nextPitchTick = 0x7fffffff;
|
||||
}
|
||||
}
|
||||
remPitchTicks -= (nextTick - pitchTick);
|
||||
pitchTick = nextTick;
|
||||
}
|
||||
}
|
||||
|
||||
/* Update continuous modulation data */
|
||||
if (m_modWheelData)
|
||||
{
|
||||
int32_t modTick = m_curTick;
|
||||
int32_t remModTicks = ticks;
|
||||
while (modTick < endTick)
|
||||
{
|
||||
/* See if there's an upcoming modulation change in this interval */
|
||||
int32_t nextTick = m_nextModTick;
|
||||
if (modTick + remModTicks > nextTick)
|
||||
{
|
||||
/* Update modulation */
|
||||
m_modVal += m_nextModDelta;
|
||||
seq.setCtrlValue(m_midiChan, 1, int8_t(clamp(0, m_modVal / 127, 127)));
|
||||
if (m_modWheelData[0] != 0x80 || m_modWheelData[1] != 0x00)
|
||||
{
|
||||
auto delta = DecodeDelta(m_modWheelData);
|
||||
m_nextModTick += delta.first;
|
||||
m_nextModDelta = delta.second;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_nextModTick = 0x7fffffff;
|
||||
}
|
||||
}
|
||||
remModTicks -= (nextTick - modTick);
|
||||
modTick = nextTick;
|
||||
}
|
||||
}
|
||||
|
||||
/* Loop through as many commands as we can for this time period */
|
||||
if (m_parent->m_sngVersion == 1)
|
||||
{
|
||||
/* Revision */
|
||||
while (true)
|
||||
{
|
||||
/* Advance wait timer if active, returning if waiting */
|
||||
if (m_eventWaitCountdown)
|
||||
{
|
||||
m_eventWaitCountdown -= ticks;
|
||||
ticks = 0;
|
||||
if (m_eventWaitCountdown > 0)
|
||||
break;
|
||||
}
|
||||
|
||||
/* Load next command */
|
||||
if (*reinterpret_cast<const uint16_t*>(m_data) == 0xffff)
|
||||
{
|
||||
/* End of channel */
|
||||
m_data = nullptr;
|
||||
break;
|
||||
}
|
||||
else if (m_data[0] & 0x80 && m_data[1] & 0x80)
|
||||
{
|
||||
/* Control change */
|
||||
uint8_t val = m_data[0] & 0x7f;
|
||||
uint8_t ctrl = m_data[1] & 0x7f;
|
||||
seq.setCtrlValue(m_midiChan, ctrl, val);
|
||||
m_data += 2;
|
||||
}
|
||||
else if (m_data[0] & 0x80)
|
||||
{
|
||||
/* Program change */
|
||||
uint8_t prog = m_data[0] & 0x7f;
|
||||
seq.setChanProgram(m_midiChan, prog);
|
||||
m_data += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_nextPitchTick = 0x7fffffff;
|
||||
}
|
||||
}
|
||||
remPitchTicks -= (nextTick - pitchTick);
|
||||
pitchTick = nextTick;
|
||||
}
|
||||
}
|
||||
|
||||
/* Update continuous modulation data */
|
||||
if (m_modWheelData)
|
||||
{
|
||||
int32_t modTick = m_parent->m_curTick;
|
||||
int32_t remModTicks = ticks;
|
||||
while (modTick < endTick)
|
||||
{
|
||||
/* See if there's an upcoming modulation change in this interval */
|
||||
int32_t nextTick = m_nextModTick;
|
||||
if (modTick + remModTicks > nextTick)
|
||||
{
|
||||
/* Update modulation */
|
||||
m_modVal += m_nextModDelta;
|
||||
seq.setCtrlValue(m_midiChan, 1, clamp(0, m_modVal / 128, 127));
|
||||
if (m_modWheelData[0] != 0x80 || m_modWheelData[1] != 0x00)
|
||||
{
|
||||
auto delta = DecodeDelta(m_modWheelData);
|
||||
m_nextModTick += delta.first;
|
||||
m_nextModDelta = delta.second;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_nextModTick = 0x7fffffff;
|
||||
}
|
||||
}
|
||||
remModTicks -= (nextTick - modTick);
|
||||
modTick = nextTick;
|
||||
}
|
||||
}
|
||||
|
||||
/* Loop through as many commands as we can for this time period */
|
||||
if (m_parent->m_sngVersion == 1)
|
||||
{
|
||||
/* Revision */
|
||||
while (true)
|
||||
{
|
||||
/* Advance wait timer if active, returning if waiting */
|
||||
if (m_eventWaitCountdown)
|
||||
{
|
||||
m_eventWaitCountdown -= ticks;
|
||||
ticks = 0;
|
||||
if (m_eventWaitCountdown > 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Load next command */
|
||||
if (*reinterpret_cast<const uint16_t*>(m_data) == 0xffff)
|
||||
{
|
||||
/* End of channel */
|
||||
m_data = nullptr;
|
||||
return !m_nextRegion->indexValid(m_parent->m_bigEndian);
|
||||
}
|
||||
else if (m_data[0] & 0x80 && m_data[1] & 0x80)
|
||||
{
|
||||
/* Control change */
|
||||
uint8_t val = m_data[0] & 0x7f;
|
||||
uint8_t ctrl = m_data[1] & 0x7f;
|
||||
seq.setCtrlValue(m_midiChan, ctrl, val);
|
||||
m_data += 2;
|
||||
}
|
||||
else if (m_data[0] & 0x80)
|
||||
{
|
||||
/* Program change */
|
||||
uint8_t prog = m_data[0] & 0x7f;
|
||||
seq.setChanProgram(m_midiChan, prog);
|
||||
m_data += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Note */
|
||||
uint8_t note = m_data[0] & 0x7f;
|
||||
uint8_t vel = m_data[1] & 0x7f;
|
||||
uint16_t length = (m_parent->m_bigEndian ? SBig(*reinterpret_cast<const uint16_t*>(m_data + 2))
|
||||
: *reinterpret_cast<const uint16_t*>(m_data + 2));
|
||||
seq.keyOn(m_midiChan, note, vel);
|
||||
if (length == 0)
|
||||
seq.keyOff(m_midiChan, note, 0);
|
||||
m_remNoteLengths[note] = length;
|
||||
m_data += 4;
|
||||
}
|
||||
|
||||
/* Set next delta-time */
|
||||
m_eventWaitCountdown += int32_t(DecodeTime(m_data));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Legacy */
|
||||
while (true)
|
||||
{
|
||||
/* Advance wait timer if active, returning if waiting */
|
||||
if (m_eventWaitCountdown)
|
||||
{
|
||||
m_eventWaitCountdown -= ticks;
|
||||
ticks = 0;
|
||||
if (m_eventWaitCountdown > 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Load next command */
|
||||
if (*reinterpret_cast<const uint16_t*>(&m_data[2]) == 0xffff)
|
||||
{
|
||||
/* End of channel */
|
||||
m_data = nullptr;
|
||||
return !m_nextRegion->indexValid(m_parent->m_bigEndian);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((m_data[2] & 0x80) != 0x80)
|
||||
{
|
||||
/* Note */
|
||||
uint16_t length = (m_parent->m_bigEndian ? SBig(*reinterpret_cast<const uint16_t*>(m_data))
|
||||
: *reinterpret_cast<const uint16_t*>(m_data));
|
||||
uint8_t note = m_data[2] & 0x7f;
|
||||
uint8_t vel = m_data[3] & 0x7f;
|
||||
uint8_t note = m_data[0] & 0x7f;
|
||||
uint8_t vel = m_data[1] & 0x7f;
|
||||
uint16_t length = (m_parent->m_bigEndian ? SBig(*reinterpret_cast<const uint16_t*>(m_data + 2))
|
||||
: *reinterpret_cast<const uint16_t*>(m_data + 2));
|
||||
seq.keyOn(m_midiChan, note, vel);
|
||||
if (length == 0)
|
||||
seq.keyOff(m_midiChan, note, 0);
|
||||
m_remNoteLengths[note] = length;
|
||||
m_data += 4;
|
||||
}
|
||||
else if (m_data[2] & 0x80 && m_data[3] & 0x80)
|
||||
|
||||
/* Set next delta-time */
|
||||
m_eventWaitCountdown += int32_t(DecodeTime(m_data));
|
||||
}
|
||||
} else
|
||||
{
|
||||
/* Legacy */
|
||||
while (true)
|
||||
{
|
||||
/* Advance wait timer if active, returning if waiting */
|
||||
if (m_eventWaitCountdown)
|
||||
{
|
||||
/* Control change */
|
||||
uint8_t val = m_data[2] & 0x7f;
|
||||
uint8_t ctrl = m_data[3] & 0x7f;
|
||||
seq.setCtrlValue(m_midiChan, ctrl, val);
|
||||
m_eventWaitCountdown -= ticks;
|
||||
ticks = 0;
|
||||
if (m_eventWaitCountdown > 0)
|
||||
break;
|
||||
}
|
||||
else if (m_data[2] & 0x80)
|
||||
|
||||
/* Load next command */
|
||||
if (*reinterpret_cast<const uint16_t*>(&m_data[2]) == 0xffff)
|
||||
{
|
||||
/* Program change */
|
||||
uint8_t prog = m_data[2] & 0x7f;
|
||||
seq.setChanProgram(m_midiChan, prog);
|
||||
/* End of channel */
|
||||
m_data = nullptr;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((m_data[2] & 0x80) != 0x80)
|
||||
{
|
||||
/* Note */
|
||||
uint16_t length = (m_parent->m_bigEndian ? SBig(*reinterpret_cast<const uint16_t*>(m_data))
|
||||
: *reinterpret_cast<const uint16_t*>(m_data));
|
||||
uint8_t note = m_data[2] & 0x7f;
|
||||
uint8_t vel = m_data[3] & 0x7f;
|
||||
seq.keyOn(m_midiChan, note, vel);
|
||||
if (length == 0)
|
||||
seq.keyOff(m_midiChan, note, 0);
|
||||
m_remNoteLengths[note] = length;
|
||||
}
|
||||
else if (m_data[2] & 0x80 && m_data[3] & 0x80)
|
||||
{
|
||||
/* Control change */
|
||||
uint8_t val = m_data[2] & 0x7f;
|
||||
uint8_t ctrl = m_data[3] & 0x7f;
|
||||
seq.setCtrlValue(m_midiChan, ctrl, val);
|
||||
}
|
||||
else if (m_data[2] & 0x80)
|
||||
{
|
||||
/* Program change */
|
||||
uint8_t prog = m_data[2] & 0x7f;
|
||||
seq.setChanProgram(m_midiChan, prog);
|
||||
}
|
||||
m_data += 4;
|
||||
}
|
||||
|
||||
/* Set next delta-time */
|
||||
int32_t absTick = (m_parent->m_bigEndian ? SBig(*reinterpret_cast<const int32_t*>(m_data))
|
||||
: *reinterpret_cast<const int32_t*>(m_data));
|
||||
m_eventWaitCountdown += absTick - m_lastN64EventTick;
|
||||
m_lastN64EventTick = absTick;
|
||||
m_data += 4;
|
||||
}
|
||||
|
||||
/* Set next delta-time */
|
||||
int32_t absTick = (m_parent->m_bigEndian ? SBig(*reinterpret_cast<const int32_t*>(m_data))
|
||||
: *reinterpret_cast<const int32_t*>(m_data));
|
||||
m_eventWaitCountdown += absTick - m_lastN64EventTick;
|
||||
m_lastN64EventTick = absTick;
|
||||
m_data += 4;
|
||||
}
|
||||
}
|
||||
|
||||
m_curTick = endTick;
|
||||
|
||||
/* Handle loop end */
|
||||
if (m_parent->m_loop)
|
||||
{
|
||||
int loopTo;
|
||||
if ((loopTo = m_nextRegion->indexLoop(m_parent->m_bigEndian)) != -1)
|
||||
{
|
||||
uint32_t loopEndTick = (m_parent->m_bigEndian ?
|
||||
SBig(m_nextRegion->m_startTick) : m_nextRegion->m_startTick);
|
||||
if (uint32_t(endTick) > loopEndTick)
|
||||
{
|
||||
m_nextRegion = &m_initRegion[loopTo];
|
||||
m_curRegion = nullptr;
|
||||
m_data = nullptr;
|
||||
m_curTick = m_loopStartTick;
|
||||
resetTempo();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_data)
|
||||
return m_nextRegion->indexDone(m_parent->m_bigEndian, m_parent->m_loop);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -599,50 +718,11 @@ bool SongState::advance(Sequencer& seq, double dt)
|
||||
if (m_songState == SongPlayState::Stopped)
|
||||
return true;
|
||||
|
||||
bool done = false;
|
||||
m_curDt += dt;
|
||||
while (m_curDt > 0.0)
|
||||
{
|
||||
done = true;
|
||||
|
||||
/* Compute ticks to compute based on current tempo */
|
||||
double ticksPerSecond = m_tempo * 384 / 60;
|
||||
int32_t remTicks = std::ceil(m_curDt * ticksPerSecond);
|
||||
if (!remTicks)
|
||||
break;
|
||||
|
||||
/* See if there's an upcoming tempo change in this interval */
|
||||
if (m_tempoPtr && m_tempoPtr->m_tick != 0xffffffff)
|
||||
{
|
||||
TempoChange change = *m_tempoPtr;
|
||||
if (m_bigEndian)
|
||||
change.swapBig();
|
||||
|
||||
if (m_curTick + remTicks > change.m_tick)
|
||||
remTicks = change.m_tick - m_curTick;
|
||||
|
||||
if (remTicks <= 0)
|
||||
{
|
||||
/* Turn over tempo */
|
||||
m_tempo = change.m_tempo & 0x7fffffff;
|
||||
seq.setTempo(m_tempo * 384 / 60);
|
||||
++m_tempoPtr;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* Advance all tracks */
|
||||
for (Track& trk : m_tracks)
|
||||
if (trk)
|
||||
done &= trk.advance(seq, remTicks);
|
||||
|
||||
m_curTick += remTicks;
|
||||
|
||||
if (m_tempo == 0)
|
||||
m_curDt = 0.0;
|
||||
else
|
||||
m_curDt -= remTicks / ticksPerSecond;
|
||||
}
|
||||
/* Advance all tracks */
|
||||
bool done = true;
|
||||
for (Track& trk : m_tracks)
|
||||
if (trk)
|
||||
done &= trk.advance(seq, dt);
|
||||
|
||||
if (done)
|
||||
m_songState = SongPlayState::Stopped;
|
||||
|
||||
Reference in New Issue
Block a user