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

226 lines
8.0 KiB

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