echo.cpp 5.6 KB

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