diff options
Diffstat (limited to 'indra/llaudio/llwindgen.h')
-rw-r--r-- | indra/llaudio/llwindgen.h | 176 |
1 files changed, 176 insertions, 0 deletions
diff --git a/indra/llaudio/llwindgen.h b/indra/llaudio/llwindgen.h new file mode 100644 index 0000000000..b9cecb60a1 --- /dev/null +++ b/indra/llaudio/llwindgen.h @@ -0,0 +1,176 @@ +/** + * @file windgen.h + * @brief Templated wind noise generation + * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ +#ifndef WINDGEN_H +#define WINDGEN_H + +#include "llcommon.h" + +template <class MIXBUFFERFORMAT_T> +class LLWindGen +{ +public: + LLWindGen(const U32 sample_rate = 44100) : + mTargetGain(0.f), + mTargetFreq(100.f), + mTargetPanGainR(0.5f), + mInputSamplingRate(sample_rate), + mSubSamples(2), + mFilterBandWidth(50.f), + mBuf0(0.0f), + mBuf1(0.0f), + mBuf2(0.0f), + mY0(0.0f), + mY1(0.0f), + mCurrentGain(0.f), + mCurrentFreq(100.f), + mCurrentPanGainR(0.5f), + mLastSample(0.f) + { + mSamplePeriod = (F32)mSubSamples / (F32)mInputSamplingRate; + mB2 = expf(-F_TWO_PI * mFilterBandWidth * mSamplePeriod); + } + + const U32 getInputSamplingRate() { return mInputSamplingRate; } + + // newbuffer = the buffer passed from the previous DSP unit. + // numsamples = length in samples-per-channel at this mix time. + // NOTE: generates L/R interleaved stereo + MIXBUFFERFORMAT_T* windGenerate(MIXBUFFERFORMAT_T *newbuffer, int numsamples) + { + MIXBUFFERFORMAT_T *cursamplep = newbuffer; + + // Filter coefficients + F32 a0 = 0.0f, b1 = 0.0f; + + // No need to clip at normal volumes + bool clip = mCurrentGain > 2.0f; + + bool interp_freq = false; + + //if the frequency isn't changing much, we don't need to interpolate in the inner loop + if (llabs(mTargetFreq - mCurrentFreq) < (mCurrentFreq * 0.112)) + { + // calculate resonant filter coefficients + mCurrentFreq = mTargetFreq; + b1 = (-4.0f * mB2) / (1.0f + mB2) * cosf(F_TWO_PI * (mCurrentFreq * mSamplePeriod)); + a0 = (1.0f - mB2) * sqrtf(1.0f - (b1 * b1) / (4.0f * mB2)); + } + else + { + interp_freq = true; + } + + while (numsamples) + { + F32 next_sample; + + // Start with white noise + // This expression is fragile, rearrange it and it will break! + next_sample = (F32)rand() * (1.0f / (F32)(RAND_MAX / (U16_MAX / 8))) + (F32)(S16_MIN / 8); + + // Apply a pinking filter + // Magic numbers taken from PKE method at http://www.firstpr.com.au/dsp/pink-noise/ + mBuf0 = mBuf0 * 0.99765f + next_sample * 0.0990460f; + mBuf1 = mBuf1 * 0.96300f + next_sample * 0.2965164f; + mBuf2 = mBuf2 * 0.57000f + next_sample * 1.0526913f; + + next_sample = mBuf0 + mBuf1 + mBuf2 + next_sample * 0.1848f; + + if (interp_freq) + { + // calculate and interpolate resonant filter coefficients + mCurrentFreq = (0.999f * mCurrentFreq) + (0.001f * mTargetFreq); + b1 = (-4.0f * mB2) / (1.0f + mB2) * cosf(F_TWO_PI * (mCurrentFreq * mSamplePeriod)); + a0 = (1.0f - mB2) * sqrtf(1.0f - (b1 * b1) / (4.0f * mB2)); + } + + // Apply a resonant low-pass filter on the pink noise + next_sample = a0 * next_sample - b1 * mY0 - mB2 * mY1; + mY1 = mY0; + mY0 = next_sample; + + mCurrentGain = (0.999f * mCurrentGain) + (0.001f * mTargetGain); + mCurrentPanGainR = (0.999f * mCurrentPanGainR) + (0.001f * mTargetPanGainR); + + // For a 3dB pan law use: + // next_sample *= mCurrentGain * ((mCurrentPanGainR*(mCurrentPanGainR-1)*1.652+1.413); + next_sample *= mCurrentGain; + + // delta is used to interpolate between synthesized samples + F32 delta = (next_sample - mLastSample) / (F32)mSubSamples; + + // Fill the audio buffer, clipping if necessary + for (U8 i=mSubSamples; i && numsamples; --i, --numsamples) + { + mLastSample = mLastSample + delta; + S32 sample_right = (S32)(mLastSample * mCurrentPanGainR); + S32 sample_left = (S32)mLastSample - sample_right; + + if (!clip) + { + *cursamplep = (MIXBUFFERFORMAT_T)sample_left; + ++cursamplep; + *cursamplep = (MIXBUFFERFORMAT_T)sample_right; + ++cursamplep; + } + else + { + *cursamplep = (MIXBUFFERFORMAT_T)llclamp(sample_left, (S32)S16_MIN, (S32)S16_MAX); + ++cursamplep; + *cursamplep = (MIXBUFFERFORMAT_T)llclamp(sample_right, (S32)S16_MIN, (S32)S16_MAX); + ++cursamplep; + } + } + } + + return newbuffer; + } + +public: + F32 mTargetGain; + F32 mTargetFreq; + F32 mTargetPanGainR; + +private: + U32 mInputSamplingRate; + U8 mSubSamples; + F32 mSamplePeriod; + F32 mFilterBandWidth; + F32 mB2; + + F32 mBuf0; + F32 mBuf1; + F32 mBuf2; + F32 mY0; + F32 mY1; + + F32 mCurrentGain; + F32 mCurrentFreq; + F32 mCurrentPanGainR; + F32 mLastSample; +}; + +#endif |