#include "fmod_settings.h" #ifdef FMOD_SUPPORT_COMPRESSOR #include "fmod.h" #include "fmod_dspi.h" #include "fmod_dsp_compressor.h" #ifdef PLATFORM_PS3_SPU #include "fmod_systemi_spu.h" #else #include "fmod_systemi.h" #endif #include #ifdef PLATFORM_PS3 extern unsigned int _binary_spu_fmod_dsp_compressor_pic_start[]; #endif namespace FMOD { FMOD_DSP_DESCRIPTION_EX dspcompressor; #ifdef PLUGIN_EXPORTS #ifdef __cplusplus extern "C" { #endif /* FMODGetDSPDescription is mandantory for every fmod plugin. This is the symbol the registerplugin function searches for. Must be declared with F_API to make it export as stdcall. */ F_DECLSPEC F_DLLEXPORT FMOD_DSP_DESCRIPTION_EX * F_API FMODGetDSPDescriptionEx() { return DSPCompressor::getDescriptionEx(); } #ifdef __cplusplus } #endif #endif /* PLUGIN_EXPORTS */ #define hold_constant (0.0025f) #ifndef PLATFORM_PS3_SPU FMOD_DSP_PARAMETERDESC dspcompressor_param[4] = { { -60.0f, 0.0f, 0.0f, "Threshold", "dB", "Compressor threshold [-60dB, 0dB] Default = 0dB." }, { 10.0f, 200.0f, 50.0f, "Attack", "ms", "Compressor attack time. [10ms,200ms] Default = 50ms." }, { 20.0f, 1000.0f, 50.0f, "Release", "ms", "Compressor release time. [10ms,1000ms] Default = 50ms." }, { 0.0f, 30.0f, 0.0f, "Make up gain", "dB", "Compressor make up gain [0dB, 30dB] Default = 0dB." } }; #endif // PLATFORM_PS3_SPU /* [ [DESCRIPTION] [PARAMETERS] [RETURN_VALUE] [REMARKS] [PLATFORMS] Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube [SEE_ALSO] ] */ FMOD_DSP_DESCRIPTION_EX *DSPCompressor::getDescriptionEx() { #ifndef PLATFORM_PS3_SPU FMOD_memset(&dspcompressor, 0, sizeof(FMOD_DSP_DESCRIPTION_EX)); FMOD_strcpy(dspcompressor.name, "FMOD Compressor"); dspcompressor.version = 0x00010100; dspcompressor.create = DSPCompressor::createCallback; #ifdef PLATFORM_PS3 dspcompressor.read = (FMOD_DSP_READCALLBACK)_binary_spu_fmod_dsp_compressor_pic_start; /* SPU PIC entry address */ #else dspcompressor.read = DSPCompressor::readCallback; #endif dspcompressor.numparameters = sizeof(dspcompressor_param) / sizeof(dspcompressor_param[0]); dspcompressor.paramdesc = dspcompressor_param; dspcompressor.setparameter = DSPCompressor::setParameterCallback; dspcompressor.getparameter = DSPCompressor::getParameterCallback; #ifdef FMOD_SUPPORT_MEMORYTRACKER dspcompressor.getmemoryused = &DSPCompressor::getMemoryUsedCallback; #endif dspcompressor.mType = FMOD_DSP_TYPE_COMPRESSOR; dspcompressor.mCategory = FMOD_DSP_CATEGORY_FILTER; dspcompressor.mSize = sizeof(DSPCompressor); #else dspcompressor.read = DSPCompressor::readCallback; /* We only care about read function on SPU */ #endif return &dspcompressor; } #ifndef PLATFORM_PS3_SPU /* [ [DESCRIPTION] [PARAMETERS] [RETURN_VALUE] [REMARKS] [PLATFORMS] Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube [SEE_ALSO] ] */ FMOD_RESULT DSPCompressor::createInternal() { init(); int count; /* Setup ... */ for (count = 0; count < mDescription.numparameters; count++) { setParameter(count, mDescription.paramdesc[count].defaultval); } for(count = 0; count < DSP_MAXLEVELS_MAX; count++) { mMaxChannelIn[count] = 0.0f; } mGain = 1.0f; return FMOD_OK; } #endif //!PLATFORM_PS3_SPU /* [ [DESCRIPTION] [PARAMETERS] [RETURN_VALUE] FMOD_OK [REMARKS] [PLATFORMS] Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube [SEE_ALSO] ] */ FMOD_RESULT DSPCompressor::readInternal(float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels) { unsigned int sample; int channel; float in_abs, x; float holdconstant = mHoldConstant; /* Copy members for better thread safety. */ float threshold_lin = mThreshold_lin; float attack_constant = mAttack_constant; float release_constant = mRelease_constant; float gainmakeup_lin = mGainMakeup_lin; if (!inbuffer) { return FMOD_OK; } if (!(speakermask & ((1 << inchannels)-1))) /*No speaker channels are active, copy in buffer to out buffer and skip the DSP*/ { FMOD_memcpy(outbuffer, inbuffer, sizeof(float)*length*inchannels); return FMOD_OK; } /* if ( (++peakTimer_ >= peakHold_) || (keyLink > maxPeak_) ) { // if either condition is met: peakTimer_ = 0; // reset peak timer maxPeak_ = keyLink; // assign new peak to max peak } // attack/release if ( maxPeak_ > env_ ) att_.run( maxPeak_, env_ ); // run attack phase else rel_.run( maxPeak_, env_ ); // run release phase */ if ((speakermask & ((1 << inchannels)-1)) != ((1 << inchannels)-1)) { for (sample = 0; sample < length; sample++) { unsigned int base = sample * inchannels; float *sample_in = &(inbuffer[base]); float *sample_out = &(outbuffer[base]); float in_max = 0.0f; float over; for (channel = 0; channel < inchannels; channel++) { if ((1 << channel) & speakermask) { float *m; x = sample_in[channel]; m = &(mMaxChannelIn[channel]); in_abs = (x<0.0f) ? -x : x; // ABS input *m -= holdconstant; // Leak hold if (in_abs > *m) // Hold new max { *m = in_abs; } if (*m > in_max) // Update total max { in_max = *m; } } } // Note: This uses linear gain curves, not dB // dB is better but more costly // Over = linear ratio of input:threshold over = in_max/threshold_lin; // Apply attack or release curve to over // to obtain gain reduction if (over > 1.0f) { mGain = over + attack_constant * (mGain-over); } else { mGain = over + release_constant * (mGain-over); } // Apply gain reduction to output if reduction exists. if (mGain > 1.0f) { for (channel = 0; channel < inchannels; channel++) { if (!((1 << channel) & speakermask)) { sample_out[channel] = sample_in[channel]; } else { sample_out[channel] = gainmakeup_lin * sample_in[channel] / mGain; } } } else { for (channel = 0; channel < inchannels; channel++) { if (!((1 << channel) & speakermask)) { sample_out[channel] = sample_in[channel]; } else { sample_out[channel] = gainmakeup_lin * sample_in[channel]; } } } } } else { for (sample = 0; sample < length; sample++) { unsigned int base = sample * inchannels; float *sample_in = &(inbuffer[base]); float *sample_out = &(outbuffer[base]); float in_max = 0.0f; float over; for (channel = 0; channel < inchannels; channel++) { float *m; x = sample_in[channel]; m = &(mMaxChannelIn[channel]); in_abs = (x<0.0f) ? -x : x; // ABS input *m -= holdconstant; // Leak hold if (in_abs > *m) // Hold new max { *m = in_abs; } if (*m > in_max) // Update total max { in_max = *m; } } // Note: This uses linear gain curves, not dB // dB is better but more costly // Over = linear ratio of input:threshold over = in_max/threshold_lin; // Apply attack or release curve to over // to obtain gain reduction if (over > 1.0f) { mGain = over + attack_constant * (mGain-over); } else { mGain = over + release_constant * (mGain-over); } // Apply gain reduction to output if reduction exists. if (mGain > 1.0f) { for (channel = 0; channel < inchannels; channel++) { sample_out[channel] = gainmakeup_lin * sample_in[channel] / mGain; } } else { for (channel = 0; channel < inchannels; channel++) { sample_out[channel] = gainmakeup_lin * sample_in[channel]; } } } } return FMOD_OK; } #ifndef PLATFORM_PS3_SPU /* [ [DESCRIPTION] [PARAMETERS] [RETURN_VALUE] FMOD_OK [REMARKS] [PLATFORMS] Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube [SEE_ALSO] ] */ FMOD_RESULT DSPCompressor::setParameterInternal(int index, float value) { FMOD_RESULT result; int outputrate; result = mSystem->getSoftwareFormat(&outputrate, 0, 0, 0, 0, 0); if (result != FMOD_OK) { return result; } mHoldConstant = 10.0f / (float)outputrate; // 10Hz switch (index) { case FMOD_DSP_COMPRESSOR_THRESHOLD: { mThreshold_dB = value; mThreshold_lin = FMOD_POW(10.0f, mThreshold_dB/20.0f); break; } case FMOD_DSP_COMPRESSOR_ATTACK: { mAttack_ms = value; mAttack_constant = FMOD_EXP(-1000.0f / (mAttack_ms * outputrate) ); break; } case FMOD_DSP_COMPRESSOR_RELEASE: { mRelease_ms = value; mRelease_constant = FMOD_EXP(-1000.0f / (mRelease_ms * outputrate) ); break; } case FMOD_DSP_COMPRESSOR_GAINMAKEUP: { mGainMakeup_dB = value; mGainMakeup_lin = FMOD_POW(10.0f, mGainMakeup_dB/20.0f); break; } } /* if (mResonance >= 1.0f) { unsigned int nInd; float a0, a1, a2, b0, b1, b2; float fs; // Sampling frequency, cutoff frequency float k[2]; // overall gain factor float ktotal; float *coef; k[0] = 1.0f; // Set overall filter gain k[1] = 1.0f; ktotal = 1.0f; coef = mCoefficients + 1; // Skip k, or gain fs = (float)outputrate; // Sampling frequency (Hz) // Update overall filter gain in coef array mCoefficients[0] = ktotal; } */ return FMOD_OK; } /* [ [DESCRIPTION] [PARAMETERS] [RETURN_VALUE] [REMARKS] [PLATFORMS] Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube [SEE_ALSO] ] */ FMOD_RESULT DSPCompressor::getParameterInternal(int index, float *value, char *valuestr) { switch (index) { case FMOD_DSP_COMPRESSOR_THRESHOLD: { *value = mThreshold_dB; sprintf(valuestr, "%.02f", mThreshold_dB); break; } case FMOD_DSP_COMPRESSOR_ATTACK: { *value = mAttack_ms; sprintf(valuestr, "%.02f", mAttack_ms); break; } case FMOD_DSP_COMPRESSOR_RELEASE: { *value = mRelease_ms; sprintf(valuestr, "%.02f", mRelease_ms); break; } case FMOD_DSP_COMPRESSOR_GAINMAKEUP: { *value = mGainMakeup_dB; sprintf(valuestr, "%.02f", mGainMakeup_dB); break; } } return FMOD_OK; } /* [ [DESCRIPTION] [PARAMETERS] [RETURN_VALUE] [REMARKS] [PLATFORMS] [SEE_ALSO] ] */ #ifdef FMOD_SUPPORT_MEMORYTRACKER FMOD_RESULT DSPCompressor::getMemoryUsedImpl(MemoryTracker *tracker) { // Size of this class is already accounted for (via description.mSize). Just add extra allocated memory here. return FMOD_OK; } #endif /* ============================================================================================================== CALLBACK INTERFACE ============================================================================================================== */ /* [ [DESCRIPTION] [PARAMETERS] [RETURN_VALUE] [REMARKS] [PLATFORMS] Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube [SEE_ALSO] ] */ FMOD_RESULT F_CALLBACK DSPCompressor::createCallback(FMOD_DSP_STATE *dsp) { DSPCompressor *compressor = (DSPCompressor *)dsp; return compressor->createInternal(); } #endif //!PLATFORM_PS3_SPU /* [ [DESCRIPTION] [PARAMETERS] [RETURN_VALUE] [REMARKS] [PLATFORMS] Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube [SEE_ALSO] ] */ FMOD_RESULT F_CALLBACK DSPCompressor::readCallback(FMOD_DSP_STATE *dsp, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels) { DSPCompressor *compressor = (DSPCompressor *)dsp; return compressor->readInternal(inbuffer, outbuffer, length, inchannels, outchannels); } #ifndef PLATFORM_PS3_SPU /* [ [DESCRIPTION] [PARAMETERS] [RETURN_VALUE] [REMARKS] [PLATFORMS] Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube [SEE_ALSO] ] */ FMOD_RESULT F_CALLBACK DSPCompressor::setParameterCallback(FMOD_DSP_STATE *dsp, int index, float value) { DSPCompressor *compressor = (DSPCompressor *)dsp; return compressor->setParameterInternal(index, value); } /* [ [DESCRIPTION] [PARAMETERS] [RETURN_VALUE] [REMARKS] [PLATFORMS] Win32, Win64, Linux, Macintosh, XBox, PlayStation 2, GameCube [SEE_ALSO] ] */ FMOD_RESULT F_CALLBACK DSPCompressor::getParameterCallback(FMOD_DSP_STATE *dsp, int index, float *value, char *valuestr) { DSPCompressor *compressor = (DSPCompressor *)dsp; return compressor->getParameterInternal(index, value, valuestr); } #ifdef FMOD_SUPPORT_MEMORYTRACKER /* [ [DESCRIPTION] [PARAMETERS] [RETURN_VALUE] [REMARKS] [PLATFORMS] [SEE_ALSO] ] */ FMOD_RESULT F_CALLBACK DSPCompressor::getMemoryUsedCallback(FMOD_DSP_STATE *dsp, MemoryTracker *tracker) { DSPCompressor *compressor = (DSPCompressor *)dsp; return compressor->DSPCompressor::getMemoryUsed(tracker); } #endif #endif //!PLATFORM_PS3_SPU } #endif