llwindgen.h 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. /**
  2. * @file windgen.h
  3. * @brief Templated wind noise generation
  4. *
  5. * $LicenseInfo:firstyear=2002&license=viewergpl$
  6. *
  7. * Copyright (c) 2002-2009, Linden Research, Inc.
  8. *
  9. * Second Life Viewer Source Code
  10. * The source code in this file ("Source Code") is provided by Linden Lab
  11. * to you under the terms of the GNU General Public License, version 2.0
  12. * ("GPL"), unless you have obtained a separate licensing agreement
  13. * ("Other License"), formally executed by you and Linden Lab. Terms of
  14. * the GPL can be found in doc/GPL-license.txt in this distribution, or
  15. * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  16. *
  17. * There are special exceptions to the terms and conditions of the GPL as
  18. * it is applied to this Source Code. View the full text of the exception
  19. * in the file doc/FLOSS-exception.txt in this software distribution, or
  20. * online at
  21. * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  22. *
  23. * By copying, modifying or distributing this software, you acknowledge
  24. * that you have read and understood your obligations described above,
  25. * and agree to abide by those obligations.
  26. *
  27. * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  28. * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  29. * COMPLETENESS OR PERFORMANCE.
  30. * $/LicenseInfo$
  31. */
  32. #ifndef WINDGEN_H
  33. #define WINDGEN_H
  34. #include "llcommon.h"
  35. #include "llpreprocessor.h"
  36. #include "llrand.h"
  37. template <class MIXBUFFERFORMAT_T>
  38. class LLWindGen
  39. {
  40. public:
  41. LLWindGen(U32 sample_rate = 44100)
  42. : mTargetGain(0.f),
  43. mTargetFreq(100.f),
  44. mTargetPanGainR(0.5f),
  45. mInputSamplingRate(sample_rate),
  46. mSubSamples(2),
  47. mFilterBandWidth(50.f),
  48. mBuf0(0.f),
  49. mBuf1(0.f),
  50. mBuf2(0.f),
  51. mY0(0.f),
  52. mY1(0.f),
  53. mCurrentGain(0.f),
  54. mCurrentFreq(100.f),
  55. mCurrentPanGainR(0.5f)
  56. {
  57. mSamplePeriod = (F32)mSubSamples / (F32)mInputSamplingRate;
  58. mB2 = expf(-F_TWO_PI * mFilterBandWidth * mSamplePeriod);
  59. }
  60. LL_INLINE U32 getInputSamplingRate() { return mInputSamplingRate; }
  61. F32 getNextSample();
  62. F32 getClampedSample(bool clamp, F32 sample);
  63. // newbuffer = the buffer passed from the previous DSP unit.
  64. // numsamples = length in samples-per-channel at this mix time.
  65. // NOTE: generates L/R interleaved stereo
  66. MIXBUFFERFORMAT_T* windGenerate(MIXBUFFERFORMAT_T* newbuffer, int numsamples)
  67. {
  68. MIXBUFFERFORMAT_T *cursamplep = newbuffer;
  69. // Filter coefficients
  70. F32 a0 = 0.f, b1 = 0.f;
  71. // No need to clip at normal volumes
  72. bool clip = mCurrentGain > 2.f;
  73. bool interp_freq = false;
  74. // If the frequency is not changing much, we do not need to interpolate
  75. // in the inner loop
  76. if (fabsf(mTargetFreq - mCurrentFreq) < (mCurrentFreq * 0.112f))
  77. {
  78. // calculate resonant filter coefficients
  79. mCurrentFreq = mTargetFreq;
  80. b1 = (-4.f * mB2) / (1.f + mB2) *
  81. cosf(F_TWO_PI * (mCurrentFreq * mSamplePeriod));
  82. a0 = (1.f - mB2) * sqrtf(1.f - (b1 * b1) / (4.f * mB2));
  83. }
  84. else
  85. {
  86. interp_freq = true;
  87. }
  88. while (numsamples)
  89. {
  90. F32 next_sample;
  91. // Start with white noise. This expression is fragile, rearrange it
  92. // and it will break !
  93. next_sample = getNextSample();
  94. // Apply a pinking filter. Magic numbers taken from PKE method at:
  95. // http://www.firstpr.com.au/dsp/pink-noise/
  96. mBuf0 = mBuf0 * 0.99765f + next_sample * 0.0990460f;
  97. mBuf1 = mBuf1 * 0.96300f + next_sample * 0.2965164f;
  98. mBuf2 = mBuf2 * 0.57000f + next_sample * 1.0526913f;
  99. next_sample = mBuf0 + mBuf1 + mBuf2 + next_sample * 0.1848f;
  100. if (interp_freq)
  101. {
  102. // calculate and interpolate resonant filter coefficients
  103. mCurrentFreq = 0.999f * mCurrentFreq + 0.001f * mTargetFreq;
  104. b1 = (-4.f * mB2) / (1.f + mB2) *
  105. cosf(F_TWO_PI * (mCurrentFreq * mSamplePeriod));
  106. a0 = (1.f - mB2) * sqrtf(1.f - (b1 * b1) / (4.f * mB2));
  107. }
  108. // Apply a resonant low-pass filter on the pink noise
  109. next_sample = a0 * next_sample - b1 * mY0 - mB2 * mY1;
  110. mY1 = mY0;
  111. mY0 = next_sample;
  112. mCurrentGain = 0.999f * mCurrentGain + 0.001f * mTargetGain;
  113. mCurrentPanGainR = 0.999f * mCurrentPanGainR +
  114. 0.001f * mTargetPanGainR;
  115. // For a 3dB pan law use:
  116. // next_sample *= mCurrentGain *
  117. // ((mCurrentPanGainR * (mCurrentPanGainR - 1) *
  118. // 1.652 + 1.413);
  119. next_sample *= mCurrentGain;
  120. // delta is used to interpolate between synthesized samples
  121. F32 delta = (next_sample - mLastSample) / (F32)mSubSamples;
  122. // Fill the audio buffer, clipping if necessary
  123. MIXBUFFERFORMAT_T sample_left, sample_right;
  124. for (U8 i = mSubSamples; i && numsamples; --i, --numsamples)
  125. {
  126. mLastSample = mLastSample + delta;
  127. sample_right =
  128. (MIXBUFFERFORMAT_T)getClampedSample(clip,
  129. mLastSample *
  130. mCurrentPanGainR);
  131. sample_left =
  132. (MIXBUFFERFORMAT_T)getClampedSample(clip,
  133. mLastSample -
  134. (F32)sample_right);
  135. *cursamplep++ = sample_left;
  136. *cursamplep++ = sample_right;
  137. }
  138. }
  139. return newbuffer;
  140. }
  141. public:
  142. F32 mTargetGain;
  143. F32 mTargetFreq;
  144. F32 mTargetPanGainR;
  145. private:
  146. U32 mInputSamplingRate;
  147. U8 mSubSamples;
  148. F32 mSamplePeriod;
  149. F32 mFilterBandWidth;
  150. F32 mB2;
  151. F32 mBuf0;
  152. F32 mBuf1;
  153. F32 mBuf2;
  154. F32 mY0;
  155. F32 mY1;
  156. F32 mCurrentGain;
  157. F32 mCurrentFreq;
  158. F32 mCurrentPanGainR;
  159. F32 mLastSample;
  160. };
  161. template<class T> LL_INLINE F32 LLWindGen<T>::getNextSample()
  162. {
  163. constexpr F32 range = 1.f / (F32)(RAND_MAX / (U16_MAX / 8));
  164. constexpr F32 offset = (F32)(S16_MIN / 8);
  165. return (F32)rand() * range + offset;
  166. }
  167. template<> LL_INLINE F32 LLWindGen<F32>::getNextSample()
  168. {
  169. return ll_frand() - .5f;
  170. }
  171. template<class T> LL_INLINE F32 LLWindGen<T>::getClampedSample(bool clamp,
  172. F32 sample)
  173. {
  174. return clamp ? (F32)llclamp((S32)sample, (S32)S16_MIN, (S32)S16_MAX)
  175. : sample;
  176. }
  177. template<> LL_INLINE F32 LLWindGen<F32>::getClampedSample(bool clamp,
  178. F32 sample)
  179. {
  180. return sample;
  181. }
  182. #endif