#include "amuse/SoundMacroState.hpp" #include "amuse/Voice.hpp" #include "amuse/Engine.hpp" #include "amuse/Common.hpp" #include "amuse/AudioGroup.hpp" #include "amuse/AudioGroupPool.hpp" #include namespace amuse { void SoundMacroState::Header::swapBig() { m_size = SBig(m_size); m_macroId = SBig(m_macroId); } void SoundMacroState::Command::swapBig() { uint32_t* words = reinterpret_cast(this); words[0] = SBig(words[0]); words[1] = SBig(words[1]); } void SoundMacroState::Evaluator::addComponent(uint8_t midiCtrl, float scale, Combine combine, VarType varType) { m_comps.push_back({midiCtrl, scale, combine, varType}); } float SoundMacroState::Evaluator::evaluate(const Voice& vox, const SoundMacroState& st) const { float value = 0.f; /* Iterate each component */ for (auto it=m_comps.cbegin() ; it != m_comps.cend() ; ++it) { const Component& comp = *it; float thisValue = 0.f; /* Load selected data */ if (comp.m_varType == VarType::Ctrl) { switch (comp.m_midiCtrl) { case 128: /* Pitchbend */ thisValue = vox.getPitchWheel(); break; case 129: /* Aftertouch */ thisValue = vox.getAftertouch(); break; case 130: /* LFO1 */ if (vox.m_lfoPeriods[0]) thisValue = std::sin(vox.m_voiceTime / vox.m_lfoPeriods[0] * 2.f * M_PIF); break; case 131: /* LFO2 */ if (vox.m_lfoPeriods[1]) thisValue = std::sin(vox.m_voiceTime / vox.m_lfoPeriods[1] * 2.f * M_PIF); break; case 132: /* Surround panning */ thisValue = vox.m_curSpan * 64.f + 64.f; break; case 133: /* Macro-starting key */ thisValue = st.m_initKey; break; case 134: /* Macro-starting velocity */ thisValue = st.m_initVel; break; case 135: /* Time since macro-start (ms) */ thisValue = st.m_execTime * 1000.f; break; default: thisValue = vox.getCtrlValue(comp.m_midiCtrl); break; } } else if (comp.m_varType == VarType::Var) thisValue = st.m_variables[std::max(0, std::min(255, int(comp.m_midiCtrl)))]; /* Apply scale */ thisValue *= comp.m_scale; /* Combine */ if (it != m_comps.cbegin()) { switch (comp.m_combine) { case Combine::Add: value += thisValue; break; case Combine::Mult: value *= thisValue; break; default: value = thisValue; break; } } else value = thisValue; } return value; } void SoundMacroState::initialize(const unsigned char* ptr, int step) { initialize(ptr, step, 1000.f, 0, 0, 0); } void SoundMacroState::initialize(const unsigned char* ptr, int step, double ticksPerSec, uint8_t midiKey, uint8_t midiVel, uint8_t midiMod) { m_ticksPerSec = ticksPerSec; m_initKey = midiKey; m_initVel = midiVel; m_initMod = midiMod; m_curVel = 0; m_curMod = 0; m_curKey = 0; m_pc.clear(); m_pc.push_back({ptr, step}); m_execTime = 0.f; m_keyoff = false; m_sampleEnd = false; m_loopCountdown = -1; m_lastPlayMacroVid = -1; m_useAdsrControllers = false; m_portamentoMode = 0; m_header = *reinterpret_cast(ptr); m_header.swapBig(); } bool SoundMacroState::advance(Voice& vox, double dt) { /* Nothing if uninitialized or finished */ if (m_pc.empty() || m_pc.back().first == nullptr || m_pc.back().second == -1) return true; /* Loop through as many commands as we can for this time period */ while (true) { /* Advance wait timer if active, returning if waiting */ if (m_inWait) { if (!m_indefiniteWait) { m_waitCountdown -= dt; if (m_waitCountdown < 0.f) m_inWait = false; } if (m_inWait) { m_execTime += dt; return false; } } /* Load next command based on counter */ const Command* commands = reinterpret_cast(m_pc.back().first + sizeof(Header)); Command cmd = commands[m_pc.back().second++]; cmd.swapBig(); /* Perform function of command */ switch (cmd.m_op) { case Op::End: case Op::Stop: m_pc.clear(); return true; case Op::SplitKey: { uint8_t keyNumber = cmd.m_data[0]; ObjectId macroId = *reinterpret_cast(&cmd.m_data[1]); int16_t macroStep = *reinterpret_cast(&cmd.m_data[3]); if (m_initKey >= keyNumber) { /* Do Branch */ if (macroId == m_header.m_macroId) m_pc.back().second = macroStep; else vox.loadSoundObject(macroId, macroStep, m_ticksPerSec, m_initKey, m_initVel, m_initMod); } break; } case Op::SplitVel: { uint8_t velocity = cmd.m_data[0]; ObjectId macroId = *reinterpret_cast(&cmd.m_data[1]); int16_t macroStep = *reinterpret_cast(&cmd.m_data[3]); if (m_curVel >= velocity) { /* Do Branch */ if (macroId == m_header.m_macroId) m_pc.back().second = macroStep; else vox.loadSoundObject(macroId, macroStep, m_ticksPerSec, m_initKey, m_initVel, m_initMod); } break; } case Op::WaitTicks: { bool keyRelease = cmd.m_data[0]; bool random = cmd.m_data[1]; bool sampleEnd = cmd.m_data[2]; bool absolute = cmd.m_data[3]; bool ms = cmd.m_data[4]; int16_t time = *reinterpret_cast(&cmd.m_data[5]); /* Set wait state */ if (time >= 0) { float q = ms ? 1000.f : m_ticksPerSec; float secTime = time / q; if (absolute) { if (secTime <= m_execTime) break; m_waitCountdown = secTime - m_execTime; } else m_waitCountdown = secTime; /* Randomize at the proper resolution */ if (random) secTime = std::fmod(vox.getEngine().nextRandom() / q, secTime); m_indefiniteWait = false; } else m_indefiniteWait = true; m_inWait = true; m_keyoffWait = keyRelease; m_sampleEndWait = sampleEnd; break; } case Op::Loop: { bool keyRelease = cmd.m_data[0]; bool random = cmd.m_data[1]; bool sampleEnd = cmd.m_data[2]; int16_t step = *reinterpret_cast(&cmd.m_data[3]); int16_t times = *reinterpret_cast(&cmd.m_data[5]); if ((keyRelease && m_keyoff) || (sampleEnd && m_sampleEnd)) { /* Break out of loop */ m_loopCountdown = -1; break; } if (random) times = vox.getEngine().nextRandom() % times; if (m_loopCountdown == -1 && times != -1) m_loopCountdown = times; if (m_loopCountdown > 0) { /* Loop back to step */ --m_loopCountdown; m_pc.back().second = step; } else /* Break out of loop */ m_loopCountdown = -1; break; } case Op::Goto: { ObjectId macroId = *reinterpret_cast(&cmd.m_data[1]); int16_t macroStep = *reinterpret_cast(&cmd.m_data[3]); /* Do Branch */ if (macroId == m_header.m_macroId) m_pc.back().second = macroStep; else vox.loadSoundObject(macroId, macroStep, m_ticksPerSec, m_initKey, m_initVel, m_initMod); break; } case Op::WaitMs: { bool keyRelease = cmd.m_data[0]; bool random = cmd.m_data[1]; bool sampleEnd = cmd.m_data[2]; bool absolute = cmd.m_data[3]; int16_t time = *reinterpret_cast(&cmd.m_data[5]); /* Set wait state */ if (time >= 0) { float secTime = time / 1000.f; if (absolute) { if (secTime <= m_execTime) break; m_waitCountdown = secTime - m_execTime; } else m_waitCountdown = secTime; /* Randomize at the proper resolution */ if (random) secTime = std::fmod(vox.getEngine().nextRandom() / 1000.f, secTime); m_indefiniteWait = false; } else m_indefiniteWait = true; m_inWait = true; m_keyoffWait = keyRelease; m_sampleEndWait = sampleEnd; break; } case Op::PlayMacro: { int8_t addNote = cmd.m_data[0]; ObjectId macroId = *reinterpret_cast(&cmd.m_data[1]); int16_t macroStep = *reinterpret_cast(&cmd.m_data[3]); //int8_t priority = cmd.m_data[5]; //int8_t maxVoices = cmd.m_data[6]; std::shared_ptr sibVox = vox.startChildMacro(addNote, macroId, macroStep); if (sibVox) m_lastPlayMacroVid = sibVox->vid(); break; } case Op::SendKeyOff: { uint8_t vid = cmd.m_data[0]; bool lastStarted = cmd.m_data[1]; if (lastStarted) { if (m_lastPlayMacroVid != -1) { std::shared_ptr otherVox = vox.getEngine().findVoice(m_lastPlayMacroVid); if (otherVox) otherVox->keyOff(); } } else { std::shared_ptr otherVox = vox.getEngine().findVoice(m_variables[vid]); if (otherVox) otherVox->keyOff(); } break; } case Op::SplitMod: { uint8_t mod = cmd.m_data[0]; ObjectId macroId = *reinterpret_cast(&cmd.m_data[1]); int16_t macroStep = *reinterpret_cast(&cmd.m_data[3]); if (m_curMod >= mod) { /* Do Branch */ if (macroId == m_header.m_macroId) m_pc.back().second = macroStep; else vox.loadSoundObject(macroId, macroStep, m_ticksPerSec, m_initKey, m_initVel, m_initMod); } break; } case Op::PianoPan: { int8_t scale = cmd.m_data[0]; int8_t cenKey = cmd.m_data[1]; int8_t cenPan = cmd.m_data[2]; int32_t pan = int32_t(m_initKey - cenKey) * scale / 127 + cenPan; pan = std::max(-127, std::min(127, pan)); vox.setPan(pan / 127.f); break; } case Op::SetAdsr: { ObjectId tableId = *reinterpret_cast(&cmd.m_data[0]); vox.setAdsr(tableId); break; } case Op::ScaleVolume: { int8_t scale = cmd.m_data[0]; int8_t add = cmd.m_data[1]; ObjectId curve = *reinterpret_cast(&cmd.m_data[2]); bool orgVel = cmd.m_data[4]; int32_t eval = int32_t(orgVel ? m_initVel : m_curVel) * scale / 127 + add; eval = std::max(0, std::min(127, eval)); if (curve != 0) { const Curve* curveData = vox.getAudioGroup().getPool().tableAsCurves(curve); if (curveData) { vox.setVolume((*curveData)[eval] / 127.f); break; } } vox.setVolume(eval / 127.f); break; } case Op::Panning: { int8_t panPos = cmd.m_data[0]; int16_t timeMs = *reinterpret_cast(&cmd.m_data[1]); int8_t width = cmd.m_data[3]; vox.startPanning(timeMs / 1000.0, panPos, width); break; } case Op::Envelope: { int8_t scale = cmd.m_data[0]; int8_t add = cmd.m_data[1]; ObjectId curve = *reinterpret_cast(&cmd.m_data[2]); bool ms = cmd.m_data[4]; int16_t fadeTime = *reinterpret_cast(&cmd.m_data[5]); double q = ms ? 1000.0 : m_ticksPerSec; double secTime = fadeTime / q; int32_t eval = int32_t(m_curVel) * scale / 127 + add; eval = std::max(0, std::min(127, eval)); const Curve* curveData; if (curve != 0) curveData = vox.getAudioGroup().getPool().tableAsCurves(curve); else curveData = nullptr; vox.startEnvelope(secTime, eval, curveData); break; } case Op::StartSample: { int16_t smpId = *reinterpret_cast(&cmd.m_data[0]); int8_t mode = cmd.m_data[2]; int32_t offset = *reinterpret_cast(&cmd.m_data[3]); switch (mode) { case 1: offset = offset * (127 - m_curVel) / 127; break; case 2: offset = offset * m_curVel / 127; break; default: break; } vox.startSample(smpId, offset); break; } case Op::StopSample: { vox.stopSample(); break; } case Op::KeyOff: { vox.keyOff(); break; } case Op::SplitRnd: { uint8_t rndVal = cmd.m_data[0]; ObjectId macroId = *reinterpret_cast(&cmd.m_data[1]); int16_t macroStep = *reinterpret_cast(&cmd.m_data[3]); if (rndVal <= vox.getEngine().nextRandom() % 256) { /* Do branch */ if (macroId == m_header.m_macroId) m_pc.back().second = macroStep; else vox.loadSoundObject(macroId, macroStep, m_ticksPerSec, m_initKey, m_initVel, m_initMod); } break; } case Op::FadeIn: { int8_t scale = cmd.m_data[0]; int8_t add = cmd.m_data[1]; ObjectId curve = *reinterpret_cast(&cmd.m_data[2]); bool ms = cmd.m_data[4]; int16_t fadeTime = *reinterpret_cast(&cmd.m_data[5]); float q = ms ? 1000.f : m_ticksPerSec; float secTime = fadeTime / q; int32_t eval = int32_t(m_curVel) * scale / 127 + add; eval = std::max(0, std::min(127, eval)); const Curve* curveData; if (curve != 0) curveData = vox.getAudioGroup().getPool().tableAsCurves(curve); else curveData = nullptr; vox.startFadeIn(secTime, eval, curveData); break; } case Op::Spanning: { int8_t panPos = cmd.m_data[0]; int16_t timeMs = *reinterpret_cast(&cmd.m_data[1]); int8_t width = cmd.m_data[3]; vox.startSpanning(timeMs / 1000.0, panPos, width); break; } case Op::SetAdsrCtrl: { m_useAdsrControllers = true; m_midiAttack = cmd.m_data[0]; m_midiDecay = cmd.m_data[1]; m_midiSustain = cmd.m_data[2]; m_midiRelease = cmd.m_data[3]; break; } case Op::RndNote: { int32_t noteLo = int32_t(cmd.m_data[0]); int8_t detune = cmd.m_data[1]; int32_t noteHi = int32_t(cmd.m_data[2]); int8_t free = cmd.m_data[3]; int8_t rel = cmd.m_data[4]; if (rel) { noteLo = m_initKey - noteLo; noteHi = noteLo + noteHi; } noteLo *= 100; noteHi *= 100; m_curKey = vox.getEngine().nextRandom() % ((noteHi - noteLo) + noteLo); if (!free) m_curKey = m_curKey / 100 * 100 + detune; vox.setPitchKey(m_curKey); break; } case Op::AddNote: { int32_t add = int32_t(cmd.m_data[0]); int8_t detune = cmd.m_data[1]; int8_t orgKey = cmd.m_data[2]; int8_t ms = cmd.m_data[4]; int16_t timeMs = *reinterpret_cast(&cmd.m_data[5]); m_curKey = (orgKey ? m_initKey : m_curKey) + add * 100 + detune; /* Set wait state */ if (timeMs) { float q = ms ? 1000.f : m_ticksPerSec; float secTime = timeMs / q; m_waitCountdown = secTime; m_inWait = true; } vox.setPitchKey(m_curKey); break; } case Op::SetNote: { int32_t key = int32_t(cmd.m_data[0]); int8_t detune = cmd.m_data[1]; int8_t ms = cmd.m_data[4]; int16_t timeMs = *reinterpret_cast(&cmd.m_data[5]); m_curKey = key * 100 + detune; /* Set wait state */ if (timeMs) { float q = ms ? 1000.f : m_ticksPerSec; float secTime = timeMs / q; m_waitCountdown = secTime; m_inWait = true; } vox.setPitchKey(m_curKey); break; } case Op::LastNote: { int32_t add = int32_t(cmd.m_data[0]); int8_t detune = cmd.m_data[1]; int8_t ms = cmd.m_data[4]; int16_t timeMs = *reinterpret_cast(&cmd.m_data[5]); m_curKey = (add + vox.getLastNote()) * 100 + detune; /* Set wait state */ if (timeMs) { float q = ms ? 1000.f : m_ticksPerSec; float secTime = timeMs / q; m_waitCountdown = secTime; m_inWait = true; } vox.setPitchKey(m_curKey); break; } case Op::Portamento: { m_portamentoMode = cmd.m_data[0]; m_portamentoType = cmd.m_data[1]; int8_t ms = cmd.m_data[4]; int16_t timeMs = *reinterpret_cast(&cmd.m_data[5]); float q = ms ? 1000.f : m_ticksPerSec; m_portamentoTime = timeMs / q; break; } case Op::Vibrato: { int32_t level = cmd.m_data[0] * 100 + cmd.m_data[1]; int32_t modLevel = cmd.m_data[2]; int8_t ms = cmd.m_data[4]; int16_t timeMs = *reinterpret_cast(&cmd.m_data[5]); float q = ms ? 1000.f : m_ticksPerSec; vox.setVibrato(level, modLevel, timeMs / q); break; } case Op::PitchSweep1: { int32_t times = int32_t(cmd.m_data[0]); int16_t add = *reinterpret_cast(&cmd.m_data[1]); int8_t ms = cmd.m_data[4]; int16_t timeMs = *reinterpret_cast(&cmd.m_data[5]); /* Set wait state */ if (timeMs) { float q = ms ? 1000.f : m_ticksPerSec; float secTime = timeMs / q; m_waitCountdown = secTime; m_inWait = true; } vox.setPitchSweep1(times, add); break; } case Op::PitchSweep2: { int32_t times = int32_t(cmd.m_data[0]); int16_t add = *reinterpret_cast(&cmd.m_data[1]); int8_t ms = cmd.m_data[4]; int16_t timeMs = *reinterpret_cast(&cmd.m_data[5]); /* Set wait state */ if (timeMs) { float q = ms ? 1000.f : m_ticksPerSec; float secTime = timeMs / q; m_waitCountdown = secTime; m_inWait = true; } vox.setPitchSweep2(times, add); break; } case Op::SetPitch: { uint32_t hz = *reinterpret_cast(&cmd.m_data[0]) >> 8; uint16_t fine = *reinterpret_cast(&cmd.m_data[3]); vox.setPitchFrequency(hz, fine); break; } case Op::SetPitchAdsr: { ObjectId adsr = *reinterpret_cast(&cmd.m_data[0]); int8_t keys = cmd.m_data[3]; int8_t cents = cmd.m_data[4]; vox.setPitchAdsr(adsr, keys * 100 + cents); break; } case Op::ScaleVolumeDLS: { int16_t scale = *reinterpret_cast(&cmd.m_data[0]); bool orgVel = cmd.m_data[2]; vox.setVolume(int32_t(orgVel ? m_initVel : m_curVel) * scale / 4096.f / 127.f); break; } case Op::Mod2Vibrange: { int8_t keys = cmd.m_data[0]; int8_t cents = cmd.m_data[1]; vox.setMod2VibratoRange(keys * 100 + cents); break; } case Op::SetupTremolo: { int16_t scale = *reinterpret_cast(&cmd.m_data[0]); int16_t modScale = *reinterpret_cast(&cmd.m_data[3]); vox.setTremolo(scale / 4096.f, modScale / 4096.f); break; } case Op::Return: { if (m_pc.size() > 1) { m_pc.pop_back(); m_header = *reinterpret_cast(m_pc.back().first); m_header.swapBig(); vox.m_objectId = m_header.m_macroId; } break; } case Op::GoSub: { ObjectId macroId = *reinterpret_cast(&cmd.m_data[1]); int16_t macroStep = *reinterpret_cast(&cmd.m_data[3]); if (macroId == m_header.m_macroId) m_pc.push_back({m_pc.back().first, macroStep}); else vox.loadSoundObject(macroId, macroStep, m_ticksPerSec, m_initKey, m_initVel, m_initMod, true); m_header = *reinterpret_cast(m_pc.back().first); m_header.swapBig(); vox.m_objectId = m_header.m_macroId; break; } case Op::TrapEvent: { uint8_t event = cmd.m_data[0]; ObjectId macroId = *reinterpret_cast(&cmd.m_data[1]); int16_t macroStep = *reinterpret_cast(&cmd.m_data[3]); switch (event) { case 0: m_keyoffTrap.macroId = macroId; m_keyoffTrap.macroStep = macroStep; break; case 1: m_sampleEndTrap.macroId = macroId; m_sampleEndTrap.macroStep = macroStep; break; case 2: m_messageTrap.macroId = macroId; m_messageTrap.macroStep = macroStep; break; default: break; } break; } case Op::UntrapEvent: { uint8_t event = cmd.m_data[0]; switch (event) { case 0: m_keyoffTrap.macroId = ObjectId(); m_keyoffTrap.macroStep = -1; break; case 1: m_sampleEndTrap.macroId = ObjectId(); m_sampleEndTrap.macroStep = -1; break; case 2: m_messageTrap.macroId = ObjectId(); m_messageTrap.macroStep = -1; break; default: break; } break; } case Op::SendMessage: { bool isVar = cmd.m_data[0]; ObjectId macroId = *reinterpret_cast(&cmd.m_data[1]); uint8_t vid = cmd.m_data[3]; uint8_t val = cmd.m_data[4]; if (isVar) { std::shared_ptr findVox = vox.getEngine().findVoice(m_variables[vid]); if (findVox) findVox->message(val); } else vox.getEngine().sendMacroMessage(macroId, val); break; } case Op::GetMessage: { uint8_t vid = cmd.m_data[0]; if (m_messageQueue.size()) { m_variables[vid] = m_messageQueue.front(); m_messageQueue.pop_front(); } else m_variables[vid] = 0; break; } case Op::GetVid: { uint8_t vid = cmd.m_data[0]; bool lastPlayMacro = cmd.m_data[1]; m_variables[vid] = lastPlayMacro ? m_lastPlayMacroVid : vox.vid(); break; } case Op::SendFlag: { int8_t id = cmd.m_data[0]; int8_t val = cmd.m_data[1]; break; /* TODO: figure out a good API */ } case Op::PitchWheelR: { int8_t up = cmd.m_data[0]; int8_t down = cmd.m_data[1]; vox.setPitchWheelRange(up, down); break; } case Op::VolSelect: { uint8_t ctrl = cmd.m_data[0]; int16_t perc = *reinterpret_cast(&cmd.m_data[1]); Evaluator::Combine combine = Evaluator::Combine(cmd.m_data[3]); Evaluator::VarType vtype = Evaluator::VarType(cmd.m_data[4]); uint8_t fine = cmd.m_data[5]; m_volumeSel.addComponent(ctrl, (perc + fine / 100.f) / 100.f, combine, vtype); break; } case Op::PanSelect: { uint8_t ctrl = cmd.m_data[0]; int16_t perc = *reinterpret_cast(&cmd.m_data[1]); Evaluator::Combine combine = Evaluator::Combine(cmd.m_data[3]); Evaluator::VarType vtype = Evaluator::VarType(cmd.m_data[4]); uint8_t fine = cmd.m_data[5]; m_panSel.addComponent(ctrl, (perc + fine / 100.f) / 100.f, combine, vtype); break; } case Op::PitchWheelSelect: { uint8_t ctrl = cmd.m_data[0]; int16_t perc = *reinterpret_cast(&cmd.m_data[1]); Evaluator::Combine combine = Evaluator::Combine(cmd.m_data[3]); Evaluator::VarType vtype = Evaluator::VarType(cmd.m_data[4]); uint8_t fine = cmd.m_data[5]; m_pitchWheelSel.addComponent(ctrl, (perc + fine / 100.f) / 100.f, combine, vtype); break; } case Op::ModWheelSelect: { uint8_t ctrl = cmd.m_data[0]; int16_t perc = *reinterpret_cast(&cmd.m_data[1]); Evaluator::Combine combine = Evaluator::Combine(cmd.m_data[3]); Evaluator::VarType vtype = Evaluator::VarType(cmd.m_data[4]); uint8_t fine = cmd.m_data[5]; m_modWheelSel.addComponent(ctrl, (perc + fine / 100.f) / 100.f, combine, vtype); break; } case Op::PedalSelect: { uint8_t ctrl = cmd.m_data[0]; int16_t perc = *reinterpret_cast(&cmd.m_data[1]); Evaluator::Combine combine = Evaluator::Combine(cmd.m_data[3]); Evaluator::VarType vtype = Evaluator::VarType(cmd.m_data[4]); uint8_t fine = cmd.m_data[5]; m_pedalSel.addComponent(ctrl, (perc + fine / 100.f) / 100.f, combine, vtype); break; } case Op::PortamentoSelect: { uint8_t ctrl = cmd.m_data[0]; int16_t perc = *reinterpret_cast(&cmd.m_data[1]); Evaluator::Combine combine = Evaluator::Combine(cmd.m_data[3]); Evaluator::VarType vtype = Evaluator::VarType(cmd.m_data[4]); uint8_t fine = cmd.m_data[5]; m_portamentoSel.addComponent(ctrl, (perc + fine / 100.f) / 100.f, combine, vtype); break; } case Op::ReverbSelect: { uint8_t ctrl = cmd.m_data[0]; int16_t perc = *reinterpret_cast(&cmd.m_data[1]); Evaluator::Combine combine = Evaluator::Combine(cmd.m_data[3]); Evaluator::VarType vtype = Evaluator::VarType(cmd.m_data[4]); uint8_t fine = cmd.m_data[5]; m_reverbSel.addComponent(ctrl, (perc + fine / 100.f) / 100.f, combine, vtype); break; } case Op::SpanSelect: { uint8_t ctrl = cmd.m_data[0]; int16_t perc = *reinterpret_cast(&cmd.m_data[1]); Evaluator::Combine combine = Evaluator::Combine(cmd.m_data[3]); Evaluator::VarType vtype = Evaluator::VarType(cmd.m_data[4]); uint8_t fine = cmd.m_data[5]; m_spanSel.addComponent(ctrl, (perc + fine / 100.f) / 100.f, combine, vtype); break; } case Op::DopplerSelect: { uint8_t ctrl = cmd.m_data[0]; int16_t perc = *reinterpret_cast(&cmd.m_data[1]); Evaluator::Combine combine = Evaluator::Combine(cmd.m_data[3]); Evaluator::VarType vtype = Evaluator::VarType(cmd.m_data[4]); uint8_t fine = cmd.m_data[5]; m_dopplerSel.addComponent(ctrl, (perc + fine / 100.f) / 100.f, combine, vtype); break; } case Op::TremoloSelect: { uint8_t ctrl = cmd.m_data[0]; int16_t perc = *reinterpret_cast(&cmd.m_data[1]); Evaluator::Combine combine = Evaluator::Combine(cmd.m_data[3]); Evaluator::VarType vtype = Evaluator::VarType(cmd.m_data[4]); uint8_t fine = cmd.m_data[5]; m_tremoloSel.addComponent(ctrl, (perc + fine / 100.f) / 100.f, combine, vtype); break; } case Op::PreASelect: { uint8_t ctrl = cmd.m_data[0]; int16_t perc = *reinterpret_cast(&cmd.m_data[1]); Evaluator::Combine combine = Evaluator::Combine(cmd.m_data[3]); Evaluator::VarType vtype = Evaluator::VarType(cmd.m_data[4]); uint8_t fine = cmd.m_data[5]; m_preAuxASel.addComponent(ctrl, (perc + fine / 100.f) / 100.f, combine, vtype); break; } case Op::PreBSelect: { uint8_t ctrl = cmd.m_data[0]; int16_t perc = *reinterpret_cast(&cmd.m_data[1]); Evaluator::Combine combine = Evaluator::Combine(cmd.m_data[3]); Evaluator::VarType vtype = Evaluator::VarType(cmd.m_data[4]); uint8_t fine = cmd.m_data[5]; m_preAuxBSel.addComponent(ctrl, (perc + fine / 100.f) / 100.f, combine, vtype); break; } case Op::PostBSelect: { uint8_t ctrl = cmd.m_data[0]; int16_t perc = *reinterpret_cast(&cmd.m_data[1]); Evaluator::Combine combine = Evaluator::Combine(cmd.m_data[3]); Evaluator::VarType vtype = Evaluator::VarType(cmd.m_data[4]); uint8_t fine = cmd.m_data[5]; m_postAuxB.addComponent(ctrl, (perc + fine / 100.f) / 100.f, combine, vtype); break; } case Op::AuxAFXSelect: { uint8_t ctrl = cmd.m_data[0]; int16_t perc = *reinterpret_cast(&cmd.m_data[1]); Evaluator::Combine combine = Evaluator::Combine(cmd.m_data[3]); Evaluator::VarType vtype = Evaluator::VarType(cmd.m_data[4]); uint8_t fine = cmd.m_data[5]; m_auxAFxSel.addComponent(ctrl, (perc + fine / 100.f) / 100.f, combine, vtype); break; } case Op::AuxBFXSelect: { uint8_t ctrl = cmd.m_data[0]; int16_t perc = *reinterpret_cast(&cmd.m_data[1]); Evaluator::Combine combine = Evaluator::Combine(cmd.m_data[3]); Evaluator::VarType vtype = Evaluator::VarType(cmd.m_data[4]); uint8_t fine = cmd.m_data[5]; m_auxBFxSel.addComponent(ctrl, (perc + fine / 100.f) / 100.f, combine, vtype); break; } case Op::SetupLFO: { uint8_t number = cmd.m_data[0]; int16_t period = *reinterpret_cast(&cmd.m_data[1]); if (number == 0) vox.setLFO1Period(period / 1000.f); else if (number == 1) vox.setLFO2Period(period / 1000.f); break; } case Op::SetKeygroup: { uint8_t id = cmd.m_data[0]; uint8_t flag = cmd.m_data[1]; vox.setKeygroup(0); if (id) { vox.getEngine().killKeygroup(id, flag); vox.setKeygroup(id); } break; } case Op::AddVars: { bool aCtrl = cmd.m_data[0]; int8_t a = cmd.m_data[1]; bool bCtrl = cmd.m_data[2]; int8_t b = cmd.m_data[3]; bool cCtrl = cmd.m_data[4]; int8_t c = cmd.m_data[5]; if (bCtrl) b = vox.getCtrlValue(b); else b = m_variables[b]; if (cCtrl) c = vox.getCtrlValue(c); else c = m_variables[c]; if (aCtrl) vox.setCtrlValue(a, b + c); else m_variables[a] = b + c; break; } case Op::SubVars: { bool aCtrl = cmd.m_data[0]; int8_t a = cmd.m_data[1]; bool bCtrl = cmd.m_data[2]; int8_t b = cmd.m_data[3]; bool cCtrl = cmd.m_data[4]; int8_t c = cmd.m_data[5]; if (bCtrl) b = vox.getCtrlValue(b); else b = m_variables[b]; if (cCtrl) c = vox.getCtrlValue(c); else c = m_variables[c]; if (aCtrl) vox.setCtrlValue(a, b - c); else m_variables[a] = b - c; break; } case Op::MulVars: { bool aCtrl = cmd.m_data[0]; int8_t a = cmd.m_data[1]; bool bCtrl = cmd.m_data[2]; int8_t b = cmd.m_data[3]; bool cCtrl = cmd.m_data[4]; int8_t c = cmd.m_data[5]; if (bCtrl) b = vox.getCtrlValue(b); else b = m_variables[b]; if (cCtrl) c = vox.getCtrlValue(c); else c = m_variables[c]; if (aCtrl) vox.setCtrlValue(a, b * c); else m_variables[a] = b * c; break; } case Op::DivVars: { bool aCtrl = cmd.m_data[0]; int8_t a = cmd.m_data[1]; bool bCtrl = cmd.m_data[2]; int8_t b = cmd.m_data[3]; bool cCtrl = cmd.m_data[4]; int8_t c = cmd.m_data[5]; if (bCtrl) b = vox.getCtrlValue(b); else b = m_variables[b]; if (cCtrl) c = vox.getCtrlValue(c); else c = m_variables[c]; if (aCtrl) vox.setCtrlValue(a, b / c); else m_variables[a] = b / c; break; } case Op::AddIVars: { bool aCtrl = cmd.m_data[0]; int8_t a = cmd.m_data[1]; bool bCtrl = cmd.m_data[2]; int8_t b = cmd.m_data[3]; int16_t imm = *reinterpret_cast(&cmd.m_data[4]); if (bCtrl) b = vox.getCtrlValue(b); else b = m_variables[b]; if (aCtrl) vox.setCtrlValue(a, b + imm); else m_variables[a] = b + imm; break; } case Op::IfEqual: { bool aCtrl = cmd.m_data[0]; int8_t a = cmd.m_data[1]; bool bCtrl = cmd.m_data[2]; int8_t b = cmd.m_data[3]; bool lnot = cmd.m_data[4]; int16_t macroStep = *reinterpret_cast(&cmd.m_data[5]); if (aCtrl) a = vox.getCtrlValue(a); else a = m_variables[a]; if (bCtrl) b = vox.getCtrlValue(b); else b = m_variables[b]; if ((a == b) ^ lnot) m_pc.back().second = macroStep; break; } case Op::IfLess: { bool aCtrl = cmd.m_data[0]; int8_t a = cmd.m_data[1]; bool bCtrl = cmd.m_data[2]; int8_t b = cmd.m_data[3]; bool lnot = cmd.m_data[4]; int16_t macroStep = *reinterpret_cast(&cmd.m_data[5]); if (aCtrl) a = vox.getCtrlValue(a); else a = m_variables[a]; if (bCtrl) b = vox.getCtrlValue(b); else b = m_variables[b]; if ((a < b) ^ lnot) m_pc.back().second = macroStep; break; } default: break; } } m_execTime += dt; return false; } void SoundMacroState::keyoffNotify(Voice& vox) { m_keyoff = true; if (m_inWait && m_keyoffWait) m_inWait = false; if (m_keyoffTrap.macroId != 0xffff) { if (m_keyoffTrap.macroId == m_header.m_macroId) m_pc.back().second = m_keyoffTrap.macroStep; else vox.loadSoundObject(m_keyoffTrap.macroId, m_keyoffTrap.macroStep, m_ticksPerSec, m_initKey, m_initVel, m_initMod); } } void SoundMacroState::sampleEndNotify(Voice& vox) { m_sampleEnd = true; if (m_inWait && m_sampleEndWait) m_inWait = false; if (m_sampleEndTrap.macroId != 0xffff) { if (m_sampleEndTrap.macroId == m_header.m_macroId) m_pc.back().second = m_sampleEndTrap.macroStep; else vox.loadSoundObject(m_sampleEndTrap.macroId, m_sampleEndTrap.macroStep, m_ticksPerSec, m_initKey, m_initVel, m_initMod); } } void SoundMacroState::messageNotify(Voice& vox, int32_t val) { m_messageQueue.push_back(val); if (m_messageTrap.macroId != 0xffff) { if (m_messageTrap.macroId == m_header.m_macroId) m_pc.back().second = m_messageTrap.macroStep; else vox.loadSoundObject(m_messageTrap.macroId, m_messageTrap.macroStep, m_ticksPerSec, m_initKey, m_initVel, m_initMod); } } }