2
0

autowah.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. /**
  2. * OpenAL cross platform audio library
  3. * Copyright (C) 2018 by Raul Herraiz.
  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 <math.h>
  22. #include <stdlib.h>
  23. #include "alMain.h"
  24. #include "alAuxEffectSlot.h"
  25. #include "alError.h"
  26. #include "alu.h"
  27. #include "filters/defs.h"
  28. #define MIN_FREQ 20.0f
  29. #define MAX_FREQ 2500.0f
  30. #define Q_FACTOR 5.0f
  31. typedef struct ALautowahState {
  32. DERIVE_FROM_TYPE(ALeffectState);
  33. /* Effect parameters */
  34. ALfloat AttackRate;
  35. ALfloat ReleaseRate;
  36. ALfloat ResonanceGain;
  37. ALfloat PeakGain;
  38. ALfloat FreqMinNorm;
  39. ALfloat BandwidthNorm;
  40. ALfloat env_delay;
  41. /* Filter components derived from the envelope. */
  42. struct {
  43. ALfloat cos_w0;
  44. ALfloat alpha;
  45. } Env[BUFFERSIZE];
  46. struct {
  47. /* Effect filters' history. */
  48. struct {
  49. ALfloat z1, z2;
  50. } Filter;
  51. /* Effect gains for each output channel */
  52. ALfloat CurrentGains[MAX_OUTPUT_CHANNELS];
  53. ALfloat TargetGains[MAX_OUTPUT_CHANNELS];
  54. } Chans[MAX_EFFECT_CHANNELS];
  55. /* Effects buffers */
  56. alignas(16) ALfloat BufferOut[BUFFERSIZE];
  57. } ALautowahState;
  58. static ALvoid ALautowahState_Destruct(ALautowahState *state);
  59. static ALboolean ALautowahState_deviceUpdate(ALautowahState *state, ALCdevice *device);
  60. static ALvoid ALautowahState_update(ALautowahState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props);
  61. static ALvoid ALautowahState_process(ALautowahState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels);
  62. DECLARE_DEFAULT_ALLOCATORS(ALautowahState)
  63. DEFINE_ALEFFECTSTATE_VTABLE(ALautowahState);
  64. static void ALautowahState_Construct(ALautowahState *state)
  65. {
  66. ALeffectState_Construct(STATIC_CAST(ALeffectState, state));
  67. SET_VTABLE2(ALautowahState, ALeffectState, state);
  68. }
  69. static ALvoid ALautowahState_Destruct(ALautowahState *state)
  70. {
  71. ALeffectState_Destruct(STATIC_CAST(ALeffectState,state));
  72. }
  73. static ALboolean ALautowahState_deviceUpdate(ALautowahState *state, ALCdevice *UNUSED(device))
  74. {
  75. /* (Re-)initializing parameters and clear the buffers. */
  76. ALsizei i, j;
  77. state->AttackRate = 1.0f;
  78. state->ReleaseRate = 1.0f;
  79. state->ResonanceGain = 10.0f;
  80. state->PeakGain = 4.5f;
  81. state->FreqMinNorm = 4.5e-4f;
  82. state->BandwidthNorm = 0.05f;
  83. state->env_delay = 0.0f;
  84. memset(state->Env, 0, sizeof(state->Env));
  85. for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
  86. {
  87. for(j = 0;j < MAX_OUTPUT_CHANNELS;j++)
  88. state->Chans[i].CurrentGains[j] = 0.0f;
  89. state->Chans[i].Filter.z1 = 0.0f;
  90. state->Chans[i].Filter.z2 = 0.0f;
  91. }
  92. return AL_TRUE;
  93. }
  94. static ALvoid ALautowahState_update(ALautowahState *state, const ALCcontext *context, const ALeffectslot *slot, const ALeffectProps *props)
  95. {
  96. const ALCdevice *device = context->Device;
  97. ALfloat ReleaseTime;
  98. ALsizei i;
  99. ReleaseTime = clampf(props->Autowah.ReleaseTime, 0.001f, 1.0f);
  100. state->AttackRate = expf(-1.0f / (props->Autowah.AttackTime*device->Frequency));
  101. state->ReleaseRate = expf(-1.0f / (ReleaseTime*device->Frequency));
  102. /* 0-20dB Resonance Peak gain */
  103. state->ResonanceGain = sqrtf(log10f(props->Autowah.Resonance)*10.0f / 3.0f);
  104. state->PeakGain = 1.0f - log10f(props->Autowah.PeakGain/AL_AUTOWAH_MAX_PEAK_GAIN);
  105. state->FreqMinNorm = MIN_FREQ / device->Frequency;
  106. state->BandwidthNorm = (MAX_FREQ-MIN_FREQ) / device->Frequency;
  107. STATIC_CAST(ALeffectState,state)->OutBuffer = device->FOAOut.Buffer;
  108. STATIC_CAST(ALeffectState,state)->OutChannels = device->FOAOut.NumChannels;
  109. for(i = 0;i < MAX_EFFECT_CHANNELS;i++)
  110. ComputePanGains(&device->FOAOut, IdentityMatrixf.m[i], slot->Params.Gain,
  111. state->Chans[i].TargetGains);
  112. }
  113. static ALvoid ALautowahState_process(ALautowahState *state, ALsizei SamplesToDo, const ALfloat (*restrict SamplesIn)[BUFFERSIZE], ALfloat (*restrict SamplesOut)[BUFFERSIZE], ALsizei NumChannels)
  114. {
  115. const ALfloat attack_rate = state->AttackRate;
  116. const ALfloat release_rate = state->ReleaseRate;
  117. const ALfloat res_gain = state->ResonanceGain;
  118. const ALfloat peak_gain = state->PeakGain;
  119. const ALfloat freq_min = state->FreqMinNorm;
  120. const ALfloat bandwidth = state->BandwidthNorm;
  121. ALfloat env_delay;
  122. ALsizei c, i;
  123. env_delay = state->env_delay;
  124. for(i = 0;i < SamplesToDo;i++)
  125. {
  126. ALfloat w0, sample, a;
  127. /* Envelope follower described on the book: Audio Effects, Theory,
  128. * Implementation and Application.
  129. */
  130. sample = peak_gain * fabsf(SamplesIn[0][i]);
  131. a = (sample > env_delay) ? attack_rate : release_rate;
  132. env_delay = lerp(sample, env_delay, a);
  133. /* Calculate the cos and alpha components for this sample's filter. */
  134. w0 = minf((bandwidth*env_delay + freq_min), 0.46f) * F_TAU;
  135. state->Env[i].cos_w0 = cosf(w0);
  136. state->Env[i].alpha = sinf(w0)/(2.0f * Q_FACTOR);
  137. }
  138. state->env_delay = env_delay;
  139. for(c = 0;c < MAX_EFFECT_CHANNELS; c++)
  140. {
  141. /* This effectively inlines BiquadFilter_setParams for a peaking
  142. * filter and BiquadFilter_processC. The alpha and cosine components
  143. * for the filter coefficients were previously calculated with the
  144. * envelope. Because the filter changes for each sample, the
  145. * coefficients are transient and don't need to be held.
  146. */
  147. ALfloat z1 = state->Chans[c].Filter.z1;
  148. ALfloat z2 = state->Chans[c].Filter.z2;
  149. for(i = 0;i < SamplesToDo;i++)
  150. {
  151. const ALfloat alpha = state->Env[i].alpha;
  152. const ALfloat cos_w0 = state->Env[i].cos_w0;
  153. ALfloat input, output;
  154. ALfloat a[3], b[3];
  155. b[0] = 1.0f + alpha*res_gain;
  156. b[1] = -2.0f * cos_w0;
  157. b[2] = 1.0f - alpha*res_gain;
  158. a[0] = 1.0f + alpha/res_gain;
  159. a[1] = -2.0f * cos_w0;
  160. a[2] = 1.0f - alpha/res_gain;
  161. input = SamplesIn[c][i];
  162. output = input*(b[0]/a[0]) + z1;
  163. z1 = input*(b[1]/a[0]) - output*(a[1]/a[0]) + z2;
  164. z2 = input*(b[2]/a[0]) - output*(a[2]/a[0]);
  165. state->BufferOut[i] = output;
  166. }
  167. state->Chans[c].Filter.z1 = z1;
  168. state->Chans[c].Filter.z2 = z2;
  169. /* Now, mix the processed sound data to the output. */
  170. MixSamples(state->BufferOut, NumChannels, SamplesOut, state->Chans[c].CurrentGains,
  171. state->Chans[c].TargetGains, SamplesToDo, 0, SamplesToDo);
  172. }
  173. }
  174. typedef struct AutowahStateFactory {
  175. DERIVE_FROM_TYPE(EffectStateFactory);
  176. } AutowahStateFactory;
  177. static ALeffectState *AutowahStateFactory_create(AutowahStateFactory *UNUSED(factory))
  178. {
  179. ALautowahState *state;
  180. NEW_OBJ0(state, ALautowahState)();
  181. if(!state) return NULL;
  182. return STATIC_CAST(ALeffectState, state);
  183. }
  184. DEFINE_EFFECTSTATEFACTORY_VTABLE(AutowahStateFactory);
  185. EffectStateFactory *AutowahStateFactory_getFactory(void)
  186. {
  187. static AutowahStateFactory AutowahFactory = { { GET_VTABLE2(AutowahStateFactory, EffectStateFactory) } };
  188. return STATIC_CAST(EffectStateFactory, &AutowahFactory);
  189. }
  190. void ALautowah_setParamf(ALeffect *effect, ALCcontext *context, ALenum param, ALfloat val)
  191. {
  192. ALeffectProps *props = &effect->Props;
  193. switch(param)
  194. {
  195. case AL_AUTOWAH_ATTACK_TIME:
  196. if(!(val >= AL_AUTOWAH_MIN_ATTACK_TIME && val <= AL_AUTOWAH_MAX_ATTACK_TIME))
  197. SETERR_RETURN(context, AL_INVALID_VALUE,,"Autowah attack time out of range");
  198. props->Autowah.AttackTime = val;
  199. break;
  200. case AL_AUTOWAH_RELEASE_TIME:
  201. if(!(val >= AL_AUTOWAH_MIN_RELEASE_TIME && val <= AL_AUTOWAH_MAX_RELEASE_TIME))
  202. SETERR_RETURN(context, AL_INVALID_VALUE,,"Autowah release time out of range");
  203. props->Autowah.ReleaseTime = val;
  204. break;
  205. case AL_AUTOWAH_RESONANCE:
  206. if(!(val >= AL_AUTOWAH_MIN_RESONANCE && val <= AL_AUTOWAH_MAX_RESONANCE))
  207. SETERR_RETURN(context, AL_INVALID_VALUE,,"Autowah resonance out of range");
  208. props->Autowah.Resonance = val;
  209. break;
  210. case AL_AUTOWAH_PEAK_GAIN:
  211. if(!(val >= AL_AUTOWAH_MIN_PEAK_GAIN && val <= AL_AUTOWAH_MAX_PEAK_GAIN))
  212. SETERR_RETURN(context, AL_INVALID_VALUE,,"Autowah peak gain out of range");
  213. props->Autowah.PeakGain = val;
  214. break;
  215. default:
  216. alSetError(context, AL_INVALID_ENUM, "Invalid autowah float property 0x%04x", param);
  217. }
  218. }
  219. void ALautowah_setParamfv(ALeffect *effect, ALCcontext *context, ALenum param, const ALfloat *vals)
  220. {
  221. ALautowah_setParamf(effect, context, param, vals[0]);
  222. }
  223. void ALautowah_setParami(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint UNUSED(val))
  224. {
  225. alSetError(context, AL_INVALID_ENUM, "Invalid autowah integer property 0x%04x", param);
  226. }
  227. void ALautowah_setParamiv(ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, const ALint *UNUSED(vals))
  228. {
  229. alSetError(context, AL_INVALID_ENUM, "Invalid autowah integer vector property 0x%04x", param);
  230. }
  231. void ALautowah_getParami(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(val))
  232. {
  233. alSetError(context, AL_INVALID_ENUM, "Invalid autowah integer property 0x%04x", param);
  234. }
  235. void ALautowah_getParamiv(const ALeffect *UNUSED(effect), ALCcontext *context, ALenum param, ALint *UNUSED(vals))
  236. {
  237. alSetError(context, AL_INVALID_ENUM, "Invalid autowah integer vector property 0x%04x", param);
  238. }
  239. void ALautowah_getParamf(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *val)
  240. {
  241. const ALeffectProps *props = &effect->Props;
  242. switch(param)
  243. {
  244. case AL_AUTOWAH_ATTACK_TIME:
  245. *val = props->Autowah.AttackTime;
  246. break;
  247. case AL_AUTOWAH_RELEASE_TIME:
  248. *val = props->Autowah.ReleaseTime;
  249. break;
  250. case AL_AUTOWAH_RESONANCE:
  251. *val = props->Autowah.Resonance;
  252. break;
  253. case AL_AUTOWAH_PEAK_GAIN:
  254. *val = props->Autowah.PeakGain;
  255. break;
  256. default:
  257. alSetError(context, AL_INVALID_ENUM, "Invalid autowah float property 0x%04x", param);
  258. }
  259. }
  260. void ALautowah_getParamfv(const ALeffect *effect, ALCcontext *context, ALenum param, ALfloat *vals)
  261. {
  262. ALautowah_getParamf(effect, context, param, vals);
  263. }
  264. DEFINE_ALEFFECT_VTABLE(ALautowah);