655 lines
15 KiB
C++
Executable file
655 lines
15 KiB
C++
Executable file
#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 <stdio.h>
|
|
|
|
#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
|