🛠️🐜 Antkeeper superbuild with dependencies included https://antkeeper.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

180 lines
5.9 KiB

  1. /**
  2. * OpenAL cross platform audio library
  3. * Copyright (C) 2009 by Chris Robinson.
  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 <cstdlib>
  24. #include <iterator>
  25. #include <tuple>
  26. #include "alc/effects/base.h"
  27. #include "almalloc.h"
  28. #include "alnumeric.h"
  29. #include "alspan.h"
  30. #include "core/bufferline.h"
  31. #include "core/context.h"
  32. #include "core/devformat.h"
  33. #include "core/device.h"
  34. #include "core/effectslot.h"
  35. #include "core/filters/biquad.h"
  36. #include "core/mixer.h"
  37. #include "intrusive_ptr.h"
  38. #include "opthelpers.h"
  39. #include "vector.h"
  40. namespace {
  41. using uint = unsigned int;
  42. constexpr float LowpassFreqRef{5000.0f};
  43. struct EchoState final : public EffectState {
  44. al::vector<float,16> mSampleBuffer;
  45. // The echo is two tap. The delay is the number of samples from before the
  46. // current offset
  47. struct {
  48. size_t delay{0u};
  49. } mTap[2];
  50. size_t mOffset{0u};
  51. /* The panning gains for the two taps */
  52. struct {
  53. float Current[MAX_OUTPUT_CHANNELS]{};
  54. float Target[MAX_OUTPUT_CHANNELS]{};
  55. } mGains[2];
  56. BiquadFilter mFilter;
  57. float mFeedGain{0.0f};
  58. alignas(16) float mTempBuffer[2][BufferLineSize];
  59. void deviceUpdate(const DeviceBase *device, const Buffer &buffer) override;
  60. void update(const ContextBase *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(EchoState)
  65. };
  66. void EchoState::deviceUpdate(const DeviceBase *Device, const Buffer&)
  67. {
  68. const auto frequency = static_cast<float>(Device->Frequency);
  69. // Use the next power of 2 for the buffer length, so the tap offsets can be
  70. // wrapped using a mask instead of a modulo
  71. const uint maxlen{NextPowerOf2(float2uint(EchoMaxDelay*frequency + 0.5f) +
  72. float2uint(EchoMaxLRDelay*frequency + 0.5f))};
  73. if(maxlen != mSampleBuffer.size())
  74. al::vector<float,16>(maxlen).swap(mSampleBuffer);
  75. std::fill(mSampleBuffer.begin(), mSampleBuffer.end(), 0.0f);
  76. for(auto &e : mGains)
  77. {
  78. std::fill(std::begin(e.Current), std::end(e.Current), 0.0f);
  79. std::fill(std::begin(e.Target), std::end(e.Target), 0.0f);
  80. }
  81. }
  82. void EchoState::update(const ContextBase *context, const EffectSlot *slot,
  83. const EffectProps *props, const EffectTarget target)
  84. {
  85. const DeviceBase *device{context->mDevice};
  86. const auto frequency = static_cast<float>(device->Frequency);
  87. mTap[0].delay = maxu(float2uint(props->Echo.Delay*frequency + 0.5f), 1);
  88. mTap[1].delay = float2uint(props->Echo.LRDelay*frequency + 0.5f) + mTap[0].delay;
  89. const float gainhf{maxf(1.0f - props->Echo.Damping, 0.0625f)}; /* Limit -24dB */
  90. mFilter.setParamsFromSlope(BiquadType::HighShelf, LowpassFreqRef/frequency, gainhf, 1.0f);
  91. mFeedGain = props->Echo.Feedback;
  92. /* Convert echo spread (where 0 = center, +/-1 = sides) to angle. */
  93. const float angle{std::asin(props->Echo.Spread)};
  94. const auto coeffs0 = CalcAngleCoeffs(-angle, 0.0f, 0.0f);
  95. const auto coeffs1 = CalcAngleCoeffs( angle, 0.0f, 0.0f);
  96. mOutTarget = target.Main->Buffer;
  97. ComputePanGains(target.Main, coeffs0.data(), slot->Gain, mGains[0].Target);
  98. ComputePanGains(target.Main, coeffs1.data(), slot->Gain, mGains[1].Target);
  99. }
  100. void EchoState::process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn, const al::span<FloatBufferLine> samplesOut)
  101. {
  102. const size_t mask{mSampleBuffer.size()-1};
  103. float *RESTRICT delaybuf{mSampleBuffer.data()};
  104. size_t offset{mOffset};
  105. size_t tap1{offset - mTap[0].delay};
  106. size_t tap2{offset - mTap[1].delay};
  107. float z1, z2;
  108. ASSUME(samplesToDo > 0);
  109. const BiquadFilter filter{mFilter};
  110. std::tie(z1, z2) = mFilter.getComponents();
  111. for(size_t i{0u};i < samplesToDo;)
  112. {
  113. offset &= mask;
  114. tap1 &= mask;
  115. tap2 &= mask;
  116. size_t td{minz(mask+1 - maxz(offset, maxz(tap1, tap2)), samplesToDo-i)};
  117. do {
  118. /* Feed the delay buffer's input first. */
  119. delaybuf[offset] = samplesIn[0][i];
  120. /* Get delayed output from the first and second taps. Use the
  121. * second tap for feedback.
  122. */
  123. mTempBuffer[0][i] = delaybuf[tap1++];
  124. mTempBuffer[1][i] = delaybuf[tap2++];
  125. const float feedb{mTempBuffer[1][i++]};
  126. /* Add feedback to the delay buffer with damping and attenuation. */
  127. delaybuf[offset++] += filter.processOne(feedb, z1, z2) * mFeedGain;
  128. } while(--td);
  129. }
  130. mFilter.setComponents(z1, z2);
  131. mOffset = offset;
  132. for(size_t c{0};c < 2;c++)
  133. MixSamples({mTempBuffer[c], samplesToDo}, samplesOut, mGains[c].Current, mGains[c].Target,
  134. samplesToDo, 0);
  135. }
  136. struct EchoStateFactory final : public EffectStateFactory {
  137. al::intrusive_ptr<EffectState> create() override
  138. { return al::intrusive_ptr<EffectState>{new EchoState{}}; }
  139. };
  140. } // namespace
  141. EffectStateFactory *EchoStateFactory_getFactory()
  142. {
  143. static EchoStateFactory EchoFactory{};
  144. return &EchoFactory;
  145. }