Add pan/span control to Voice backend

This commit is contained in:
Jack Andersen 2016-05-21 11:40:03 -10:00
parent d06fa3f0dc
commit d551b2b1c6
9 changed files with 112 additions and 42 deletions

View File

@ -49,7 +49,7 @@ int main(int argc, char* argv[])
int sfxId = 0x1337; int sfxId = 0x1337;
float vol = 1.0f; float vol = 1.0f;
float pan = 0.0f; float pan = 0.0f;
std::shared_ptr<Voice> voice = snd.fxStart(sfxId, vol, pan); std::shared_ptr<amuse::Voice> voice = snd.fxStart(sfxId, vol, pan);
/* Play for ~5 sec */ /* Play for ~5 sec */
int passedFrames = 0; int passedFrames = 0;

View File

@ -9,7 +9,7 @@
#include <string.h> #include <string.h>
#include <thread> #include <thread>
#include <map> #include <map>
#include <varargs.h> #include <stdarg.h>
static logvisor::Module Log("amuseplay"); static logvisor::Module Log("amuseplay");

View File

@ -14,7 +14,7 @@ namespace amuse
/** Converts time-cents representation to seconds */ /** Converts time-cents representation to seconds */
static inline double TimeCentsToSeconds(int32_t tc) static inline double TimeCentsToSeconds(int32_t tc)
{ {
if (tc == 0x80000000) if (uint32_t(tc) == 0x80000000)
return 0.0; return 0.0;
return std::exp2(tc / (1200.0 * 65536.0)); return std::exp2(tc / (1200.0 * 65536.0));
} }
@ -84,9 +84,9 @@ struct LayerMapping
int8_t keyHi; int8_t keyHi;
int8_t transpose; int8_t transpose;
int8_t volume; int8_t volume;
int8_t pan; /* -128 for surround-channel only */
int8_t prioOffset; int8_t prioOffset;
int8_t unk; /* usually 0x40 */ int8_t span;
int8_t pan;
}; };
/** Database of functional objects within Audio Group */ /** Database of functional objects within Audio Group */

View File

@ -33,7 +33,7 @@ public:
BooBackendVoice(boo::IAudioSubmix& submix, Voice& clientVox, BooBackendVoice(boo::IAudioSubmix& submix, Voice& clientVox,
double sampleRate, bool dynamicPitch); double sampleRate, bool dynamicPitch);
void resetSampleRate(double sampleRate); void resetSampleRate(double sampleRate);
void setMatrixCoefficients(const float coefs[8]); void setMatrixCoefficients(const float coefs[8], bool slew);
void setPitchRatio(double ratio, bool slew); void setPitchRatio(double ratio, bool slew);
void start(); void start();
void stop(); void stop();

View File

@ -35,7 +35,7 @@ public:
virtual void resetSampleRate(double sampleRate)=0; virtual void resetSampleRate(double sampleRate)=0;
/** Set channel-gains for audio source (AudioChannel enum for array index) */ /** Set channel-gains for audio source (AudioChannel enum for array index) */
virtual void setMatrixCoefficients(const float coefs[8])=0; virtual void setMatrixCoefficients(const float coefs[8], bool slew)=0;
/** Called by client to dynamically adjust the pitch of voices with dynamic pitch enabled */ /** Called by client to dynamically adjust the pitch of voices with dynamic pitch enabled */
virtual void setPitchRatio(double ratio, bool slew)=0; virtual void setPitchRatio(double ratio, bool slew)=0;

View File

@ -68,10 +68,12 @@ class Voice : public Entity
uint8_t m_curAftertouch = 0; /**< Aftertouch value (key pressure when 'bottoming out') */ uint8_t m_curAftertouch = 0; /**< Aftertouch value (key pressure when 'bottoming out') */
float m_userVol = 1.f; /**< User volume of voice */ float m_userVol = 1.f; /**< User volume of voice */
float m_curVol; /**< Current volume of voice */ float m_curVol = 1.f; /**< Current volume of voice */
float m_curReverbVol; /**< Current reverb volume of voice */ float m_curReverbVol = 0.f; /**< Current reverb volume of voice */
float m_curPan; /**< Current pan of voice */ float m_userPan = 0.f; /**< User pan of voice */
float m_curSpan; /**< Current surround pan of voice */ float m_curPan = 0.f; /**< Current pan of voice */
float m_userSpan = 0.f; /**< User span of voice */
float m_curSpan = 0.f; /**< Current surround pan of voice */
float m_curPitchWheel = 0.f; /**< Current normalized wheel value for control */ float m_curPitchWheel = 0.f; /**< Current normalized wheel value for control */
int32_t m_pitchWheelUp; /**< Up range for pitchwheel control in cents */ int32_t m_pitchWheelUp; /**< Up range for pitchwheel control in cents */
int32_t m_pitchWheelDown; /**< Down range for pitchwheel control in cents */ int32_t m_pitchWheelDown; /**< Down range for pitchwheel control in cents */
@ -102,12 +104,12 @@ class Voice : public Entity
float m_panningTime; /**< time since last PANNING command, -1 for no active pan-sweep */ float m_panningTime; /**< time since last PANNING command, -1 for no active pan-sweep */
float m_panningDur; /**< requested duration of last PANNING command */ float m_panningDur; /**< requested duration of last PANNING command */
uint8_t m_panPos; /**< initial pan value of last PANNING command */ uint8_t m_panPos; /**< initial pan value of last PANNING command */
uint8_t m_panWidth; /**< delta pan value to target of last PANNING command */ int8_t m_panWidth; /**< delta pan value to target of last PANNING command */
float m_spanningTime; /**< time since last SPANNING command, -1 for no active span-sweep */ float m_spanningTime; /**< time since last SPANNING command, -1 for no active span-sweep */
float m_spanningDur; /**< requested duration of last SPANNING command */ float m_spanningDur; /**< requested duration of last SPANNING command */
uint8_t m_spanPos; /**< initial pan value of last SPANNING command */ uint8_t m_spanPos; /**< initial pan value of last SPANNING command */
uint8_t m_spanWidth; /**< delta pan value to target of last SPANNING command */ int8_t m_spanWidth; /**< delta pan value to target of last SPANNING command */
int32_t m_vibratoLevel; /**< scale of vibrato effect (in cents) */ int32_t m_vibratoLevel; /**< scale of vibrato effect (in cents) */
int32_t m_vibratoModLevel; /**< scale of vibrato mod-wheel influence (in cents) */ int32_t m_vibratoModLevel; /**< scale of vibrato mod-wheel influence (in cents) */
@ -145,6 +147,10 @@ class Voice : public Entity
uint8_t midiKey, uint8_t midiVel, uint8_t midiMod, bool pushPc=false); uint8_t midiKey, uint8_t midiVel, uint8_t midiMod, bool pushPc=false);
std::shared_ptr<Voice> _startChildMacro(ObjectId macroId, int macroStep, double ticksPerSec, std::shared_ptr<Voice> _startChildMacro(ObjectId macroId, int macroStep, double ticksPerSec,
uint8_t midiKey, uint8_t midiVel, uint8_t midiMod, bool pushPc=false); uint8_t midiKey, uint8_t midiVel, uint8_t midiMod, bool pushPc=false);
void _setPan(float pan);
void _setSurroundPan(float span);
void _notifyCtrlChange(uint8_t ctrl, int8_t val);
public: public:
~Voice(); ~Voice();
Voice(Engine& engine, const AudioGroup& group, int groupId, int vid, bool emitter, Submix* smx); Voice(Engine& engine, const AudioGroup& group, int groupId, int vid, bool emitter, Submix* smx);
@ -202,10 +208,10 @@ public:
void startFadeIn(double dur, float vol, const Curve* envCurve); void startFadeIn(double dur, float vol, const Curve* envCurve);
/** Start pan envelope to specified position */ /** Start pan envelope to specified position */
void startPanning(double dur, uint8_t panPos, uint8_t panWidth); void startPanning(double dur, uint8_t panPos, int8_t panWidth);
/** Start span envelope to specified position */ /** Start span envelope to specified position */
void startSpanning(double dur, uint8_t spanPos, uint8_t spanWidth); void startSpanning(double dur, uint8_t spanPos, int8_t spanWidth);
/** Set voice relative-pitch in cents */ /** Set voice relative-pitch in cents */
void setPitchKey(int32_t cents); void setPitchKey(int32_t cents);
@ -264,8 +270,6 @@ public:
/** Get note played on voice */ /** Get note played on voice */
uint8_t getLastNote() const {return m_state.m_initKey;} uint8_t getLastNote() const {return m_state.m_initKey;}
void notifyCtrlChange(uint8_t ctrl, int8_t val);
/** Get MIDI Controller value on voice */ /** Get MIDI Controller value on voice */
int8_t getCtrlValue(uint8_t ctrl) const int8_t getCtrlValue(uint8_t ctrl) const
{ {
@ -288,7 +292,7 @@ public:
} }
else else
m_extCtrlVals[ctrl] = val; m_extCtrlVals[ctrl] = val;
notifyCtrlChange(ctrl, val); _notifyCtrlChange(ctrl, val);
} }
/** Get ModWheel value on voice */ /** Get ModWheel value on voice */

View File

@ -29,9 +29,9 @@ void BooBackendVoice::resetSampleRate(double sampleRate)
m_booVoice->resetSampleRate(sampleRate); m_booVoice->resetSampleRate(sampleRate);
} }
void BooBackendVoice::setMatrixCoefficients(const float coefs[8]) void BooBackendVoice::setMatrixCoefficients(const float coefs[8], bool slew)
{ {
m_booVoice->setMonoMatrixCoefficients(coefs); m_booVoice->setMonoMatrixCoefficients(coefs, slew);
} }
void BooBackendVoice::setPitchRatio(double ratio, bool slew) void BooBackendVoice::setPitchRatio(double ratio, bool slew)

View File

@ -207,9 +207,9 @@ void Sequencer::ChannelState::setCtrlValue(uint8_t ctrl, int8_t val)
{ {
m_ctrlVals[ctrl] = val; m_ctrlVals[ctrl] = val;
for (const auto& vox : m_chanVoxs) for (const auto& vox : m_chanVoxs)
vox.second->notifyCtrlChange(ctrl, val); vox.second->_notifyCtrlChange(ctrl, val);
for (const auto& vox : m_keyoffVoxs) for (const auto& vox : m_keyoffVoxs)
vox->notifyCtrlChange(ctrl, val); vox->_notifyCtrlChange(ctrl, val);
if (ctrl == 7) if (ctrl == 7)
setVolume(val / 127.f); setVolume(val / 127.f);

View File

@ -39,10 +39,6 @@ Voice::Voice(Engine& engine, const AudioGroup& group, int groupId, ObjectId oid,
void Voice::_reset() void Voice::_reset()
{ {
m_curVol = 1.f;
m_curReverbVol = 0.f;
m_curPan = 0.f;
m_curSpan = 0.f;
m_curAftertouch = 0; m_curAftertouch = 0;
m_pitchWheelUp = 600; m_pitchWheelUp = 600;
m_pitchWheelDown = 600; m_pitchWheelDown = 600;
@ -56,6 +52,7 @@ void Voice::_reset()
m_pitchSweep2It = 0; m_pitchSweep2It = 0;
m_envelopeTime = -1.f; m_envelopeTime = -1.f;
m_panningTime = -1.f; m_panningTime = -1.f;
m_spanningTime = -1.f;
m_vibratoLevel = 0; m_vibratoLevel = 0;
m_vibratoModLevel = 0; m_vibratoModLevel = 0;
m_vibratoPeriod = 0.f; m_vibratoPeriod = 0.f;
@ -268,7 +265,7 @@ bool Voice::_advanceSample(int16_t& samp, int32_t& newPitch)
float start = (m_panPos - 64) / 64.f; float start = (m_panPos - 64) / 64.f;
float end = (m_panPos + m_panWidth - 64) / 64.f; float end = (m_panPos + m_panWidth - 64) / 64.f;
float t = std::max(0.f, std::min(1.f, m_panningTime / m_panningDur)); float t = std::max(0.f, std::min(1.f, m_panningTime / m_panningDur));
m_curPan = (start * (1.0f - t)) + (end * t); _setPan((start * (1.0f - t)) + (end * t));
refresh = true; refresh = true;
/* Done with panning */ /* Done with panning */
@ -283,7 +280,7 @@ bool Voice::_advanceSample(int16_t& samp, int32_t& newPitch)
float start = (m_spanPos - 64) / 64.f; float start = (m_spanPos - 64) / 64.f;
float end = (m_spanPos + m_spanWidth - 64) / 64.f; float end = (m_spanPos + m_spanWidth - 64) / 64.f;
float t = std::max(0.f, std::min(1.f, m_spanningTime / m_spanningDur)); float t = std::max(0.f, std::min(1.f, m_spanningTime / m_spanningDur));
m_curSpan = (start * (1.0f - t)) + (end * t); _setSurroundPan((start * (1.0f - t)) + (end * t));
refresh = true; refresh = true;
/* Done with spanning */ /* Done with spanning */
@ -377,7 +374,7 @@ size_t Voice::supplyAudio(size_t samples, int16_t* data)
} }
/* Per-sample processing */ /* Per-sample processing */
for (int i=0 ; i<decSamples ; ++i) for (uint32_t i=0 ; i<decSamples ; ++i)
{ {
++samplesProc; ++samplesProc;
++m_curSamplePos; ++m_curSamplePos;
@ -425,7 +422,7 @@ size_t Voice::supplyAudio(size_t samples, int16_t* data)
} }
/* Per-sample processing */ /* Per-sample processing */
for (int i=0 ; i<decSamples ; ++i) for (uint32_t i=0 ; i<decSamples ; ++i)
{ {
++samplesProc; ++samplesProc;
++m_curSamplePos; ++m_curSamplePos;
@ -478,6 +475,8 @@ std::shared_ptr<Voice> Voice::_startChildMacro(ObjectId macroId, int macroStep,
return {}; return {};
} }
vox->setVolume(m_userVol); vox->setVolume(m_userVol);
vox->setPan(m_userPan);
vox->setSurroundPan(m_userSpan);
return vox; return vox;
} }
@ -511,7 +510,11 @@ bool Voice::_loadKeymap(const Keymap* keymap, int macroStep, double ticksPerSec,
{ {
const Keymap& km = keymap[midiKey]; const Keymap& km = keymap[midiKey];
midiKey += km.transpose; midiKey += km.transpose;
return loadSoundObject(SBig(km.objectId), macroStep, ticksPerSec, midiKey, midiVel, midiMod, pushPc); bool ret = loadSoundObject(SBig(km.objectId), macroStep, ticksPerSec, midiKey, midiVel, midiMod, pushPc);
m_curVol = 1.f;
_setPan((km.pan - 64) / 64.f);
_setSurroundPan(-1.f);
return ret;
} }
bool Voice::_loadLayer(const std::vector<const LayerMapping*>& layer, int macroStep, double ticksPerSec, bool Voice::_loadLayer(const std::vector<const LayerMapping*>& layer, int macroStep, double ticksPerSec,
@ -524,11 +527,26 @@ bool Voice::_loadLayer(const std::vector<const LayerMapping*>& layer, int macroS
{ {
uint8_t mappingKey = midiKey + mapping->transpose; uint8_t mappingKey = midiKey + mapping->transpose;
if (m_voxState != VoiceState::Playing) if (m_voxState != VoiceState::Playing)
{
ret |= loadSoundObject(SBig(mapping->objectId), macroStep, ticksPerSec, ret |= loadSoundObject(SBig(mapping->objectId), macroStep, ticksPerSec,
mappingKey, midiVel, midiMod, pushPc); mappingKey, midiVel, midiMod, pushPc);
m_curVol = mapping->volume / 127.f;
_setPan((mapping->pan - 64) / 64.f);
_setSurroundPan((mapping->span - 64) / 64.f);
}
else else
ret |= _startChildMacro(SBig(mapping->objectId), macroStep, ticksPerSec, {
mappingKey, midiVel, midiMod, pushPc).operator bool(); std::shared_ptr<Voice> vox =
_startChildMacro(SBig(mapping->objectId), macroStep, ticksPerSec,
mappingKey, midiVel, midiMod, pushPc);
if (vox)
{
vox->m_curVol = mapping->volume / 127.f;
vox->_setPan((mapping->pan - 64) / 64.f);
vox->_setSurroundPan((mapping->span - 64) / 64.f);
ret = true;
}
}
} }
} }
return ret; return ret;
@ -617,7 +635,7 @@ void Voice::startSample(int16_t sampId, int32_t offset)
{ {
if (m_curSample->first.m_loopLengthSamples) if (m_curSample->first.m_loopLengthSamples)
{ {
if (offset > m_curSample->first.m_loopStartSample) if (offset > int32_t(m_curSample->first.m_loopStartSample))
offset = ((offset - m_curSample->first.m_loopStartSample) % offset = ((offset - m_curSample->first.m_loopStartSample) %
m_curSample->first.m_loopLengthSamples) + m_curSample->first.m_loopLengthSamples) +
m_curSample->first.m_loopStartSample; m_curSample->first.m_loopStartSample;
@ -669,16 +687,64 @@ void Voice::setVolume(float vol)
vox->setVolume(vol); vox->setVolume(vol);
} }
void Voice::setPan(float pan) void Voice::_setPan(float pan)
{ {
m_curPan = clamp(-1.f, pan, 1.f); m_curPan = clamp(-1.f, pan, 1.f);
float totalPan = clamp(-1.f, m_curPan + m_userPan, 1.f);
float totalSpan = clamp(-1.f, m_curSpan + m_userSpan, 1.f);
float coefs[8];
/* Left */
coefs[0] = (totalPan <= 0.f) ? 1.f : (1.f - totalPan);
coefs[0] *= (totalSpan <= 0.f) ? 1.f : (1.f - totalSpan);
/* Right */
coefs[1] = (totalPan >= 0.f) ? 1.f : (1.f + totalPan);
coefs[1] *= (totalSpan <= 0.f) ? 1.f : (1.f - totalSpan);
/* Back Left */
coefs[2] = (totalPan <= 0.f) ? 1.f : (1.f - totalPan);
coefs[2] *= (totalSpan >= 0.f) ? 1.f : (1.f + totalSpan);
/* Back Right */
coefs[3] = (totalPan >= 0.f) ? 1.f : (1.f + totalPan);
coefs[3] *= (totalSpan >= 0.f) ? 1.f : (1.f + totalSpan);
/* Center */
coefs[4] = 1.f - std::fabs(totalPan);
/* LFE */
coefs[5] = 1.f;
/* Side Left */
coefs[6] = (totalPan <= 0.f) ? 1.f : (1.f - totalPan);
coefs[6] *= 1.f - std::fabs(totalSpan);
/* Side Right */
coefs[7] = (totalPan >= 0.f) ? 1.f : (1.f + totalPan);
coefs[7] *= 1.f - std::fabs(totalSpan);
m_backendVoice->setMatrixCoefficients(coefs, true);
}
void Voice::setPan(float pan)
{
m_userPan = pan;
_setPan(m_curPan);
for (std::shared_ptr<Voice>& vox : m_childVoices) for (std::shared_ptr<Voice>& vox : m_childVoices)
vox->setPan(pan); vox->setPan(pan);
} }
void Voice::setSurroundPan(float span) void Voice::_setSurroundPan(float span)
{ {
m_curSpan = clamp(-1.f, span, 1.f); m_curSpan = clamp(-1.f, span, 1.f);
_setPan(m_curPan);
}
void Voice::setSurroundPan(float span)
{
m_userSpan = span;
_setSurroundPan(m_curSpan);
for (std::shared_ptr<Voice>& vox : m_childVoices) for (std::shared_ptr<Voice>& vox : m_childVoices)
vox->setSurroundPan(span); vox->setSurroundPan(span);
} }
@ -697,11 +763,11 @@ void Voice::startFadeIn(double dur, float vol, const Curve* envCurve)
m_envelopeTime = m_voiceTime; m_envelopeTime = m_voiceTime;
m_envelopeDur = dur; m_envelopeDur = dur;
m_envelopeStart = 0.f; m_envelopeStart = 0.f;
m_envelopeEnd = clamp(0.f, m_curVol, 1.f); m_envelopeEnd = clamp(0.f, vol, 1.f);
m_envelopeCurve = envCurve; m_envelopeCurve = envCurve;
} }
void Voice::startPanning(double dur, uint8_t panPos, uint8_t panWidth) void Voice::startPanning(double dur, uint8_t panPos, int8_t panWidth)
{ {
m_panningTime = m_voiceTime; m_panningTime = m_voiceTime;
m_panningDur = dur; m_panningDur = dur;
@ -709,7 +775,7 @@ void Voice::startPanning(double dur, uint8_t panPos, uint8_t panWidth)
m_panWidth = panWidth; m_panWidth = panWidth;
} }
void Voice::startSpanning(double dur, uint8_t spanPos, uint8_t spanWidth) void Voice::startSpanning(double dur, uint8_t spanPos, int8_t spanWidth)
{ {
m_spanningTime = m_voiceTime; m_spanningTime = m_voiceTime;
m_spanningDur = dur; m_spanningDur = dur;
@ -736,7 +802,7 @@ void Voice::setPedal(bool pedal)
vox->setPedal(pedal); vox->setPedal(pedal);
} }
void Voice::setDoppler(float doppler) void Voice::setDoppler(float)
{ {
} }
@ -850,7 +916,7 @@ void Voice::setAftertouch(uint8_t aftertouch)
vox->setAftertouch(aftertouch); vox->setAftertouch(aftertouch);
} }
void Voice::notifyCtrlChange(uint8_t ctrl, int8_t val) void Voice::_notifyCtrlChange(uint8_t ctrl, int8_t val)
{ {
if (ctrl == 64) if (ctrl == 64)
{ {
@ -861,7 +927,7 @@ void Voice::notifyCtrlChange(uint8_t ctrl, int8_t val)
} }
for (std::shared_ptr<Voice>& vox : m_childVoices) for (std::shared_ptr<Voice>& vox : m_childVoices)
vox->notifyCtrlChange(ctrl, val); vox->_notifyCtrlChange(ctrl, val);
} }
size_t Voice::getTotalVoices() const size_t Voice::getTotalVoices() const