autowah.cpp 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  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 <algorithm>
  22. #include <array>
  23. #include <cmath>
  24. #include <cstdlib>
  25. #include <variant>
  26. #include "alc/effects/base.h"
  27. #include "alnumbers.h"
  28. #include "alnumeric.h"
  29. #include "alspan.h"
  30. #include "core/ambidefs.h"
  31. #include "core/bufferline.h"
  32. #include "core/context.h"
  33. #include "core/device.h"
  34. #include "core/effects/base.h"
  35. #include "core/effectslot.h"
  36. #include "core/mixer.h"
  37. #include "intrusive_ptr.h"
  38. struct BufferStorage;
  39. namespace {
  40. constexpr float GainScale{31621.0f};
  41. constexpr float MinFreq{20.0f};
  42. constexpr float MaxFreq{2500.0f};
  43. constexpr float QFactor{5.0f};
  44. struct AutowahState final : public EffectState {
  45. /* Effect parameters */
  46. float mAttackRate{};
  47. float mReleaseRate{};
  48. float mResonanceGain{};
  49. float mPeakGain{};
  50. float mFreqMinNorm{};
  51. float mBandwidthNorm{};
  52. float mEnvDelay{};
  53. /* Filter components derived from the envelope. */
  54. struct FilterParam {
  55. float cos_w0{};
  56. float alpha{};
  57. };
  58. std::array<FilterParam,BufferLineSize> mEnv;
  59. struct ChannelData {
  60. uint mTargetChannel{InvalidChannelIndex};
  61. struct FilterHistory {
  62. float z1{}, z2{};
  63. };
  64. FilterHistory mFilter;
  65. /* Effect gains for each output channel */
  66. float mCurrentGain{};
  67. float mTargetGain{};
  68. };
  69. std::array<ChannelData,MaxAmbiChannels> mChans;
  70. /* Effects buffers */
  71. alignas(16) FloatBufferLine mBufferOut{};
  72. void deviceUpdate(const DeviceBase *device, const BufferStorage *buffer) override;
  73. void update(const ContextBase *context, const EffectSlot *slot, const EffectProps *props,
  74. const EffectTarget target) override;
  75. void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn,
  76. const al::span<FloatBufferLine> samplesOut) override;
  77. };
  78. void AutowahState::deviceUpdate(const DeviceBase*, const BufferStorage*)
  79. {
  80. /* (Re-)initializing parameters and clear the buffers. */
  81. mAttackRate = 1.0f;
  82. mReleaseRate = 1.0f;
  83. mResonanceGain = 10.0f;
  84. mPeakGain = 4.5f;
  85. mFreqMinNorm = 4.5e-4f;
  86. mBandwidthNorm = 0.05f;
  87. mEnvDelay = 0.0f;
  88. for(auto &e : mEnv)
  89. {
  90. e.cos_w0 = 0.0f;
  91. e.alpha = 0.0f;
  92. }
  93. for(auto &chan : mChans)
  94. {
  95. chan.mTargetChannel = InvalidChannelIndex;
  96. chan.mFilter.z1 = 0.0f;
  97. chan.mFilter.z2 = 0.0f;
  98. chan.mCurrentGain = 0.0f;
  99. }
  100. }
  101. void AutowahState::update(const ContextBase *context, const EffectSlot *slot,
  102. const EffectProps *props_, const EffectTarget target)
  103. {
  104. auto &props = std::get<AutowahProps>(*props_);
  105. const DeviceBase *device{context->mDevice};
  106. const auto frequency = static_cast<float>(device->Frequency);
  107. const float ReleaseTime{std::clamp(props.ReleaseTime, 0.001f, 1.0f)};
  108. mAttackRate = std::exp(-1.0f / (props.AttackTime*frequency));
  109. mReleaseRate = std::exp(-1.0f / (ReleaseTime*frequency));
  110. /* 0-20dB Resonance Peak gain */
  111. mResonanceGain = std::sqrt(std::log10(props.Resonance)*10.0f / 3.0f);
  112. mPeakGain = 1.0f - std::log10(props.PeakGain / GainScale);
  113. mFreqMinNorm = MinFreq / frequency;
  114. mBandwidthNorm = (MaxFreq-MinFreq) / frequency;
  115. mOutTarget = target.Main->Buffer;
  116. auto set_channel = [this](size_t idx, uint outchan, float outgain)
  117. {
  118. mChans[idx].mTargetChannel = outchan;
  119. mChans[idx].mTargetGain = outgain;
  120. };
  121. target.Main->setAmbiMixParams(slot->Wet, slot->Gain, set_channel);
  122. }
  123. void AutowahState::process(const size_t samplesToDo,
  124. const al::span<const FloatBufferLine> samplesIn, const al::span<FloatBufferLine> samplesOut)
  125. {
  126. const float attack_rate{mAttackRate};
  127. const float release_rate{mReleaseRate};
  128. const float res_gain{mResonanceGain};
  129. const float peak_gain{mPeakGain};
  130. const float freq_min{mFreqMinNorm};
  131. const float bandwidth{mBandwidthNorm};
  132. float env_delay{mEnvDelay};
  133. for(size_t i{0u};i < samplesToDo;i++)
  134. {
  135. /* Envelope follower described on the book: Audio Effects, Theory,
  136. * Implementation and Application.
  137. */
  138. const float sample{peak_gain * std::fabs(samplesIn[0][i])};
  139. const float a{(sample > env_delay) ? attack_rate : release_rate};
  140. env_delay = lerpf(sample, env_delay, a);
  141. /* Calculate the cos and alpha components for this sample's filter. */
  142. const float w0{std::min(bandwidth*env_delay + freq_min, 0.46f) *
  143. (al::numbers::pi_v<float>*2.0f)};
  144. mEnv[i].cos_w0 = std::cos(w0);
  145. mEnv[i].alpha = std::sin(w0)/(2.0f * QFactor);
  146. }
  147. mEnvDelay = env_delay;
  148. auto chandata = mChans.begin();
  149. for(const auto &insamples : samplesIn)
  150. {
  151. const size_t outidx{chandata->mTargetChannel};
  152. if(outidx == InvalidChannelIndex)
  153. {
  154. ++chandata;
  155. continue;
  156. }
  157. /* This effectively inlines BiquadFilter_setParams for a peaking
  158. * filter and BiquadFilter_processC. The alpha and cosine components
  159. * for the filter coefficients were previously calculated with the
  160. * envelope. Because the filter changes for each sample, the
  161. * coefficients are transient and don't need to be held.
  162. */
  163. float z1{chandata->mFilter.z1};
  164. float z2{chandata->mFilter.z2};
  165. for(size_t i{0u};i < samplesToDo;i++)
  166. {
  167. const float alpha{mEnv[i].alpha};
  168. const float cos_w0{mEnv[i].cos_w0};
  169. const std::array b{
  170. 1.0f + alpha*res_gain,
  171. -2.0f * cos_w0,
  172. 1.0f - alpha*res_gain};
  173. const std::array a{
  174. 1.0f + alpha/res_gain,
  175. -2.0f * cos_w0,
  176. 1.0f - alpha/res_gain};
  177. const float input{insamples[i]};
  178. const float output{input*(b[0]/a[0]) + z1};
  179. z1 = input*(b[1]/a[0]) - output*(a[1]/a[0]) + z2;
  180. z2 = input*(b[2]/a[0]) - output*(a[2]/a[0]);
  181. mBufferOut[i] = output;
  182. }
  183. chandata->mFilter.z1 = z1;
  184. chandata->mFilter.z2 = z2;
  185. /* Now, mix the processed sound data to the output. */
  186. MixSamples(al::span{mBufferOut}.first(samplesToDo), samplesOut[outidx],
  187. chandata->mCurrentGain, chandata->mTargetGain, samplesToDo);
  188. ++chandata;
  189. }
  190. }
  191. struct AutowahStateFactory final : public EffectStateFactory {
  192. al::intrusive_ptr<EffectState> create() override
  193. { return al::intrusive_ptr<EffectState>{new AutowahState{}}; }
  194. };
  195. } // namespace
  196. EffectStateFactory *AutowahStateFactory_getFactory()
  197. {
  198. static AutowahStateFactory AutowahFactory{};
  199. return &AutowahFactory;
  200. }