From d551b2b1c6a081c087d6853a08e9a4d15bb72690 Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Sat, 21 May 2016 11:40:03 -1000 Subject: [PATCH] Add pan/span control to Voice backend --- README.md | 2 +- driver/main.cpp | 2 +- include/amuse/AudioGroupPool.hpp | 6 +- include/amuse/BooBackend.hpp | 2 +- include/amuse/IBackendVoice.hpp | 2 +- include/amuse/Voice.hpp | 26 ++++---- lib/BooBackend.cpp | 4 +- lib/Sequencer.cpp | 4 +- lib/Voice.cpp | 106 +++++++++++++++++++++++++------ 9 files changed, 112 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index 123b678..6f7d56c 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ int main(int argc, char* argv[]) int sfxId = 0x1337; float vol = 1.0f; float pan = 0.0f; - std::shared_ptr voice = snd.fxStart(sfxId, vol, pan); + std::shared_ptr voice = snd.fxStart(sfxId, vol, pan); /* Play for ~5 sec */ int passedFrames = 0; diff --git a/driver/main.cpp b/driver/main.cpp index e256a6c..8af02f5 100644 --- a/driver/main.cpp +++ b/driver/main.cpp @@ -9,7 +9,7 @@ #include #include #include -#include +#include static logvisor::Module Log("amuseplay"); diff --git a/include/amuse/AudioGroupPool.hpp b/include/amuse/AudioGroupPool.hpp index 726f311..913a44c 100644 --- a/include/amuse/AudioGroupPool.hpp +++ b/include/amuse/AudioGroupPool.hpp @@ -14,7 +14,7 @@ namespace amuse /** Converts time-cents representation to seconds */ static inline double TimeCentsToSeconds(int32_t tc) { - if (tc == 0x80000000) + if (uint32_t(tc) == 0x80000000) return 0.0; return std::exp2(tc / (1200.0 * 65536.0)); } @@ -84,9 +84,9 @@ struct LayerMapping int8_t keyHi; int8_t transpose; int8_t volume; - int8_t pan; /* -128 for surround-channel only */ int8_t prioOffset; - int8_t unk; /* usually 0x40 */ + int8_t span; + int8_t pan; }; /** Database of functional objects within Audio Group */ diff --git a/include/amuse/BooBackend.hpp b/include/amuse/BooBackend.hpp index 63ca63c..ff1a688 100644 --- a/include/amuse/BooBackend.hpp +++ b/include/amuse/BooBackend.hpp @@ -33,7 +33,7 @@ public: BooBackendVoice(boo::IAudioSubmix& submix, Voice& clientVox, double sampleRate, bool dynamicPitch); 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 start(); void stop(); diff --git a/include/amuse/IBackendVoice.hpp b/include/amuse/IBackendVoice.hpp index 3b03d48..c2a9676 100644 --- a/include/amuse/IBackendVoice.hpp +++ b/include/amuse/IBackendVoice.hpp @@ -35,7 +35,7 @@ public: virtual void resetSampleRate(double sampleRate)=0; /** 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 */ virtual void setPitchRatio(double ratio, bool slew)=0; diff --git a/include/amuse/Voice.hpp b/include/amuse/Voice.hpp index 400b00e..f263481 100644 --- a/include/amuse/Voice.hpp +++ b/include/amuse/Voice.hpp @@ -68,10 +68,12 @@ class Voice : public Entity uint8_t m_curAftertouch = 0; /**< Aftertouch value (key pressure when 'bottoming out') */ float m_userVol = 1.f; /**< User volume of voice */ - float m_curVol; /**< Current volume of voice */ - float m_curReverbVol; /**< Current reverb volume of voice */ - float m_curPan; /**< Current pan of voice */ - float m_curSpan; /**< Current surround pan of voice */ + float m_curVol = 1.f; /**< Current volume of voice */ + float m_curReverbVol = 0.f; /**< Current reverb volume of voice */ + float m_userPan = 0.f; /**< User 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 */ int32_t m_pitchWheelUp; /**< Up 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_panningDur; /**< requested duration 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_spanningDur; /**< requested duration 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_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); std::shared_ptr _startChildMacro(ObjectId macroId, int macroStep, double ticksPerSec, 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: ~Voice(); 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); /** 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 */ - 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 */ void setPitchKey(int32_t cents); @@ -264,8 +270,6 @@ public: /** Get note played on voice */ uint8_t getLastNote() const {return m_state.m_initKey;} - void notifyCtrlChange(uint8_t ctrl, int8_t val); - /** Get MIDI Controller value on voice */ int8_t getCtrlValue(uint8_t ctrl) const { @@ -288,7 +292,7 @@ public: } else m_extCtrlVals[ctrl] = val; - notifyCtrlChange(ctrl, val); + _notifyCtrlChange(ctrl, val); } /** Get ModWheel value on voice */ diff --git a/lib/BooBackend.cpp b/lib/BooBackend.cpp index 8f19160..42ebd02 100644 --- a/lib/BooBackend.cpp +++ b/lib/BooBackend.cpp @@ -29,9 +29,9 @@ void BooBackendVoice::resetSampleRate(double 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) diff --git a/lib/Sequencer.cpp b/lib/Sequencer.cpp index 0109051..660a49a 100644 --- a/lib/Sequencer.cpp +++ b/lib/Sequencer.cpp @@ -207,9 +207,9 @@ void Sequencer::ChannelState::setCtrlValue(uint8_t ctrl, int8_t val) { m_ctrlVals[ctrl] = val; for (const auto& vox : m_chanVoxs) - vox.second->notifyCtrlChange(ctrl, val); + vox.second->_notifyCtrlChange(ctrl, val); for (const auto& vox : m_keyoffVoxs) - vox->notifyCtrlChange(ctrl, val); + vox->_notifyCtrlChange(ctrl, val); if (ctrl == 7) setVolume(val / 127.f); diff --git a/lib/Voice.cpp b/lib/Voice.cpp index 7254712..9a265f8 100644 --- a/lib/Voice.cpp +++ b/lib/Voice.cpp @@ -39,10 +39,6 @@ Voice::Voice(Engine& engine, const AudioGroup& group, int groupId, ObjectId oid, void Voice::_reset() { - m_curVol = 1.f; - m_curReverbVol = 0.f; - m_curPan = 0.f; - m_curSpan = 0.f; m_curAftertouch = 0; m_pitchWheelUp = 600; m_pitchWheelDown = 600; @@ -56,6 +52,7 @@ void Voice::_reset() m_pitchSweep2It = 0; m_envelopeTime = -1.f; m_panningTime = -1.f; + m_spanningTime = -1.f; m_vibratoLevel = 0; m_vibratoModLevel = 0; 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 end = (m_panPos + m_panWidth - 64) / 64.f; 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; /* Done with panning */ @@ -283,7 +280,7 @@ bool Voice::_advanceSample(int16_t& samp, int32_t& newPitch) float start = (m_spanPos - 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)); - m_curSpan = (start * (1.0f - t)) + (end * t); + _setSurroundPan((start * (1.0f - t)) + (end * t)); refresh = true; /* Done with spanning */ @@ -377,7 +374,7 @@ size_t Voice::supplyAudio(size_t samples, int16_t* data) } /* Per-sample processing */ - for (int i=0 ; i Voice::_startChildMacro(ObjectId macroId, int macroStep, return {}; } vox->setVolume(m_userVol); + vox->setPan(m_userPan); + vox->setSurroundPan(m_userSpan); return vox; } @@ -511,7 +510,11 @@ bool Voice::_loadKeymap(const Keymap* keymap, int macroStep, double ticksPerSec, { const Keymap& km = keymap[midiKey]; 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& layer, int macroStep, double ticksPerSec, @@ -524,11 +527,26 @@ bool Voice::_loadLayer(const std::vector& layer, int macroS { uint8_t mappingKey = midiKey + mapping->transpose; if (m_voxState != VoiceState::Playing) + { ret |= loadSoundObject(SBig(mapping->objectId), macroStep, ticksPerSec, mappingKey, midiVel, midiMod, pushPc); + m_curVol = mapping->volume / 127.f; + _setPan((mapping->pan - 64) / 64.f); + _setSurroundPan((mapping->span - 64) / 64.f); + } else - ret |= _startChildMacro(SBig(mapping->objectId), macroStep, ticksPerSec, - mappingKey, midiVel, midiMod, pushPc).operator bool(); + { + std::shared_ptr 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; @@ -617,7 +635,7 @@ void Voice::startSample(int16_t sampId, int32_t offset) { 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) % m_curSample->first.m_loopLengthSamples) + m_curSample->first.m_loopStartSample; @@ -669,16 +687,64 @@ void Voice::setVolume(float vol) vox->setVolume(vol); } -void Voice::setPan(float pan) +void Voice::_setPan(float pan) { 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& vox : m_childVoices) vox->setPan(pan); } -void Voice::setSurroundPan(float span) +void Voice::_setSurroundPan(float span) { 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& vox : m_childVoices) vox->setSurroundPan(span); } @@ -697,11 +763,11 @@ void Voice::startFadeIn(double dur, float vol, const Curve* envCurve) m_envelopeTime = m_voiceTime; m_envelopeDur = dur; m_envelopeStart = 0.f; - m_envelopeEnd = clamp(0.f, m_curVol, 1.f); + m_envelopeEnd = clamp(0.f, vol, 1.f); 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_panningDur = dur; @@ -709,7 +775,7 @@ void Voice::startPanning(double dur, uint8_t panPos, uint8_t 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_spanningDur = dur; @@ -736,7 +802,7 @@ void Voice::setPedal(bool 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); } -void Voice::notifyCtrlChange(uint8_t ctrl, int8_t val) +void Voice::_notifyCtrlChange(uint8_t ctrl, int8_t val) { if (ctrl == 64) { @@ -861,7 +927,7 @@ void Voice::notifyCtrlChange(uint8_t ctrl, int8_t val) } for (std::shared_ptr& vox : m_childVoices) - vox->notifyCtrlChange(ctrl, val); + vox->_notifyCtrlChange(ctrl, val); } size_t Voice::getTotalVoices() const