amuse/lib/Voice.cpp

1531 lines
43 KiB
C++
Raw Normal View History

#include "amuse/Voice.hpp"
2016-05-07 22:10:57 +00:00
#include "amuse/Submix.hpp"
#include "amuse/IBackendVoice.hpp"
2016-05-11 21:30:45 +00:00
#include "amuse/IBackendVoiceAllocator.hpp"
#include "amuse/AudioGroup.hpp"
2016-05-11 21:30:45 +00:00
#include "amuse/Common.hpp"
#include "amuse/Engine.hpp"
#include "amuse/DSPCodec.hpp"
#include "amuse/N64MusyXCodec.hpp"
2018-08-16 06:26:44 +00:00
#include "amuse/VolumeTable.hpp"
2016-05-11 21:30:45 +00:00
#include <cmath>
2017-12-29 07:57:22 +00:00
#include <cstring>
namespace amuse
{
2018-08-16 06:26:44 +00:00
float Voice::VolumeCache::getVolume(float vol, bool dls)
{
if (vol != m_curVolLUTKey || dls != m_curDLS)
{
m_curVolLUTKey = vol;
m_curDLS = dls;
if (dls)
m_curVolLUTVal = LookupDLSVolume(vol);
else
m_curVolLUTVal = LookupVolume(vol);
}
return m_curVolLUTVal;
}
void Voice::_destroy()
{
Entity::_destroy();
2016-05-14 06:33:21 +00:00
2018-07-29 03:37:06 +00:00
for (auto& vox : m_childVoices)
2016-05-14 06:33:21 +00:00
vox->_destroy();
2018-07-29 03:37:06 +00:00
m_studio.reset();
m_backendVoice.reset();
m_curSample.reset();
2018-08-10 06:19:23 +00:00
m_sequencer.reset();
2016-05-07 22:10:57 +00:00
}
2016-05-15 21:56:23 +00:00
Voice::~Voice()
{
2016-07-14 04:54:46 +00:00
// fprintf(stderr, "DEALLOC %d\n", m_vid);
2016-05-15 21:56:23 +00:00
}
2016-05-14 22:38:37 +00:00
2018-07-29 03:37:06 +00:00
Voice::Voice(Engine& engine, const AudioGroup& group, int groupId, int vid, bool emitter, ObjToken<Studio> studio)
: Entity(engine, group, groupId), m_vid(vid), m_emitter(emitter), m_studio(studio)
2016-05-07 22:10:57 +00:00
{
2016-07-14 04:54:46 +00:00
// fprintf(stderr, "ALLOC %d\n", m_vid);
2016-05-07 22:10:57 +00:00
}
2016-07-14 04:54:46 +00:00
Voice::Voice(Engine& engine, const AudioGroup& group, int groupId, ObjectId oid, int vid, bool emitter,
2018-07-29 03:37:06 +00:00
ObjToken<Studio> studio)
: Entity(engine, group, groupId, oid), m_vid(vid), m_emitter(emitter), m_studio(studio)
2016-05-07 22:10:57 +00:00
{
2016-07-14 04:54:46 +00:00
// fprintf(stderr, "ALLOC %d\n", m_vid);
}
void Voice::_macroSampleEnd()
{
if (m_sampleEndTrap.macroId != 0xffff)
{
if (m_sampleEndTrap.macroId == std::get<0>(m_state.m_pc.back()))
{
std::get<2>(m_state.m_pc.back()) = std::get<1>(m_state.m_pc.back())->assertPC(m_sampleEndTrap.macroStep);
m_state.m_inWait = false;
}
else
2018-07-15 06:10:50 +00:00
loadMacroObject(m_sampleEndTrap.macroId, m_sampleEndTrap.macroStep, m_state.m_ticksPerSec,
2016-07-14 04:54:46 +00:00
m_state.m_initKey, m_state.m_initVel, m_state.m_initMod);
}
else
m_state.sampleEndNotify(*this);
}
bool Voice::_checkSamplePos(bool& looped)
{
looped = false;
if (!m_curSample)
return true;
if (m_curSamplePos >= m_lastSamplePos)
{
2018-07-16 07:41:15 +00:00
if (m_curSample->m_loopLengthSamples)
{
/* Turn over looped sample */
2018-07-16 07:41:15 +00:00
m_curSamplePos = m_curSample->m_loopStartSample;
if (m_curFormat == SampleFormat::DSP)
{
2018-07-16 07:41:15 +00:00
m_prev1 = m_curSample->m_ADPCMParms.dsp.m_hist1;
m_prev2 = m_curSample->m_ADPCMParms.dsp.m_hist2;
}
looped = true;
}
else
{
/* Notify sample end */
_macroSampleEnd();
m_curSample = nullptr;
return true;
}
}
2016-05-15 21:56:23 +00:00
/* Looped samples issue sample end when ADSR envelope complete */
if (m_volAdsr.isComplete())
{
_macroSampleEnd();
2016-05-15 21:56:23 +00:00
m_curSample = nullptr;
return true;
}
return false;
}
void Voice::_doKeyOff()
{
m_voxState = VoiceState::KeyOff;
if (m_state.m_inWait && m_state.m_keyoffWait)
{
if (m_volAdsr.isAdsrSet() || m_state.m_useAdsrControllers)
m_volAdsr.keyOff(*this);
if (m_pitchAdsr.isAdsrSet())
m_pitchAdsr.keyOff();
}
else
{
m_volAdsr.keyOff(*this);
m_pitchAdsr.keyOff();
}
2016-05-14 22:38:37 +00:00
m_state.keyoffNotify(*this);
2016-05-11 21:30:45 +00:00
}
2016-05-17 03:47:12 +00:00
void Voice::_setTotalPitch(int32_t cents, bool slew)
2016-05-11 21:30:45 +00:00
{
2016-07-14 04:54:46 +00:00
// fprintf(stderr, "PITCH %d %d \n", cents, slew);
2018-07-30 06:20:03 +00:00
int32_t interval = clamp(0, cents, 12700) - m_curSample->getPitch() * 100;
double ratio = std::exp2(interval / 1200.0) * m_dopplerRatio;
2018-07-16 07:41:15 +00:00
m_sampleRate = m_curSample->m_sampleRate * ratio;
2016-05-17 03:47:12 +00:00
m_backendVoice->setPitchRatio(ratio, slew);
2016-05-11 21:30:45 +00:00
}
2016-05-15 06:48:26 +00:00
bool Voice::_isRecursivelyDead()
{
if (m_voxState != VoiceState::Dead)
return false;
2018-07-29 03:37:06 +00:00
for (auto& vox : m_childVoices)
2016-05-15 06:48:26 +00:00
if (!vox->_isRecursivelyDead())
return false;
return true;
}
2016-05-14 04:46:39 +00:00
void Voice::_bringOutYourDead()
2016-05-11 21:30:45 +00:00
{
2016-07-14 04:54:46 +00:00
for (auto it = m_childVoices.begin(); it != m_childVoices.end();)
2016-05-14 04:46:39 +00:00
{
Voice* vox = it->get();
vox->_bringOutYourDead();
2016-05-15 06:48:26 +00:00
if (vox->_isRecursivelyDead())
2016-05-14 04:46:39 +00:00
{
it = _destroyVoice(it);
2016-05-14 04:46:39 +00:00
continue;
}
++it;
}
2016-05-11 21:30:45 +00:00
}
2018-07-29 03:37:06 +00:00
ObjToken<Voice> Voice::_findVoice(int vid, ObjToken<Voice> thisPtr)
2016-05-14 06:33:21 +00:00
{
if (m_vid == vid)
2018-07-29 03:37:06 +00:00
return thisPtr;
2016-05-14 06:33:21 +00:00
2018-07-29 03:37:06 +00:00
for (ObjToken<Voice>& vox : m_childVoices)
2016-05-14 06:33:21 +00:00
{
2018-07-29 03:37:06 +00:00
ObjToken<Voice> ret = vox->_findVoice(vid, vox);
2016-05-14 06:33:21 +00:00
if (ret)
return ret;
}
return {};
}
2016-05-15 21:56:23 +00:00
std::unique_ptr<int8_t[]>& Voice::_ensureCtrlVals()
{
if (m_ctrlValsSelf)
return m_ctrlValsSelf;
m_ctrlValsSelf.reset(new int8_t[128]);
memset(m_ctrlValsSelf.get(), 0, 128);
return m_ctrlValsSelf;
}
2018-07-29 03:37:06 +00:00
std::list<ObjToken<Voice>>::iterator Voice::_allocateVoice(double sampleRate, bool dynamicPitch)
2016-05-14 04:46:39 +00:00
{
2018-07-30 06:20:03 +00:00
amuse::ObjToken<Voice> tok =
MakeObj<Voice>(m_engine, m_audioGroup, m_groupId, m_engine.m_nextVid++, m_emitter, m_studio);
auto it = m_childVoices.emplace(m_childVoices.end(), tok);
m_childVoices.back()->m_backendVoice =
m_engine.getBackend().allocateVoice(*m_childVoices.back(), sampleRate, dynamicPitch);
return it;
2016-05-14 04:46:39 +00:00
}
2018-07-29 03:37:06 +00:00
std::list<ObjToken<Voice>>::iterator Voice::_destroyVoice(std::list<ObjToken<Voice>>::iterator it)
2016-05-11 21:30:45 +00:00
{
if ((*it)->m_destroyed)
2016-05-16 22:13:24 +00:00
return m_childVoices.begin();
(*it)->_destroy();
return m_childVoices.erase(it);
2016-05-11 21:30:45 +00:00
}
2016-07-14 04:54:46 +00:00
template <typename T>
static T ApplyVolume(float vol, T samp)
2016-05-21 01:15:31 +00:00
{
return samp * vol;
2016-05-21 01:15:31 +00:00
}
2016-07-14 04:54:46 +00:00
void Voice::_procSamplePre(int16_t& samp)
2016-05-11 21:30:45 +00:00
{
2016-05-22 08:35:55 +00:00
double dt;
/* Block linearized will use a larger `dt` for amplitude sampling;
* significantly reducing the processing expense */
switch (m_engine.m_ampMode)
{
case AmplitudeMode::PerSample:
m_voiceSamples += 1;
dt = 1.0 / m_sampleRate;
break;
case AmplitudeMode::BlockLinearized:
{
uint32_t rem = m_voiceSamples % 160;
m_voiceSamples += 1;
2016-07-14 04:54:46 +00:00
dt = m_sampleRate * 160;
2016-05-22 08:35:55 +00:00
if (rem != 0)
{
/* Lerp within 160-sample block */
float t = rem / 160.f;
float l = clamp(0.f, m_lastLevel * (1.f - t) + m_nextLevel * t, 1.f);
2016-05-22 08:35:55 +00:00
/* Apply total volume to sample using decibel scale */
2018-08-16 06:26:44 +00:00
samp = ApplyVolume(m_lerpedCache.getVolume(l * m_engine.m_masterVolume, m_dlsVol), samp);
return;
2016-05-22 08:35:55 +00:00
}
dt = 160.0 / m_sampleRate;
break;
}
}
2016-05-11 21:30:45 +00:00
m_voiceTime += dt;
/* Process active envelope */
if (m_envelopeTime >= 0.0)
{
m_envelopeTime += dt;
float start = m_envelopeStart;
float end = m_envelopeEnd;
2016-07-14 04:54:46 +00:00
float t = clamp(0.f, float(m_envelopeTime / m_envelopeDur), 1.f);
2018-07-17 04:48:38 +00:00
if (m_envelopeCurve && m_envelopeCurve->data.size() >= 128)
t = m_envelopeCurve->data[t * 127.f] / 127.f;
m_curVol = clamp(0.f, (start * (1.0f - t)) + (end * t), 1.f);
2016-07-14 04:54:46 +00:00
// printf("%d %f\n", m_vid, m_curVol);
2016-05-11 21:30:45 +00:00
/* Done with envelope */
if (m_envelopeTime > m_envelopeDur)
m_envelopeTime = -1.f;
}
/* Dynamically evaluate per-sample SoundMacro parameters */
/* Process user volume slew */
if (m_engine.m_ampMode == AmplitudeMode::PerSample)
{
if (m_targetUserVol != m_curUserVol)
{
float samplesPer5Ms = m_sampleRate * 5.f / 1000.f;
if (samplesPer5Ms > 1.f)
{
float adjRate = 1.f / samplesPer5Ms;
if (m_targetUserVol < m_curUserVol)
{
m_curUserVol -= adjRate;
if (m_targetUserVol > m_curUserVol)
m_curUserVol = m_targetUserVol;
}
else
{
m_curUserVol += adjRate;
if (m_targetUserVol < m_curUserVol)
m_curUserVol = m_targetUserVol;
}
}
else
m_curUserVol = m_targetUserVol;
}
}
else
m_curUserVol = m_targetUserVol;
2016-05-11 21:30:45 +00:00
/* Factor in ADSR envelope state */
float adsr = m_volAdsr.advance(dt, *this);
2016-05-22 08:35:55 +00:00
m_lastLevel = m_nextLevel;
2016-07-14 04:54:46 +00:00
m_nextLevel = m_curUserVol * m_curVol * adsr * (m_state.m_curVel / 127.f);
2016-05-11 21:30:45 +00:00
/* Apply tremolo */
if (m_state.m_tremoloSel && (m_tremoloScale || m_tremoloModScale))
{
2018-07-30 06:20:03 +00:00
float t = m_state.m_tremoloSel.evaluate(m_voiceTime, *this, m_state) / 127.f;
2016-05-11 21:30:45 +00:00
if (m_tremoloScale && m_tremoloModScale)
{
float fac = (1.0f - t) + (m_tremoloScale * t);
2018-07-30 06:20:03 +00:00
float modT = m_state.m_modWheelSel ? (m_state.m_modWheelSel.evaluate(m_voiceTime, *this, m_state) / 127.f)
2016-07-14 04:54:46 +00:00
: (getCtrlValue(1) / 127.f);
2016-05-11 21:30:45 +00:00
float modFac = (1.0f - modT) + (m_tremoloModScale * modT);
2016-05-22 08:35:55 +00:00
m_nextLevel *= fac * modFac;
2016-05-11 21:30:45 +00:00
}
else if (m_tremoloScale)
{
float fac = (1.0f - t) + (m_tremoloScale * t);
2016-05-22 08:35:55 +00:00
m_nextLevel *= fac;
2016-05-11 21:30:45 +00:00
}
else if (m_tremoloModScale)
{
2018-07-30 06:20:03 +00:00
float modT = m_state.m_modWheelSel ? (m_state.m_modWheelSel.evaluate(m_voiceTime, *this, m_state) / 127.f)
2016-07-14 04:54:46 +00:00
: (getCtrlValue(1) / 127.f);
2016-05-11 21:30:45 +00:00
float modFac = (1.0f - modT) + (m_tremoloModScale * modT);
2016-05-22 08:35:55 +00:00
m_nextLevel *= modFac;
2016-05-11 21:30:45 +00:00
}
}
m_nextLevel = clamp(0.f, m_nextLevel, 1.f);
2016-05-22 08:35:55 +00:00
/* Apply total volume to sample using decibel scale */
2018-08-16 06:26:44 +00:00
samp = ApplyVolume(m_nextLevelCache.getVolume(m_nextLevel * m_engine.m_masterVolume, m_dlsVol), samp);
2016-07-14 04:54:46 +00:00
}
template <typename T>
T Voice::_procSampleMaster(double time, T samp)
{
2018-07-30 06:20:03 +00:00
float evalVol = m_state.m_volumeSel ? (m_state.m_volumeSel.evaluate(time, *this, m_state) / 127.f) : 1.f;
2018-08-16 06:26:44 +00:00
return ApplyVolume(m_masterCache.getVolume(clamp(0.f, evalVol, 1.f), m_dlsVol), samp);
2016-07-14 04:54:46 +00:00
}
template <typename T>
T Voice::_procSampleAuxA(double time, T samp)
{
2018-07-30 06:20:03 +00:00
float evalVol = m_state.m_volumeSel ? (m_state.m_volumeSel.evaluate(time, *this, m_state) / 127.f) : 1.f;
evalVol *= m_state.m_reverbSel ? (m_state.m_reverbSel.evaluate(time, *this, m_state) / 127.f) : m_curReverbVol;
evalVol += m_state.m_preAuxASel ? (m_state.m_preAuxASel.evaluate(time, *this, m_state) / 127.f) : 0.f;
2018-08-16 06:26:44 +00:00
return ApplyVolume(m_auxACache.getVolume(clamp(0.f, evalVol, 1.f), m_dlsVol), samp);
2016-07-14 04:54:46 +00:00
}
template <typename T>
T Voice::_procSampleAuxB(double time, T samp)
{
2018-07-30 06:20:03 +00:00
float evalVol = m_state.m_volumeSel ? (m_state.m_volumeSel.evaluate(time, *this, m_state) / 127.f) : 1.f;
evalVol *= m_state.m_postAuxB ? (m_state.m_postAuxB.evaluate(time, *this, m_state) / 127.f) : m_curAuxBVol;
evalVol += m_state.m_preAuxBSel ? (m_state.m_preAuxBSel.evaluate(time, *this, m_state) / 127.f) : 0.f;
2018-08-16 06:26:44 +00:00
return ApplyVolume(m_auxBCache.getVolume(clamp(0.f, evalVol, 1.f), m_dlsVol), samp);
}
uint32_t Voice::_GetBlockSampleCount(SampleFormat fmt)
{
switch (fmt)
{
default:
return 1;
2018-07-16 07:41:15 +00:00
case SampleFormat::DSP:
case SampleFormat::DSP_DRUM:
return 14;
2018-07-16 07:41:15 +00:00
case SampleFormat::N64:
return 64;
}
}
static float TriangleWave(float t)
{
t = std::fmod(t, 1.f);
if (t < 0.25f)
return t / 0.25f;
if (t >= 0.75f)
return (t - 0.75f) / 0.25f - 1.f;
return (t - 0.25f) / 0.5f * -2.f + 1.f;
}
void Voice::preSupplyAudio(double dt)
{
/* Process SoundMacro; bootstrapping sample if needed */
bool dead = m_state.advance(*this, dt);
/* Process per-block evaluators here */
if (m_state.m_pedalSel)
{
2018-07-30 06:20:03 +00:00
bool pedal = m_state.m_pedalSel.evaluate(m_voiceTime, *this, m_state) >= 64.f;
if (pedal != m_sustained)
setPedal(pedal);
}
bool panDirty = false;
if (m_state.m_panSel)
{
2018-07-30 06:20:03 +00:00
float evalPan = (m_state.m_panSel.evaluate(m_voiceTime, *this, m_state) - 64.f) / 63.f;
evalPan = clamp(-1.f, evalPan, 1.f);
if (evalPan != m_curPan)
{
m_curPan = evalPan;
panDirty = true;
}
}
if (m_state.m_spanSel)
{
2018-07-30 06:20:03 +00:00
float evalSpan = (m_state.m_spanSel.evaluate(m_voiceTime, *this, m_state) - 64.f) / 63.f;
evalSpan = clamp(-1.f, evalSpan, 1.f);
if (evalSpan != m_curSpan)
{
m_curSpan = evalSpan;
panDirty = true;
}
}
if (panDirty)
_setPan(m_curPan);
if (m_state.m_pitchWheelSel)
2018-07-30 06:20:03 +00:00
{
float evalPWheel = (m_state.m_pitchWheelSel.evaluate(m_voiceTime, *this, m_state) - 64.f) / 63.f;
_setPitchWheel(clamp(-1.f, evalPWheel, 1.f));
}
2016-05-11 21:30:45 +00:00
/* Process active pan-sweep */
bool refresh = false;
2018-09-03 00:43:05 +00:00
if (!m_panningQueue.empty())
2016-05-11 21:30:45 +00:00
{
2018-09-03 00:43:05 +00:00
Panning& p = m_panningQueue.front();
p.m_time += dt;
float start = (p.m_pos - 64) / 64.f;
float end = (p.m_pos + p.m_width - 64) / 64.f;
float t = clamp(0.f, p.m_time / p.m_dur, 1.f);
2016-05-21 21:40:03 +00:00
_setPan((start * (1.0f - t)) + (end * t));
2016-05-11 21:30:45 +00:00
refresh = true;
/* Done with panning */
2018-09-03 00:43:05 +00:00
if (p.m_time > p.m_dur)
m_panningQueue.pop();
2016-05-11 21:30:45 +00:00
}
/* Process active span-sweep */
2018-09-03 00:43:05 +00:00
if (!m_spanningQueue.empty())
2016-05-11 21:30:45 +00:00
{
2018-09-03 00:43:05 +00:00
Panning& s = m_spanningQueue.front();
s.m_time += dt;
float start = (s.m_pos - 64) / 64.f;
float end = (s.m_pos + s.m_width - 64) / 64.f;
float t = clamp(0.f, s.m_time / s.m_dur, 1.f);
2016-05-21 21:40:03 +00:00
_setSurroundPan((start * (1.0f - t)) + (end * t));
2016-05-11 21:30:45 +00:00
refresh = true;
/* Done with spanning */
2018-09-03 00:43:05 +00:00
if (s.m_time > s.m_dur)
m_spanningQueue.pop();
2016-05-11 21:30:45 +00:00
}
/* Calculate total pitch */
int32_t newPitch = m_curPitch;
refresh |= m_pitchDirty;
2016-05-11 21:30:45 +00:00
m_pitchDirty = false;
2016-06-01 21:17:16 +00:00
if (m_portamentoTime >= 0.f)
{
m_portamentoTime += dt;
2016-07-14 04:54:46 +00:00
float t = clamp(0.f, m_portamentoTime / m_state.m_portamentoTime, 1.f);
2016-06-01 21:17:16 +00:00
newPitch = (m_curPitch * (1.0f - t)) + (m_portamentoTarget * t);
refresh = true;
/* Done with portamento */
if (m_portamentoTime > m_state.m_portamentoTime)
{
m_portamentoTime = -1.f;
m_curPitch = m_portamentoTarget;
}
}
2016-05-11 21:30:45 +00:00
if (m_pitchEnv)
{
newPitch += m_pitchAdsr.advance(dt) * m_pitchEnvRange;
refresh = true;
2016-05-11 21:30:45 +00:00
}
2018-05-19 05:15:12 +00:00
/* Process vibrato */
if (m_vibratoTime >= 0.f)
{
m_vibratoTime += dt;
float vibrato = TriangleWave(m_vibratoTime / m_vibratoPeriod);
2018-05-19 05:15:12 +00:00
if (m_vibratoModWheel)
newPitch += m_vibratoModLevel * vibrato * (m_state.m_curMod / 127.f);
2018-05-19 05:15:12 +00:00
else
newPitch += m_vibratoLevel * vibrato;
refresh = true;
}
2016-05-11 21:30:45 +00:00
/* Process pitch sweep 1 */
2016-05-17 03:23:35 +00:00
if (m_pitchSweep1It < m_pitchSweep1Times)
2016-05-11 21:30:45 +00:00
{
2016-05-17 03:23:35 +00:00
++m_pitchSweep1It;
m_pitchSweep1 = m_pitchSweep1Add * m_pitchSweep1It;
refresh = true;
}
else if (m_pitchSweep1Times != 0)
{
m_pitchSweep1It = 0;
m_pitchSweep1 = 0;
refresh = true;
2016-05-11 21:30:45 +00:00
}
/* Process pitch sweep 2 */
2016-05-17 03:23:35 +00:00
if (m_pitchSweep2It < m_pitchSweep2Times)
2016-05-11 21:30:45 +00:00
{
2016-05-17 03:23:35 +00:00
++m_pitchSweep2It;
m_pitchSweep2 = m_pitchSweep2Add * m_pitchSweep2It;
refresh = true;
}
else if (m_pitchSweep2Times != 0)
{
m_pitchSweep2It = 0;
m_pitchSweep2 = 0;
2016-05-11 21:30:45 +00:00
refresh = true;
}
if (m_curSample && refresh)
{
_setTotalPitch(newPitch + m_pitchSweep1 + m_pitchSweep2 + m_pitchWheelVal, m_needsSlew);
m_needsSlew = true;
}
2016-07-14 04:54:46 +00:00
if (dead && (!m_curSample || m_voxState == VoiceState::KeyOff) && m_sampleEndTrap.macroId == 0xffff &&
m_messageTrap.macroId == 0xffff && (!m_curSample || (m_curSample && m_volAdsr.isComplete())))
{
m_voxState = VoiceState::Dead;
m_backendVoice->stop();
}
}
size_t Voice::supplyAudio(size_t samples, int16_t* data)
{
uint32_t samplesRem = samples;
2016-05-14 22:38:37 +00:00
if (m_curSample)
{
uint32_t blockSampleCount = _GetBlockSampleCount(m_curFormat);
uint32_t block;
bool looped = true;
while (looped && samplesRem)
{
block = m_curSamplePos / blockSampleCount;
uint32_t rem = m_curSamplePos % blockSampleCount;
if (rem)
{
uint32_t remCount = std::min(samplesRem, m_lastSamplePos - block * blockSampleCount);
uint32_t decSamples;
switch (m_curFormat)
{
case SampleFormat::DSP:
{
2016-07-14 04:54:46 +00:00
decSamples =
2018-07-16 07:41:15 +00:00
DSPDecompressFrameRanged(data, m_curSampleData + 8 * block,
m_curSample->m_ADPCMParms.dsp.m_coefs,
2016-07-14 04:54:46 +00:00
&m_prev1, &m_prev2, rem, remCount);
break;
}
case SampleFormat::N64:
{
decSamples = N64MusyXDecompressFrameRanged(data, m_curSampleData + 256 + 40 * block,
2018-07-16 07:41:15 +00:00
m_curSample->m_ADPCMParms.vadpcm.m_coefs, rem, remCount);
break;
}
case SampleFormat::PCM:
{
const int16_t* pcm = reinterpret_cast<const int16_t*>(m_curSampleData);
remCount = std::min(samplesRem, m_lastSamplePos - m_curSamplePos);
2016-07-14 04:54:46 +00:00
for (uint32_t i = 0; i < remCount; ++i)
data[i] = SBig(pcm[m_curSamplePos + i]);
decSamples = remCount;
break;
}
case SampleFormat::PCM_PC:
{
const int16_t* pcm = reinterpret_cast<const int16_t*>(m_curSampleData);
remCount = std::min(samplesRem, m_lastSamplePos - m_curSamplePos);
2016-06-08 04:33:15 +00:00
memmove(data, pcm + m_curSamplePos, remCount * sizeof(int16_t));
decSamples = remCount;
break;
}
default:
memset(data, 0, sizeof(int16_t) * samples);
return samples;
}
/* Per-sample processing */
2016-07-14 04:54:46 +00:00
for (uint32_t i = 0; i < decSamples; ++i)
{
++m_curSamplePos;
2016-07-14 04:54:46 +00:00
_procSamplePre(data[i]);
}
samplesRem -= decSamples;
data += decSamples;
}
if (_checkSamplePos(looped))
{
if (samplesRem)
memset(data, 0, sizeof(int16_t) * samplesRem);
return samples;
}
if (looped)
continue;
2016-05-11 21:30:45 +00:00
while (samplesRem)
2016-05-11 21:30:45 +00:00
{
block = m_curSamplePos / blockSampleCount;
uint32_t remCount = std::min(samplesRem, m_lastSamplePos - block * blockSampleCount);
uint32_t decSamples;
2016-05-11 21:30:45 +00:00
switch (m_curFormat)
{
2016-07-14 04:54:46 +00:00
case SampleFormat::DSP:
{
2018-07-16 07:41:15 +00:00
decSamples = DSPDecompressFrame(data, m_curSampleData + 8 * block,
m_curSample->m_ADPCMParms.dsp.m_coefs, &m_prev1, &m_prev2, remCount);
2016-07-14 04:54:46 +00:00
break;
}
case SampleFormat::N64:
{
decSamples = N64MusyXDecompressFrame(data, m_curSampleData + 256 + 40 * block,
2018-07-16 07:41:15 +00:00
m_curSample->m_ADPCMParms.vadpcm.m_coefs, remCount);
2016-07-14 04:54:46 +00:00
break;
}
case SampleFormat::PCM:
{
const int16_t* pcm = reinterpret_cast<const int16_t*>(m_curSampleData);
remCount = std::min(samplesRem, m_lastSamplePos - m_curSamplePos);
for (uint32_t i = 0; i < remCount; ++i)
data[i] = SBig(pcm[m_curSamplePos + i]);
decSamples = remCount;
break;
}
case SampleFormat::PCM_PC:
{
const int16_t* pcm = reinterpret_cast<const int16_t*>(m_curSampleData);
remCount = std::min(samplesRem, m_lastSamplePos - m_curSamplePos);
memmove(data, pcm + m_curSamplePos, remCount * sizeof(int16_t));
decSamples = remCount;
break;
}
default:
memset(data, 0, sizeof(int16_t) * samples);
return samples;
}
/* Per-sample processing */
2016-07-14 04:54:46 +00:00
for (uint32_t i = 0; i < decSamples; ++i)
{
++m_curSamplePos;
2016-07-14 04:54:46 +00:00
_procSamplePre(data[i]);
}
samplesRem -= decSamples;
data += decSamples;
if (_checkSamplePos(looped))
{
if (samplesRem)
memset(data, 0, sizeof(int16_t) * samplesRem);
return samples;
}
if (looped)
break;
}
}
}
else
2018-09-03 00:43:05 +00:00
{
_macroSampleEnd();
memset(data, 0, sizeof(int16_t) * samples);
2018-09-03 00:43:05 +00:00
}
if (m_voxState == VoiceState::Dead)
2018-07-29 03:37:06 +00:00
m_curSample.reset();
return samples;
}
2016-07-14 04:54:46 +00:00
void Voice::routeAudio(size_t frames, double dt, int busId, int16_t* in, int16_t* out)
{
dt /= double(frames);
switch (busId)
{
case 0:
default:
{
for (uint32_t i = 0; i < frames; ++i)
out[i] = _procSampleMaster(dt * i + m_voiceTime, in[i]);
break;
}
case 1:
{
for (uint32_t i = 0; i < frames; ++i)
out[i] = _procSampleAuxA(dt * i + m_voiceTime, in[i]);
break;
}
case 2:
{
for (uint32_t i = 0; i < frames; ++i)
out[i] = _procSampleAuxB(dt * i + m_voiceTime, in[i]);
break;
}
}
}
void Voice::routeAudio(size_t frames, double dt, int busId, int32_t* in, int32_t* out)
{
dt /= double(frames);
switch (busId)
{
case 0:
default:
{
for (uint32_t i = 0; i < frames; ++i)
out[i] = _procSampleMaster(dt * i + m_voiceTime, in[i]);
break;
}
case 1:
{
for (uint32_t i = 0; i < frames; ++i)
out[i] = _procSampleAuxA(dt * i + m_voiceTime, in[i]);
break;
}
case 2:
{
for (uint32_t i = 0; i < frames; ++i)
out[i] = _procSampleAuxB(dt * i + m_voiceTime, in[i]);
break;
}
}
}
void Voice::routeAudio(size_t frames, double dt, int busId, float* in, float* out)
{
dt /= double(frames);
switch (busId)
{
case 0:
default:
{
for (uint32_t i = 0; i < frames; ++i)
out[i] = _procSampleMaster(dt * i + m_voiceTime, in[i]);
break;
}
case 1:
{
for (uint32_t i = 0; i < frames; ++i)
out[i] = _procSampleAuxA(dt * i + m_voiceTime, in[i]);
break;
}
case 2:
{
for (uint32_t i = 0; i < frames; ++i)
out[i] = _procSampleAuxB(dt * i + m_voiceTime, in[i]);
break;
}
}
}
2016-05-11 21:30:45 +00:00
int Voice::maxVid() const
{
int maxVid = m_vid;
2018-07-29 03:37:06 +00:00
for (const ObjToken<Voice>& vox : m_childVoices)
2016-05-14 04:46:39 +00:00
maxVid = std::max(maxVid, vox->maxVid());
2016-05-11 21:30:45 +00:00
return maxVid;
}
2018-07-29 03:37:06 +00:00
ObjToken<Voice> Voice::_startChildMacro(ObjectId macroId, int macroStep, double ticksPerSec, uint8_t midiKey,
uint8_t midiVel, uint8_t midiMod, bool pushPc)
{
2018-07-29 03:37:06 +00:00
std::list<ObjToken<Voice>>::iterator vox = _allocateVoice(NativeSampleRate, true);
2018-07-15 06:10:50 +00:00
if (!(*vox)->loadMacroObject(macroId, macroStep, ticksPerSec, midiKey, midiVel, midiMod, pushPc))
2016-05-14 06:33:21 +00:00
{
_destroyVoice(vox);
2016-05-14 06:33:21 +00:00
return {};
}
(*vox)->setVolume(m_targetUserVol);
2018-07-29 03:37:06 +00:00
(*vox)->setPan(m_curPan);
(*vox)->setSurroundPan(m_curSpan);
2018-08-07 07:09:23 +00:00
if (m_extCtrlVals)
(*vox)->installCtrlValues(m_extCtrlVals);
return *vox;
}
2018-07-29 03:37:06 +00:00
ObjToken<Voice> Voice::startChildMacro(int8_t addNote, ObjectId macroId, int macroStep)
{
2016-07-14 04:54:46 +00:00
return _startChildMacro(macroId, macroStep, 1000.0, m_state.m_initKey + addNote, m_state.m_initVel,
m_state.m_initMod);
2016-05-15 06:48:26 +00:00
}
2016-05-11 21:30:45 +00:00
2018-07-15 06:10:50 +00:00
bool Voice::_loadSoundMacro(SoundMacroId id, const SoundMacro* macroData, int macroStep, double ticksPerSec,
uint8_t midiKey, uint8_t midiVel, uint8_t midiMod, bool pushPc)
2016-05-15 06:48:26 +00:00
{
2018-07-15 06:10:50 +00:00
m_objectId = id;
2016-05-11 21:30:45 +00:00
if (m_state.m_pc.empty())
m_state.initialize(id, macroData, macroStep, ticksPerSec, midiKey, midiVel, midiMod);
2016-05-11 21:30:45 +00:00
else
{
if (!pushPc)
m_state.m_pc.clear();
m_state.m_pc.emplace_back(id, macroData, macroData->assertPC(macroStep));
2016-05-11 21:30:45 +00:00
}
2016-05-14 04:46:39 +00:00
m_voxState = VoiceState::Playing;
2016-05-14 22:38:37 +00:00
m_backendVoice->start();
2016-05-11 21:30:45 +00:00
return true;
}
2018-07-15 06:10:50 +00:00
bool Voice::_loadKeymap(const Keymap* keymap, double ticksPerSec,
uint8_t midiKey, uint8_t midiVel, uint8_t midiMod, bool pushPc)
2016-05-15 06:48:26 +00:00
{
const Keymap& km = keymap[midiKey];
midiKey += km.transpose;
2018-07-15 06:10:50 +00:00
bool ret = loadMacroObject(km.macro.id, 0, ticksPerSec, midiKey, midiVel, midiMod, pushPc);
2016-05-21 21:40:03 +00:00
m_curVol = 1.f;
2018-08-06 04:20:42 +00:00
if (km.pan == -128)
{
_setSurroundPan(1.f);
}
else
{
_setPan((km.pan - 64) / 64.f);
_setSurroundPan(-1.f);
}
2016-05-21 21:40:03 +00:00
return ret;
2016-05-15 06:48:26 +00:00
}
2018-07-15 06:10:50 +00:00
bool Voice::_loadLayer(const std::vector<LayerMapping>& layer, double ticksPerSec,
2016-05-15 06:48:26 +00:00
uint8_t midiKey, uint8_t midiVel, uint8_t midiMod, bool pushPc)
{
bool ret = false;
for (const LayerMapping& mapping : layer)
2016-05-15 06:48:26 +00:00
{
if (midiKey >= mapping.keyLo && midiKey <= mapping.keyHi)
2016-05-15 06:48:26 +00:00
{
uint8_t mappingKey = midiKey + mapping.transpose;
2016-05-15 06:48:26 +00:00
if (m_voxState != VoiceState::Playing)
2016-05-21 21:40:03 +00:00
{
2018-07-15 06:10:50 +00:00
ret |= loadMacroObject(mapping.macro.id, 0, ticksPerSec, mappingKey, midiVel, midiMod, pushPc);
2018-08-07 07:09:23 +00:00
m_curUserVol = m_targetUserVol = mapping.volume / 127.f;
_setPan((mapping.pan - 64) / 64.f);
_setSurroundPan((mapping.span - 64) / 64.f);
2016-05-21 21:40:03 +00:00
}
2016-05-15 06:48:26 +00:00
else
2016-05-21 21:40:03 +00:00
{
2018-07-29 03:37:06 +00:00
ObjToken<Voice> vox =
2018-07-15 06:10:50 +00:00
_startChildMacro(mapping.macro.id, 0, ticksPerSec, mappingKey, midiVel, midiMod, pushPc);
2016-05-21 21:40:03 +00:00
if (vox)
{
2018-08-07 07:09:23 +00:00
vox->m_curUserVol = vox->m_targetUserVol = mapping.volume / 127.f;
vox->_setPan((mapping.pan - 64) / 64.f);
vox->_setSurroundPan((mapping.span - 64) / 64.f);
2016-05-21 21:40:03 +00:00
ret = true;
}
}
2016-05-15 06:48:26 +00:00
}
}
return ret;
}
2018-07-15 06:10:50 +00:00
bool Voice::loadMacroObject(SoundMacroId macroId, int macroStep, double ticksPerSec, uint8_t midiKey, uint8_t midiVel,
2016-07-14 04:54:46 +00:00
uint8_t midiMod, bool pushPc)
2016-05-15 06:48:26 +00:00
{
2017-11-28 02:44:35 +00:00
if (m_destroyed)
return false;
2018-07-15 06:10:50 +00:00
const SoundMacro* macroData = m_audioGroup.getPool().soundMacro(macroId);
2016-05-15 06:48:26 +00:00
if (macroData)
2018-07-15 06:10:50 +00:00
return _loadSoundMacro(macroId, macroData, macroStep, ticksPerSec, midiKey, midiVel, midiMod, pushPc);
return false;
}
2016-05-15 06:48:26 +00:00
2018-07-31 08:04:43 +00:00
bool Voice::loadMacroObject(const SoundMacro* macro, int macroStep, double ticksPerSec,
uint8_t midiKey, uint8_t midiVel, uint8_t midiMod, bool pushPc)
{
if (m_destroyed)
return false;
if (macro)
return _loadSoundMacro({}, macro, macroStep, ticksPerSec, midiKey, midiVel, midiMod, pushPc);
return false;
}
2018-07-15 06:10:50 +00:00
bool Voice::loadPageObject(ObjectId objectId, double ticksPerSec, uint8_t midiKey, uint8_t midiVel, uint8_t midiMod)
{
if (m_destroyed)
return false;
if (objectId.id & 0x8000)
2017-11-28 02:44:35 +00:00
{
2018-07-15 06:10:50 +00:00
const std::vector<LayerMapping>* layer = m_audioGroup.getPool().layer(objectId);
if (layer)
return _loadLayer(*layer, ticksPerSec, midiKey, midiVel, midiMod);
2017-11-28 02:44:35 +00:00
}
2018-08-07 07:09:23 +00:00
else if (objectId.id & 0x4000)
2017-11-28 02:44:35 +00:00
{
2018-07-15 06:10:50 +00:00
const Keymap* keymap = m_audioGroup.getPool().keymap(objectId);
if (keymap)
return _loadKeymap(keymap, ticksPerSec, midiKey, midiVel, midiMod);
2017-11-28 02:44:35 +00:00
}
2018-08-07 07:09:23 +00:00
else
{
const SoundMacro* sm = m_audioGroup.getPool().soundMacro(objectId);
if (sm)
return _loadSoundMacro(objectId, sm, 0, ticksPerSec, midiKey, midiVel, midiMod);
}
2016-05-15 06:48:26 +00:00
return false;
}
void Voice::_macroKeyOff()
2016-05-09 07:22:58 +00:00
{
if (m_voxState == VoiceState::Playing)
{
if (m_sustained)
m_sustainKeyOff = true;
else
_doKeyOff();
}
2016-05-09 07:22:58 +00:00
}
void Voice::keyOff()
{
2017-11-28 02:44:35 +00:00
if (m_destroyed)
return;
if (m_keyoffTrap.macroId != 0xffff)
{
if (m_keyoffTrap.macroId == std::get<0>(m_state.m_pc.back()))
{
std::get<2>(m_state.m_pc.back()) = std::get<1>(m_state.m_pc.back())->assertPC(m_keyoffTrap.macroStep);
m_state.m_inWait = false;
}
else
2018-07-15 06:10:50 +00:00
loadMacroObject(m_keyoffTrap.macroId, m_keyoffTrap.macroStep, m_state.m_ticksPerSec, m_state.m_initKey,
m_state.m_initVel, m_state.m_initMod);
}
2018-08-10 08:11:08 +00:00
else
_macroKeyOff();
2016-05-19 05:27:39 +00:00
2018-07-29 03:37:06 +00:00
for (const ObjToken<Voice>& vox : m_childVoices)
2016-05-19 05:27:39 +00:00
vox->keyOff();
}
2016-05-09 07:22:58 +00:00
void Voice::message(int32_t val)
{
2017-11-28 02:44:35 +00:00
if (m_destroyed)
return;
m_latestMessage = val;
if (m_messageTrap.macroId != 0xffff)
{
if (m_messageTrap.macroId == std::get<0>(m_state.m_pc.back()))
std::get<2>(m_state.m_pc.back()) = std::get<1>(m_state.m_pc.back())->assertPC(m_messageTrap.macroStep);
else
2018-07-15 06:10:50 +00:00
loadMacroObject(m_messageTrap.macroId, m_messageTrap.macroStep, m_state.m_ticksPerSec, m_state.m_initKey,
m_state.m_initVel, m_state.m_initMod);
}
2016-05-09 07:22:58 +00:00
}
2018-07-16 07:41:15 +00:00
void Voice::startSample(SampleId sampId, int32_t offset)
{
2017-11-28 02:44:35 +00:00
if (m_destroyed)
return;
2018-07-29 03:37:06 +00:00
if (const SampleEntry* sample = m_audioGroup.getSample(sampId))
{
2018-07-29 03:37:06 +00:00
std::tie(m_curSample, m_curSampleData) = m_audioGroup.getSampleData(sampId, sample);
2018-07-16 07:41:15 +00:00
m_sampleRate = m_curSample->m_sampleRate;
2018-07-30 06:20:03 +00:00
m_curPitch = m_curSample->getPitch();
m_pitchDirty = true;
_setPitchWheel(m_curPitchWheel);
2018-07-16 07:41:15 +00:00
m_backendVoice->resetSampleRate(m_curSample->m_sampleRate);
m_needsSlew = false;
2018-07-30 06:20:03 +00:00
int32_t numSamples = m_curSample->getNumSamples();
if (offset)
{
2018-07-16 07:41:15 +00:00
if (m_curSample->m_loopLengthSamples)
{
2018-07-16 07:41:15 +00:00
if (offset > int32_t(m_curSample->m_loopStartSample))
2016-07-14 04:54:46 +00:00
offset =
2018-07-16 07:41:15 +00:00
((offset - m_curSample->m_loopStartSample) % m_curSample->m_loopLengthSamples) +
m_curSample->m_loopStartSample;
}
else
offset = clamp(0, offset, numSamples);
}
m_curSamplePos = offset;
m_prev1 = 0;
m_prev2 = 0;
2018-07-30 06:20:03 +00:00
m_curFormat = m_curSample->getSampleFormat();
if (m_curFormat == SampleFormat::DSP_DRUM)
m_curFormat = SampleFormat::DSP;
2018-07-16 07:41:15 +00:00
m_lastSamplePos = m_curSample->m_loopLengthSamples
? (m_curSample->m_loopStartSample + m_curSample->m_loopLengthSamples)
2016-07-14 04:54:46 +00:00
: numSamples;
2018-09-03 00:43:05 +00:00
if (m_lastSamplePos)
--m_lastSamplePos;
2016-05-14 04:46:39 +00:00
bool looped;
_checkSamplePos(looped);
2016-05-13 01:46:41 +00:00
/* Seek DSPADPCM state if needed */
if (m_curSample && m_curSamplePos && m_curFormat == SampleFormat::DSP)
2016-05-13 01:46:41 +00:00
{
uint32_t block = m_curSamplePos / 14;
uint32_t rem = m_curSamplePos % 14;
2016-07-14 04:54:46 +00:00
for (uint32_t b = 0; b < block; ++b)
2018-07-16 07:41:15 +00:00
DSPDecompressFrameStateOnly(m_curSampleData + 8 * b, m_curSample->m_ADPCMParms.dsp.m_coefs, &m_prev1,
2016-07-14 04:54:46 +00:00
&m_prev2, 14);
2016-05-13 01:46:41 +00:00
if (rem)
2018-07-16 07:41:15 +00:00
DSPDecompressFrameStateOnly(m_curSampleData + 8 * block, m_curSample->m_ADPCMParms.dsp.m_coefs, &m_prev1,
2016-07-14 04:54:46 +00:00
&m_prev2, rem);
2016-05-13 01:46:41 +00:00
}
}
}
2018-07-29 03:37:06 +00:00
void Voice::stopSample() { m_curSample.reset(); }
2016-05-09 07:22:58 +00:00
void Voice::setVolume(float vol)
{
2017-11-28 02:44:35 +00:00
if (m_destroyed)
return;
m_targetUserVol = clamp(0.f, vol, 1.f);
2018-07-29 03:37:06 +00:00
for (ObjToken<Voice>& vox : m_childVoices)
2016-05-16 22:13:24 +00:00
vox->setVolume(vol);
2016-05-11 21:30:45 +00:00
}
void Voice::_panLaw(float coefs[8], float frontPan, float backPan, float totalSpan) const
2016-05-11 21:30:45 +00:00
{
/* -3dB panning law for various channel configs */
switch (m_engine.m_channelSet)
{
case AudioChannelSet::Stereo:
default:
/* Left */
2017-11-28 10:05:59 +00:00
coefs[0] = std::sqrt(-frontPan * 0.5f + 0.5f);
2016-05-21 21:40:03 +00:00
/* Right */
2017-11-28 10:05:59 +00:00
coefs[1] = std::sqrt(frontPan * 0.5f + 0.5f);
2016-05-21 21:40:03 +00:00
break;
2016-05-21 21:40:03 +00:00
case AudioChannelSet::Quad:
/* Left */
coefs[0] = -frontPan * 0.5f + 0.5f;
coefs[0] *= -totalSpan * 0.5f + 0.5f;
2017-11-28 10:05:59 +00:00
coefs[0] = std::sqrt(coefs[0]);
2016-05-21 21:40:03 +00:00
/* Right */
coefs[1] = frontPan * 0.5f + 0.5f;
coefs[1] *= -totalSpan * 0.5f + 0.5f;
2017-11-28 10:05:59 +00:00
coefs[1] = std::sqrt(coefs[1]);
2016-05-21 21:40:03 +00:00
/* Back Left */
coefs[2] = -backPan * 0.5f + 0.5f;
coefs[2] *= totalSpan * 0.5f + 0.5f;
2017-11-28 10:05:59 +00:00
coefs[2] = std::sqrt(coefs[2]);
2016-05-21 21:40:03 +00:00
/* Back Right */
coefs[3] = backPan * 0.5f + 0.5f;
coefs[3] *= totalSpan * 0.5f + 0.5f;
2017-11-28 10:05:59 +00:00
coefs[3] = std::sqrt(coefs[3]);
2016-05-21 21:40:03 +00:00
break;
2016-05-21 21:40:03 +00:00
case AudioChannelSet::Surround51:
/* Left */
coefs[0] = (frontPan <= 0.f) ? -frontPan : 0.f;
coefs[0] *= -totalSpan * 0.5f + 0.5f;
2017-11-28 10:05:59 +00:00
coefs[0] = std::sqrt(coefs[0]);
2016-05-21 21:40:03 +00:00
/* Right */
coefs[1] = (frontPan >= 0.f) ? frontPan : 0.f;
coefs[1] *= -totalSpan * 0.5f + 0.5f;
2017-11-28 10:05:59 +00:00
coefs[1] = std::sqrt(coefs[1]);
/* Back Left */
coefs[2] = -backPan * 0.5f + 0.5f;
coefs[2] *= totalSpan * 0.5f + 0.5f;
2017-11-28 10:05:59 +00:00
coefs[2] = std::sqrt(coefs[2]);
/* Back Right */
coefs[3] = backPan * 0.5f + 0.5f;
coefs[3] *= totalSpan * 0.5f + 0.5f;
2017-11-28 10:05:59 +00:00
coefs[3] = std::sqrt(coefs[3]);
/* Center */
coefs[4] = 1.f - std::fabs(frontPan);
coefs[4] *= -totalSpan * 0.5f + 0.5f;
2017-11-28 10:05:59 +00:00
coefs[4] = std::sqrt(coefs[4]);
/* LFE */
2017-11-28 03:38:58 +00:00
coefs[5] = 0.25f;
break;
case AudioChannelSet::Surround71:
/* Left */
coefs[0] = (frontPan <= 0.f) ? -frontPan : 0.f;
coefs[0] *= (totalSpan <= 0.f) ? -totalSpan : 0.f;
2017-11-28 10:05:59 +00:00
coefs[0] = std::sqrt(coefs[0]);
/* Right */
coefs[1] = (frontPan >= 0.f) ? frontPan : 0.f;
coefs[1] *= (totalSpan <= 0.f) ? -totalSpan : 0.f;
2017-11-28 10:05:59 +00:00
coefs[1] = std::sqrt(coefs[1]);
/* Back Left */
coefs[2] = -backPan * 0.5f + 0.5f;
coefs[2] *= (totalSpan >= 0.f) ? totalSpan : 0.f;
2017-11-28 10:05:59 +00:00
coefs[2] = std::sqrt(coefs[2]);
/* Back Right */
coefs[3] = backPan * 0.5f + 0.5f;
coefs[3] *= (totalSpan >= 0.f) ? totalSpan : 0.f;
2017-11-28 10:05:59 +00:00
coefs[3] = std::sqrt(coefs[3]);
/* Center */
coefs[4] = 1.f - std::fabs(frontPan);
coefs[4] *= (totalSpan <= 0.f) ? -totalSpan : 0.f;
2017-11-28 10:05:59 +00:00
coefs[4] = std::sqrt(coefs[4]);
/* LFE */
2017-11-28 03:38:58 +00:00
coefs[5] = 0.25f;
/* Side Left */
2017-11-28 03:38:58 +00:00
coefs[6] = -backPan * 0.5f + 0.5f;
coefs[6] *= 1.f - std::fabs(totalSpan);
2017-11-28 10:05:59 +00:00
coefs[6] = std::sqrt(coefs[6]);
/* Side Right */
2017-11-28 03:38:58 +00:00
coefs[7] = backPan * 0.5f + 0.5f;
coefs[7] *= 1.f - std::fabs(totalSpan);
2017-11-28 10:05:59 +00:00
coefs[7] = std::sqrt(coefs[7]);
break;
}
}
void Voice::_setPan(float pan)
{
2017-11-28 02:44:35 +00:00
if (m_destroyed || m_emitter)
return;
m_curPan = clamp(-1.f, pan, 1.f);
2018-07-29 03:37:06 +00:00
float totalPan = clamp(-1.f, m_curPan, 1.f);
float totalSpan = clamp(-1.f, m_curSpan, 1.f);
float coefs[8] = {};
_panLaw(coefs, totalPan, totalPan, totalSpan);
_setChannelCoefs(coefs);
2016-05-21 21:40:03 +00:00
}
void Voice::setPan(float pan)
{
2017-11-28 02:44:35 +00:00
if (m_destroyed)
return;
2018-07-29 03:37:06 +00:00
_setPan(pan);
for (ObjToken<Voice>& vox : m_childVoices)
2016-05-16 22:13:24 +00:00
vox->setPan(pan);
2016-05-09 07:22:58 +00:00
}
2016-05-21 21:40:03 +00:00
void Voice::_setSurroundPan(float span)
2016-05-09 07:22:58 +00:00
{
m_curSpan = clamp(-1.f, span, 1.f);
2016-05-21 21:40:03 +00:00
_setPan(m_curPan);
}
void Voice::setSurroundPan(float span)
{
2017-11-28 02:44:35 +00:00
if (m_destroyed)
return;
2018-07-29 03:37:06 +00:00
_setSurroundPan(span);
for (ObjToken<Voice>& vox : m_childVoices)
2016-05-16 22:13:24 +00:00
vox->setSurroundPan(span);
2016-05-09 07:22:58 +00:00
}
void Voice::_setChannelCoefs(const float coefs[8])
{
m_backendVoice->setChannelLevels(m_studio->getMaster().m_backendSubmix.get(), coefs, true);
m_backendVoice->setChannelLevels(m_studio->getAuxA().m_backendSubmix.get(), coefs, true);
m_backendVoice->setChannelLevels(m_studio->getAuxB().m_backendSubmix.get(), coefs, true);
}
void Voice::setChannelCoefs(const float coefs[8])
{
2017-11-28 02:44:35 +00:00
if (m_destroyed)
return;
_setChannelCoefs(coefs);
2018-07-29 03:37:06 +00:00
for (ObjToken<Voice>& vox : m_childVoices)
vox->setChannelCoefs(coefs);
}
2016-05-11 21:30:45 +00:00
void Voice::startEnvelope(double dur, float vol, const Curve* envCurve)
{
m_envelopeTime = 0.f;
2016-05-11 21:30:45 +00:00
m_envelopeDur = dur;
m_envelopeStart = clamp(0.f, m_curVol, 1.f);
m_envelopeEnd = clamp(0.f, vol, 1.f);
2016-05-11 21:30:45 +00:00
m_envelopeCurve = envCurve;
}
void Voice::startFadeIn(double dur, float vol, const Curve* envCurve)
{
m_envelopeTime = 0.f;
2016-05-11 21:30:45 +00:00
m_envelopeDur = dur;
m_envelopeStart = 0.f;
2016-05-21 21:40:03 +00:00
m_envelopeEnd = clamp(0.f, vol, 1.f);
2016-05-11 21:30:45 +00:00
m_envelopeCurve = envCurve;
}
2016-05-21 21:40:03 +00:00
void Voice::startPanning(double dur, uint8_t panPos, int8_t panWidth)
2016-05-11 21:30:45 +00:00
{
2018-09-03 00:43:05 +00:00
m_panningQueue.push({0.f, dur, panPos, panWidth});
2016-05-11 21:30:45 +00:00
}
2016-05-21 21:40:03 +00:00
void Voice::startSpanning(double dur, uint8_t spanPos, int8_t spanWidth)
2016-05-11 21:30:45 +00:00
{
2018-09-03 00:43:05 +00:00
m_spanningQueue.push({0.f, dur, spanPos, spanWidth});
}
void Voice::setPitchKey(int32_t cents)
{
2016-05-11 21:30:45 +00:00
m_curPitch = cents;
m_pitchDirty = true;
}
void Voice::setPedal(bool pedal)
{
2017-11-28 02:44:35 +00:00
if (m_destroyed)
return;
if (m_sustained && !pedal && m_sustainKeyOff)
{
m_sustainKeyOff = false;
_doKeyOff();
}
m_sustained = pedal;
2016-05-14 04:46:39 +00:00
2018-07-29 03:37:06 +00:00
for (ObjToken<Voice>& vox : m_childVoices)
2016-05-14 04:46:39 +00:00
vox->setPedal(pedal);
}
2016-07-14 04:54:46 +00:00
void Voice::setDoppler(float) {}
2018-05-19 05:15:12 +00:00
void Voice::setVibrato(int32_t level, bool modScale, float period)
2016-05-11 21:30:45 +00:00
{
2018-05-19 05:15:12 +00:00
m_vibratoTime = std::fabs(period) < FLT_EPSILON ? -1.f : 0.f;
2016-05-11 21:30:45 +00:00
m_vibratoLevel = level;
2018-05-19 05:15:12 +00:00
m_vibratoModWheel = modScale;
2016-05-11 21:30:45 +00:00
m_vibratoPeriod = period;
}
2016-07-14 04:54:46 +00:00
void Voice::setMod2VibratoRange(int32_t modLevel) { m_vibratoModLevel = modLevel; }
2016-05-11 21:30:45 +00:00
void Voice::setTremolo(float tremoloScale, float tremoloModScale)
{
m_tremoloScale = tremoloScale;
m_tremoloModScale = tremoloModScale;
}
void Voice::setPitchSweep1(uint8_t times, int16_t add)
{
m_pitchSweep1 = 0;
2016-05-17 03:23:35 +00:00
m_pitchSweep1It = 0;
m_pitchSweep1Times = times;
2016-05-11 21:30:45 +00:00
m_pitchSweep1Add = add;
}
void Voice::setPitchSweep2(uint8_t times, int16_t add)
{
m_pitchSweep2 = 0;
2016-05-17 03:23:35 +00:00
m_pitchSweep2It = 0;
m_pitchSweep2Times = times;
2016-05-11 21:30:45 +00:00
m_pitchSweep2Add = add;
}
void Voice::setReverbVol(float rvol)
{
2017-11-28 02:44:35 +00:00
if (m_destroyed)
return;
m_curReverbVol = clamp(0.f, rvol, 1.f);
2018-07-29 03:37:06 +00:00
for (ObjToken<Voice>& vox : m_childVoices)
vox->setReverbVol(rvol);
}
2016-07-14 04:54:46 +00:00
void Voice::setAuxBVol(float bvol)
{
2017-11-28 02:44:35 +00:00
if (m_destroyed)
return;
2016-07-14 04:54:46 +00:00
m_curAuxBVol = clamp(0.f, bvol, 1.f);
2018-07-29 03:37:06 +00:00
for (ObjToken<Voice>& vox : m_childVoices)
2016-07-14 04:54:46 +00:00
vox->setAuxBVol(bvol);
}
void Voice::setAdsr(ObjectId adsrId, bool dls)
{
2017-11-28 02:44:35 +00:00
if (m_destroyed)
return;
if (dls)
{
const ADSRDLS* adsr = m_audioGroup.getPool().tableAsAdsrDLS(adsrId);
if (adsr)
2016-05-19 05:27:39 +00:00
{
m_volAdsr.reset(adsr, m_state.m_initKey, m_state.m_initVel);
2016-05-19 05:27:39 +00:00
if (m_voxState == VoiceState::KeyOff)
m_volAdsr.keyOff(*this);
2016-05-19 05:27:39 +00:00
}
}
else
{
const ADSR* adsr = m_audioGroup.getPool().tableAsAdsr(adsrId);
if (adsr)
2016-05-19 05:27:39 +00:00
{
m_volAdsr.reset(adsr);
2016-05-19 05:27:39 +00:00
if (m_voxState == VoiceState::KeyOff)
m_volAdsr.keyOff(*this);
2016-05-19 05:27:39 +00:00
}
}
}
void Voice::setPitchFrequency(uint32_t hz, uint16_t fine)
{
2017-11-28 02:44:35 +00:00
if (m_destroyed)
return;
2016-05-11 21:30:45 +00:00
m_sampleRate = hz + fine / 65536.0;
2016-05-17 03:47:12 +00:00
m_backendVoice->setPitchRatio(1.0, false);
2016-05-11 21:30:45 +00:00
m_backendVoice->resetSampleRate(m_sampleRate);
}
void Voice::setPitchAdsr(ObjectId adsrId, int32_t cents)
{
2017-11-28 02:44:35 +00:00
if (m_destroyed)
return;
const ADSRDLS* adsr = m_audioGroup.getPool().tableAsAdsrDLS(adsrId);
2016-05-14 22:38:37 +00:00
if (adsr)
{
m_pitchAdsr.reset(adsr, m_state.m_initKey, m_state.m_initVel);
2016-05-14 22:38:37 +00:00
m_pitchEnvRange = cents;
m_pitchEnv = true;
}
}
void Voice::_setPitchWheel(float pitchWheel)
2016-05-15 06:48:26 +00:00
{
2017-11-28 02:44:35 +00:00
if (m_destroyed)
return;
2016-05-15 06:48:26 +00:00
if (pitchWheel > 0.f)
2016-05-17 03:23:35 +00:00
m_pitchWheelVal = m_pitchWheelUp * m_curPitchWheel;
2016-05-15 06:48:26 +00:00
else if (pitchWheel < 0.f)
2016-05-17 03:23:35 +00:00
m_pitchWheelVal = m_pitchWheelDown * m_curPitchWheel;
2016-05-15 06:48:26 +00:00
else
m_pitchWheelVal = 0;
m_pitchDirty = true;
}
void Voice::setPitchWheel(float pitchWheel)
{
2017-11-28 02:44:35 +00:00
if (m_destroyed)
return;
m_curPitchWheel = amuse::clamp(-1.f, pitchWheel, 1.f);
_setPitchWheel(m_curPitchWheel);
2016-05-16 22:13:24 +00:00
2018-07-29 03:37:06 +00:00
for (ObjToken<Voice>& vox : m_childVoices)
2016-05-16 22:13:24 +00:00
vox->setPitchWheel(pitchWheel);
2016-05-15 06:48:26 +00:00
}
void Voice::setPitchWheelRange(int8_t up, int8_t down)
{
2017-11-28 02:44:35 +00:00
if (m_destroyed)
return;
2016-05-15 06:48:26 +00:00
m_pitchWheelUp = up * 100;
m_pitchWheelDown = down * 100;
_setPitchWheel(m_curPitchWheel);
2016-05-15 06:48:26 +00:00
}
void Voice::setAftertouch(uint8_t aftertouch)
{
2017-11-28 02:44:35 +00:00
if (m_destroyed)
return;
2016-05-15 06:48:26 +00:00
m_curAftertouch = aftertouch;
2018-07-29 03:37:06 +00:00
for (ObjToken<Voice>& vox : m_childVoices)
2016-05-16 22:13:24 +00:00
vox->setAftertouch(aftertouch);
2016-05-15 06:48:26 +00:00
}
2016-06-01 21:17:16 +00:00
bool Voice::doPortamento(uint8_t newNote)
{
bool pState;
switch (m_state.m_portamentoMode)
{
case SoundMacro::CmdPortamento::PortState::Disable:
2016-06-01 21:17:16 +00:00
default:
pState = false;
break;
case SoundMacro::CmdPortamento::PortState::Enable:
2016-06-01 21:17:16 +00:00
pState = true;
break;
case SoundMacro::CmdPortamento::PortState::MIDIControlled:
2018-07-30 06:20:03 +00:00
pState = m_state.m_portamentoSel ? (m_state.m_portamentoSel.evaluate(m_voiceTime, *this, m_state) >= 64.f)
2016-07-14 04:54:46 +00:00
: (getCtrlValue(65) >= 64);
2016-06-01 21:17:16 +00:00
break;
}
if (!pState)
return false;
m_portamentoTime = 0.f;
m_portamentoTarget = newNote * 100;
m_state.m_initKey = newNote;
return true;
}
2016-05-21 21:40:03 +00:00
void Voice::_notifyCtrlChange(uint8_t ctrl, int8_t val)
{
if (ctrl == 0x40)
{
setPedal(val >= 0x40);
}
else if (ctrl == 0x5b)
{
setReverbVol(val / 127.f);
}
2016-07-14 04:54:46 +00:00
else if (ctrl == 0x5d)
{
setAuxBVol(val / 127.f);
}
else if (ctrl == 0x1)
{
m_state.m_curMod = uint8_t(val);
}
else if (ctrl == 0x64)
{
// RPN LSB
m_rpn &= ~0x7f;
m_rpn |= val;
}
else if (ctrl == 0x65)
{
// RPN MSB
m_rpn &= ~0x3f80;
m_rpn |= val << 7;
}
else if (ctrl == 0x6)
{
if (m_rpn == 0)
m_pitchWheelUp = m_pitchWheelDown = val * 100;
}
else if (ctrl == 0x60)
{
if (m_rpn == 0)
{
m_pitchWheelUp += 100;
m_pitchWheelDown += 100;
}
}
else if (ctrl == 0x61)
{
if (m_rpn == 0)
{
m_pitchWheelUp -= 100;
m_pitchWheelDown -= 100;
}
}
2016-05-16 22:13:24 +00:00
2018-07-29 03:37:06 +00:00
for (ObjToken<Voice>& vox : m_childVoices)
2016-05-21 21:40:03 +00:00
vox->_notifyCtrlChange(ctrl, val);
}
2016-05-15 06:48:26 +00:00
size_t Voice::getTotalVoices() const
{
size_t ret = 1;
2018-07-29 03:37:06 +00:00
for (const ObjToken<Voice>& vox : m_childVoices)
2016-05-15 06:48:26 +00:00
ret += vox->getTotalVoices();
return ret;
}
void Voice::kill()
{
2017-11-28 02:44:35 +00:00
if (m_destroyed)
return;
m_voxState = VoiceState::Dead;
m_backendVoice->stop();
2018-07-29 03:37:06 +00:00
for (const ObjToken<Voice>& vox : m_childVoices)
vox->kill();
}
}