compressor.cpp 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. /**
  2. * OpenAL cross platform audio library
  3. * Copyright (C) 2013 by Anis A. Hireche
  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 <cstdlib>
  22. #include "al/auxeffectslot.h"
  23. #include "alcmain.h"
  24. #include "alcontext.h"
  25. #include "alu.h"
  26. #include "vecmat.h"
  27. namespace {
  28. #define AMP_ENVELOPE_MIN 0.5f
  29. #define AMP_ENVELOPE_MAX 2.0f
  30. #define ATTACK_TIME 0.1f /* 100ms to rise from min to max */
  31. #define RELEASE_TIME 0.2f /* 200ms to drop from max to min */
  32. struct CompressorState final : public EffectState {
  33. /* Effect gains for each channel */
  34. ALfloat mGain[MAX_AMBI_CHANNELS][MAX_OUTPUT_CHANNELS]{};
  35. /* Effect parameters */
  36. ALboolean mEnabled{AL_TRUE};
  37. ALfloat mAttackMult{1.0f};
  38. ALfloat mReleaseMult{1.0f};
  39. ALfloat mEnvFollower{1.0f};
  40. ALboolean deviceUpdate(const ALCdevice *device) override;
  41. void update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) override;
  42. void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn, const al::span<FloatBufferLine> samplesOut) override;
  43. DEF_NEWDEL(CompressorState)
  44. };
  45. ALboolean CompressorState::deviceUpdate(const ALCdevice *device)
  46. {
  47. /* Number of samples to do a full attack and release (non-integer sample
  48. * counts are okay).
  49. */
  50. const ALfloat attackCount = static_cast<ALfloat>(device->Frequency) * ATTACK_TIME;
  51. const ALfloat releaseCount = static_cast<ALfloat>(device->Frequency) * RELEASE_TIME;
  52. /* Calculate per-sample multipliers to attack and release at the desired
  53. * rates.
  54. */
  55. mAttackMult = std::pow(AMP_ENVELOPE_MAX/AMP_ENVELOPE_MIN, 1.0f/attackCount);
  56. mReleaseMult = std::pow(AMP_ENVELOPE_MIN/AMP_ENVELOPE_MAX, 1.0f/releaseCount);
  57. return AL_TRUE;
  58. }
  59. void CompressorState::update(const ALCcontext*, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target)
  60. {
  61. mEnabled = props->Compressor.OnOff;
  62. mOutTarget = target.Main->Buffer;
  63. for(size_t i{0u};i < slot->Wet.Buffer.size();++i)
  64. {
  65. auto coeffs = GetAmbiIdentityRow(i);
  66. ComputePanGains(target.Main, coeffs.data(), slot->Params.Gain, mGain[i]);
  67. }
  68. }
  69. void CompressorState::process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn, const al::span<FloatBufferLine> samplesOut)
  70. {
  71. for(size_t base{0u};base < samplesToDo;)
  72. {
  73. ALfloat gains[256];
  74. const size_t td{minz(256, samplesToDo-base)};
  75. /* Generate the per-sample gains from the signal envelope. */
  76. ALfloat env{mEnvFollower};
  77. if(mEnabled)
  78. {
  79. for(size_t i{0u};i < td;++i)
  80. {
  81. /* Clamp the absolute amplitude to the defined envelope limits,
  82. * then attack or release the envelope to reach it.
  83. */
  84. const ALfloat amplitude{clampf(std::fabs(samplesIn[0][base+i]), AMP_ENVELOPE_MIN,
  85. AMP_ENVELOPE_MAX)};
  86. if(amplitude > env)
  87. env = minf(env*mAttackMult, amplitude);
  88. else if(amplitude < env)
  89. env = maxf(env*mReleaseMult, amplitude);
  90. /* Apply the reciprocal of the envelope to normalize the volume
  91. * (compress the dynamic range).
  92. */
  93. gains[i] = 1.0f / env;
  94. }
  95. }
  96. else
  97. {
  98. /* Same as above, except the amplitude is forced to 1. This helps
  99. * ensure smooth gain changes when the compressor is turned on and
  100. * off.
  101. */
  102. for(size_t i{0u};i < td;++i)
  103. {
  104. const ALfloat amplitude{1.0f};
  105. if(amplitude > env)
  106. env = minf(env*mAttackMult, amplitude);
  107. else if(amplitude < env)
  108. env = maxf(env*mReleaseMult, amplitude);
  109. gains[i] = 1.0f / env;
  110. }
  111. }
  112. mEnvFollower = env;
  113. /* Now compress the signal amplitude to output. */
  114. auto changains = std::addressof(mGain[0]);
  115. for(const auto &input : samplesIn)
  116. {
  117. const ALfloat *outgains{*(changains++)};
  118. for(FloatBufferLine &output : samplesOut)
  119. {
  120. const ALfloat gain{*(outgains++)};
  121. if(!(std::fabs(gain) > GAIN_SILENCE_THRESHOLD))
  122. continue;
  123. for(size_t i{0u};i < td;i++)
  124. output[base+i] += input[base+i] * gains[i] * gain;
  125. }
  126. }
  127. base += td;
  128. }
  129. }
  130. void Compressor_setParami(EffectProps *props, ALCcontext *context, ALenum param, ALint val)
  131. {
  132. switch(param)
  133. {
  134. case AL_COMPRESSOR_ONOFF:
  135. if(!(val >= AL_COMPRESSOR_MIN_ONOFF && val <= AL_COMPRESSOR_MAX_ONOFF))
  136. SETERR_RETURN(context, AL_INVALID_VALUE,, "Compressor state out of range");
  137. props->Compressor.OnOff = val != AL_FALSE;
  138. break;
  139. default:
  140. context->setError(AL_INVALID_ENUM, "Invalid compressor integer property 0x%04x",
  141. param);
  142. }
  143. }
  144. void Compressor_setParamiv(EffectProps *props, ALCcontext *context, ALenum param, const ALint *vals)
  145. { Compressor_setParami(props, context, param, vals[0]); }
  146. void Compressor_setParamf(EffectProps*, ALCcontext *context, ALenum param, ALfloat)
  147. { context->setError(AL_INVALID_ENUM, "Invalid compressor float property 0x%04x", param); }
  148. void Compressor_setParamfv(EffectProps*, ALCcontext *context, ALenum param, const ALfloat*)
  149. { context->setError(AL_INVALID_ENUM, "Invalid compressor float-vector property 0x%04x", param); }
  150. void Compressor_getParami(const EffectProps *props, ALCcontext *context, ALenum param, ALint *val)
  151. {
  152. switch(param)
  153. {
  154. case AL_COMPRESSOR_ONOFF:
  155. *val = props->Compressor.OnOff;
  156. break;
  157. default:
  158. context->setError(AL_INVALID_ENUM, "Invalid compressor integer property 0x%04x",
  159. param);
  160. }
  161. }
  162. void Compressor_getParamiv(const EffectProps *props, ALCcontext *context, ALenum param, ALint *vals)
  163. { Compressor_getParami(props, context, param, vals); }
  164. void Compressor_getParamf(const EffectProps*, ALCcontext *context, ALenum param, ALfloat*)
  165. { context->setError(AL_INVALID_ENUM, "Invalid compressor float property 0x%04x", param); }
  166. void Compressor_getParamfv(const EffectProps*, ALCcontext *context, ALenum param, ALfloat*)
  167. { context->setError(AL_INVALID_ENUM, "Invalid compressor float-vector property 0x%04x", param); }
  168. DEFINE_ALEFFECT_VTABLE(Compressor);
  169. struct CompressorStateFactory final : public EffectStateFactory {
  170. EffectState *create() override { return new CompressorState{}; }
  171. EffectProps getDefaultProps() const noexcept override;
  172. const EffectVtable *getEffectVtable() const noexcept override { return &Compressor_vtable; }
  173. };
  174. EffectProps CompressorStateFactory::getDefaultProps() const noexcept
  175. {
  176. EffectProps props{};
  177. props.Compressor.OnOff = AL_COMPRESSOR_DEFAULT_ONOFF;
  178. return props;
  179. }
  180. } // namespace
  181. EffectStateFactory *CompressorStateFactory_getFactory()
  182. {
  183. static CompressorStateFactory CompressorFactory{};
  184. return &CompressorFactory;
  185. }