distortion.cpp 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  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 <cmath>
  22. #include <cstdlib>
  23. #include <cmath>
  24. #include "al/auxeffectslot.h"
  25. #include "alcmain.h"
  26. #include "alcontext.h"
  27. #include "alu.h"
  28. #include "filters/biquad.h"
  29. namespace {
  30. struct DistortionState final : public EffectState {
  31. /* Effect gains for each channel */
  32. ALfloat mGain[MAX_OUTPUT_CHANNELS]{};
  33. /* Effect parameters */
  34. BiquadFilter mLowpass;
  35. BiquadFilter mBandpass;
  36. ALfloat mAttenuation{};
  37. ALfloat mEdgeCoeff{};
  38. ALfloat mBuffer[2][BUFFERSIZE]{};
  39. ALboolean deviceUpdate(const ALCdevice *device) override;
  40. void update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) override;
  41. void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn, const al::span<FloatBufferLine> samplesOut) override;
  42. DEF_NEWDEL(DistortionState)
  43. };
  44. ALboolean DistortionState::deviceUpdate(const ALCdevice*)
  45. {
  46. mLowpass.clear();
  47. mBandpass.clear();
  48. return AL_TRUE;
  49. }
  50. void DistortionState::update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target)
  51. {
  52. const ALCdevice *device{context->mDevice.get()};
  53. /* Store waveshaper edge settings. */
  54. const ALfloat edge{
  55. minf(std::sin(al::MathDefs<float>::Pi()*0.5f * props->Distortion.Edge), 0.99f)};
  56. mEdgeCoeff = 2.0f * edge / (1.0f-edge);
  57. ALfloat cutoff{props->Distortion.LowpassCutoff};
  58. /* Bandwidth value is constant in octaves. */
  59. ALfloat bandwidth{(cutoff / 2.0f) / (cutoff * 0.67f)};
  60. /* Multiply sampling frequency by the amount of oversampling done during
  61. * processing.
  62. */
  63. auto frequency = static_cast<ALfloat>(device->Frequency);
  64. mLowpass.setParams(BiquadType::LowPass, 1.0f, cutoff / (frequency*4.0f),
  65. mLowpass.rcpQFromBandwidth(cutoff / (frequency*4.0f), bandwidth));
  66. cutoff = props->Distortion.EQCenter;
  67. /* Convert bandwidth in Hz to octaves. */
  68. bandwidth = props->Distortion.EQBandwidth / (cutoff * 0.67f);
  69. mBandpass.setParams(BiquadType::BandPass, 1.0f, cutoff / (frequency*4.0f),
  70. mBandpass.rcpQFromBandwidth(cutoff / (frequency*4.0f), bandwidth));
  71. ALfloat coeffs[MAX_AMBI_CHANNELS];
  72. CalcDirectionCoeffs({0.0f, 0.0f, -1.0f}, 0.0f, coeffs);
  73. mOutTarget = target.Main->Buffer;
  74. ComputePanGains(target.Main, coeffs, slot->Params.Gain*props->Distortion.Gain, mGain);
  75. }
  76. void DistortionState::process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn, const al::span<FloatBufferLine> samplesOut)
  77. {
  78. const ALfloat fc{mEdgeCoeff};
  79. for(size_t base{0u};base < samplesToDo;)
  80. {
  81. /* Perform 4x oversampling to avoid aliasing. Oversampling greatly
  82. * improves distortion quality and allows to implement lowpass and
  83. * bandpass filters using high frequencies, at which classic IIR
  84. * filters became unstable.
  85. */
  86. size_t todo{minz(BUFFERSIZE, (samplesToDo-base) * 4)};
  87. /* Fill oversample buffer using zero stuffing. Multiply the sample by
  88. * the amount of oversampling to maintain the signal's power.
  89. */
  90. for(size_t i{0u};i < todo;i++)
  91. mBuffer[0][i] = !(i&3) ? samplesIn[0][(i>>2)+base] * 4.0f : 0.0f;
  92. /* First step, do lowpass filtering of original signal. Additionally
  93. * perform buffer interpolation and lowpass cutoff for oversampling
  94. * (which is fortunately first step of distortion). So combine three
  95. * operations into the one.
  96. */
  97. mLowpass.process(mBuffer[1], mBuffer[0], todo);
  98. /* Second step, do distortion using waveshaper function to emulate
  99. * signal processing during tube overdriving. Three steps of
  100. * waveshaping are intended to modify waveform without boost/clipping/
  101. * attenuation process.
  102. */
  103. for(size_t i{0u};i < todo;i++)
  104. {
  105. ALfloat smp{mBuffer[1][i]};
  106. smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp));
  107. smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp)) * -1.0f;
  108. smp = (1.0f + fc) * smp/(1.0f + fc*fabsf(smp));
  109. mBuffer[0][i] = smp;
  110. }
  111. /* Third step, do bandpass filtering of distorted signal. */
  112. mBandpass.process(mBuffer[1], mBuffer[0], todo);
  113. todo >>= 2;
  114. const ALfloat *outgains{mGain};
  115. for(FloatBufferLine &output : samplesOut)
  116. {
  117. /* Fourth step, final, do attenuation and perform decimation,
  118. * storing only one sample out of four.
  119. */
  120. const ALfloat gain{*(outgains++)};
  121. if(!(std::fabs(gain) > GAIN_SILENCE_THRESHOLD))
  122. continue;
  123. for(size_t i{0u};i < todo;i++)
  124. output[base+i] += gain * mBuffer[1][i*4];
  125. }
  126. base += todo;
  127. }
  128. }
  129. void Distortion_setParami(EffectProps*, ALCcontext *context, ALenum param, ALint)
  130. { context->setError(AL_INVALID_ENUM, "Invalid distortion integer property 0x%04x", param); }
  131. void Distortion_setParamiv(EffectProps*, ALCcontext *context, ALenum param, const ALint*)
  132. { context->setError(AL_INVALID_ENUM, "Invalid distortion integer-vector property 0x%04x", param); }
  133. void Distortion_setParamf(EffectProps *props, ALCcontext *context, ALenum param, ALfloat val)
  134. {
  135. switch(param)
  136. {
  137. case AL_DISTORTION_EDGE:
  138. if(!(val >= AL_DISTORTION_MIN_EDGE && val <= AL_DISTORTION_MAX_EDGE))
  139. SETERR_RETURN(context, AL_INVALID_VALUE,, "Distortion edge out of range");
  140. props->Distortion.Edge = val;
  141. break;
  142. case AL_DISTORTION_GAIN:
  143. if(!(val >= AL_DISTORTION_MIN_GAIN && val <= AL_DISTORTION_MAX_GAIN))
  144. SETERR_RETURN(context, AL_INVALID_VALUE,, "Distortion gain out of range");
  145. props->Distortion.Gain = val;
  146. break;
  147. case AL_DISTORTION_LOWPASS_CUTOFF:
  148. if(!(val >= AL_DISTORTION_MIN_LOWPASS_CUTOFF && val <= AL_DISTORTION_MAX_LOWPASS_CUTOFF))
  149. SETERR_RETURN(context, AL_INVALID_VALUE,, "Distortion low-pass cutoff out of range");
  150. props->Distortion.LowpassCutoff = val;
  151. break;
  152. case AL_DISTORTION_EQCENTER:
  153. if(!(val >= AL_DISTORTION_MIN_EQCENTER && val <= AL_DISTORTION_MAX_EQCENTER))
  154. SETERR_RETURN(context, AL_INVALID_VALUE,, "Distortion EQ center out of range");
  155. props->Distortion.EQCenter = val;
  156. break;
  157. case AL_DISTORTION_EQBANDWIDTH:
  158. if(!(val >= AL_DISTORTION_MIN_EQBANDWIDTH && val <= AL_DISTORTION_MAX_EQBANDWIDTH))
  159. SETERR_RETURN(context, AL_INVALID_VALUE,, "Distortion EQ bandwidth out of range");
  160. props->Distortion.EQBandwidth = val;
  161. break;
  162. default:
  163. context->setError(AL_INVALID_ENUM, "Invalid distortion float property 0x%04x", param);
  164. }
  165. }
  166. void Distortion_setParamfv(EffectProps *props, ALCcontext *context, ALenum param, const ALfloat *vals)
  167. { Distortion_setParamf(props, context, param, vals[0]); }
  168. void Distortion_getParami(const EffectProps*, ALCcontext *context, ALenum param, ALint*)
  169. { context->setError(AL_INVALID_ENUM, "Invalid distortion integer property 0x%04x", param); }
  170. void Distortion_getParamiv(const EffectProps*, ALCcontext *context, ALenum param, ALint*)
  171. { context->setError(AL_INVALID_ENUM, "Invalid distortion integer-vector property 0x%04x", param); }
  172. void Distortion_getParamf(const EffectProps *props, ALCcontext *context, ALenum param, ALfloat *val)
  173. {
  174. switch(param)
  175. {
  176. case AL_DISTORTION_EDGE:
  177. *val = props->Distortion.Edge;
  178. break;
  179. case AL_DISTORTION_GAIN:
  180. *val = props->Distortion.Gain;
  181. break;
  182. case AL_DISTORTION_LOWPASS_CUTOFF:
  183. *val = props->Distortion.LowpassCutoff;
  184. break;
  185. case AL_DISTORTION_EQCENTER:
  186. *val = props->Distortion.EQCenter;
  187. break;
  188. case AL_DISTORTION_EQBANDWIDTH:
  189. *val = props->Distortion.EQBandwidth;
  190. break;
  191. default:
  192. context->setError(AL_INVALID_ENUM, "Invalid distortion float property 0x%04x", param);
  193. }
  194. }
  195. void Distortion_getParamfv(const EffectProps *props, ALCcontext *context, ALenum param, ALfloat *vals)
  196. { Distortion_getParamf(props, context, param, vals); }
  197. DEFINE_ALEFFECT_VTABLE(Distortion);
  198. struct DistortionStateFactory final : public EffectStateFactory {
  199. EffectState *create() override { return new DistortionState{}; }
  200. EffectProps getDefaultProps() const noexcept override;
  201. const EffectVtable *getEffectVtable() const noexcept override { return &Distortion_vtable; }
  202. };
  203. EffectProps DistortionStateFactory::getDefaultProps() const noexcept
  204. {
  205. EffectProps props{};
  206. props.Distortion.Edge = AL_DISTORTION_DEFAULT_EDGE;
  207. props.Distortion.Gain = AL_DISTORTION_DEFAULT_GAIN;
  208. props.Distortion.LowpassCutoff = AL_DISTORTION_DEFAULT_LOWPASS_CUTOFF;
  209. props.Distortion.EQCenter = AL_DISTORTION_DEFAULT_EQCENTER;
  210. props.Distortion.EQBandwidth = AL_DISTORTION_DEFAULT_EQBANDWIDTH;
  211. return props;
  212. }
  213. } // namespace
  214. EffectStateFactory *DistortionStateFactory_getFactory()
  215. {
  216. static DistortionStateFactory DistortionFactory{};
  217. return &DistortionFactory;
  218. }