🛠️🐜 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.

192 lines
8.4 KiB

  1. /**
  2. * OpenAL cross platform audio library
  3. * Copyright (C) 2013 by Mike Gorchak
  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 <functional>
  25. #include <iterator>
  26. #include <utility>
  27. #include "alc/effects/base.h"
  28. #include "almalloc.h"
  29. #include "alspan.h"
  30. #include "core/ambidefs.h"
  31. #include "core/bufferline.h"
  32. #include "core/context.h"
  33. #include "core/devformat.h"
  34. #include "core/device.h"
  35. #include "core/effectslot.h"
  36. #include "core/filters/biquad.h"
  37. #include "core/mixer.h"
  38. #include "intrusive_ptr.h"
  39. namespace {
  40. /* The document "Effects Extension Guide.pdf" says that low and high *
  41. * frequencies are cutoff frequencies. This is not fully correct, they *
  42. * are corner frequencies for low and high shelf filters. If they were *
  43. * just cutoff frequencies, there would be no need in cutoff frequency *
  44. * gains, which are present. Documentation for "Creative Proteus X2" *
  45. * software describes 4-band equalizer functionality in a much better *
  46. * way. This equalizer seems to be a predecessor of OpenAL 4-band *
  47. * equalizer. With low and high shelf filters we are able to cutoff *
  48. * frequencies below and/or above corner frequencies using attenuation *
  49. * gains (below 1.0) and amplify all low and/or high frequencies using *
  50. * gains above 1.0. *
  51. * *
  52. * Low-shelf Low Mid Band High Mid Band High-shelf *
  53. * corner center center corner *
  54. * frequency frequency frequency frequency *
  55. * 50Hz..800Hz 200Hz..3000Hz 1000Hz..8000Hz 4000Hz..16000Hz *
  56. * *
  57. * | | | | *
  58. * | | | | *
  59. * B -----+ /--+--\ /--+--\ +----- *
  60. * O |\ | | | | | | /| *
  61. * O | \ - | - - | - / | *
  62. * S + | \ | | | | | | / | *
  63. * T | | | | | | | | | | *
  64. * ---------+---------------+------------------+---------------+-------- *
  65. * C | | | | | | | | | | *
  66. * U - | / | | | | | | \ | *
  67. * T | / - | - - | - \ | *
  68. * O |/ | | | | | | \| *
  69. * F -----+ \--+--/ \--+--/ +----- *
  70. * F | | | | *
  71. * | | | | *
  72. * *
  73. * Gains vary from 0.126 up to 7.943, which means from -18dB attenuation *
  74. * up to +18dB amplification. Band width varies from 0.01 up to 1.0 in *
  75. * octaves for two mid bands. *
  76. * *
  77. * Implementation is based on the "Cookbook formulae for audio EQ biquad *
  78. * filter coefficients" by Robert Bristow-Johnson *
  79. * http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt */
  80. struct EqualizerState final : public EffectState {
  81. struct {
  82. /* Effect parameters */
  83. BiquadFilter filter[4];
  84. /* Effect gains for each channel */
  85. float CurrentGains[MAX_OUTPUT_CHANNELS]{};
  86. float TargetGains[MAX_OUTPUT_CHANNELS]{};
  87. } mChans[MaxAmbiChannels];
  88. FloatBufferLine mSampleBuffer{};
  89. void deviceUpdate(const DeviceBase *device, const Buffer &buffer) override;
  90. void update(const ContextBase *context, const EffectSlot *slot, const EffectProps *props,
  91. const EffectTarget target) override;
  92. void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn,
  93. const al::span<FloatBufferLine> samplesOut) override;
  94. DEF_NEWDEL(EqualizerState)
  95. };
  96. void EqualizerState::deviceUpdate(const DeviceBase*, const Buffer&)
  97. {
  98. for(auto &e : mChans)
  99. {
  100. std::for_each(std::begin(e.filter), std::end(e.filter), std::mem_fn(&BiquadFilter::clear));
  101. std::fill(std::begin(e.CurrentGains), std::end(e.CurrentGains), 0.0f);
  102. }
  103. }
  104. void EqualizerState::update(const ContextBase *context, const EffectSlot *slot,
  105. const EffectProps *props, const EffectTarget target)
  106. {
  107. const DeviceBase *device{context->mDevice};
  108. auto frequency = static_cast<float>(device->Frequency);
  109. float gain, f0norm;
  110. /* Calculate coefficients for the each type of filter. Note that the shelf
  111. * and peaking filters' gain is for the centerpoint of the transition band,
  112. * while the effect property gains are for the shelf/peak itself. So the
  113. * property gains need their dB halved (sqrt of linear gain) for the
  114. * shelf/peak to reach the provided gain.
  115. */
  116. gain = std::sqrt(props->Equalizer.LowGain);
  117. f0norm = props->Equalizer.LowCutoff / frequency;
  118. mChans[0].filter[0].setParamsFromSlope(BiquadType::LowShelf, f0norm, gain, 0.75f);
  119. gain = std::sqrt(props->Equalizer.Mid1Gain);
  120. f0norm = props->Equalizer.Mid1Center / frequency;
  121. mChans[0].filter[1].setParamsFromBandwidth(BiquadType::Peaking, f0norm, gain,
  122. props->Equalizer.Mid1Width);
  123. gain = std::sqrt(props->Equalizer.Mid2Gain);
  124. f0norm = props->Equalizer.Mid2Center / frequency;
  125. mChans[0].filter[2].setParamsFromBandwidth(BiquadType::Peaking, f0norm, gain,
  126. props->Equalizer.Mid2Width);
  127. gain = std::sqrt(props->Equalizer.HighGain);
  128. f0norm = props->Equalizer.HighCutoff / frequency;
  129. mChans[0].filter[3].setParamsFromSlope(BiquadType::HighShelf, f0norm, gain, 0.75f);
  130. /* Copy the filter coefficients for the other input channels. */
  131. for(size_t i{1u};i < slot->Wet.Buffer.size();++i)
  132. {
  133. mChans[i].filter[0].copyParamsFrom(mChans[0].filter[0]);
  134. mChans[i].filter[1].copyParamsFrom(mChans[0].filter[1]);
  135. mChans[i].filter[2].copyParamsFrom(mChans[0].filter[2]);
  136. mChans[i].filter[3].copyParamsFrom(mChans[0].filter[3]);
  137. }
  138. mOutTarget = target.Main->Buffer;
  139. auto set_gains = [slot,target](auto &chan, al::span<const float,MaxAmbiChannels> coeffs)
  140. { ComputePanGains(target.Main, coeffs.data(), slot->Gain, chan.TargetGains); };
  141. SetAmbiPanIdentity(std::begin(mChans), slot->Wet.Buffer.size(), set_gains);
  142. }
  143. void EqualizerState::process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn, const al::span<FloatBufferLine> samplesOut)
  144. {
  145. const al::span<float> buffer{mSampleBuffer.data(), samplesToDo};
  146. auto chan = std::addressof(mChans[0]);
  147. for(const auto &input : samplesIn)
  148. {
  149. const al::span<const float> inbuf{input.data(), samplesToDo};
  150. DualBiquad{chan->filter[0], chan->filter[1]}.process(inbuf, buffer.begin());
  151. DualBiquad{chan->filter[2], chan->filter[3]}.process(buffer, buffer.begin());
  152. MixSamples(buffer, samplesOut, chan->CurrentGains, chan->TargetGains, samplesToDo, 0u);
  153. ++chan;
  154. }
  155. }
  156. struct EqualizerStateFactory final : public EffectStateFactory {
  157. al::intrusive_ptr<EffectState> create() override
  158. { return al::intrusive_ptr<EffectState>{new EqualizerState{}}; }
  159. };
  160. } // namespace
  161. EffectStateFactory *EqualizerStateFactory_getFactory()
  162. {
  163. static EqualizerStateFactory EqualizerFactory{};
  164. return &EqualizerFactory;
  165. }