2016-05-06 21:22:39 +00:00
|
|
|
#include "amuse/Sequencer.hpp"
|
2016-05-15 06:48:26 +00:00
|
|
|
#include "amuse/Submix.hpp"
|
|
|
|
#include "amuse/Voice.hpp"
|
|
|
|
#include "amuse/Engine.hpp"
|
2016-05-06 21:22:39 +00:00
|
|
|
|
|
|
|
namespace amuse
|
|
|
|
{
|
|
|
|
|
2016-05-15 21:56:23 +00:00
|
|
|
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;
|
|
|
|
}
|
2016-05-16 02:40:18 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2016-05-15 21:56:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Sequencer::_bringOutYourDead()
|
|
|
|
{
|
|
|
|
for (auto& chan : m_chanStates)
|
2016-05-19 02:19:43 +00:00
|
|
|
if (chan)
|
|
|
|
chan->_bringOutYourDead();
|
2016-05-15 21:56:23 +00:00
|
|
|
|
|
|
|
if (!m_arrData && m_dieOnEnd && getVoiceCount() == 0)
|
|
|
|
m_state = SequencerState::Dead;
|
|
|
|
}
|
|
|
|
|
2016-05-15 06:48:26 +00:00
|
|
|
void Sequencer::_destroy()
|
|
|
|
{
|
|
|
|
Entity::_destroy();
|
|
|
|
if (m_submix)
|
2016-05-16 22:13:24 +00:00
|
|
|
{
|
|
|
|
m_engine.removeSubmix(m_submix);
|
|
|
|
m_submix = nullptr;
|
2016-05-31 10:16:52 +00:00
|
|
|
}
|
2016-05-15 06:48:26 +00:00
|
|
|
}
|
|
|
|
|
2016-05-31 05:15:20 +00:00
|
|
|
Sequencer::~Sequencer()
|
|
|
|
{
|
|
|
|
if (m_submix)
|
2016-05-31 21:07:46 +00:00
|
|
|
{
|
2016-05-31 05:15:20 +00:00
|
|
|
m_engine.removeSubmix(m_submix);
|
2016-05-31 21:07:46 +00:00
|
|
|
m_submix = nullptr;
|
|
|
|
}
|
2016-05-31 05:15:20 +00:00
|
|
|
}
|
2016-05-14 22:38:37 +00:00
|
|
|
|
2016-05-15 21:56:23 +00:00
|
|
|
Sequencer::Sequencer(Engine& engine, const AudioGroup& group, int groupId,
|
2016-05-15 06:48:26 +00:00
|
|
|
const SongGroupIndex& songGroup, int setupId, Submix* smx)
|
2016-05-31 05:15:20 +00:00
|
|
|
: Entity(engine, group, groupId), m_songGroup(songGroup)
|
2016-05-15 06:48:26 +00:00
|
|
|
{
|
|
|
|
auto it = m_songGroup.m_midiSetups.find(setupId);
|
|
|
|
if (it != m_songGroup.m_midiSetups.cend())
|
|
|
|
m_midiSetup = it->second->data();
|
2016-05-31 05:15:20 +00:00
|
|
|
|
|
|
|
m_submix = m_engine.addSubmix(smx);
|
2016-06-03 02:53:52 +00:00
|
|
|
m_submix->makeReverbHi(0.2f, 0.65f, 1.f, 0.5f, 0.f, 0.f);
|
2016-05-15 06:48:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Sequencer::ChannelState::~ChannelState()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
Sequencer::ChannelState::ChannelState(Sequencer& parent, uint8_t chanId)
|
2016-05-16 20:20:29 +00:00
|
|
|
: m_parent(parent), m_chanId(chanId), m_setup(m_parent.m_midiSetup[chanId])
|
2016-05-15 06:48:26 +00:00
|
|
|
{
|
2016-05-16 20:20:29 +00:00
|
|
|
if (chanId == 9)
|
2016-05-15 06:48:26 +00:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2016-05-19 02:19:43 +00:00
|
|
|
m_curVol = m_setup.volume / 127.f;
|
|
|
|
m_curPan = m_setup.panning / 64.f - 1.f;
|
2016-05-31 05:15:20 +00:00
|
|
|
m_ctrlVals[0x5b] = m_setup.reverb;
|
|
|
|
m_ctrlVals[0x5d] = m_setup.chorus;
|
2016-05-19 02:19:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Sequencer::advance(double dt)
|
|
|
|
{
|
|
|
|
if (m_state == SequencerState::Playing)
|
|
|
|
if (m_songState.advance(*this, dt))
|
|
|
|
{
|
|
|
|
m_arrData = nullptr;
|
|
|
|
m_state = SequencerState::Interactive;
|
|
|
|
allOff();
|
|
|
|
}
|
2016-05-15 06:48:26 +00:00
|
|
|
}
|
|
|
|
|
2016-05-16 02:40:18 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2016-05-15 06:48:26 +00:00
|
|
|
size_t Sequencer::getVoiceCount() const
|
|
|
|
{
|
|
|
|
size_t ret = 0;
|
|
|
|
for (const auto& chan : m_chanStates)
|
2016-05-19 02:19:43 +00:00
|
|
|
if (chan)
|
|
|
|
ret += chan->getVoiceCount();
|
2016-05-15 06:48:26 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::shared_ptr<Voice> Sequencer::ChannelState::keyOn(uint8_t note, uint8_t velocity)
|
|
|
|
{
|
|
|
|
if (!m_page)
|
|
|
|
return {};
|
|
|
|
|
2016-06-01 21:17:16 +00:00
|
|
|
/* If portamento is enabled for voice, pre-empt spawning new voices */
|
|
|
|
if (std::shared_ptr<Voice> lastVoice = m_lastVoice.lock())
|
|
|
|
{
|
|
|
|
uint8_t lastNote = lastVoice->getLastNote();
|
|
|
|
if (lastVoice->doPortamento(note))
|
|
|
|
{
|
|
|
|
m_chanVoxs.erase(lastNote);
|
|
|
|
m_chanVoxs[note] = lastVoice;
|
|
|
|
return lastVoice;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-19 05:27:39 +00:00
|
|
|
/* Ensure keyoff sent first */
|
2016-05-19 11:03:38 +00:00
|
|
|
auto keySearch = m_chanVoxs.find(note);
|
|
|
|
if (keySearch != m_chanVoxs.cend())
|
|
|
|
{
|
2016-06-01 21:17:16 +00:00
|
|
|
if (keySearch->second == m_lastVoice.lock())
|
|
|
|
m_lastVoice.reset();
|
2016-05-19 11:03:38 +00:00
|
|
|
keySearch->second->keyOff();
|
|
|
|
keySearch->second->setPedal(false);
|
|
|
|
m_keyoffVoxs.emplace(std::move(keySearch->second));
|
|
|
|
m_chanVoxs.erase(keySearch);
|
|
|
|
}
|
2016-05-19 05:27:39 +00:00
|
|
|
|
2016-05-31 20:40:51 +00:00
|
|
|
std::list<std::shared_ptr<Voice>>::iterator ret =
|
|
|
|
m_parent.m_engine._allocateVoice(m_parent.m_audioGroup,
|
|
|
|
m_parent.m_groupId, 32000.0,
|
|
|
|
true, false, m_parent.m_submix);
|
|
|
|
if (*ret)
|
2016-05-15 06:48:26 +00:00
|
|
|
{
|
2016-05-31 20:40:51 +00:00
|
|
|
m_chanVoxs[note] = *ret;
|
|
|
|
(*ret)->installCtrlValues(m_ctrlVals);
|
2016-05-28 02:28:59 +00:00
|
|
|
|
|
|
|
ObjectId oid = (m_parent.m_audioGroup.getDataFormat() == DataFormat::PC) ? m_page->objId : SBig(m_page->objId);
|
2016-06-01 21:32:48 +00:00
|
|
|
if (!(*ret)->loadSoundObject(oid, 0, m_parent.m_ticksPerSec, note, velocity, m_ctrlVals[1]))
|
2016-05-22 23:08:28 +00:00
|
|
|
{
|
2016-05-31 20:40:51 +00:00
|
|
|
m_parent.m_engine._destroyVoice(ret);
|
2016-05-22 23:08:28 +00:00
|
|
|
return {};
|
|
|
|
}
|
2016-05-31 20:40:51 +00:00
|
|
|
(*ret)->setVolume(m_parent.m_curVol * m_curVol);
|
|
|
|
(*ret)->setReverbVol(m_ctrlVals[0x5b] / 127.f);
|
|
|
|
(*ret)->setPan(m_curPan);
|
|
|
|
(*ret)->setPitchWheel(m_curPitchWheel);
|
2016-05-16 20:20:29 +00:00
|
|
|
|
2016-05-22 23:08:28 +00:00
|
|
|
if (m_ctrlVals[64] > 64)
|
2016-05-31 20:40:51 +00:00
|
|
|
(*ret)->setPedal(true);
|
2016-06-01 21:17:16 +00:00
|
|
|
|
|
|
|
m_lastVoice = *ret;
|
2016-05-22 23:08:28 +00:00
|
|
|
}
|
2016-05-16 20:20:29 +00:00
|
|
|
|
2016-05-31 20:40:51 +00:00
|
|
|
return *ret;
|
2016-05-15 06:48:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
std::shared_ptr<Voice> Sequencer::keyOn(uint8_t chan, uint8_t note, uint8_t velocity)
|
|
|
|
{
|
2016-05-19 02:19:43 +00:00
|
|
|
if (chan > 15)
|
|
|
|
return {};
|
2016-05-15 06:48:26 +00:00
|
|
|
|
2016-05-19 02:19:43 +00:00
|
|
|
if (!m_chanStates[chan])
|
|
|
|
m_chanStates[chan].emplace(*this, chan);
|
|
|
|
|
|
|
|
return m_chanStates[chan]->keyOn(note, velocity);
|
2016-05-15 06:48:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Sequencer::ChannelState::keyOff(uint8_t note, uint8_t velocity)
|
|
|
|
{
|
|
|
|
auto keySearch = m_chanVoxs.find(note);
|
|
|
|
if (keySearch == m_chanVoxs.cend())
|
|
|
|
return;
|
|
|
|
|
2016-06-01 21:17:16 +00:00
|
|
|
if (keySearch->second == m_lastVoice.lock())
|
|
|
|
m_lastVoice.reset();
|
2016-05-15 06:48:26 +00:00
|
|
|
keySearch->second->keyOff();
|
2016-05-16 02:40:18 +00:00
|
|
|
m_keyoffVoxs.emplace(std::move(keySearch->second));
|
2016-05-15 06:48:26 +00:00
|
|
|
m_chanVoxs.erase(keySearch);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Sequencer::keyOff(uint8_t chan, uint8_t note, uint8_t velocity)
|
|
|
|
{
|
2016-05-19 02:19:43 +00:00
|
|
|
if (chan > 15 || !m_chanStates[chan])
|
2016-05-15 06:48:26 +00:00
|
|
|
return;
|
|
|
|
|
2016-05-19 02:19:43 +00:00
|
|
|
m_chanStates[chan]->keyOff(note, velocity);
|
2016-05-15 06:48:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Sequencer::ChannelState::setCtrlValue(uint8_t ctrl, int8_t val)
|
|
|
|
{
|
|
|
|
m_ctrlVals[ctrl] = val;
|
2016-05-16 20:20:29 +00:00
|
|
|
for (const auto& vox : m_chanVoxs)
|
2016-05-21 21:40:03 +00:00
|
|
|
vox.second->_notifyCtrlChange(ctrl, val);
|
2016-05-16 20:20:29 +00:00
|
|
|
for (const auto& vox : m_keyoffVoxs)
|
2016-05-21 21:40:03 +00:00
|
|
|
vox->_notifyCtrlChange(ctrl, val);
|
2016-05-19 02:19:43 +00:00
|
|
|
|
2016-05-22 08:35:55 +00:00
|
|
|
switch (ctrl)
|
|
|
|
{
|
|
|
|
case 7:
|
2016-05-19 02:19:43 +00:00
|
|
|
setVolume(val / 127.f);
|
2016-05-22 08:35:55 +00:00
|
|
|
break;
|
|
|
|
case 10:
|
2016-05-19 02:19:43 +00:00
|
|
|
setPan(val / 64.f - 1.f);
|
2016-05-22 08:35:55 +00:00
|
|
|
break;
|
|
|
|
default: break;
|
|
|
|
}
|
2016-05-16 20:20:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool Sequencer::ChannelState::programChange(int8_t prog)
|
|
|
|
{
|
|
|
|
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;
|
2016-05-15 06:48:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Sequencer::setCtrlValue(uint8_t chan, uint8_t ctrl, int8_t val)
|
|
|
|
{
|
2016-05-19 02:19:43 +00:00
|
|
|
if (chan > 15)
|
2016-05-15 06:48:26 +00:00
|
|
|
return;
|
|
|
|
|
2016-05-19 02:19:43 +00:00
|
|
|
if (!m_chanStates[chan])
|
|
|
|
m_chanStates[chan].emplace(*this, chan);
|
|
|
|
|
|
|
|
m_chanStates[chan]->setCtrlValue(ctrl, val);
|
2016-05-15 06:48:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Sequencer::ChannelState::setPitchWheel(float pitchWheel)
|
|
|
|
{
|
2016-05-16 20:20:29 +00:00
|
|
|
m_curPitchWheel = pitchWheel;
|
2016-05-15 06:48:26 +00:00
|
|
|
for (const auto& vox : m_chanVoxs)
|
|
|
|
vox.second->setPitchWheel(pitchWheel);
|
2016-05-16 02:40:18 +00:00
|
|
|
for (const auto& vox : m_keyoffVoxs)
|
|
|
|
vox->setPitchWheel(pitchWheel);
|
2016-05-15 06:48:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Sequencer::setPitchWheel(uint8_t chan, float pitchWheel)
|
|
|
|
{
|
2016-05-19 02:19:43 +00:00
|
|
|
if (chan > 15)
|
2016-05-15 06:48:26 +00:00
|
|
|
return;
|
|
|
|
|
2016-05-19 02:19:43 +00:00
|
|
|
if (!m_chanStates[chan])
|
|
|
|
m_chanStates[chan].emplace(*this, chan);
|
|
|
|
|
|
|
|
m_chanStates[chan]->setPitchWheel(pitchWheel);
|
2016-05-15 21:56:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Sequencer::setTempo(double ticksPerSec)
|
|
|
|
{
|
|
|
|
m_ticksPerSec = ticksPerSec;
|
2016-05-15 06:48:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Sequencer::ChannelState::allOff()
|
|
|
|
{
|
2016-05-22 23:08:28 +00:00
|
|
|
for (auto it = m_chanVoxs.begin() ; it != m_chanVoxs.end() ;)
|
|
|
|
{
|
2016-06-01 21:17:16 +00:00
|
|
|
if (it->second == m_lastVoice.lock())
|
|
|
|
m_lastVoice.reset();
|
2016-05-22 23:08:28 +00:00
|
|
|
it->second->keyOff();
|
|
|
|
m_keyoffVoxs.emplace(std::move(it->second));
|
|
|
|
it = m_chanVoxs.erase(it);
|
|
|
|
}
|
2016-05-15 06:48:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Sequencer::allOff(bool now)
|
|
|
|
{
|
|
|
|
if (now)
|
|
|
|
for (auto& chan : m_chanStates)
|
2016-05-16 20:20:29 +00:00
|
|
|
{
|
2016-05-19 02:19:43 +00:00
|
|
|
if (chan)
|
|
|
|
{
|
|
|
|
for (const auto& vox : chan->m_chanVoxs)
|
2016-05-31 20:40:51 +00:00
|
|
|
vox.second->kill();
|
2016-05-19 02:19:43 +00:00
|
|
|
for (const auto& vox : chan->m_keyoffVoxs)
|
2016-05-31 20:40:51 +00:00
|
|
|
vox->kill();
|
2016-05-19 02:19:43 +00:00
|
|
|
chan->m_chanVoxs.clear();
|
|
|
|
chan->m_keyoffVoxs.clear();
|
|
|
|
}
|
2016-05-16 20:20:29 +00:00
|
|
|
}
|
2016-05-15 06:48:26 +00:00
|
|
|
else
|
|
|
|
for (auto& chan : m_chanStates)
|
2016-05-19 02:19:43 +00:00
|
|
|
if (chan)
|
|
|
|
chan->allOff();
|
2016-05-15 21:56:23 +00:00
|
|
|
}
|
|
|
|
|
2016-05-20 06:17:02 +00:00
|
|
|
void Sequencer::allOff(uint8_t chan, bool now)
|
|
|
|
{
|
|
|
|
if (chan > 15 || !m_chanStates[chan])
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (now)
|
|
|
|
{
|
|
|
|
for (const auto& vox : m_chanStates[chan]->m_chanVoxs)
|
2016-05-31 20:40:51 +00:00
|
|
|
vox.second->kill();
|
2016-05-20 06:17:02 +00:00
|
|
|
for (const auto& vox : m_chanStates[chan]->m_keyoffVoxs)
|
2016-05-31 20:40:51 +00:00
|
|
|
vox->kill();
|
2016-05-20 06:17:02 +00:00
|
|
|
m_chanStates[chan]->m_chanVoxs.clear();
|
|
|
|
m_chanStates[chan]->m_keyoffVoxs.clear();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
m_chanStates[chan]->allOff();
|
|
|
|
}
|
|
|
|
|
2016-05-15 21:56:23 +00:00
|
|
|
void Sequencer::ChannelState::killKeygroup(uint8_t kg, bool now)
|
|
|
|
{
|
|
|
|
for (auto it = m_chanVoxs.begin() ; it != m_chanVoxs.end() ;)
|
|
|
|
{
|
|
|
|
Voice* vox = it->second.get();
|
|
|
|
if (vox->m_keygroup == kg)
|
|
|
|
{
|
2016-06-01 21:17:16 +00:00
|
|
|
if (it->second == m_lastVoice.lock())
|
|
|
|
m_lastVoice.reset();
|
2016-05-15 21:56:23 +00:00
|
|
|
if (now)
|
|
|
|
{
|
|
|
|
it = m_chanVoxs.erase(it);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
vox->keyOff();
|
2016-05-22 23:08:28 +00:00
|
|
|
m_keyoffVoxs.emplace(std::move(it->second));
|
|
|
|
it = m_chanVoxs.erase(it);
|
|
|
|
continue;
|
2016-05-15 21:56:23 +00:00
|
|
|
}
|
|
|
|
++it;
|
|
|
|
}
|
2016-05-16 02:40:18 +00:00
|
|
|
|
|
|
|
if (now)
|
|
|
|
{
|
|
|
|
for (auto it = m_keyoffVoxs.begin() ; it != m_keyoffVoxs.end() ;)
|
|
|
|
{
|
|
|
|
Voice* vox = it->get();
|
|
|
|
if (vox->m_keygroup == kg)
|
|
|
|
{
|
|
|
|
it = m_keyoffVoxs.erase(it);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
++it;
|
|
|
|
}
|
|
|
|
}
|
2016-05-15 21:56:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Sequencer::killKeygroup(uint8_t kg, bool now)
|
|
|
|
{
|
|
|
|
for (auto& chan : m_chanStates)
|
2016-05-19 02:19:43 +00:00
|
|
|
if (chan)
|
|
|
|
chan->killKeygroup(kg, now);
|
2016-05-15 21:56:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
std::shared_ptr<Voice> Sequencer::ChannelState::findVoice(int vid)
|
|
|
|
{
|
|
|
|
for (const auto& vox : m_chanVoxs)
|
|
|
|
if (vox.second->vid() == vid)
|
|
|
|
return vox.second;
|
2016-05-16 02:40:18 +00:00
|
|
|
for (const auto& vox : m_keyoffVoxs)
|
|
|
|
if (vox->vid() == vid)
|
|
|
|
return vox;
|
2016-05-15 21:56:23 +00:00
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
std::shared_ptr<Voice> Sequencer::findVoice(int vid)
|
|
|
|
{
|
|
|
|
for (auto& chan : m_chanStates)
|
|
|
|
{
|
2016-05-19 02:19:43 +00:00
|
|
|
if (chan)
|
|
|
|
{
|
|
|
|
std::shared_ptr<Voice> ret = chan->findVoice(vid);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
}
|
2016-05-15 21:56:23 +00:00
|
|
|
}
|
|
|
|
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);
|
|
|
|
}
|
2016-05-16 02:40:18 +00:00
|
|
|
for (const auto& v : m_keyoffVoxs)
|
|
|
|
{
|
|
|
|
Voice* vox = v.get();
|
|
|
|
if (vox->getObjectId() == macroId)
|
|
|
|
vox->message(val);
|
|
|
|
}
|
2016-05-15 21:56:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Sequencer::sendMacroMessage(ObjectId macroId, int32_t val)
|
|
|
|
{
|
|
|
|
for (auto& chan : m_chanStates)
|
2016-05-19 02:19:43 +00:00
|
|
|
if (chan)
|
|
|
|
chan->sendMacroMessage(macroId, val);
|
2016-05-15 21:56:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Sequencer::playSong(const unsigned char* arrData, bool dieOnEnd)
|
|
|
|
{
|
|
|
|
m_arrData = arrData;
|
|
|
|
m_dieOnEnd = dieOnEnd;
|
2016-05-19 02:19:43 +00:00
|
|
|
m_songState.initialize(arrData);
|
2016-06-01 21:32:48 +00:00
|
|
|
setTempo(m_songState.getTempo() * 384 / 60);
|
2016-05-15 21:56:23 +00:00
|
|
|
m_state = SequencerState::Playing;
|
2016-05-15 06:48:26 +00:00
|
|
|
}
|
|
|
|
|
2016-05-19 10:12:32 +00:00
|
|
|
void Sequencer::stopSong(bool now)
|
|
|
|
{
|
|
|
|
allOff(now);
|
|
|
|
m_arrData = nullptr;
|
|
|
|
m_state = SequencerState::Interactive;
|
|
|
|
}
|
|
|
|
|
2016-05-16 02:40:18 +00:00
|
|
|
void Sequencer::ChannelState::setVolume(float vol)
|
|
|
|
{
|
2016-05-19 02:19:43 +00:00
|
|
|
m_curVol = vol;
|
|
|
|
float voxVol = m_parent.m_curVol * m_curVol;
|
2016-05-16 02:40:18 +00:00
|
|
|
for (const auto& v : m_chanVoxs)
|
|
|
|
{
|
|
|
|
Voice* vox = v.second.get();
|
2016-05-19 02:19:43 +00:00
|
|
|
vox->setVolume(voxVol);
|
2016-05-16 02:40:18 +00:00
|
|
|
}
|
|
|
|
for (const auto& v : m_keyoffVoxs)
|
|
|
|
{
|
|
|
|
Voice* vox = v.get();
|
2016-05-19 02:19:43 +00:00
|
|
|
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);
|
2016-05-16 02:40:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Sequencer::setVolume(float vol)
|
|
|
|
{
|
|
|
|
m_curVol = vol;
|
|
|
|
for (auto& chan : m_chanStates)
|
2016-05-19 02:19:43 +00:00
|
|
|
if (chan)
|
|
|
|
chan->setVolume(chan->m_curVol);
|
2016-05-16 02:40:18 +00:00
|
|
|
}
|
|
|
|
|
2016-05-16 20:20:29 +00:00
|
|
|
int8_t Sequencer::getChanProgram(int8_t chanId) const
|
|
|
|
{
|
2016-05-19 02:19:43 +00:00
|
|
|
if (chanId > 15 || !m_chanStates[chanId])
|
2016-05-16 20:20:29 +00:00
|
|
|
return 0;
|
|
|
|
|
2016-05-19 02:19:43 +00:00
|
|
|
return m_chanStates[chanId]->m_curProgram;
|
2016-05-16 20:20:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool Sequencer::setChanProgram(int8_t chanId, int8_t prog)
|
|
|
|
{
|
2016-05-19 02:19:43 +00:00
|
|
|
if (chanId > 15)
|
2016-05-16 20:20:29 +00:00
|
|
|
return false;
|
|
|
|
|
2016-05-19 02:19:43 +00:00
|
|
|
if (!m_chanStates[chanId])
|
|
|
|
m_chanStates[chanId].emplace(*this, chanId);
|
|
|
|
|
|
|
|
return m_chanStates[chanId]->programChange(prog);
|
2016-05-16 20:20:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Sequencer::nextChanProgram(int8_t chanId)
|
|
|
|
{
|
2016-05-19 02:19:43 +00:00
|
|
|
if (chanId > 15)
|
2016-05-16 20:20:29 +00:00
|
|
|
return;
|
|
|
|
|
2016-05-19 02:19:43 +00:00
|
|
|
if (!m_chanStates[chanId])
|
|
|
|
m_chanStates[chanId].emplace(*this, chanId);
|
|
|
|
|
|
|
|
return m_chanStates[chanId]->nextProgram();
|
2016-05-16 20:20:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Sequencer::prevChanProgram(int8_t chanId)
|
|
|
|
{
|
2016-05-19 02:19:43 +00:00
|
|
|
if (chanId > 15)
|
2016-05-16 20:20:29 +00:00
|
|
|
return;
|
|
|
|
|
2016-05-19 02:19:43 +00:00
|
|
|
if (!m_chanStates[chanId])
|
|
|
|
m_chanStates[chanId].emplace(*this, chanId);
|
|
|
|
|
|
|
|
return m_chanStates[chanId]->prevProgram();
|
2016-05-16 20:20:29 +00:00
|
|
|
}
|
|
|
|
|
2016-05-06 21:22:39 +00:00
|
|
|
}
|