--- linden/indra/llmath/llmath.h.orig 2008-03-06 17:12:26.000000000 +0000 +++ linden/indra/llmath/llmath.h 2008-03-06 15:00:47.000000000 +0000 @@ -48,12 +48,12 @@ #endif // Single Precision Floating Point Routines -#ifndef fsqrtf -#define fsqrtf(x) ((F32)sqrt((F64)(x))) -#endif #ifndef sqrtf #define sqrtf(x) ((F32)sqrt((F64)(x))) #endif +#ifndef fsqrtf +#define fsqrtf(x) sqrtf(x) +#endif #ifndef cosf #define cosf(x) ((F32)cos((F64)(x))) @@ -71,6 +71,9 @@ #ifndef powf #define powf(x,y) ((F32)pow((F64)(x),(F64)(y))) #endif +#ifndef expf +#define expf(x) ((F32)exp((F64)(x))) +#endif const F32 GRAVITY = -9.8f; --- linden/indra/llaudio/audioengine_fmod.cpp.orig 2008-03-05 21:58:38.000000000 +0000 +++ linden/indra/llaudio/audioengine_fmod.cpp 2008-03-15 14:20:43.000000000 +0000 @@ -54,21 +54,9 @@ FSOUND_DSPUNIT *gWindDSP = NULL; // These globals for the wind filter. Blech! -F64 gbuf0 = 0.0; -F64 gbuf1 = 0.0; -F64 gbuf2 = 0.0; -F64 gbuf3 = 0.0; -F64 gbuf4 = 0.0; -F64 gbuf5 = 0.0; -F64 gY0 = 0.0; -F64 gY1 = 0.0; - F32 gTargetGain = 0.f; -F32 gCurrentGain = 0.f; F32 gTargetFreq = 100.f; -F32 gCurrentFreq = 100.f; F32 gTargetPanGainR = 0.5f; -F32 gCurrentPanGainR = 0.5f; // Safe strcpy @@ -348,8 +336,8 @@ void LLAudioEngine_FMOD::updateWind(LLVector3 wind_vec, F32 camera_height_above_water) { LLVector3 wind_pos; - F64 pitch; - F64 center_freq; + F32 pitch; + F32 center_freq; if (!mEnableWind) { @@ -367,10 +355,10 @@ // cerr << "Wind update" << endl; - pitch = 1.0 + mapWindVecToPitch(wind_vec); - center_freq = 80.0 * pow(pitch,2.5*(mapWindVecToGain(wind_vec)+1.0)); + pitch = 1.0f + mapWindVecToPitch(wind_vec); + center_freq = 80.0f * powf(pitch,2.5f*((F32)mapWindVecToGain(wind_vec)+1.0f)); - gTargetFreq = (F32)center_freq; + gTargetFreq = center_freq; gTargetGain = (F32)mapWindVecToGain(wind_vec) * mMaxWindGain; gTargetPanGainR = (F32)mapWindVecToPan(wind_vec); } @@ -1080,93 +1068,162 @@ return open_state; } -/* This determines the format of the mixbuffer being passed in. change if you want to support int32 or float32 */ +// This determines the format of the mixbuffer being passed in. Change if you want to support int32 or float32. #if LL_DARWIN #define MIXBUFFERFORMAT S32 #else #define MIXBUFFERFORMAT S16 #endif -inline MIXBUFFERFORMAT clipSample(MIXBUFFERFORMAT sample, MIXBUFFERFORMAT min, MIXBUFFERFORMAT max) +inline MIXBUFFERFORMAT clipSample(S32 sample, MIXBUFFERFORMAT min, MIXBUFFERFORMAT max) { if (sample > max) + { sample = max; + } else if (sample < min) + { sample = min; + } return sample; } + +// *NOTE: This function gets called a *lot*. +// The inner loop runs 44,100 times per second. +// Keep performance in mind if you mess with this. void * F_CALLBACKAPI windCallback(void *originalbuffer, void *newbuffer, int length, void*) { -// originalbuffer = fsounds original mixbuffer. +// originalbuffer = FSOUND's original mixbuffer. // newbuffer = the buffer passed from the previous DSP unit. // length = length in samples at this mix time. // param = user parameter passed through in FSOUND_DSP_Create. -// -// modify the buffer in some fashion + + // This turns off wind synth if it is muted or very very low volume + if (gTargetGain < 0.0005f) + { + return newbuffer; + } + + static const U8 SUBSAMPLES = 2; + static const F32 FILTER_SAMPLE_PERIOD = (F32)SUBSAMPLES / 44100.0f; + static const F32 BANDWIDTH = 50.0f; + static const F32 B2 = expf(-F_TWO_PI * BANDWIDTH * FILTER_SAMPLE_PERIOD); + + static F32 pinking_buf0 = 0.0f; + static F32 pinking_buf1 = 0.0f; + static F32 pinking_buf2 = 0.0f; + static F32 Y0 = 0.0f; + static F32 Y1 = 0.0f; + static F32 last_sample = 0.0f; + static F32 current_freq = 0.0f; + static F32 current_gain = 0.0f; + static F32 current_pan_gain_r = 0.0f; + + F32 a0 = 0.0f, b1 = 0.0f; U8 *cursamplep = (U8*)newbuffer; - U8 wordsize = 2; + U8 wordsize = 2; + bool clip = TRUE; #if LL_DARWIN wordsize = sizeof(MIXBUFFERFORMAT); + clip = (2 == wordsize); #else - int mixertype = FSOUND_GetMixer(); - if (mixertype == FSOUND_MIXER_BLENDMODE || mixertype == FSOUND_MIXER_QUALITY_FPU) - { + int mixertype = FSOUND_GetMixer(); + if (mixertype == FSOUND_MIXER_BLENDMODE || mixertype == FSOUND_MIXER_QUALITY_FPU) + { wordsize = 4; + clip = FALSE; } #endif - double bandwidth = 50; - double inputSamplingRate = 44100; - double a0,b1,b2; - - // calculate resonant filter coeffs - b2 = exp(-(F_TWO_PI) * (bandwidth / inputSamplingRate)); - - while (length--) - { - gCurrentFreq = (float)((0.999 * gCurrentFreq) + (0.001 * gTargetFreq)); - gCurrentGain = (float)((0.999 * gCurrentGain) + (0.001 * gTargetGain)); - gCurrentPanGainR = (float)((0.999 * gCurrentPanGainR) + (0.001 * gTargetPanGainR)); - b1 = (-4.0 * b2) / (1.0 + b2) * cos(F_TWO_PI * (gCurrentFreq / inputSamplingRate)); - a0 = (1.0 - b2) * sqrt(1.0 - (b1 * b1) / (4.0 * b2)); - double nextSample; + bool interp_freq = false; + + //if the frequency isn't changing much, we don't need to interpolate in the inner loop + if (llabs(gTargetFreq - current_freq) > 200.0f) + { + interp_freq = true; + } + else + { + // calculate resonant filter coefficients + current_freq = gTargetFreq; + b1 = (-4.0f * B2) / (1.0f + B2) * cosf(F_TWO_PI * (current_freq * FILTER_SAMPLE_PERIOD)); + a0 = (1.0f - B2) * sqrtf(1.0f - (b1 * b1) / (4.0f * B2)); + } + + while (length) + { + F32 next_sample; - // start with white noise - nextSample = ll_frand(2.0f) - 1.0f; + // Start with white noise [-16384, 16383] + next_sample = (F32)rand() * (1.0f / (F32)(RAND_MAX / (U16_MAX / 4))) + (S16_MIN / 4); -#if 1 // LLAE_WIND_PINK apply pinking filter - gbuf0 = 0.997f * gbuf0 + 0.0126502f * nextSample; - gbuf1 = 0.985f * gbuf1 + 0.0139083f * nextSample; - gbuf2 = 0.950f * gbuf2 + 0.0205439f * nextSample; - gbuf3 = 0.850f * gbuf3 + 0.0387225f * nextSample; - gbuf4 = 0.620f * gbuf4 + 0.0465932f * nextSample; - gbuf5 = 0.250f * gbuf5 + 0.1093477f * nextSample; - - nextSample = gbuf0 + gbuf1 + gbuf2 + gbuf3 + gbuf4 + gbuf5; -#endif - -#if 1 //LLAE_WIND_RESONANT // do a resonant filter on the noise - nextSample = (double)( a0 * nextSample - b1 * gY0 - b2 * gY1 ); + // Apply a pinking filter + // Magic numbers taken from PKE method at http://www.firstpr.com.au/dsp/pink-noise/ + pinking_buf0 = pinking_buf0 * 0.99765f + next_sample * 0.0990460f; + pinking_buf1 = pinking_buf1 * 0.96300f + next_sample * 0.2965164f; + pinking_buf2 = pinking_buf2 * 0.57000f + next_sample * 1.0526913f; + + next_sample = pinking_buf0 + pinking_buf1 + pinking_buf2 + next_sample * 0.1848f; - gY1 = gY0; - gY0 = nextSample; -#endif + if (interp_freq) + { + // calculate resonant filter coefficients + current_freq = (0.999f * current_freq) + (0.001f * gTargetFreq); + b1 = (-4.0f * B2) / (1.0f + B2) * cosf(F_TWO_PI * (current_freq * FILTER_SAMPLE_PERIOD)); + a0 = (1.0f - B2) * sqrtf(1.0f - (b1 * b1) / (4.0f * B2)); + } + + // Apply a resonant low-pass filter on the pink noise + next_sample = ( a0 * next_sample - b1 * Y0 - B2 * Y1 ); - nextSample *= gCurrentGain; + Y1 = Y0; + Y0 = next_sample; + + current_gain = (0.999f * current_gain) + (0.001f * gTargetGain); + current_pan_gain_r = (0.999f * current_pan_gain_r) + (0.001f * gTargetPanGainR); + + next_sample *= current_gain; + F32 delta = (next_sample - last_sample) / (F32)SUBSAMPLES; - MIXBUFFERFORMAT sample; + S32 sample_left; + S32 sample_right; + + // Mix into the audio buffer, clipping if necessary for 16-bit mix buffers. + // *TODO: Should do something more intelligent like reducing wind gain to avoid clipping + if (clip) + { + for (int i=SUBSAMPLES; i && length; --i, --length) + { + last_sample = last_sample + delta; + sample_right = last_sample * current_pan_gain_r; + sample_left = last_sample - sample_right; - sample = llfloor(((F32)nextSample*32768.f*(1.0f - gCurrentPanGainR))+0.5f); - *(MIXBUFFERFORMAT*)cursamplep = clipSample((*(MIXBUFFERFORMAT*)cursamplep) + sample, -32768, 32767); - cursamplep += wordsize; - - sample = llfloor(((F32)nextSample*32768.f*gCurrentPanGainR)+0.5f); - *(MIXBUFFERFORMAT*)cursamplep = clipSample((*(MIXBUFFERFORMAT*)cursamplep) + sample, -32768, 32767); - cursamplep += wordsize; + *(MIXBUFFERFORMAT*)cursamplep = clipSample((*(MIXBUFFERFORMAT*)cursamplep) + sample_left, S16_MIN, S16_MAX); + cursamplep += wordsize; + + *(MIXBUFFERFORMAT*)cursamplep = clipSample((*(MIXBUFFERFORMAT*)cursamplep) + sample_right, S16_MIN, S16_MAX); + cursamplep += wordsize; + } + } + else + { + for (int i=SUBSAMPLES; i && length; --i, --length) + { + last_sample = last_sample + delta; + sample_right = last_sample * current_pan_gain_r; + sample_left = last_sample - sample_right; + + *(MIXBUFFERFORMAT*)cursamplep = (*(MIXBUFFERFORMAT*)cursamplep) + sample_left; + cursamplep += wordsize; + + *(MIXBUFFERFORMAT*)cursamplep = (*(MIXBUFFERFORMAT*)cursamplep) + sample_right; + cursamplep += wordsize; + } + } } return newbuffer;