autowah.cpp 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. /**
  2. * OpenAL cross platform audio library
  3. * Copyright (C) 2018 by Raul Herraiz.
  4. * This library is free software; you can redistribute it and/or
  5. * modify it under the terms of the GNU Library General Public
  6. * License as published by the Free Software Foundation; either
  7. * version 2 of the License, or (at your option) any later version.
  8. *
  9. * This library is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. * Library General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU Library General Public
  15. * License along with this library; if not, write to the
  16. * Free Software Foundation, Inc.,
  17. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  18. * Or go to http://www.gnu.org/copyleft/lgpl.html
  19. */
  20. #include "config.h"
  21. #include <cmath>
  22. #include <cstdlib>
  23. #include <algorithm>
  24. #include "alcmain.h"
  25. #include "alcontext.h"
  26. #include "core/filters/biquad.h"
  27. #include "effectslot.h"
  28. #include "vecmat.h"
  29. namespace {
  30. constexpr float GainScale{31621.0f};
  31. constexpr float MinFreq{20.0f};
  32. constexpr float MaxFreq{2500.0f};
  33. constexpr float QFactor{5.0f};
  34. struct AutowahState final : public EffectState {
  35. /* Effect parameters */
  36. float mAttackRate;
  37. float mReleaseRate;
  38. float mResonanceGain;
  39. float mPeakGain;
  40. float mFreqMinNorm;
  41. float mBandwidthNorm;
  42. float mEnvDelay;
  43. /* Filter components derived from the envelope. */
  44. struct {
  45. float cos_w0;
  46. float alpha;
  47. } mEnv[BufferLineSize];
  48. struct {
  49. /* Effect filters' history. */
  50. struct {
  51. float z1, z2;
  52. } Filter;
  53. /* Effect gains for each output channel */
  54. float CurrentGains[MAX_OUTPUT_CHANNELS];
  55. float TargetGains[MAX_OUTPUT_CHANNELS];
  56. } mChans[MaxAmbiChannels];
  57. /* Effects buffers */
  58. alignas(16) float mBufferOut[BufferLineSize];
  59. void deviceUpdate(const ALCdevice *device, const Buffer &buffer) override;
  60. void update(const ALCcontext *context, const EffectSlot *slot, const EffectProps *props,
  61. const EffectTarget target) override;
  62. void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn,
  63. const al::span<FloatBufferLine> samplesOut) override;
  64. DEF_NEWDEL(AutowahState)
  65. };
  66. void AutowahState::deviceUpdate(const ALCdevice*, const Buffer&)
  67. {
  68. /* (Re-)initializing parameters and clear the buffers. */
  69. mAttackRate = 1.0f;
  70. mReleaseRate = 1.0f;
  71. mResonanceGain = 10.0f;
  72. mPeakGain = 4.5f;
  73. mFreqMinNorm = 4.5e-4f;
  74. mBandwidthNorm = 0.05f;
  75. mEnvDelay = 0.0f;
  76. for(auto &e : mEnv)
  77. {
  78. e.cos_w0 = 0.0f;
  79. e.alpha = 0.0f;
  80. }
  81. for(auto &chan : mChans)
  82. {
  83. std::fill(std::begin(chan.CurrentGains), std::end(chan.CurrentGains), 0.0f);
  84. chan.Filter.z1 = 0.0f;
  85. chan.Filter.z2 = 0.0f;
  86. }
  87. }
  88. void AutowahState::update(const ALCcontext *context, const EffectSlot *slot,
  89. const EffectProps *props, const EffectTarget target)
  90. {
  91. const ALCdevice *device{context->mDevice.get()};
  92. const auto frequency = static_cast<float>(device->Frequency);
  93. const float ReleaseTime{clampf(props->Autowah.ReleaseTime, 0.001f, 1.0f)};
  94. mAttackRate = std::exp(-1.0f / (props->Autowah.AttackTime*frequency));
  95. mReleaseRate = std::exp(-1.0f / (ReleaseTime*frequency));
  96. /* 0-20dB Resonance Peak gain */
  97. mResonanceGain = std::sqrt(std::log10(props->Autowah.Resonance)*10.0f / 3.0f);
  98. mPeakGain = 1.0f - std::log10(props->Autowah.PeakGain / GainScale);
  99. mFreqMinNorm = MinFreq / frequency;
  100. mBandwidthNorm = (MaxFreq-MinFreq) / frequency;
  101. mOutTarget = target.Main->Buffer;
  102. auto set_gains = [slot,target](auto &chan, al::span<const float,MaxAmbiChannels> coeffs)
  103. { ComputePanGains(target.Main, coeffs.data(), slot->Gain, chan.TargetGains); };
  104. SetAmbiPanIdentity(std::begin(mChans), slot->Wet.Buffer.size(), set_gains);
  105. }
  106. void AutowahState::process(const size_t samplesToDo,
  107. const al::span<const FloatBufferLine> samplesIn, const al::span<FloatBufferLine> samplesOut)
  108. {
  109. const float attack_rate{mAttackRate};
  110. const float release_rate{mReleaseRate};
  111. const float res_gain{mResonanceGain};
  112. const float peak_gain{mPeakGain};
  113. const float freq_min{mFreqMinNorm};
  114. const float bandwidth{mBandwidthNorm};
  115. float env_delay{mEnvDelay};
  116. for(size_t i{0u};i < samplesToDo;i++)
  117. {
  118. float w0, sample, a;
  119. /* Envelope follower described on the book: Audio Effects, Theory,
  120. * Implementation and Application.
  121. */
  122. sample = peak_gain * std::fabs(samplesIn[0][i]);
  123. a = (sample > env_delay) ? attack_rate : release_rate;
  124. env_delay = lerp(sample, env_delay, a);
  125. /* Calculate the cos and alpha components for this sample's filter. */
  126. w0 = minf((bandwidth*env_delay + freq_min), 0.46f) * al::MathDefs<float>::Tau();
  127. mEnv[i].cos_w0 = std::cos(w0);
  128. mEnv[i].alpha = std::sin(w0)/(2.0f * QFactor);
  129. }
  130. mEnvDelay = env_delay;
  131. auto chandata = std::addressof(mChans[0]);
  132. for(const auto &insamples : samplesIn)
  133. {
  134. /* This effectively inlines BiquadFilter_setParams for a peaking
  135. * filter and BiquadFilter_processC. The alpha and cosine components
  136. * for the filter coefficients were previously calculated with the
  137. * envelope. Because the filter changes for each sample, the
  138. * coefficients are transient and don't need to be held.
  139. */
  140. float z1{chandata->Filter.z1};
  141. float z2{chandata->Filter.z2};
  142. for(size_t i{0u};i < samplesToDo;i++)
  143. {
  144. const float alpha{mEnv[i].alpha};
  145. const float cos_w0{mEnv[i].cos_w0};
  146. float input, output;
  147. float a[3], b[3];
  148. b[0] = 1.0f + alpha*res_gain;
  149. b[1] = -2.0f * cos_w0;
  150. b[2] = 1.0f - alpha*res_gain;
  151. a[0] = 1.0f + alpha/res_gain;
  152. a[1] = -2.0f * cos_w0;
  153. a[2] = 1.0f - alpha/res_gain;
  154. input = insamples[i];
  155. output = input*(b[0]/a[0]) + z1;
  156. z1 = input*(b[1]/a[0]) - output*(a[1]/a[0]) + z2;
  157. z2 = input*(b[2]/a[0]) - output*(a[2]/a[0]);
  158. mBufferOut[i] = output;
  159. }
  160. chandata->Filter.z1 = z1;
  161. chandata->Filter.z2 = z2;
  162. /* Now, mix the processed sound data to the output. */
  163. MixSamples({mBufferOut, samplesToDo}, samplesOut, chandata->CurrentGains,
  164. chandata->TargetGains, samplesToDo, 0);
  165. ++chandata;
  166. }
  167. }
  168. struct AutowahStateFactory final : public EffectStateFactory {
  169. al::intrusive_ptr<EffectState> create() override
  170. { return al::intrusive_ptr<EffectState>{new AutowahState{}}; }
  171. };
  172. } // namespace
  173. EffectStateFactory *AutowahStateFactory_getFactory()
  174. {
  175. static AutowahStateFactory AutowahFactory{};
  176. return &AutowahFactory;
  177. }