summaryrefslogtreecommitdiff
path: root/indra/llaudio/llwindgen.h
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llaudio/llwindgen.h')
-rw-r--r--indra/llaudio/llwindgen.h202
1 files changed, 121 insertions, 81 deletions
diff --git a/indra/llaudio/llwindgen.h b/indra/llaudio/llwindgen.h
index 847bfa6e9d..b9cecb60a1 100644
--- a/indra/llaudio/llwindgen.h
+++ b/indra/llaudio/llwindgen.h
@@ -2,135 +2,175 @@
* @file windgen.h
* @brief Templated wind noise generation
*
- * $LicenseInfo:firstyear=2002&license=viewergpl$
- *
- * Copyright (c) 2002-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * 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.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * 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.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * 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
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef WINDGEN_H
#define WINDGEN_H
#include "llcommon.h"
-#include "llrand.h"
template <class MIXBUFFERFORMAT_T>
class LLWindGen
{
public:
- LLWindGen() :
+ LLWindGen(const U32 sample_rate = 44100) :
mTargetGain(0.f),
mTargetFreq(100.f),
mTargetPanGainR(0.5f),
- mbuf0(0.0),
- mbuf1(0.0),
- mbuf2(0.0),
- mbuf3(0.0),
- mbuf4(0.0),
- mbuf5(0.0),
- mY0(0.0),
- mY1(0.0),
+ 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) {};
-
- static const U32 getInputSamplingRate() {return mInputSamplingRate;}
+ 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.
- // stride = number of bytes between start of each sample.
// NOTE: generates L/R interleaved stereo
- MIXBUFFERFORMAT_T* windGenerate(MIXBUFFERFORMAT_T *newbuffer, int numsamples, int stride)
+ MIXBUFFERFORMAT_T* windGenerate(MIXBUFFERFORMAT_T *newbuffer, int numsamples)
{
- U8 *cursamplep = (U8*)newbuffer;
+ MIXBUFFERFORMAT_T *cursamplep = newbuffer;
+
+ // Filter coefficients
+ F32 a0 = 0.0f, b1 = 0.0f;
- double bandwidth = 50.0F;
- double a0,b1,b2;
+ // No need to clip at normal volumes
+ bool clip = mCurrentGain > 2.0f;
- // calculate resonant filter coeffs
- b2 = exp(-(F_TWO_PI) * (bandwidth / mInputSamplingRate));
+ 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--)
+ while (numsamples)
{
- mCurrentFreq = (float)((0.999 * mCurrentFreq) + (0.001 * mTargetFreq));
- mCurrentGain = (float)((0.999 * mCurrentGain) + (0.001 * mTargetGain));
- mCurrentPanGainR = (float)((0.999 * mCurrentPanGainR) + (0.001 * mTargetPanGainR));
- b1 = (-4.0 * b2) / (1.0 + b2) * cos(F_TWO_PI * (mCurrentFreq / mInputSamplingRate));
- a0 = (1.0 - b2) * sqrt(1.0 - (b1 * b1) / (4.0 * b2));
- double nextSample;
+ F32 next_sample;
- // start with white noise
- nextSample = ll_frand(2.0f) - 1.0f;
+ // 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 pinking filter
- mbuf0 = 0.997f * mbuf0 + 0.0126502f * nextSample;
- mbuf1 = 0.985f * mbuf1 + 0.0139083f * nextSample;
- mbuf2 = 0.950f * mbuf2 + 0.0205439f * nextSample;
- mbuf3 = 0.850f * mbuf3 + 0.0387225f * nextSample;
- mbuf4 = 0.620f * mbuf4 + 0.0465932f * nextSample;
- mbuf5 = 0.250f * mbuf5 + 0.1093477f * nextSample;
+ // 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;
- nextSample = mbuf0 + mbuf1 + mbuf2 + mbuf3 + mbuf4 + mbuf5;
+ next_sample = mBuf0 + mBuf1 + mBuf2 + next_sample * 0.1848f;
- // do a resonant filter on the noise
- nextSample = (double)( a0 * nextSample - b1 * mY0 - b2 * mY1 );
+ 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 = nextSample;
+ mY0 = next_sample;
- nextSample *= mCurrentGain;
+ mCurrentGain = (0.999f * mCurrentGain) + (0.001f * mTargetGain);
+ mCurrentPanGainR = (0.999f * mCurrentPanGainR) + (0.001f * mTargetPanGainR);
- MIXBUFFERFORMAT_T sample;
+ // For a 3dB pan law use:
+ // next_sample *= mCurrentGain * ((mCurrentPanGainR*(mCurrentPanGainR-1)*1.652+1.413);
+ next_sample *= mCurrentGain;
- sample = llfloor(((F32)nextSample*32768.f*(1.0f - mCurrentPanGainR))+0.5f);
- *(MIXBUFFERFORMAT_T*)cursamplep = llclamp(sample, (MIXBUFFERFORMAT_T)-32768, (MIXBUFFERFORMAT_T)32767);
- cursamplep += stride;
-
- sample = llfloor(((F32)nextSample*32768.f*mCurrentPanGainR)+0.5f);
- *(MIXBUFFERFORMAT_T*)cursamplep = llclamp(sample, (MIXBUFFERFORMAT_T)-32768, (MIXBUFFERFORMAT_T)32767);
- cursamplep += stride;
+ // 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:
- static const U32 mInputSamplingRate = 44100;
- F64 mbuf0;
- F64 mbuf1;
- F64 mbuf2;
- F64 mbuf3;
- F64 mbuf4;
- F64 mbuf5;
- F64 mY0;
- F64 mY1;
+ 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