From 2e7345f11d73219d77d345f3c7a039ab68be0e57 Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Sun, 22 Jan 2017 21:21:50 -1000 Subject: [PATCH] Add info structures for holding effect parameters --- include/amuse/BooBackend.hpp | 1 + include/amuse/EffectChorus.hpp | 22 ++++++++++ include/amuse/EffectDelay.hpp | 47 +++++++++++++++++++++ include/amuse/EffectReverb.hpp | 54 ++++++++++++++++++++++++ include/amuse/IBackendVoiceAllocator.hpp | 3 ++ include/amuse/Submix.hpp | 12 ++++++ lib/BooBackend.cpp | 5 +++ lib/EffectDelay.cpp | 13 ++++++ lib/Engine.cpp | 1 + lib/Submix.cpp | 20 +++++++++ 10 files changed, 178 insertions(+) diff --git a/include/amuse/BooBackend.hpp b/include/amuse/BooBackend.hpp index 92b8196..c38cdc3 100644 --- a/include/amuse/BooBackend.hpp +++ b/include/amuse/BooBackend.hpp @@ -128,6 +128,7 @@ public: std::vector> enumerateMIDIDevices(); std::unique_ptr allocateMIDIReader(Engine& engine, const char* name = nullptr); void register5MsCallback(std::function&& callback); + void unregister5MsCallback(); AudioChannelSet getAvailableSet(); void pumpAndMixVoices(); void setVolume(float vol); diff --git a/include/amuse/EffectChorus.hpp b/include/amuse/EffectChorus.hpp index 0d4196f..62c514a 100644 --- a/include/amuse/EffectChorus.hpp +++ b/include/amuse/EffectChorus.hpp @@ -12,6 +12,18 @@ class EffectChorusImp; #define AMUSE_CHORUS_NUM_BLOCKS 3 +/** Parameters needed to create EffectChorus */ +struct EffectChorusInfo +{ + uint32_t baseDelay = 5; /**< [5, 15] minimum value (in ms) for computed delay */ + uint32_t variation = 0; /**< [0, 5] time error (in ms) to set delay within */ + uint32_t period = 500; /**< [500, 10000] time (in ms) of one delay-shift cycle */ + + EffectChorusInfo() = default; + EffectChorusInfo(uint32_t baseDelay, uint32_t variation, uint32_t period) + : baseDelay(baseDelay), variation(variation), period(period) {} +}; + /** Mixes the audio back into itself after continuously-varying delay */ class EffectChorus { @@ -48,6 +60,13 @@ public: x98_period = period; m_dirty = true; } + + void updateParams(const EffectChorusInfo& info) + { + setBaseDelay(info.baseDelay); + setVariation(info.variation); + setPeriod(info.period); + } }; /** Type-specific implementation of chorus effect */ @@ -91,6 +110,9 @@ class EffectChorusImp : public EffectBase, public EffectChorus public: ~EffectChorusImp(); EffectChorusImp(uint32_t baseDelay, uint32_t variation, uint32_t period, double sampleRate); + EffectChorusImp(const EffectChorusInfo& info, double sampleRate) + : EffectChorusImp(info.baseDelay, info.variation, info.period, sampleRate) {} + void applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap); void resetOutputSampleRate(double sampleRate) { _setup(sampleRate); } }; diff --git a/include/amuse/EffectDelay.hpp b/include/amuse/EffectDelay.hpp index ab981f3..3c94b23 100644 --- a/include/amuse/EffectDelay.hpp +++ b/include/amuse/EffectDelay.hpp @@ -2,6 +2,7 @@ #define __AMUSE_EFFECTDELAY_HPP__ #include "EffectBase.hpp" +#include "IBackendVoice.hpp" #include "Common.hpp" #include #include @@ -11,6 +12,38 @@ namespace amuse template class EffectDelayImp; +/** Parameters needed to create EffectDelay */ +struct EffectDelayInfo +{ + uint32_t delay[8]; /**< [10, 5000] time in ms of each channel's delay */ + uint32_t feedback[8] = {}; /**< [0, 100] percent to mix delayed signal with input signal */ + uint32_t output[8] = {}; /**< [0, 100] total output percent */ + + static uint32_t lerp(uint32_t v0, uint32_t v1, float t) { return (1.f - t) * v0 + t * v1; } + + static void Interp3To8(uint32_t arr[8], uint32_t L, uint32_t R, uint32_t S) + { + arr[int(AudioChannel::FrontLeft)] = L; + arr[int(AudioChannel::FrontRight)] = R; + arr[int(AudioChannel::RearLeft)] = lerp(L, S, 0.75f); + arr[int(AudioChannel::RearRight)] = lerp(R, S, 0.75f); + arr[int(AudioChannel::FrontCenter)] = lerp(L, R, 0.5f); + arr[int(AudioChannel::LFE)] = arr[int(AudioChannel::FrontCenter)]; + arr[int(AudioChannel::SideLeft)] = lerp(L, S, 0.5f); + arr[int(AudioChannel::SideRight)] = lerp(R, S, 0.5f); + } + + EffectDelayInfo() { std::fill_n(delay, 8, 10); } + EffectDelayInfo(uint32_t delayL, uint32_t delayR, uint32_t delayS, + uint32_t feedbackL, uint32_t feedbackR, uint32_t feedbackS, + uint32_t outputL, uint32_t outputR, uint32_t outputS) + { + Interp3To8(delay, delayL, delayR, delayS); + Interp3To8(feedback, feedbackL, feedbackR, feedbackS); + Interp3To8(output, outputL, outputR, outputS); + } +}; + /** Mixes the audio back into itself after specified delay */ class EffectDelay { @@ -59,12 +92,24 @@ public: x54_output[i] = output; m_dirty = true; } + void setChanOutput(int chanIdx, uint32_t output) { output = clamp(0u, output, 100u); x54_output[chanIdx] = output; m_dirty = true; } + + void setParams(const EffectDelayInfo& info) + { + for (int i = 0; i < 8; ++i) + { + x3c_delay[i] = clamp(10u, info.delay[i], 5000u); + x48_feedback[i] = clamp(0u, info.feedback[i], 100u); + x54_output[i] = clamp(0u, info.output[i], 100u); + } + m_dirty = true; + } }; /** Type-specific implementation of delay effect */ @@ -85,6 +130,8 @@ class EffectDelayImp : public EffectBase, public EffectDelay public: EffectDelayImp(uint32_t initDelay, uint32_t initFeedback, uint32_t initOutput, double sampleRate); + EffectDelayImp(const EffectDelayInfo& info, double sampleRate); + void applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap); void resetOutputSampleRate(double sampleRate) { _setup(sampleRate); } }; diff --git a/include/amuse/EffectReverb.hpp b/include/amuse/EffectReverb.hpp index 3bffae0..e4cb047 100644 --- a/include/amuse/EffectReverb.hpp +++ b/include/amuse/EffectReverb.hpp @@ -8,6 +8,35 @@ namespace amuse { +/** Parameters needed to create EffectReverbStd */ +struct EffectReverbStdInfo +{ + float coloration = 0.f; /**< [0.0, 1.0] influences filter coefficients to define surface characteristics of a room */ + float mix = 0.f; /**< [0.0, 1.0] dry/wet mix factor of reverb effect */ + float time = 0.01f; /**< [0.01, 10.0] time in seconds for reflection decay */ + float damping = 0.f; /**< [0.0, 1.0] damping factor influencing low-pass filter of reflections */ + float preDelay = 0.f; /**< [0.0, 0.1] time in seconds before initial reflection heard */ + + EffectReverbStdInfo() = default; + EffectReverbStdInfo(float coloration, float mix, float time, float damping, float preDelay) + : coloration(coloration), mix(mix), time(time), damping(damping), preDelay(preDelay) {} +}; + +/** Parameters needed to create EffectReverbHi */ +struct EffectReverbHiInfo +{ + float coloration = 0.f; /**< [0.0, 1.0] influences filter coefficients to define surface characteristics of a room */ + float mix = 0.f; /**< [0.0, 1.0] dry/wet mix factor of reverb effect */ + float time = 0.01f; /**< [0.01, 10.0] time in seconds for reflection decay */ + float damping = 0.f; /**< [0.0, 1.0] damping factor influencing low-pass filter of reflections */ + float preDelay = 0.f; /**< [0.0, 0.1] time in seconds before initial reflection heard */ + float crosstalk = 0.f; /**< [0.0, 1.0] factor defining how much reflections are allowed to bleed to other channels */ + + EffectReverbHiInfo() = default; + EffectReverbHiInfo(float coloration, float mix, float time, float damping, float preDelay, float crosstalk) + : coloration(coloration), mix(mix), time(time), damping(damping), preDelay(preDelay), crosstalk(crosstalk) {} +}; + /** Delay state for one 'tap' of the reverb effect */ struct ReverbDelayLine { @@ -78,6 +107,15 @@ public: x150_x1d8_preDelay = clamp(0.f, preDelay, 0.1f); m_dirty = true; } + + void setParams(const EffectReverbStdInfo& info) + { + setColoration(info.coloration); + setMix(info.mix); + setTime(info.time); + setDamping(info.damping); + setPreDelay(info.preDelay); + } }; /** Reverb effect with configurable reflection filtering, adds per-channel low-pass and crosstalk */ @@ -98,6 +136,16 @@ public: x1dc_crosstalk = clamp(0.f, crosstalk, 1.f); m_dirty = true; } + + void setParams(const EffectReverbHiInfo& info) + { + setColoration(info.coloration); + setMix(info.mix); + setTime(info.time); + setDamping(info.damping); + setPreDelay(info.preDelay); + setCrosstalk(info.crosstalk); + } }; /** Standard-quality 2-stage reverb */ @@ -121,6 +169,9 @@ class EffectReverbStdImp : public EffectBase, public EffectReverbStd public: EffectReverbStdImp(float coloration, float mix, float time, float damping, float preDelay, double sampleRate); + EffectReverbStdImp(const EffectReverbStdInfo& info, double sampleRate) + : EffectReverbStdImp(info.coloration, info.mix, info.time, info.damping, info.preDelay, sampleRate) {} + void applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap); void resetOutputSampleRate(double sampleRate) { _setup(sampleRate); } }; @@ -151,6 +202,9 @@ class EffectReverbHiImp : public EffectBase, public EffectReverbHi public: EffectReverbHiImp(float coloration, float mix, float time, float damping, float preDelay, float crosstalk, double sampleRate); + EffectReverbHiImp(const EffectReverbHiInfo& info, double sampleRate) + : EffectReverbHiImp(info.coloration, info.mix, info.time, info.damping, info.preDelay, info.crosstalk, sampleRate) {} + void applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap); void resetOutputSampleRate(double sampleRate) { _setup(sampleRate); } }; diff --git a/include/amuse/IBackendVoiceAllocator.hpp b/include/amuse/IBackendVoiceAllocator.hpp index ecfd7df..9490c86 100644 --- a/include/amuse/IBackendVoiceAllocator.hpp +++ b/include/amuse/IBackendVoiceAllocator.hpp @@ -61,6 +61,9 @@ public: /** Amuse may request callbacks 200-updates-per-second virtually */ virtual void register5MsCallback(std::function&& callback) = 0; + + /** This is important to ensure orderly cleanup */ + virtual void unregister5MsCallback() = 0; }; } diff --git a/include/amuse/Submix.hpp b/include/amuse/Submix.hpp index 0126627..80b25c5 100644 --- a/include/amuse/Submix.hpp +++ b/include/amuse/Submix.hpp @@ -60,16 +60,28 @@ public: /** Add new chorus effect to effect stack and assume ownership */ EffectChorus& makeChorus(uint32_t baseDelay, uint32_t variation, uint32_t period); + /** Add new chorus effect to effect stack and assume ownership */ + EffectChorus& makeChorus(const EffectChorusInfo& info); + /** Add new delay effect to effect stack and assume ownership */ EffectDelay& makeDelay(uint32_t initDelay, uint32_t initFeedback, uint32_t initOutput); + /** Add new delay effect to effect stack and assume ownership */ + EffectDelay& makeDelay(const EffectDelayInfo& info); + /** Add new standard-quality reverb effect to effect stack and assume ownership */ EffectReverbStd& makeReverbStd(float coloration, float mix, float time, float damping, float preDelay); + /** Add new standard-quality reverb effect to effect stack and assume ownership */ + EffectReverbStd& makeReverbStd(const EffectReverbStdInfo& info); + /** Add new high-quality reverb effect to effect stack and assume ownership */ EffectReverbHi& makeReverbHi(float coloration, float mix, float time, float damping, float preDelay, float crosstalk); + /** Add new high-quality reverb effect to effect stack and assume ownership */ + EffectReverbHi& makeReverbHi(const EffectReverbHiInfo& info); + /** Remove and deallocate all effects from effect stack */ void clearEffects() { m_effectStack.clear(); } diff --git a/lib/BooBackend.cpp b/lib/BooBackend.cpp index 8dad539..44f796e 100644 --- a/lib/BooBackend.cpp +++ b/lib/BooBackend.cpp @@ -285,6 +285,11 @@ void BooBackendVoiceAllocator::register5MsCallback(std::function&& m_booEngine.register5MsCallback(std::move(callback)); } +void BooBackendVoiceAllocator::unregister5MsCallback() +{ + m_booEngine.unregister5MsCallback(); +} + AudioChannelSet BooBackendVoiceAllocator::getAvailableSet() { return AudioChannelSet(m_booEngine.getAvailableSet()); } void BooBackendVoiceAllocator::pumpAndMixVoices() { m_booEngine.pumpAndMixVoices(); } diff --git a/lib/EffectDelay.cpp b/lib/EffectDelay.cpp index dfc5a8e..c6094df 100644 --- a/lib/EffectDelay.cpp +++ b/lib/EffectDelay.cpp @@ -24,6 +24,19 @@ EffectDelayImp::EffectDelayImp(uint32_t initDelay, uint32_t initFeedback, uin _setup(sampleRate); } +template +EffectDelayImp::EffectDelayImp(const EffectDelayInfo& info, double sampleRate) +{ + for (int i = 0; i < 8; ++i) + { + x3c_delay[i] = clamp(10u, info.delay[i], 5000u); + x48_feedback[i] = clamp(0u, info.feedback[i], 100u); + x54_output[i] = clamp(0u, info.output[i], 100u); + } + + _setup(sampleRate); +} + template void EffectDelayImp::_setup(double sampleRate) { diff --git a/lib/Engine.cpp b/lib/Engine.cpp index f6e0345..f43f546 100644 --- a/lib/Engine.cpp +++ b/lib/Engine.cpp @@ -15,6 +15,7 @@ static const float FullLevels[8] = {1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f}; Engine::~Engine() { + m_backend.unregister5MsCallback(); for (std::shared_ptr& seq : m_activeSequencers) if (!seq->m_destroyed) seq->_destroy(); diff --git a/lib/Submix.cpp b/lib/Submix.cpp index 01a98b2..83b0e81 100644 --- a/lib/Submix.cpp +++ b/lib/Submix.cpp @@ -10,22 +10,42 @@ EffectChorus& Submix::makeChorus(uint32_t baseDelay, uint32_t variation, uint32_ return makeEffect(baseDelay, variation, period); } +EffectChorus& Submix::makeChorus(const EffectChorusInfo& info) +{ + return makeEffect(info); +} + EffectDelay& Submix::makeDelay(uint32_t initDelay, uint32_t initFeedback, uint32_t initOutput) { return makeEffect(initDelay, initFeedback, initOutput); } +EffectDelay& Submix::makeDelay(const EffectDelayInfo& info) +{ + return makeEffect(info); +} + EffectReverbStd& Submix::makeReverbStd(float coloration, float mix, float time, float damping, float preDelay) { return makeEffect(coloration, mix, time, damping, preDelay); } +EffectReverbStd& Submix::makeReverbStd(const EffectReverbStdInfo& info) +{ + return makeEffect(info); +} + EffectReverbHi& Submix::makeReverbHi(float coloration, float mix, float time, float damping, float preDelay, float crosstalk) { return makeEffect(coloration, mix, time, damping, preDelay, crosstalk); } +EffectReverbHi& Submix::makeReverbHi(const EffectReverbHiInfo& info) +{ + return makeEffect(info); +} + void Submix::applyEffect(int16_t* audio, size_t frameCount, const ChannelMap& chanMap) const { for (const std::unique_ptr& effect : m_effectStack)