#include "amuse/Sequencer.hpp" #include #include "amuse/Engine.hpp" #include "amuse/Voice.hpp" namespace amuse { void Sequencer::ChannelState::_bringOutYourDead() { for (auto it = m_chanVoxs.begin(); it != m_chanVoxs.end();) { Voice* vox = it->second.get(); vox->_bringOutYourDead(); if (vox->_isRecursivelyDead()) { it = m_chanVoxs.erase(it); continue; } ++it; } for (auto it = m_keyoffVoxs.begin(); it != m_keyoffVoxs.end();) { Voice* vox = it->get(); vox->_bringOutYourDead(); if (vox->_isRecursivelyDead()) { it = m_keyoffVoxs.erase(it); continue; } ++it; } std::unordered_set> newSet = m_keyoffVoxs; m_keyoffVoxs = newSet; } void Sequencer::_bringOutYourDead() { for (auto& chan : m_chanStates) if (chan) chan._bringOutYourDead(); if (!m_arrData && m_dieOnEnd && getVoiceCount() == 0) m_state = SequencerState::Dead; } void Sequencer::_destroy() { Entity::_destroy(); if (m_studio) m_studio.reset(); } Sequencer::~Sequencer() { if (m_studio) m_studio.reset(); } Sequencer::Sequencer(Engine& engine, const AudioGroup& group, GroupId groupId, const SongGroupIndex* songGroup, SongId setupId, ObjToken studio) : Entity(engine, group, groupId), m_songGroup(songGroup), m_studio(std::move(studio)) { auto it = m_songGroup->m_midiSetups.find(setupId); if (it != m_songGroup->m_midiSetups.cend()) m_midiSetup = it->second.data(); } Sequencer::Sequencer(Engine& engine, const AudioGroup& group, GroupId groupId, const SFXGroupIndex* sfxGroup, ObjToken studio) : Entity(engine, group, groupId), m_sfxGroup(sfxGroup), m_studio(std::move(studio)) { std::map sortSFX; for (const auto& sfx : sfxGroup->m_sfxEntries) sortSFX[sfx.first] = &sfx.second; m_sfxMappings.reserve(sortSFX.size()); for (const auto& sfx : sortSFX) m_sfxMappings.push_back(sfx.second); } Sequencer::ChannelState::~ChannelState() = default; Sequencer::ChannelState::ChannelState(Sequencer& parent, uint8_t chanId) : m_parent(&parent), m_chanId(chanId) { if (m_parent->m_songGroup) { if (m_parent->m_midiSetup) { m_setup = &m_parent->m_midiSetup[chanId]; if (chanId == 9) { auto it = m_parent->m_songGroup->m_drumPages.find(m_setup->programNo); if (it != m_parent->m_songGroup->m_drumPages.cend()) { m_page = &it->second; m_curProgram = m_setup->programNo; } } else { auto it = m_parent->m_songGroup->m_normPages.find(m_setup->programNo); if (it != m_parent->m_songGroup->m_normPages.cend()) { m_page = &it->second; m_curProgram = m_setup->programNo; } } m_curVol = m_setup->volume / 127.f; m_curPan = m_setup->panning / 64.f - 1.f; m_ctrlVals[0x5b] = m_setup->reverb; m_ctrlVals[0x5d] = m_setup->chorus; } else { if (chanId == 9) { auto it = m_parent->m_songGroup->m_drumPages.find(0); if (it != m_parent->m_songGroup->m_drumPages.cend()) m_page = &it->second; } else { auto it = m_parent->m_songGroup->m_normPages.find(0); if (it != m_parent->m_songGroup->m_normPages.cend()) m_page = &it->second; } m_curVol = 1.f; m_curPan = 0.f; m_ctrlVals[0x5b] = 0; m_ctrlVals[0x5d] = 0; } } else if (m_parent->m_sfxGroup) { m_curVol = 1.f; m_curPan = 0.f; m_ctrlVals[0x5b] = 0; m_ctrlVals[0x5d] = 0; } m_ctrlVals[7] = 127; m_ctrlVals[10] = 64; } void Sequencer::advance(double dt) { if (m_state == SequencerState::Playing) { if (m_stopFadeTime) { float step = dt / m_stopFadeTime * m_stopFadeBeginVol; float vol = std::max(0.f, m_curVol - step); if (vol == 0.f) { m_arrData = nullptr; m_state = SequencerState::Interactive; allOff(true); m_stopFadeTime = 0.f; return; } else { setVolume(vol); } } else if (m_volFadeTime) { float step = dt / m_volFadeTime * std::fabs(m_volFadeTarget - m_volFadeStart); float vol; if (m_curVol < m_volFadeTarget) vol = std::min(m_volFadeTarget, m_curVol + step); else vol = std::max(m_volFadeTarget, m_curVol - step); if (vol == m_volFadeTarget) m_volFadeTime = 0.f; else setVolume(vol); } if (m_songState.advance(*this, dt)) { m_arrData = nullptr; m_state = SequencerState::Interactive; allOff(); } } } size_t Sequencer::ChannelState::getVoiceCount() const { size_t ret = 0; for (const auto& vox : m_chanVoxs) ret += vox.second->getTotalVoices(); for (const auto& vox : m_keyoffVoxs) ret += vox->getTotalVoices(); return ret; } size_t Sequencer::getVoiceCount() const { size_t ret = 0; for (const auto& chan : m_chanStates) if (chan) ret += chan.getVoiceCount(); return ret; } ObjToken Sequencer::ChannelState::keyOn(uint8_t note, uint8_t velocity) { if (m_parent->m_songGroup && !m_page) return {}; if (m_lastVoice && m_lastVoice->isDestroyed()) m_lastVoice.reset(); /* If portamento is enabled for voice, pre-empt spawning new voices */ if (ObjToken lastVoice = m_lastVoice) { uint8_t lastNote = lastVoice->getLastNote(); if (lastVoice->doPortamento(note)) { m_chanVoxs.erase(lastNote); m_chanVoxs[note] = lastVoice; return lastVoice; } } /* Ensure keyoff sent first */ auto keySearch = m_chanVoxs.find(note); if (keySearch != m_chanVoxs.cend()) { if (keySearch->second == m_lastVoice) m_lastVoice.reset(); keySearch->second->keyOff(); keySearch->second->setPedal(false); m_keyoffVoxs.emplace(keySearch->second); m_chanVoxs.erase(keySearch); } std::list>::iterator ret = m_parent->m_engine._allocateVoice( m_parent->m_audioGroup, m_parent->m_groupId, NativeSampleRate, true, false, m_parent->m_studio); if (*ret) { (*ret)->m_sequencer = m_parent; m_chanVoxs[note] = *ret; (*ret)->installCtrlValues(m_ctrlVals.data()); ObjectId oid; bool res; if (m_parent->m_songGroup) { oid = m_page->objId; res = (*ret)->loadPageObject(oid, m_ticksPerSec, note, velocity, m_ctrlVals[1]); } else if (m_parent->m_sfxMappings.size()) { size_t lookupIdx = note % m_parent->m_sfxMappings.size(); const SFXGroupIndex::SFXEntry* sfxEntry = m_parent->m_sfxMappings[lookupIdx]; oid = sfxEntry->objId; note = sfxEntry->defKey; res = (*ret)->loadPageObject(oid, m_ticksPerSec, note, velocity, m_ctrlVals[1]); } else return {}; if (!res) { m_parent->m_engine._destroyVoice(ret); return {}; } (*ret)->setVolume(m_parent->m_curVol * m_curVol); (*ret)->setReverbVol(m_ctrlVals[0x5b] / 127.f); (*ret)->setAuxBVol(m_ctrlVals[0x5d] / 127.f); (*ret)->setPan(m_curPan); (*ret)->setPitchWheel(m_curPitchWheel); if (m_pitchWheelRange != -1) (*ret)->setPitchWheelRange(m_pitchWheelRange, m_pitchWheelRange); if (m_ctrlVals[64] > 64) (*ret)->setPedal(true); m_lastVoice = *ret; } return *ret; } ObjToken Sequencer::keyOn(uint8_t chan, uint8_t note, uint8_t velocity) { if (chan >= m_chanStates.size()) { return {}; } if (!m_chanStates[chan]) { m_chanStates[chan] = ChannelState(*this, chan); } return m_chanStates[chan].keyOn(note, velocity); } void Sequencer::ChannelState::keyOff(uint8_t note, uint8_t velocity) { auto keySearch = m_chanVoxs.find(note); if (keySearch == m_chanVoxs.cend()) return; if ((m_lastVoice && m_lastVoice->isDestroyed()) || keySearch->second == m_lastVoice) m_lastVoice.reset(); keySearch->second->keyOff(); m_keyoffVoxs.emplace(keySearch->second); m_chanVoxs.erase(keySearch); } void Sequencer::keyOff(uint8_t chan, uint8_t note, uint8_t velocity) { if (chan >= m_chanStates.size() || !m_chanStates[chan]) { return; } m_chanStates[chan].keyOff(note, velocity); } void Sequencer::ChannelState::setCtrlValue(uint8_t ctrl, int8_t val) { m_ctrlVals[ctrl] = val; for (const auto& vox : m_chanVoxs) vox.second->_notifyCtrlChange(ctrl, val); for (const auto& vox : m_keyoffVoxs) vox->_notifyCtrlChange(ctrl, val); switch (ctrl) { case 7: setVolume(val / 127.f); break; case 10: setPan(val / 64.f - 1.f); break; case 98: // RPN LSB m_rpn &= ~0x7f; m_rpn |= val; break; case 99: // RPN MSB m_rpn &= ~0x3f80; m_rpn |= val << 7; break; case 6: if (m_rpn == 0) m_pitchWheelRange = val; break; case 96: if (m_rpn == 0) m_pitchWheelRange += 1; break; case 97: if (m_rpn == 0) m_pitchWheelRange -= 1; break; default: break; } } bool Sequencer::ChannelState::programChange(int8_t prog) { if (m_parent->m_songGroup) { if (m_chanId == 9) { auto it = m_parent->m_songGroup->m_drumPages.find(prog); if (it != m_parent->m_songGroup->m_drumPages.cend()) { m_page = &it->second; m_curProgram = prog; return true; } } else { auto it = m_parent->m_songGroup->m_normPages.find(prog); if (it != m_parent->m_songGroup->m_normPages.cend()) { m_page = &it->second; m_curProgram = prog; return true; } } } return false; } void Sequencer::ChannelState::nextProgram() { int newProg = m_curProgram; while ((newProg += 1) <= 127) if (programChange(newProg)) break; } void Sequencer::ChannelState::prevProgram() { int newProg = m_curProgram; while ((newProg -= 1) >= 0) if (programChange(newProg)) break; } void Sequencer::setCtrlValue(uint8_t chan, uint8_t ctrl, int8_t val) { if (chan >= m_chanStates.size()) { return; } if (ctrl == 0x66) { fmt::print(FMT_STRING("Loop Start\n")); } else if (ctrl == 0x67) { fmt::print(FMT_STRING("Loop End\n")); } if (!m_chanStates[chan]) { m_chanStates[chan] = ChannelState(*this, chan); } m_chanStates[chan].setCtrlValue(ctrl, val); } void Sequencer::ChannelState::setPitchWheel(float pitchWheel) { m_curPitchWheel = pitchWheel; for (const auto& vox : m_chanVoxs) vox.second->setPitchWheel(pitchWheel); for (const auto& vox : m_keyoffVoxs) vox->setPitchWheel(pitchWheel); } void Sequencer::setPitchWheel(uint8_t chan, float pitchWheel) { if (chan >= m_chanStates.size()) { return; } if (!m_chanStates[chan]) { m_chanStates[chan] = ChannelState(*this, chan); } m_chanStates[chan].setPitchWheel(pitchWheel); } void Sequencer::setTempo(uint8_t chan, double ticksPerSec) { m_chanStates[chan].m_ticksPerSec = ticksPerSec; } void Sequencer::setTempo(double ticksPerSec) { for (auto& c : m_chanStates) c.m_ticksPerSec = ticksPerSec; } void Sequencer::ChannelState::allOff() { if (m_lastVoice && m_lastVoice->isDestroyed()) m_lastVoice.reset(); for (auto it = m_chanVoxs.begin(); it != m_chanVoxs.end();) { if (it->second == m_lastVoice) m_lastVoice.reset(); it->second->keyOff(); m_keyoffVoxs.emplace(it->second); it = m_chanVoxs.erase(it); } } void Sequencer::allOff(bool now) { if (now) for (auto& chan : m_chanStates) { if (chan) { for (const auto& vox : chan.m_chanVoxs) vox.second->kill(); for (const auto& vox : chan.m_keyoffVoxs) vox->kill(); chan.m_chanVoxs.clear(); chan.m_keyoffVoxs.clear(); } } else for (auto& chan : m_chanStates) if (chan) chan.allOff(); } void Sequencer::allOff(uint8_t chan, bool now) { if (chan >= m_chanStates.size() || !m_chanStates[chan]) { return; } if (now) { for (const auto& vox : m_chanStates[chan].m_chanVoxs) { vox.second->kill(); } for (const auto& vox : m_chanStates[chan].m_keyoffVoxs) { vox->kill(); } m_chanStates[chan].m_chanVoxs.clear(); m_chanStates[chan].m_keyoffVoxs.clear(); } else { m_chanStates[chan].allOff(); } } void Sequencer::ChannelState::killKeygroup(uint8_t kg, bool now) { if (m_lastVoice && m_lastVoice->isDestroyed()) m_lastVoice.reset(); for (auto it = m_chanVoxs.begin(); it != m_chanVoxs.end();) { Voice* vox = it->second.get(); if (vox->m_keygroup == kg) { if (it->second == m_lastVoice) m_lastVoice.reset(); if (now) { vox->kill(); it = m_chanVoxs.erase(it); continue; } vox->keyOff(); m_keyoffVoxs.emplace(it->second); it = m_chanVoxs.erase(it); continue; } ++it; } if (now) { for (auto it = m_keyoffVoxs.begin(); it != m_keyoffVoxs.end();) { Voice* vox = it->get(); if (vox->m_keygroup == kg) { vox->kill(); it = m_keyoffVoxs.erase(it); continue; } ++it; } } } void Sequencer::killKeygroup(uint8_t kg, bool now) { for (auto& chan : m_chanStates) if (chan) chan.killKeygroup(kg, now); } ObjToken Sequencer::ChannelState::findVoice(int vid) { for (const auto& vox : m_chanVoxs) if (vox.second->vid() == vid) return vox.second; for (const auto& vox : m_keyoffVoxs) if (vox->vid() == vid) return vox; return {}; } ObjToken Sequencer::findVoice(int vid) { for (auto& chan : m_chanStates) { if (chan) { ObjToken ret = chan.findVoice(vid); if (ret) return ret; } } return {}; } void Sequencer::ChannelState::sendMacroMessage(ObjectId macroId, int32_t val) { for (const auto& v : m_chanVoxs) { Voice* vox = v.second.get(); if (vox->getObjectId() == macroId) vox->message(val); } for (const auto& v : m_keyoffVoxs) { Voice* vox = v.get(); if (vox->getObjectId() == macroId) vox->message(val); } } void Sequencer::sendMacroMessage(ObjectId macroId, int32_t val) { for (auto& chan : m_chanStates) if (chan) chan.sendMacroMessage(macroId, val); } void Sequencer::playSong(const unsigned char* arrData, bool loop, bool dieOnEnd) { m_arrData = arrData; m_dieOnEnd = dieOnEnd; m_songState.initialize(arrData, loop); setTempo(m_songState.getInitialTempo() * 384 / 60.0); m_state = SequencerState::Playing; } void Sequencer::stopSong(float fadeTime, bool now) { if (fadeTime == 0.f) { allOff(now); m_arrData = nullptr; m_state = SequencerState::Interactive; } else { m_stopFadeTime = fadeTime; m_stopFadeBeginVol = m_curVol; } } void Sequencer::ChannelState::setVolume(float vol) { m_curVol = vol; float voxVol = m_parent->m_curVol * m_curVol; for (const auto& v : m_chanVoxs) { Voice* vox = v.second.get(); vox->setVolume(voxVol); } for (const auto& v : m_keyoffVoxs) { Voice* vox = v.get(); vox->setVolume(voxVol); } } void Sequencer::ChannelState::setPan(float pan) { m_curPan = pan; for (const auto& v : m_chanVoxs) { Voice* vox = v.second.get(); vox->setPan(m_curPan); } for (const auto& v : m_keyoffVoxs) { Voice* vox = v.get(); vox->setPan(m_curPan); } } void Sequencer::setVolume(float vol, float fadeTime) { if (fadeTime == 0.f) { m_curVol = vol; for (auto& chan : m_chanStates) if (chan) chan.setVolume(chan.m_curVol); } else { m_volFadeTime = fadeTime; m_volFadeTarget = vol; m_volFadeStart = m_curVol; } } int8_t Sequencer::getChanProgram(int8_t chanId) const { if (static_cast(chanId) >= m_chanStates.size()) { return 0; } if (!m_chanStates[chanId]) { if (!m_midiSetup) { return 0; } return m_midiSetup[chanId].programNo; } return m_chanStates[chanId].m_curProgram; } bool Sequencer::setChanProgram(int8_t chanId, int8_t prog) { if (static_cast(chanId) >= m_chanStates.size()) { return false; } if (!m_chanStates[chanId]) { m_chanStates[chanId] = ChannelState(*this, chanId); } return m_chanStates[chanId].programChange(prog); } void Sequencer::nextChanProgram(int8_t chanId) { if (static_cast(chanId) >= m_chanStates.size()) { return; } if (!m_chanStates[chanId]) { m_chanStates[chanId] = ChannelState(*this, chanId); } return m_chanStates[chanId].nextProgram(); } void Sequencer::prevChanProgram(int8_t chanId) { if (static_cast(chanId) >= m_chanStates.size()) { return; } if (!m_chanStates[chanId]) { m_chanStates[chanId] = ChannelState(*this, chanId); } return m_chanStates[chanId].prevProgram(); } } // namespace amuse