mirror of https://github.com/AxioDL/amuse.git
Bug fixes in sequencer and SoundMacro event trapping
This commit is contained in:
parent
2c3c9d3885
commit
44bb7a155b
|
@ -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,8 +667,9 @@ struct AppCallback : boo::IApplicationCallback
|
|||
void EventCallback::charKeyDown(unsigned long charCode, boo::EModifierKey mods, bool isRepeat)
|
||||
{
|
||||
if (isRepeat)
|
||||
return;
|
||||
m_app.charKeyDown(charCode);
|
||||
m_app.charKeyDownRepeat(charCode);
|
||||
else
|
||||
m_app.charKeyDown(charCode);
|
||||
}
|
||||
|
||||
void EventCallback::charKeyUp(unsigned long charCode, boo::EModifierKey mods)
|
||||
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;}
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -7,15 +7,18 @@ void Envelope::reset(const ADSR* adsr)
|
|||
{
|
||||
m_phase = State::Attack;
|
||||
m_curADSR = adsr;
|
||||
m_curMs = 0;
|
||||
m_sustainFactor = (adsr->sustainCoarse * 6.25 + adsr->sustainFine * 0.0244) / 100.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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
m_phase = State::Decay;
|
||||
m_curMs = 0;
|
||||
if (m_curADSR->decayCoarse == 128)
|
||||
m_phase = State::Sustain;
|
||||
else
|
||||
m_phase = State::Decay;
|
||||
m_curMs = 0.0;
|
||||
m_releaseStartFactor = 1.f;
|
||||
return 1.f;
|
||||
}
|
||||
double attackFac = m_curMs / double(attack);
|
||||
if (attackFac >= 1.0)
|
||||
{
|
||||
m_phase = State::Decay;
|
||||
m_curMs = 0;
|
||||
if (m_curADSR->decayCoarse == 128)
|
||||
m_phase = State::Sustain;
|
||||
else
|
||||
m_phase = State::Decay;
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
{
|
||||
|
@ -193,7 +195,7 @@ bool SoundMacroState::advance(Voice& vox, double dt)
|
|||
m_pc.back().second = macroStep;
|
||||
else
|
||||
vox.loadSoundObject(macroId, macroStep, m_ticksPerSec,
|
||||
m_initKey, m_initVel, m_initMod);
|
||||
m_initKey, m_initVel, m_initMod);
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
233
lib/Voice.cpp
233
lib/Voice.cpp
|
@ -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,48 +332,91 @@ 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;
|
||||
|
||||
if (rem)
|
||||
{
|
||||
uint32_t block = m_curSamplePos / 14;
|
||||
uint32_t rem = m_curSamplePos % 14;
|
||||
uint32_t remCount = std::min(samplesRem, m_lastSamplePos - block * 14);
|
||||
uint32_t decSamples;
|
||||
|
||||
if (rem)
|
||||
switch (m_curFormat)
|
||||
{
|
||||
uint32_t remCount = std::min(samplesRem, m_lastSamplePos - block * 14);
|
||||
uint32_t decSamples;
|
||||
|
||||
switch (m_curFormat)
|
||||
{
|
||||
case SampleFormat::DSP:
|
||||
{
|
||||
decSamples = DSPDecompressFrameRanged(data, m_curSampleData + 8 * block,
|
||||
m_curSample->second.m_coefs,
|
||||
&m_prev1, &m_prev2, rem, remCount);
|
||||
break;
|
||||
}
|
||||
case SampleFormat::PCM:
|
||||
{
|
||||
const int16_t* pcm = reinterpret_cast<const int16_t*>(m_curSampleData);
|
||||
for (uint32_t i=0 ; i<remCount ; ++i)
|
||||
data[i] = SBig(pcm[m_curSamplePos+i]);
|
||||
decSamples = remCount;
|
||||
break;
|
||||
}
|
||||
default: return 0;
|
||||
}
|
||||
|
||||
/* Per-sample processing */
|
||||
for (int i=0 ; i<decSamples ; ++i)
|
||||
{
|
||||
++samplesProc;
|
||||
++m_curSamplePos;
|
||||
if (_advanceSample(data[i]))
|
||||
return samplesProc;
|
||||
}
|
||||
|
||||
samplesRem -= decSamples;
|
||||
data += decSamples;
|
||||
case SampleFormat::DSP:
|
||||
{
|
||||
decSamples = DSPDecompressFrameRanged(data, m_curSampleData + 8 * block,
|
||||
m_curSample->second.m_coefs,
|
||||
&m_prev1, &m_prev2, rem, remCount);
|
||||
break;
|
||||
}
|
||||
case SampleFormat::PCM:
|
||||
{
|
||||
const int16_t* pcm = reinterpret_cast<const int16_t*>(m_curSampleData);
|
||||
for (uint32_t i=0 ; i<remCount ; ++i)
|
||||
data[i] = SBig(pcm[m_curSamplePos+i]);
|
||||
decSamples = remCount;
|
||||
break;
|
||||
}
|
||||
default: return 0;
|
||||
}
|
||||
|
||||
/* Per-sample processing */
|
||||
for (int i=0 ; i<decSamples ; ++i)
|
||||
{
|
||||
++samplesProc;
|
||||
++m_curSamplePos;
|
||||
if (_advanceSample(data[i]))
|
||||
return samplesProc;
|
||||
}
|
||||
|
||||
samplesRem -= decSamples;
|
||||
data += decSamples;
|
||||
}
|
||||
|
||||
if (_checkSamplePos())
|
||||
{
|
||||
if (samplesRem)
|
||||
memset(data, 0, sizeof(int16_t) * samplesRem);
|
||||
return samples;
|
||||
}
|
||||
|
||||
while (samplesRem)
|
||||
{
|
||||
block = m_curSamplePos / 14;
|
||||
uint32_t remCount = std::min(samplesRem, m_lastSamplePos - block * 14);
|
||||
uint32_t decSamples;
|
||||
|
||||
switch (m_curFormat)
|
||||
{
|
||||
case SampleFormat::DSP:
|
||||
{
|
||||
decSamples = DSPDecompressFrame(data, m_curSampleData + 8 * block,
|
||||
m_curSample->second.m_coefs,
|
||||
&m_prev1, &m_prev2, remCount);
|
||||
break;
|
||||
}
|
||||
case SampleFormat::PCM:
|
||||
{
|
||||
const int16_t* pcm = reinterpret_cast<const int16_t*>(m_curSampleData);
|
||||
for (uint32_t i=0 ; i<remCount ; ++i)
|
||||
data[i] = SBig(pcm[m_curSamplePos+i]);
|
||||
decSamples = remCount;
|
||||
break;
|
||||
}
|
||||
default: return 0;
|
||||
}
|
||||
|
||||
/* Per-sample processing */
|
||||
for (int i=0 ; i<decSamples ; ++i)
|
||||
{
|
||||
++samplesProc;
|
||||
++m_curSamplePos;
|
||||
if (_advanceSample(data[i]))
|
||||
return samplesProc;
|
||||
}
|
||||
|
||||
samplesRem -= decSamples;
|
||||
data += decSamples;
|
||||
|
||||
if (_checkSamplePos())
|
||||
{
|
||||
|
@ -362,58 +424,15 @@ size_t Voice::supplyAudio(size_t samples, int16_t* data)
|
|||
memset(data, 0, sizeof(int16_t) * samplesRem);
|
||||
return samples;
|
||||
}
|
||||
|
||||
while (samplesRem)
|
||||
{
|
||||
block = m_curSamplePos / 14;
|
||||
uint32_t remCount = std::min(samplesRem, m_lastSamplePos - block * 14);
|
||||
uint32_t decSamples;
|
||||
|
||||
switch (m_curFormat)
|
||||
{
|
||||
case SampleFormat::DSP:
|
||||
{
|
||||
decSamples = DSPDecompressFrame(data, m_curSampleData + 8 * block,
|
||||
m_curSample->second.m_coefs,
|
||||
&m_prev1, &m_prev2, remCount);
|
||||
break;
|
||||
}
|
||||
case SampleFormat::PCM:
|
||||
{
|
||||
const int16_t* pcm = reinterpret_cast<const int16_t*>(m_curSampleData);
|
||||
for (uint32_t i=0 ; i<remCount ; ++i)
|
||||
data[i] = SBig(pcm[m_curSamplePos+i]);
|
||||
decSamples = remCount;
|
||||
break;
|
||||
}
|
||||
default: return 0;
|
||||
}
|
||||
|
||||
/* Per-sample processing */
|
||||
for (int i=0 ; i<decSamples ; ++i)
|
||||
{
|
||||
++samplesProc;
|
||||
++m_curSamplePos;
|
||||
if (_advanceSample(data[i]))
|
||||
return samplesProc;
|
||||
}
|
||||
|
||||
samplesRem -= decSamples;
|
||||
data += decSamples;
|
||||
|
||||
if (_checkSamplePos())
|
||||
{
|
||||
if (samplesRem)
|
||||
memset(data, 0, sizeof(int16_t) * samplesRem);
|
||||
return samples;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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)
|
||||
|
|
Loading…
Reference in New Issue