/*
* SSEQ Player - Track structure
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
*
* Adapted from source code of FeOS Sound System
* By fincs
* https://github.com/fincs/FSS
*/
#include <algorithm>
#include <functional>
#include <cstddef>
#include <cstdint>
#include "Player.h"
#include "SBNK.h"
#include "SSEQ.h"
#include "SWAR.h"
#include "SWAV.h"
#include "Track.h"
#include "common.h"
#include "consts.h"
Track::Track()
{
this->Zero();
}
// Original FSS Function: Player_InitTrack
void Track::Init(std::uint8_t handle, Player *player, const std::uint8_t *dataPos, int n)
{
this->trackId = handle;
this->num = n;
this->ply = player;
this->startPos = dataPos;
this->ClearState();
}
void Track::Zero()
{
this->trackId = -1;
this->state.reset();
this->num = this->prio = 0;
this->ply = nullptr;
this->startPos = this->pos = nullptr;
std::fill_n(&this->stack[0], FSS_TRACKSTACKSIZE, StackValue());
this->stackPos = 0;
std::fill_n(&this->loopCount[0], FSS_TRACKSTACKSIZE, 0);
this->overriding() = false;
this->lastComparisonResult = true;
this->wait = 0;
this->patch = 0;
this->portaKey = this->portaTime = 0;
this->sweepPitch = 0;
this->vol = this->expr = 0;
this->pan = 0;
this->pitchBendRange = 0;
this->pitchBend = this->transpose = 0;
this->a = this->d = this->s = this->r = 0;
this->modType = this->modSpeed = this->modDepth = this->modRange = 0;
this->modDelay = 0;
this->updateFlags.reset();
}
// Original FSS Function: Track_ClearState
void Track::ClearState()
{
this->state.reset();
this->state.set(ToIntegral(TrackState::AllocateBit));
this->state.set(ToIntegral(TrackState::NoteWait));
this->prio = this->ply->prio + 64;
this->pos = this->startPos;
this->stackPos = 0;
this->wait = 0;
this->patch = 0;
this->portaKey = 60;
this->portaTime = 0;
this->sweepPitch = 0;
this->vol = this->expr = 127;
this->pan = 0;
this->pitchBendRange = 2;
this->pitchBend = this->transpose = 0;
this->a = this->d = this->s = this->r = 0xFF;
this->modType = 0;
this->modRange = 1;
this->modSpeed = 16;
this->modDelay = 0;
this->modDepth = 0;
}
// Original FSS Function: Track_Free
void Track::Free()
{
this->state.reset();
this->updateFlags.reset();
}
// Original FSS Function: Note_On
int Track::NoteOn(int key, int vel, int len)
{
auto sbnk = this->ply->sseq->bank;
if (this->patch >= sbnk->entries.size())
return -1;
bool bIsPCM = true;
Channel *chn = nullptr;
int nCh = -1;
auto &entry = sbnk->entries[this->patch];
const SBNKInstrument *noteDef = nullptr;
int fRecord = entry.record;
if (fRecord == 16)
{
if (!(entry.instruments[0].lowNote <= key && key <= entry.instruments[entry.instruments.size() - 1].highNote))
return -1;
int rn = key - entry.instruments[0].lowNote;
noteDef = &entry.instruments[rn];
fRecord = noteDef->record;
}
else if (fRecord == 17)
{
std::size_t reg, entries;
for (reg = 0, entries = entry.instruments.size(); reg < entries; ++reg)
if (key <= entry.instruments[reg].highNote)
break;
if (reg == entries)
return -1;
noteDef = &entry.instruments[reg];
fRecord = noteDef->record;
}
if (!fRecord)
return -1;
else if (fRecord == 1)
{
if (!noteDef)
noteDef = &entry.instruments[0];
}
else if (fRecord < 4)
{
// PSG
// fRecord = 2 -> PSG tone, pNoteDef->wavid -> PSG duty
// fRecord = 3 -> PSG noise
bIsPCM = false;
if (!noteDef)
noteDef = &entry.instruments[0];
if (fRecord == 3)
{
nCh = this->ply->ChannelAlloc(ChannelAllocateType::Noise, this->prio);
if (nCh < 0)
return -1;
chn = &this->ply->channels[nCh];
chn->tempReg.CR = SOUND_FORMAT_PSG | SCHANNEL_ENABLE;
}
else
{
nCh = this->ply->ChannelAlloc(ChannelAllocateType::PSG, this->prio);
if (nCh < 0)
return -1;
chn = &this->ply->channels[nCh];
chn->tempReg.CR = SOUND_FORMAT_PSG | SCHANNEL_ENABLE | SOUND_DUTY(noteDef->swav & 0x7);
}
chn->tempReg.TIMER = -SOUND_FREQ(262 * 8); // key #60 (C4)
chn->reg.samplePosition = -1;
chn->reg.psgX = 0x7FFF;
}
if (bIsPCM)
{
nCh = this->ply->ChannelAlloc(ChannelAllocateType::PCM, this->prio);
if (nCh < 0)
return -1;
chn = &this->ply->channels[nCh];
auto swav = &sbnk->waveArc[noteDef->swar]->swavs.find(noteDef->swav)->second;
chn->tempReg.CR = SOUND_FORMAT(swav->waveType & 3) | SOUND_LOOP(!!swav->loop) | SCHANNEL_ENABLE;
chn->tempReg.SOURCE = swav;
chn->tempReg.TIMER = swav->time;
chn->tempReg.REPEAT_POINT = swav->loopOffset;
chn->tempReg.LENGTH = swav->nonLoopLength;
chn->reg.samplePosition = -3;
}
chn->state = ChannelState::Start;
chn->trackId = this->trackId;
chn->flags.reset();
chn->prio = this->prio;
chn->key = key;
chn->orgKey = noteDef->noteNumber;
chn->velocity = Cnv_Sust(vel);
chn->pan = static_cast<int>(noteDef->pan) - 64;
chn->modDelayCnt = 0;
chn->modCounter = 0;
chn->noteLength = len;
chn->reg.sampleIncrease = 0;
chn->attackLvl = Cnv_Attack(this->a == 0xFF ? noteDef->attackRate : this->a);
chn->decayRate = Cnv_Fall(this->d == 0xFF ? noteDef->decayRate : this->d);
chn->sustainLvl = this->s == 0xFF ? noteDef->sustainLevel : this->s;
chn->releaseRate = Cnv_Fall(this->r == 0xFF ? noteDef->releaseRate : this->r);
chn->UpdateVol(*this);
chn->UpdatePan(*this);
chn->UpdateTune(*this);
chn->UpdateMod(*this);
chn->UpdatePorta(*this);
this->portaKey = key;
return nCh;
}
// Original FSS Function: Note_On_Tie
int Track::NoteOnTie(int key, int vel)
{
// Find an existing note
int i;
Channel *chn = nullptr;
for (i = 0; i < 16; ++i)
{
chn = &this->ply->channels[i];
if (chn->state > ChannelState::None && chn->trackId == this->trackId && chn->state != ChannelState::Release)
break;
}
if (i == 16)
// Can't find note -> create an endless one
return this->NoteOn(key, vel, -1);
chn->flags.reset();
chn->prio = this->prio;
chn->key = key;
chn->velocity = Cnv_Sust(vel);
chn->modDelayCnt = 0;
chn->modCounter = 0;
chn->UpdateVol(*this);
//chn->UpdatePan(*this);
chn->UpdateTune(*this);
chn->UpdateMod(*this);
chn->UpdatePorta(*this);
this->portaKey = key;
chn->flags.set(ToIntegral(ChannelFlag::UpdateTimer));
return i;
}
// Original FSS Function: Track_ReleaseAllNotes
void Track::ReleaseAllNotes()
{
for (int i = 0; i < 16; ++i)
{
Channel &chn = this->ply->channels[i];
if (chn.state > ChannelState::None && chn.trackId == this->trackId && chn.state != ChannelState::Release)
chn.Release();
}
}
enum class SSEQCommand
{
AllocateTrack = 0xFE, // Silently ignored
OpenTrack = 0x93,
Rest = 0x80,
Patch = 0x81,
Pan = 0xC0,
Volume = 0xC1,
MasterVolume = 0xC2,
Priority = 0xC6,
NoteWait = 0xC7,
Tie = 0xC8,
Expression = 0xD5,
Tempo = 0xE1,
End = 0xFF,
Goto = 0x94,
Call = 0x95,
Return = 0xFD,
LoopStart = 0xD4,
LoopEnd = 0xFC,
Transpose = 0xC3,
PitchBend = 0xC4,
PitchBendRange = 0xC5,
Attack = 0xD0,
Decay = 0xD1,
Sustain = 0xD2,
Release = 0xD3,
PortamentoKey = 0xC9,
PortamentoFlag = 0xCE,
PortamentoTime = 0xCF,
SweepPitch = 0xE3,
ModulationDepth = 0xCA,
ModulationSpeed = 0xCB,
ModulationType = 0xCC,
ModulationRange = 0xCD,
ModulationDelay = 0xE0,
Random = 0xA0,
PrintVariable = 0xD6,
If = 0xA2,
FromVariable = 0xA1,
SetVariable = 0xB0,
AddVariable = 0xB1,
SubtractVariable = 0xB2,
MultiplyVariable = 0xB3,
DivideVariable = 0xB4,
ShiftVariable = 0xB5,
RandomVariable = 0xB6,
CompareEqualTo = 0xB8,
CompareGreaterThanOrEqualTo = 0xB9,
CompareGreaterThan = 0xBA,
CompareLessThanOrEqualTo = 0xBB,
CompareLessThan = 0xBC,
CompareNotEqualTo = 0xBD,
Mute = 0xD7 // Unsupported
};
static const std::uint8_t VariableByteCount = 1 << 7;
static const std::uint8_t ExtraByteOnNoteOrVarOrCmp = 1 << 6;
static inline std::uint8_t SseqCommandByteCount(int cmd)
{
if (cmd < 0x80)
return 1 | VariableByteCount;
else
switch (static_cast<SSEQCommand>(cmd))
{
case SSEQCommand::Rest:
case SSEQCommand::Patch:
return VariableByteCount;
case SSEQCommand::Pan:
case SSEQCommand::Volume:
case SSEQCommand::MasterVolume:
case SSEQCommand::Priority:
case SSEQCommand::NoteWait:
case SSEQCommand::Tie:
case SSEQCommand::Expression:
case SSEQCommand::LoopStart:
case SSEQCommand::Transpose:
case SSEQCommand::PitchBend:
case SSEQCommand::PitchBendRange:
case SSEQCommand::Attack:
case SSEQCommand::Decay:
case SSEQCommand::Sustain:
case SSEQCommand::Release:
case SSEQCommand::PortamentoKey:
case SSEQCommand::PortamentoFlag:
case SSEQCommand::PortamentoTime:
case SSEQCommand::ModulationDepth:
case SSEQCommand::ModulationSpeed:
case SSEQCommand::ModulationType:
case SSEQCommand::ModulationRange:
case SSEQCommand::PrintVariable:
case SSEQCommand::Mute:
return 1;
case SSEQCommand::AllocateTrack:
case SSEQCommand::Tempo:
case SSEQCommand::SweepPitch:
case SSEQCommand::ModulationDelay:
return 2;
case SSEQCommand::Goto:
case SSEQCommand::Call:
case SSEQCommand::SetVariable:
case SSEQCommand::AddVariable:
case SSEQCommand::SubtractVariable:
case SSEQCommand::MultiplyVariable:
case SSEQCommand::DivideVariable:
case SSEQCommand::ShiftVariable:
case SSEQCommand::RandomVariable:
case SSEQCommand::CompareEqualTo:
case SSEQCommand::CompareGreaterThanOrEqualTo:
case SSEQCommand::CompareGreaterThan:
case SSEQCommand::CompareLessThanOrEqualTo:
case SSEQCommand::CompareLessThan:
case SSEQCommand::CompareNotEqualTo:
return 3;
case SSEQCommand::OpenTrack:
return 4;
case SSEQCommand::FromVariable:
return 1 | ExtraByteOnNoteOrVarOrCmp; // Technically 2 bytes with an additional 1, leaving 1 off because we will be reading it to determine if the additional byte is needed
case SSEQCommand::Random:
return 4 | ExtraByteOnNoteOrVarOrCmp; // Technically 5 bytes with an additional 1, leaving 1 off because we will be reading it to determine if the additional byte is needed
default:
return 0;
}
}
static std::uint32_t RandomU{ 0x12345678 };
static std::uint16_t CalcRandom()
{
RandomU = RandomU * 1664525 + 1013904223;
return static_cast<std::uint16_t>(RandomU >> 16);
}
static auto varFuncSet = [](std::int16_t, std::int16_t value) { return value; };
static auto varFuncAdd = [](std::int16_t var, std::int16_t value) -> std::int16_t { return var + value; };
static auto varFuncSub = [](std::int16_t var, std::int16_t value) -> std::int16_t { return var - value; };
static auto varFuncMul = [](std::int16_t var, std::int16_t value) -> std::int16_t { return var * value; };
static auto varFuncDiv = [](std::int16_t var, std::int16_t value) -> std::int16_t { return var / value; };
static auto varFuncShift = [](std::int16_t var, std::int16_t value) -> std::int16_t
{
if (value < 0)
return var >> -value;
else
return var << value;
};
static auto varFuncRand = [](std::int16_t, std::int16_t value) -> std::int16_t
{
if (value < 0)
return -(CalcRandom() % (-value + 1));
else
return CalcRandom() % (value + 1);
};
static inline std::function<std::int16_t (std::int16_t, std::int16_t)> VarFunc(int cmd)
{
switch (static_cast<SSEQCommand>(cmd))
{
case SSEQCommand::SetVariable:
return varFuncSet;
case SSEQCommand::AddVariable:
return varFuncAdd;
case SSEQCommand::SubtractVariable:
return varFuncSub;
case SSEQCommand::MultiplyVariable:
return varFuncMul;
case SSEQCommand::DivideVariable:
return varFuncDiv;
case SSEQCommand::ShiftVariable:
return varFuncShift;
case SSEQCommand::RandomVariable:
return varFuncRand;
default:
return nullptr;
}
}
static auto compareFuncEq = [](std::int16_t a, std::int16_t b) { return a == b; };
static auto compareFuncGe = [](std::int16_t a, std::int16_t b) { return a >= b; };
static auto compareFuncGt = [](std::int16_t a, std::int16_t b) { return a > b; };
static auto compareFuncLe = [](std::int16_t a, std::int16_t b) { return a <= b; };
static auto compareFuncLt = [](std::int16_t a, std::int16_t b) { return a < b; };
static auto compareFuncNe = [](std::int16_t a, std::int16_t b) { return a != b; };
static inline std::function<bool (std::int16_t, std::int16_t)> CompareFunc(int cmd)
{
switch (static_cast<SSEQCommand>(cmd))
{
case SSEQCommand::CompareEqualTo:
return compareFuncEq;
case SSEQCommand::CompareGreaterThanOrEqualTo:
return compareFuncGe;
case SSEQCommand::CompareGreaterThan:
return compareFuncGt;
case SSEQCommand::CompareLessThanOrEqualTo:
return compareFuncLe;
case SSEQCommand::CompareLessThan:
return compareFuncLt;
case SSEQCommand::CompareNotEqualTo:
return compareFuncNe;
default:
return nullptr;
}
}
// Original FSS Function: Track_Run
void Track::Run()
{
// Indicate "heartbeat" for this track
this->updateFlags.set(ToIntegral(TrackUpdateFlag::Length));
// Exit if the track has already ended
if (this->state[ToIntegral(TrackState::End)])
return;
if (this->wait)
{
--this->wait;
if (this->wait)
return;
}
auto pData = &this->pos;
while (!this->wait)
{
int cmd;
if (this->overriding())
cmd = this->overriding.cmd;
else
cmd = read8(pData);
if (cmd < 0x80)
{
// Note on
int key = cmd + this->transpose;
int vel = this->overriding.val(pData, read8, true);
int len = this->overriding.val(pData, readvl);
if (this->state[ToIntegral(TrackState::NoteWait)])
this->wait = len;
if (this->state[ToIntegral(TrackState::TieBit)])
this->NoteOnTie(key, vel);
else
this->NoteOn(key, vel, len);
}
else
{
int value;
switch (static_cast<SSEQCommand>(cmd))
{
//-----------------------------------------------------------------
// Main commands
//-----------------------------------------------------------------
case SSEQCommand::OpenTrack:
{
int tNum = read8(pData);
auto trackPos = &this->ply->sseq->data[read24(pData)];
int newTrack = this->ply->TrackAlloc();
if (newTrack != -1)
{
this->ply->tracks[newTrack].Init(newTrack, this->ply, trackPos, tNum);
this->ply->trackIds[this->ply->nTracks++] = newTrack;
}
break;
}
case SSEQCommand::Rest:
this->wait = this->overriding.val(pData, readvl);
break;
case SSEQCommand::Patch:
this->patch = this->overriding.val(pData, readvl);
break;
case SSEQCommand::Goto:
*pData = &this->ply->sseq->data[read24(pData)];
break;
case SSEQCommand::Call:
value = read24(pData);
if (this->stackPos < FSS_TRACKSTACKSIZE)
{
const std::uint8_t *dest = &this->ply->sseq->data[value];
this->stack[this->stackPos++] = StackValue(StackType::Call, *pData);
*pData = dest;
}
break;
case SSEQCommand::Return:
if (this->stackPos && this->stack[this->stackPos - 1].type == StackType::Call)
*pData = this->stack[--this->stackPos].dest;
break;
case SSEQCommand::Pan:
this->pan = this->overriding.val(pData, read8) - 64;
this->updateFlags.set(ToIntegral(TrackUpdateFlag::Pan));
break;
case SSEQCommand::Volume:
this->vol = this->overriding.val(pData, read8);
this->updateFlags.set(ToIntegral(TrackUpdateFlag::Volume));
break;
case SSEQCommand::MasterVolume:
this->ply->masterVol = Cnv_Sust(this->overriding.val(pData, read8));
for (std::uint8_t i = 0; i < this->ply->nTracks; ++i)
this->ply->tracks[this->ply->trackIds[i]].updateFlags.set(ToIntegral(TrackUpdateFlag::Volume));
break;
case SSEQCommand::Priority:
this->prio = this->ply->prio + read8(pData);
// Update here?
break;
case SSEQCommand::NoteWait:
this->state.set(ToIntegral(TrackState::NoteWait), !!read8(pData));
break;
case SSEQCommand::Tie:
this->state.set(ToIntegral(TrackState::TieBit), !!read8(pData));
this->ReleaseAllNotes();
break;
case SSEQCommand::Expression:
this->expr = this->overriding.val(pData, read8);
this->updateFlags.set(ToIntegral(TrackUpdateFlag::Volume));
break;
case SSEQCommand::Tempo:
this->ply->tempo = read16(pData);
break;
case SSEQCommand::End:
this->state.set(ToIntegral(TrackState::End));
return;
case SSEQCommand::LoopStart:
value = this->overriding.val(pData, read8);
if (this->stackPos < FSS_TRACKSTACKSIZE)
{
this->loopCount[this->stackPos] = value;
this->stack[this->stackPos++] = StackValue(StackType::Loop, *pData);
}
break;
case SSEQCommand::LoopEnd:
if (this->stackPos && this->stack[this->stackPos - 1].type == StackType::Loop)
{
const std::uint8_t *rPos = this->stack[this->stackPos - 1].dest;
std::uint8_t &nR = this->loopCount[this->stackPos - 1];
std::uint8_t prevR = nR;
if (!prevR || --nR)
*pData = rPos;
else
--this->stackPos;
}
break;
//-----------------------------------------------------------------
// Tuning commands
//-----------------------------------------------------------------
case SSEQCommand::Transpose:
this->transpose = this->overriding.val(pData, read8);
break;
case SSEQCommand::PitchBend:
this->pitchBend = this->overriding.val(pData, read8);
this->updateFlags.set(ToIntegral(TrackUpdateFlag::Timer));
break;
case SSEQCommand::PitchBendRange:
this->pitchBendRange = read8(pData);
this->updateFlags.set(ToIntegral(TrackUpdateFlag::Timer));
break;
//-----------------------------------------------------------------
// Envelope-related commands
//-----------------------------------------------------------------
case SSEQCommand::Attack:
this->a = this->overriding.val(pData, read8);
break;
case SSEQCommand::Decay:
this->d = this->overriding.val(pData, read8);
break;
case SSEQCommand::Sustain:
this->s = this->overriding.val(pData, read8);
break;
case SSEQCommand::Release:
this->r = this->overriding.val(pData, read8);
break;
//-----------------------------------------------------------------
// Portamento-related commands
//-----------------------------------------------------------------
case SSEQCommand::PortamentoKey:
this->portaKey = read8(pData) + this->transpose;
this->state.set(ToIntegral(TrackState::PortamentoBit));
// Update here?
break;
case SSEQCommand::PortamentoFlag:
this->state.set(ToIntegral(TrackState::PortamentoBit), !!read8(pData));
// Update here?
break;
case SSEQCommand::PortamentoTime:
this->portaTime = this->overriding.val(pData, read8);
// Update here?
break;
case SSEQCommand::SweepPitch:
this->sweepPitch = this->overriding.val(pData, read16);
this->state.set(ToIntegral(TrackState::PortamentoBit));
// Update here?
break;
//-----------------------------------------------------------------
// Modulation-related commands
//-----------------------------------------------------------------
case SSEQCommand::ModulationDepth:
this->modDepth = this->overriding.val(pData, read8);
this->updateFlags.set(ToIntegral(TrackUpdateFlag::Modulation));
break;
case SSEQCommand::ModulationSpeed:
this->modSpeed = this->overriding.val(pData, read8);
this->updateFlags.set(ToIntegral(TrackUpdateFlag::Modulation));
break;
case SSEQCommand::ModulationType:
this->modType = read8(pData);
this->updateFlags.set(ToIntegral(TrackUpdateFlag::Modulation));
break;
case SSEQCommand::ModulationRange:
this->modRange = read8(pData);
this->updateFlags.set(ToIntegral(TrackUpdateFlag::Modulation));
break;
case SSEQCommand::ModulationDelay:
this->modDelay = this->overriding.val(pData, read16);
this->updateFlags.set(ToIntegral(TrackUpdateFlag::Modulation));
break;
//-----------------------------------------------------------------
// Randomness-related commands
//-----------------------------------------------------------------
case SSEQCommand::Random:
{
this->overriding() = true;
this->overriding.cmd = read8(pData);
if ((this->overriding.cmd >= ToIntegral(SSEQCommand::SetVariable) && this->overriding.cmd <= ToIntegral(SSEQCommand::CompareNotEqualTo)) || this->overriding.cmd < 0x80)
this->overriding.extraValue = read8(pData);
std::int16_t minVal = read16(pData);
std::int16_t maxVal = read16(pData);
this->overriding.value = (CalcRandom() % (maxVal - minVal + 1)) + minVal;
break;
}
//-----------------------------------------------------------------
// Variable-related commands
//-----------------------------------------------------------------
case SSEQCommand::FromVariable:
this->overriding() = true;
this->overriding.cmd = read8(pData);
if ((this->overriding.cmd >= ToIntegral(SSEQCommand::SetVariable) && this->overriding.cmd <= ToIntegral(SSEQCommand::CompareNotEqualTo)) || this->overriding.cmd < 0x80)
this->overriding.extraValue = read8(pData);
this->overriding.value = this->ply->variables[read8(pData)];
break;
case SSEQCommand::SetVariable:
case SSEQCommand::AddVariable:
case SSEQCommand::SubtractVariable:
case SSEQCommand::MultiplyVariable:
case SSEQCommand::DivideVariable:
case SSEQCommand::ShiftVariable:
case SSEQCommand::RandomVariable:
{
std::int8_t varNo = this->overriding.val(pData, read8, true);
value = this->overriding.val(pData, read16);
if (cmd == ToIntegral(SSEQCommand::DivideVariable) && !value) // Division by 0, skip it to prevent crashing
break;
this->ply->variables[varNo] = VarFunc(cmd)(this->ply->variables[varNo], value);
break;
}
//-----------------------------------------------------------------
// Conditional-related commands
//-----------------------------------------------------------------
case SSEQCommand::CompareEqualTo:
case SSEQCommand::CompareGreaterThanOrEqualTo:
case SSEQCommand::CompareGreaterThan:
case SSEQCommand::CompareLessThanOrEqualTo:
case SSEQCommand::CompareLessThan:
case SSEQCommand::CompareNotEqualTo:
{
std::int8_t varNo = this->overriding.val(pData, read8, true);
value = this->overriding.val(pData, read16);
this->lastComparisonResult = CompareFunc(cmd)(this->ply->variables[varNo], value);
break;
}
case SSEQCommand::If:
if (!this->lastComparisonResult)
{
int nextCmd = read8(pData);
std::uint8_t cmdBytes = SseqCommandByteCount(nextCmd);
bool variableBytes = !!(cmdBytes & VariableByteCount);
bool extraByte = !!(cmdBytes & ExtraByteOnNoteOrVarOrCmp);
cmdBytes &= ~(VariableByteCount | ExtraByteOnNoteOrVarOrCmp);
if (extraByte)
{
int extraCmd = read8(pData);
if ((extraCmd >= ToIntegral(SSEQCommand::SetVariable) && extraCmd <= ToIntegral(SSEQCommand::CompareNotEqualTo)) || extraCmd < 0x80)
++cmdBytes;
}
*pData += cmdBytes;
if (variableBytes)
readvl(pData);
}
break;
default:
*pData += SseqCommandByteCount(cmd);
}
}
if (cmd != ToIntegral(SSEQCommand::Random) && cmd != ToIntegral(SSEQCommand::FromVariable))
this->overriding() = false;
}
}