Bug fixes in sequencer and SoundMacro event trapping

This commit is contained in:
Jack Andersen 2016-05-15 16:40:18 -10:00
parent 2c3c9d3885
commit 44bb7a155b
9 changed files with 326 additions and 193 deletions

View File

@ -195,8 +195,8 @@ struct AppCallback : boo::IApplicationCallback
if (m_seq)
voxCount = m_seq->getVoiceCount();
printf("\r "
"\r %" PRISize " Setup %d, Chan %d, VOL: %d%%\r", voxCount,
m_setupId, m_chanId, int(std::rint(m_volume * 100)));
"\r %" PRISize " Setup %d, Chan %d, Octave: %d, Vel: %d, VOL: %d%%\r", voxCount,
m_setupId, m_chanId, m_octave, m_velocity, int(std::rint(m_volume * 100)));
fflush(stdout);
}
@ -353,6 +353,32 @@ struct AppCallback : boo::IApplicationCallback
}
}
void charKeyDownRepeat(unsigned long charCode)
{
if (m_seq && m_chanId != -1)
{
switch (charCode)
{
case 'z':
m_octave = amuse::clamp(-1, m_octave - 1, 8);
m_updateDisp = true;
break;
case 'x':
m_octave = amuse::clamp(-1, m_octave + 1, 8);
m_updateDisp = true;
break;
case 'c':
m_velocity = amuse::clamp(0, m_velocity - 1, 127);
m_updateDisp = true;
break;
case 'v':
m_velocity = amuse::clamp(0, m_velocity + 1, 127);
m_updateDisp = true;
break;
}
}
}
void charKeyDown(unsigned long charCode)
{
charCode = tolower(charCode);
@ -381,15 +407,19 @@ struct AppCallback : boo::IApplicationCallback
{
case 'z':
m_octave = amuse::clamp(-1, m_octave - 1, 8);
m_updateDisp = true;
break;
case 'x':
m_octave = amuse::clamp(-1, m_octave + 1, 8);
m_updateDisp = true;
break;
case 'c':
m_velocity = amuse::clamp(0, m_velocity - 1, 127);
m_updateDisp = true;
break;
case 'v':
m_velocity = amuse::clamp(0, m_velocity + 1, 127);
m_updateDisp = true;
break;
case '\t':
m_seq->setCtrlValue(m_chanId, 64, 127);
@ -637,7 +667,8 @@ struct AppCallback : boo::IApplicationCallback
void EventCallback::charKeyDown(unsigned long charCode, boo::EModifierKey mods, bool isRepeat)
{
if (isRepeat)
return;
m_app.charKeyDownRepeat(charCode);
else
m_app.charKeyDown(charCode);
}
@ -661,6 +692,8 @@ void EventCallback::specialKeyDown(boo::ESpecialKey key, boo::EModifierKey mods,
m_app.m_volume = amuse::clamp(0.f, m_app.m_volume + 0.05f, 1.f);
if (m_app.m_vox)
m_app.m_vox->setVolume(m_app.m_volume);
if (m_app.m_seq)
m_app.m_seq->setVolume(m_app.m_volume);
m_app.m_updateDisp = true;
break;
case boo::ESpecialKey::Down:
@ -668,6 +701,8 @@ void EventCallback::specialKeyDown(boo::ESpecialKey key, boo::EModifierKey mods,
m_app.m_volume = amuse::clamp(0.f, m_app.m_volume - 0.05f, 1.f);
if (m_app.m_vox)
m_app.m_vox->setVolume(m_app.m_volume);
if (m_app.m_seq)
m_app.m_seq->setVolume(m_app.m_volume);
m_app.m_updateDisp = true;
break;
default: break;

View File

@ -23,7 +23,7 @@ private:
const ADSR* m_curADSR = nullptr; /**< Current timing envelope */
double m_sustainFactor; /**< Evaluated sustain percentage as a double */
double m_releaseStartFactor; /**< Level at whenever release event occurs */
unsigned m_curMs; /**< Current time of envelope stage */
double m_curMs; /**< Current time of envelope stage */
public:
void reset(const ADSR* adsr);
void keyOff();

View File

@ -4,6 +4,7 @@
#include "Entity.hpp"
#include "AudioGroupProject.hpp"
#include <unordered_map>
#include <unordered_set>
#include <memory>
#include <list>
@ -33,6 +34,8 @@ class Sequencer : public Entity
SequencerState m_state = SequencerState::Interactive; /**< Current high-level state of sequencer */
bool m_dieOnEnd = false; /**< Sequencer will be killed when current arrangement completes */
float m_curVol = 1.f; /**< Current volume of sequencer */
/** State of a single MIDI channel */
struct ChannelState
{
@ -45,13 +48,16 @@ class Sequencer : public Entity
/** Voices corresponding to currently-pressed keys in channel */
std::unordered_map<uint8_t, std::shared_ptr<Voice>> m_chanVoxs;
std::unordered_set<std::shared_ptr<Voice>> m_keyoffVoxs;
int8_t m_ctrlVals[128]; /**< MIDI controller values */
void _bringOutYourDead();
size_t getVoiceCount() const;
std::shared_ptr<Voice> keyOn(uint8_t note, uint8_t velocity);
void keyOff(uint8_t note, uint8_t velocity);
void setCtrlValue(uint8_t ctrl, int8_t val);
void setPitchWheel(float pitchWheel);
void setVolume(float vol);
void allOff();
void killKeygroup(uint8_t kg, bool now);
std::shared_ptr<Voice> findVoice(int vid);
@ -105,6 +111,9 @@ public:
/** Play MIDI arrangement */
void playSong(const unsigned char* arrData, bool dieOnEnd=true);
/** Set total volume of sequencer */
void setVolume(float vol);
/** Manually kill sequencer for deferred release from engine */
void kill() {m_state = SequencerState::Dead;}
};

View File

@ -123,7 +123,7 @@ class SoundMacroState
uint8_t m_initKey; /**< Key played for this macro invocation */
uint8_t m_curVel; /**< Current velocity played for this macro invocation */
uint8_t m_curMod; /**< Current modulation played for this macro invocation */
uint32_t m_curKey; /**< Current key played for this macro invocation (in cents) */
uint32_t m_curPitch; /**< Current key played for this macro invocation (in cents) */
double m_execTime; /**< time in seconds of SoundMacro execution (per-update resolution) */
bool m_keyoff; /**< keyoff message has been received */
@ -205,18 +205,12 @@ class SoundMacroState
int32_t m_variables[256]; /**< 32-bit variables set with relevant commands */
/** Messages pending processing for this SoundMacro voice */
std::list<int32_t> m_messageQueue;
/** Event registration data for TRAP_EVENT */
struct EventTrap
{
ObjectId macroId = 0xffff;
uint16_t macroStep;
};
EventTrap m_keyoffTrap;
EventTrap m_sampleEndTrap;
EventTrap m_messageTrap;
public:
/** initialize state for SoundMacro data at `ptr` */
@ -234,9 +228,6 @@ public:
/** sample end event */
void sampleEndNotify(Voice& vox);
/** SEND_MESSAGE receive event */
void messageNotify(Voice& vox, int32_t val);
};
}

View File

@ -39,6 +39,10 @@ class Voice : public Entity
std::unique_ptr<IBackendVoice> m_backendVoice; /**< Handle to client-implemented backend voice */
SoundMacroState m_state; /**< State container for SoundMacro playback */
SoundMacroState::EventTrap m_keyoffTrap; /**< Trap for keyoff (SoundMacro overrides default envelope behavior) */
SoundMacroState::EventTrap m_sampleEndTrap; /**< Trap for sampleend (SoundMacro overrides voice removal) */
SoundMacroState::EventTrap m_messageTrap; /**< Trap for messages sent from other SoundMacros */
std::list<int32_t> m_messageQueue; /**< Messages pending processing for SoundMacros in this voice */
std::list<std::shared_ptr<Voice>> m_childVoices; /**< Child voices for PLAYMACRO usage */
uint8_t m_keygroup = 0; /**< Keygroup voice is a member of */
@ -118,6 +122,8 @@ class Voice : public Entity
void _reset();
bool _checkSamplePos();
void _doKeyOff();
void _macroKeyOff();
void _macroSampleEnd();
bool _advanceSample(int16_t& samp);
void _setTotalPitch(int32_t cents);
bool _isRecursivelyDead();

View File

@ -7,7 +7,10 @@ void Envelope::reset(const ADSR* adsr)
{
m_phase = State::Attack;
m_curADSR = adsr;
m_curMs = 0;
m_curMs = 0.0;
if (m_curADSR->decayCoarse == 128)
m_sustainFactor = 1.f;
else
m_sustainFactor = (adsr->sustainCoarse * 6.25 + adsr->sustainFine * 0.0244) / 100.0;
m_releaseStartFactor = 0.0;
}
@ -15,7 +18,7 @@ void Envelope::reset(const ADSR* adsr)
void Envelope::keyOff()
{
m_phase = State::Release;
m_curMs = 0;
m_curMs = 0.0;
}
float Envelope::nextSample(double sampleRate)
@ -36,15 +39,23 @@ float Envelope::nextSample(double sampleRate)
uint16_t attack = m_curADSR->attackCoarse * 255 + m_curADSR->attackFine;
if (attack == 0)
{
if (m_curADSR->decayCoarse == 128)
m_phase = State::Sustain;
else
m_phase = State::Decay;
m_curMs = 0;
m_curMs = 0.0;
m_releaseStartFactor = 1.f;
return 1.f;
}
double attackFac = m_curMs / double(attack);
if (attackFac >= 1.0)
{
if (m_curADSR->decayCoarse == 128)
m_phase = State::Sustain;
else
m_phase = State::Decay;
m_curMs = 0;
m_curMs = 0.0;
m_releaseStartFactor = 1.f;
return 1.f;
}
m_releaseStartFactor = attackFac;
@ -56,7 +67,7 @@ float Envelope::nextSample(double sampleRate)
if (decay == 0)
{
m_phase = State::Sustain;
m_curMs = 0;
m_curMs = 0.0;
m_releaseStartFactor = m_sustainFactor;
return m_sustainFactor;
}
@ -64,7 +75,7 @@ float Envelope::nextSample(double sampleRate)
if (decayFac >= 1.0)
{
m_phase = State::Sustain;
m_curMs = 0;
m_curMs = 0.0;
m_releaseStartFactor = m_sustainFactor;
return m_sustainFactor;
}

View File

@ -19,6 +19,18 @@ void Sequencer::ChannelState::_bringOutYourDead()
}
++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;
}
}
void Sequencer::_bringOutYourDead()
@ -35,10 +47,6 @@ void Sequencer::_destroy()
Entity::_destroy();
if (m_submix)
m_submix->m_activeSequencers.erase(this);
for (const auto& chan : m_chanStates)
for (const auto& vox : chan.second->m_chanVoxs)
vox.second->_destroy();
}
Sequencer::~Sequencer() {}
@ -83,12 +91,21 @@ Sequencer::ChannelState::ChannelState(Sequencer& parent, uint8_t chanId)
m_submix->makeChorus(15, m_setup.chorus * 5 / 127, 5000);
}
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)
for (const auto& vox : chan.second->m_chanVoxs)
ret += vox.second->getTotalVoices();
ret += chan.second->getVoiceCount();
return ret;
}
@ -107,7 +124,7 @@ std::shared_ptr<Voice> Sequencer::ChannelState::keyOn(uint8_t note, uint8_t velo
m_parent.m_engine._destroyVoice(ret.get());
return {};
}
ret->setVolume(m_setup.volume / 127.f);
ret->setVolume(m_parent.m_curVol * m_setup.volume / 127.f);
ret->setPan(m_setup.panning / 64.f - 127.f);
return ret;
}
@ -131,6 +148,7 @@ void Sequencer::ChannelState::keyOff(uint8_t note, uint8_t velocity)
return;
keySearch->second->keyOff();
m_keyoffVoxs.emplace(std::move(keySearch->second));
m_chanVoxs.erase(keySearch);
}
@ -161,6 +179,8 @@ void Sequencer::ChannelState::setPitchWheel(float 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)
@ -209,6 +229,20 @@ void Sequencer::ChannelState::killKeygroup(uint8_t kg, bool now)
}
++it;
}
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;
}
}
}
void Sequencer::killKeygroup(uint8_t kg, bool now)
@ -222,6 +256,9 @@ std::shared_ptr<Voice> 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 {};
}
@ -244,6 +281,12 @@ void Sequencer::ChannelState::sendMacroMessage(ObjectId macroId, int32_t val)
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)
@ -259,4 +302,26 @@ void Sequencer::playSong(const unsigned char* arrData, bool dieOnEnd)
m_state = SequencerState::Playing;
}
void Sequencer::ChannelState::setVolume(float vol)
{
vol = vol * m_setup.volume / 127.f;
for (const auto& v : m_chanVoxs)
{
Voice* vox = v.second.get();
vox->setVolume(vol);
}
for (const auto& v : m_keyoffVoxs)
{
Voice* vox = v.get();
vox->setVolume(vol);
}
}
void Sequencer::setVolume(float vol)
{
m_curVol = vol;
for (auto& chan : m_chanStates)
chan.second->setVolume(vol);
}
}

View File

@ -123,11 +123,12 @@ void SoundMacroState::initialize(const unsigned char* ptr, int step, double tick
m_initKey = midiKey;
m_initVel = midiVel;
m_initMod = midiMod;
m_curVel = 0;
m_curMod = 0;
m_curKey = 0;
m_curVel = midiVel;
m_curMod = midiMod;
m_curPitch = midiKey * 100;
m_pc.clear();
m_pc.push_back({ptr, step});
m_inWait = false;
m_execTime = 0.f;
m_keyoff = false;
m_sampleEnd = false;
@ -135,9 +136,6 @@ void SoundMacroState::initialize(const unsigned char* ptr, int step, double tick
m_lastPlayMacroVid = -1;
m_useAdsrControllers = false;
m_portamentoMode = 0;
m_keyoffTrap.macroId = 0xffff;
m_sampleEndTrap.macroId = 0xffff;
m_messageTrap.macroId = 0xffff;
m_header = *reinterpret_cast<const Header*>(ptr);
m_header.swapBig();
}
@ -154,7 +152,11 @@ bool SoundMacroState::advance(Voice& vox, double dt)
/* Advance wait timer if active, returning if waiting */
if (m_inWait)
{
if (!m_indefiniteWait)
if (m_keyoffWait && m_keyoff)
m_inWait = false;
else if (m_sampleEndWait && m_sampleEnd)
m_inWait = false;
else if (!m_indefiniteWait)
{
m_waitCountdown -= dt;
if (m_waitCountdown < 0.f)
@ -178,7 +180,7 @@ bool SoundMacroState::advance(Voice& vox, double dt)
{
case Op::End:
case Op::Stop:
m_pc.clear();
m_pc.back().second = -1;
return true;
case Op::SplitKey:
{
@ -480,6 +482,7 @@ bool SoundMacroState::advance(Voice& vox, double dt)
}
vox.startSample(smpId, offset);
vox.setPitchKey(m_curPitch);
break;
}
case Op::StopSample:
@ -489,7 +492,7 @@ bool SoundMacroState::advance(Voice& vox, double dt)
}
case Op::KeyOff:
{
vox.keyOff();
vox._macroKeyOff();
break;
}
case Op::SplitRnd:
@ -568,11 +571,11 @@ bool SoundMacroState::advance(Voice& vox, double dt)
noteLo *= 100;
noteHi *= 100;
m_curKey = vox.getEngine().nextRandom() % ((noteHi - noteLo) + noteLo);
m_curPitch = vox.getEngine().nextRandom() % ((noteHi - noteLo) + noteLo);
if (!free)
m_curKey = m_curKey / 100 * 100 + detune;
m_curPitch = m_curPitch / 100 * 100 + detune;
vox.setPitchKey(m_curKey);
vox.setPitchKey(m_curPitch);
break;
}
case Op::AddNote:
@ -583,7 +586,7 @@ bool SoundMacroState::advance(Voice& vox, double dt)
int8_t ms = cmd.m_data[4];
int16_t timeMs = *reinterpret_cast<int16_t*>(&cmd.m_data[5]);
m_curKey = (orgKey ? m_initKey : m_curKey) + add * 100 + detune;
m_curPitch = (orgKey ? (m_initKey * 100) : m_curPitch) + add * 100 + detune;
/* Set wait state */
if (timeMs)
@ -594,7 +597,7 @@ bool SoundMacroState::advance(Voice& vox, double dt)
m_inWait = true;
}
vox.setPitchKey(m_curKey);
vox.setPitchKey(m_curPitch);
break;
}
case Op::SetNote:
@ -604,7 +607,7 @@ bool SoundMacroState::advance(Voice& vox, double dt)
int8_t ms = cmd.m_data[4];
int16_t timeMs = *reinterpret_cast<int16_t*>(&cmd.m_data[5]);
m_curKey = key * 100 + detune;
m_curPitch = key * 100 + detune;
/* Set wait state */
if (timeMs)
@ -615,7 +618,7 @@ bool SoundMacroState::advance(Voice& vox, double dt)
m_inWait = true;
}
vox.setPitchKey(m_curKey);
vox.setPitchKey(m_curPitch);
break;
}
case Op::LastNote:
@ -625,7 +628,7 @@ bool SoundMacroState::advance(Voice& vox, double dt)
int8_t ms = cmd.m_data[4];
int16_t timeMs = *reinterpret_cast<int16_t*>(&cmd.m_data[5]);
m_curKey = (add + vox.getLastNote()) * 100 + detune;
m_curPitch = (add + vox.getLastNote()) * 100 + detune;
/* Set wait state */
if (timeMs)
@ -636,7 +639,7 @@ bool SoundMacroState::advance(Voice& vox, double dt)
m_inWait = true;
}
vox.setPitchKey(m_curKey);
vox.setPitchKey(m_curPitch);
break;
}
case Op::Portamento:
@ -774,16 +777,16 @@ bool SoundMacroState::advance(Voice& vox, double dt)
switch (event)
{
case 0:
m_keyoffTrap.macroId = macroId;
m_keyoffTrap.macroStep = macroStep;
vox.m_keyoffTrap.macroId = macroId;
vox.m_keyoffTrap.macroStep = macroStep;
break;
case 1:
m_sampleEndTrap.macroId = macroId;
m_sampleEndTrap.macroStep = macroStep;
vox.m_sampleEndTrap.macroId = macroId;
vox.m_sampleEndTrap.macroStep = macroStep;
break;
case 2:
m_messageTrap.macroId = macroId;
m_messageTrap.macroStep = macroStep;
vox.m_messageTrap.macroId = macroId;
vox.m_messageTrap.macroStep = macroStep;
break;
default: break;
}
@ -797,16 +800,16 @@ bool SoundMacroState::advance(Voice& vox, double dt)
switch (event)
{
case 0:
m_keyoffTrap.macroId = 0xffff;
m_keyoffTrap.macroStep = -1;
vox.m_keyoffTrap.macroId = 0xffff;
vox.m_keyoffTrap.macroStep = -1;
break;
case 1:
m_sampleEndTrap.macroId = 0xffff;
m_sampleEndTrap.macroStep = -1;
vox.m_sampleEndTrap.macroId = 0xffff;
vox.m_sampleEndTrap.macroStep = -1;
break;
case 2:
m_messageTrap.macroId = 0xffff;
m_messageTrap.macroStep = -1;
vox.m_messageTrap.macroId = 0xffff;
vox.m_messageTrap.macroStep = -1;
break;
default: break;
}
@ -834,10 +837,10 @@ bool SoundMacroState::advance(Voice& vox, double dt)
case Op::GetMessage:
{
uint8_t vid = cmd.m_data[0];
if (m_messageQueue.size())
if (vox.m_messageQueue.size())
{
m_variables[vid] = m_messageQueue.front();
m_messageQueue.pop_front();
m_variables[vid] = vox.m_messageQueue.front();
vox.m_messageQueue.pop_front();
}
else
m_variables[vid] = 0;
@ -1221,47 +1224,11 @@ bool SoundMacroState::advance(Voice& vox, double dt)
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);
}
}
}

View File

@ -70,6 +70,24 @@ void Voice::_reset()
memset(m_extCtrlVals, 0, 128);
}
void Voice::_macroSampleEnd()
{
if (m_sampleEndTrap.macroId != 0xffff)
{
if (m_sampleEndTrap.macroId == m_state.m_header.m_macroId)
{
m_state.m_pc.back().second = m_sampleEndTrap.macroStep;
m_state.m_inWait = false;
}
else
loadSoundObject(m_sampleEndTrap.macroId, m_sampleEndTrap.macroStep,
m_state.m_ticksPerSec, m_state.m_initKey,
m_state.m_initVel, m_state.m_initMod);
}
else
m_state.sampleEndNotify(*this);
}
bool Voice::_checkSamplePos()
{
if (m_curSamplePos >= m_lastSamplePos)
@ -84,7 +102,7 @@ bool Voice::_checkSamplePos()
else
{
/* Notify sample end */
m_state.sampleEndNotify(*this);
_macroSampleEnd();
m_curSample = nullptr;
return true;
}
@ -93,7 +111,7 @@ bool Voice::_checkSamplePos()
/* Looped samples issue sample end when ADSR envelope complete */
if (m_volAdsr.isComplete())
{
m_state.sampleEndNotify(*this);
_macroSampleEnd();
m_curSample = nullptr;
return true;
}
@ -110,6 +128,7 @@ void Voice::_doKeyOff()
void Voice::_setTotalPitch(int32_t cents)
{
fprintf(stderr, "PITCH %d\n", cents);
int32_t interval = cents - m_curSample->first.m_pitch * 100;
double ratio = std::exp2(interval / 1200.0);
m_sampleRate = m_curSample->first.m_sampleRate * ratio;
@ -304,7 +323,7 @@ size_t Voice::supplyAudio(size_t samples, int16_t* data)
{
uint32_t samplesRem = samples;
size_t samplesProc = 0;
bool dead = false;
bool dead = true;
/* Attempt to load stopped sample for immediate decoding */
if (!m_curSample)
@ -313,8 +332,6 @@ size_t Voice::supplyAudio(size_t samples, int16_t* data)
if (m_curSample)
{
dead = m_state.advance(*this, samples / m_sampleRate);
if (!dead)
{
uint32_t block = m_curSamplePos / 14;
uint32_t rem = m_curSamplePos % 14;
@ -409,11 +426,13 @@ size_t Voice::supplyAudio(size_t samples, int16_t* data)
}
}
}
}
else
memset(data, 0, sizeof(int16_t) * samples);
if (dead)
if (dead && m_voxState == VoiceState::KeyOff &&
m_sampleEndTrap.macroId == 0xffff &&
m_messageTrap.macroId == 0xffff &&
m_volAdsr.isComplete())
{
m_voxState = VoiceState::Dead;
m_backendVoice->stop();
@ -483,13 +502,13 @@ bool Voice::_loadLayer(const std::vector<const LayerMapping*>& layer, int macroS
{
if (midiKey >= mapping->keyLo && midiKey <= mapping->keyHi)
{
midiKey += mapping->transpose;
uint8_t mappingKey = midiKey + mapping->transpose;
if (m_voxState != VoiceState::Playing)
ret |= loadSoundObject(SBig(mapping->objectId), macroStep, ticksPerSec,
midiKey, midiVel, midiMod, pushPc);
mappingKey, midiVel, midiMod, pushPc);
else
ret |= _startChildMacro(SBig(mapping->objectId), macroStep, ticksPerSec,
midiKey, midiVel, midiMod, pushPc).operator bool();
mappingKey, midiVel, midiMod, pushPc).operator bool();
}
}
return ret;
@ -514,7 +533,7 @@ bool Voice::loadSoundObject(ObjectId objectId, int macroStep, double ticksPerSec
return false;
}
void Voice::keyOff()
void Voice::_macroKeyOff()
{
if (m_sustained)
m_sustainKeyOff = true;
@ -527,8 +546,38 @@ void Voice::keyOff()
m_voxState = VoiceState::KeyOff;
}
void Voice::keyOff()
{
if (m_keyoffTrap.macroId != 0xffff)
{
if (m_keyoffTrap.macroId == m_state.m_header.m_macroId)
{
m_state.m_pc.back().second = m_keyoffTrap.macroStep;
m_state.m_inWait = false;
}
else
loadSoundObject(m_keyoffTrap.macroId, m_keyoffTrap.macroStep,
m_state.m_ticksPerSec, m_state.m_initKey,
m_state.m_initVel, m_state.m_initMod);
return;
}
else
_macroKeyOff();
}
void Voice::message(int32_t val)
{
m_messageQueue.push_back(val);
if (m_messageTrap.macroId != 0xffff)
{
if (m_messageTrap.macroId == m_state.m_header.m_macroId)
m_state.m_pc.back().second = m_messageTrap.macroStep;
else
loadSoundObject(m_messageTrap.macroId, m_messageTrap.macroStep,
m_state.m_ticksPerSec, m_state.m_initKey,
m_state.m_initVel, m_state.m_initMod);
}
}
void Voice::startSample(int16_t sampId, int32_t offset)