windows-nt/Source/XPSP1/NT/multimedia/directx/dsound/dsdmo/compress.cpp

683 lines
17 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
#include <windows.h>
#include "compressp.h"
#include "clone.h"
STD_CREATE(Compressor)
//////////////////////////////////////////////////////////////////////////////
//
// CDirectSoundCompressorDMO::QueryInterface
//
// Subclass can override if it wants to implement more interfaces.
//
STDMETHODIMP CDirectSoundCompressorDMO::NDQueryInterface(THIS_ REFIID riid, LPVOID *ppv)
{
IMP_DSDMO_QI(riid,ppv);
if (riid == IID_IPersist)
{
return GetInterface((IPersist*)this, ppv);
}
else if (riid == IID_IMediaObject)
{
return GetInterface((IMediaObject*)this, ppv);
}
else if (riid == IID_IDirectSoundFXCompressor)
{
return GetInterface((IDirectSoundFXCompressor*)this, ppv);
}
else if (riid == IID_ISpecifyPropertyPages)
{
return GetInterface((ISpecifyPropertyPages*)this, ppv);
}
else if (riid == IID_IMediaParams)
{
return GetInterface((IMediaParams*)this, ppv);
}
else if (riid == IID_IMediaParamInfo)
{
return GetInterface((IMediaParamInfo*)this, ppv);
}
else
return CComBase::NDQueryInterface(riid, ppv);
}
//////////////////////////////////////////////////////////////////////////////
//
// CDirectSoundCompressorDMO::CDirectSoundCompressorDMO
//
CDirectSoundCompressorDMO::CDirectSoundCompressorDMO( IUnknown *pUnk, HRESULT *phr )
: CComBase( pUnk, phr),
m_fDirty(false)
// { EAX: put init data here if any (otherwise use Discontinuity).
// } EAX
{
m_EaxSamplesPerSec = 22050;
m_LeftDelay. Init(0);
m_RightDelay.Init(0);
}
//////////////////////////////////////////////////////////////////////////////
//
// CDirectSoundCompressorDMO::Init()
//
HRESULT CDirectSoundCompressorDMO::Init()
{
DSFXCompressor compress;
HRESULT hr;
// Force recalc of all internal parameters
//
hr = GetAllParameters(&compress);
if (SUCCEEDED(hr)) hr = SetAllParameters(&compress);
if (SUCCEEDED(hr)) hr = m_LeftDelay. Init(m_EaxSamplesPerSec);
if (SUCCEEDED(hr) && m_cChannels == 2) {
hr = m_RightDelay.Init(m_EaxSamplesPerSec);
}
if (SUCCEEDED(hr)) hr = Discontinuity();
return hr;
}
const MP_CAPS g_capsAll = MP_CAPS_CURVE_JUMP | MP_CAPS_CURVE_LINEAR | MP_CAPS_CURVE_SQUARE | MP_CAPS_CURVE_INVSQUARE | MP_CAPS_CURVE_SINE;
static ParamInfo g_params[] =
{
// index type caps min, max, neutral, unit text, label, pwchText
CPFP_Gain, MPT_FLOAT, g_capsAll, DSFXCOMPRESSOR_GAIN_MIN, DSFXCOMPRESSOR_GAIN_MAX, 0, L"", L"Gain", L"",
CPFP_Attack, MPT_FLOAT, g_capsAll, DSFXCOMPRESSOR_ATTACK_MIN, DSFXCOMPRESSOR_ATTACK_MAX, 10, L"", L"Attack", L"",
CPFP_Release, MPT_FLOAT, g_capsAll, DSFXCOMPRESSOR_RELEASE_MIN, DSFXCOMPRESSOR_RELEASE_MAX, 200, L"", L"Release", L"",
CPFP_Threshold, MPT_FLOAT, g_capsAll, DSFXCOMPRESSOR_THRESHOLD_MIN, DSFXCOMPRESSOR_THRESHOLD_MAX, -20, L"", L"Threshold", L"",
CPFP_Ratio, MPT_FLOAT, g_capsAll, DSFXCOMPRESSOR_RATIO_MIN, DSFXCOMPRESSOR_RATIO_MAX, 3, L"", L"Ratio", L"",
CPFP_Predelay, MPT_FLOAT, g_capsAll, DSFXCOMPRESSOR_PREDELAY_MIN, DSFXCOMPRESSOR_PREDELAY_MAX, 4, L"", L"Predelay", L"",
};
HRESULT CDirectSoundCompressorDMO::InitOnCreation()
{
HRESULT hr = InitParams(1, &GUID_TIME_REFERENCE, 0, 0, sizeof(g_params)/sizeof(*g_params), g_params);
return hr;
}
//////////////////////////////////////////////////////////////////////////////
//
// CDirectSoundCompressorDMO::~CDirectSoundCompressorDMO
//
CDirectSoundCompressorDMO::~CDirectSoundCompressorDMO()
{
m_LeftDelay. Init(-1);
m_RightDelay.Init(-1);
}
//////////////////////////////////////////////////////////////////////////////
//
// CDirectSoundCompressorDMO::Clone
//
STDMETHODIMP CDirectSoundCompressorDMO::Clone(IMediaObjectInPlace **pp)
{
return StandardDMOClone<CDirectSoundCompressorDMO, DSFXCompressor>(this, pp);
}
//
// Bump - bump the delay pointers.
//
void CDirectSoundCompressorDMO::Bump(void)
{
// EAX {
m_LeftDelay.Bump();
m_RightDelay.Bump();
// }
}
HRESULT CDirectSoundCompressorDMO::Discontinuity()
{
// { EAX
m_LeftDelay.ZeroBuffer();
if (m_cChannels == 2) {
m_RightDelay.ZeroBuffer();
}
m_Envelope = m_CompGain = 0;
// } EAX
return S_OK;
}
//////////////////////////////////////////////////////////////////////////////
float myexp( float finput, unsigned long maxexponent)
{
unsigned long mantissa, exponent, exponentwidth ;
long sign;
long input;
#ifdef DONTUSEi386
_asm {
fld finput
fistp input
}
#else
input = (int)(finput);
#endif
mantissa = input & 0x7FFFFFFFL ;
sign = input & 0x80000000L ; /* Preserve sign */
exponentwidth = 5;
if ((0x80000000L & input) != 0) { /* Take absolute value of input */
input = -input ;
}
/* Left-justify the mantissa and right-justify the exponent to separate */
mantissa = input << exponentwidth ;
exponent = input >> ( 31-exponentwidth ) ;
/*
* Insert the implied '1' at the mantissa MSB if not a zero exponent and
* adjust it.
*/
if( exponent != 0 ) {
mantissa = mantissa | 0x80000000L ;
exponent-- ;
}
mantissa = mantissa >> ( maxexponent-exponent ) ;
if( sign != 0 )
mantissa = ~mantissa ;
float x = (float)mantissa;
return(x);
}
__forceinline void CDirectSoundCompressorDMO::DoOneSampleMono(int *l)
{
int Pos0, PosX;
float inPortL = (float)*l;
float outPortL;
float temp1, temp2;
temp1 = inPortL;
// left_delay[] = temp1;
Pos0 = m_LeftDelay.Pos(0);
m_LeftDelay[Pos0] = temp1;
temp1 = (float)fabs(temp1);
// Take the log
#define LOG(x,y) mylog(x,y)
temp1 = (float)fabs(LOG(temp1 * 0x8000,31));
temp1 /= 0x80000000;
// Sidechain level meter
#ifndef MAX
#define MAX(x,y) ((x > y) ? x : y)
#endif
m_EaxCompInputPeak = MAX(temp1, m_EaxCompInputPeak);
// Envelope follower
temp2 = temp1 >= m_Envelope ? m_EaxAttackCoef : -m_EaxAttackCoef;
temp2 = temp2 <= 0 ? m_EaxReleaseCoef : temp2;
// m_Envelope = temp2 : temp1 < m_Envelope;
m_Envelope = Interpolate(temp1, m_Envelope, temp2);
m_CompGain = MAX(m_Envelope, m_EaxCompThresh);
// Log Difference between signal level and threshold level
m_CompGain = m_EaxCompThresh - m_CompGain;
#define cPOSFSCALE (float)0.9999999
m_CompGain = cPOSFSCALE + m_CompGain * m_EaxCompressionRatio;
// Compressor gain reduction meter
#ifndef MIN
#define MIN(x,y) ((x < y) ? x : y)
#endif
#define EXP(x,y) myexp(x,y)
m_EaxCompGainMin= MIN(m_CompGain, m_EaxCompGainMin);
m_CompGain = (float)EXP(m_CompGain * 0x80000000, 31);
m_CompGain /= 0x80000000;
// outPortL = left_point[@] * compGain;
PosX = m_LeftDelay.LastPos((int)m_EaxLeftPoint);
outPortL = m_LeftDelay[PosX] * m_CompGain;
temp1 = outPortL * m_EaxGainBiasIP;
outPortL = temp1 + outPortL * m_EaxGainBiasFP;
*l = Saturate(outPortL);
//Bump();
m_LeftDelay.Bump();
}
__forceinline void CDirectSoundCompressorDMO::DoOneSample(int *l, int *r)
{
int Pos0, PosX;
float inPortL = (float)*l;
float inPortR = (float)*r;
float outPortL, outPortR;
float temp1, temp2;
temp1 = inPortL;
temp2 = inPortR;
// left_delay[] = temp1;
Pos0 = m_LeftDelay.Pos(0);
m_LeftDelay[Pos0] = temp1;
// right_delay[] = temp2;
Pos0 = m_RightDelay.Pos(0);
m_RightDelay[Pos0] = temp2;
//Take the magnitude
temp1 = (float)fabs(temp1);
temp2 = (float)fabs(temp2);
// Take the average
// temp1 = 0.5 : temp1 < temp2;
temp1 = (temp1 + temp2) / 2;
// Take the log
#define LOG(x,y) mylog(x,y)
temp1 = (float)fabs(LOG(temp1 * 0x8000,31));
temp1 /= 0x80000000;
// Sidechain level meter
#ifndef MAX
#define MAX(x,y) ((x > y) ? x : y)
#endif
m_EaxCompInputPeak = MAX(temp1, m_EaxCompInputPeak);
// Envelope follower
temp2 = temp1 >= m_Envelope ? m_EaxAttackCoef : -m_EaxAttackCoef;
temp2 = temp2 <= 0 ? m_EaxReleaseCoef : temp2;
// m_Envelope = temp2 : temp1 < m_Envelope;
m_Envelope = Interpolate(temp1, m_Envelope, temp2);
m_CompGain = MAX(m_Envelope, m_EaxCompThresh);
// Log Difference between signal level and threshold level
m_CompGain = m_EaxCompThresh - m_CompGain;
#define cPOSFSCALE (float)0.9999999
m_CompGain = cPOSFSCALE + m_CompGain * m_EaxCompressionRatio;
// Compressor gain reduction meter
#ifndef MIN
#define MIN(x,y) ((x < y) ? x : y)
#endif
#define EXP(x,y) myexp(x,y)
m_EaxCompGainMin= MIN(m_CompGain, m_EaxCompGainMin);
m_CompGain = (float)EXP(m_CompGain * 0x80000000, 31);
m_CompGain /= 0x80000000;
// outPortL = left_point[@] * compGain;
PosX = m_LeftDelay.LastPos((int)m_EaxLeftPoint);
outPortL = m_LeftDelay[PosX] * m_CompGain;
// outPortR = right_point[@] * compGain;
PosX = m_RightDelay.LastPos((int)m_EaxRightPoint);
outPortR = m_RightDelay[PosX] * m_CompGain;
temp1 = outPortL * m_EaxGainBiasIP;
outPortL = temp1 + outPortL * m_EaxGainBiasFP;
temp1 = outPortR * m_EaxGainBiasIP;
outPortR = temp1 + outPortR * m_EaxGainBiasFP;
*l = Saturate(outPortL);
*r = Saturate(outPortR);
Bump();
}
//////////////////////////////////////////////////////////////////////////////
//
// CDirectSoundCompressorDMO::FBRProcess
//
HRESULT CDirectSoundCompressorDMO::FBRProcess(DWORD cCompressors, BYTE *pIn, BYTE *pOut)
{
// { EAX
#define cb cCompressors
#define pin pIn
#define pout pOut
if (m_cChannels == 1) {
if (m_b8bit) {
for (;cb > 0; --cb) {
int i, j;
i = *(pin+0)-128;
i *=256;
// j = i;
DoOneSampleMono(&i);
// i += j;
// i /= 2;
i /= 256;
*(pout+0) = (unsigned char)(i + 128);
pin += sizeof(unsigned char);
pout += sizeof(unsigned char);
}
}
else if (!m_b8bit) {
for (;cb > 0; --cb) { // for (;cb > 0; cb -= sizeof(short)) {
short int *psi = (short int *)pin;
short int *pso = (short int *)pout;
int i, j;
i = *psi;
// j = i;
DoOneSampleMono(&i);
// i += j;
// i /= 2;
*pso = (short)i;
pin += sizeof(short);
pout += sizeof(short);
}
}
}
else if (m_cChannels == 2) {
if (m_b8bit) {
for (;cb > 0; --cb) { // for (;cb > 0; cb -= 2 * sizeof(unsigned char)) {
int i, j;
i = *(pin+0)-128;
j = *(pin+1)-128;
i *=256; j *=256;
DoOneSample(&i, &j);
i /= 256; j /= 256;
*(pout+0) = (unsigned char)(i + 128);
*(pout+1) = (unsigned char)(j + 128);
pin += 2 * sizeof(unsigned char);
pout += 2 * sizeof(unsigned char);
}
}
else if (!m_b8bit) {
for (;cb > 0; --cb) { // for (;cb > 0; cb -= 2 * sizeof(short)) {
short int *psi = (short int *)pin;
short int *pso = (short int *)pout;
int i, j;
i = *(psi+0);
j = *(psi+1);
DoOneSample(&i, &j);
*(pso+0) = (short)i;
*(pso+1) = (short)j;
pin += 2 * sizeof(short);
pout += 2 * sizeof(short);
}
}
}
// } EAX
return S_OK;
}
//////////////////////////////////////////////////////////////////////////////
//
// CDirectSoundCompressorDMO::ProcessInPlace
//
HRESULT CDirectSoundCompressorDMO::ProcessInPlace(ULONG ulQuanta, LPBYTE pcbData, REFERENCE_TIME rtStart, DWORD dwFlags)
{
// Update parameter values from any curves that may be in effect.
this->UpdateActiveParams(rtStart, *this);
return FBRProcess(ulQuanta, pcbData, pcbData);
}
//////////////////////////////////////////////////////////////////////////////
//
// CDirectSoundCompressorDMO::SetParam
//
// { EAX
// }
HRESULT CDirectSoundCompressorDMO::SetParamInternal(DWORD dwParamIndex, MP_DATA value, bool fSkipPasssingToParamManager)
{
float fVal;
if (!m_EaxSamplesPerSec) return DMO_E_TYPE_NOT_ACCEPTED; // NO TYPE!
switch (dwParamIndex)
{
// { EAX
case CPFP_Gain : {
CHECK_PARAM(DSFXCOMPRESSOR_GAIN_MIN, DSFXCOMPRESSOR_GAIN_MAX);
fVal = (float)pow(10, value/20); //Convert from dB to linear
float _gainBiasIP, _gainBiasFP;
double d;
_gainBiasFP = (float)modf((double)fVal, &d);
_gainBiasIP = (float)d;
INTERPOLATE (GainBiasFP, TOFRACTION(_gainBiasFP));
PUT_EAX_FVAL(GainBiasIP, TOFRACTION(_gainBiasIP));
break;
}
case CPFP_Attack :
CHECK_PARAM(DSFXCOMPRESSOR_ATTACK_MIN, DSFXCOMPRESSOR_ATTACK_MAX);
m_EaxAttackCoef = (float)pow(10, -1/(value*m_EaxSamplesPerSec/1000));
PUT_EAX_FVAL(AttackCoef, TOFRACTION(m_EaxAttackCoef));
break;
case CPFP_Release :
CHECK_PARAM(DSFXCOMPRESSOR_RELEASE_MIN, DSFXCOMPRESSOR_RELEASE_MAX);
m_EaxReleaseCoef = (float)pow(10, -1/(value*m_EaxSamplesPerSec/1000));
break;
case CPFP_Threshold : {
CHECK_PARAM(DSFXCOMPRESSOR_THRESHOLD_MIN, DSFXCOMPRESSOR_THRESHOLD_MAX);
fVal = (float)pow(10, value/20); //Convert from dB to linear
float _compThresh;
float a, b;
a = (float)(pow(2, 26) * log(fVal * pow(2, 31))/log(2) + pow(2, 26));
b = (float)(pow(2, 31) - 1.0);
_compThresh = a < b ? a : b;
_compThresh /= (float)0x80000000;
PUT_EAX_FVAL(CompThresh, _compThresh);
break;
}
case CPFP_Ratio :
CHECK_PARAM(DSFXCOMPRESSOR_RATIO_MIN, DSFXCOMPRESSOR_RATIO_MAX);
m_EaxCompressionRatio = (float)(1.0 - 1.0/value);
PUT_EAX_FVAL(CompressionRatio, TOFRACTION(m_EaxCompressionRatio));
break;
case CPFP_Predelay : {
CHECK_PARAM(DSFXCOMPRESSOR_PREDELAY_MIN, DSFXCOMPRESSOR_PREDELAY_MAX);
float _length = (float)(value * m_EaxSamplesPerSec/1000.0);
PUT_EAX_LVAL(LeftPoint, _length + 2);
PUT_EAX_LVAL(RightPoint, _length + 2);
break;
}
/*
** Removed from PropertySet, Processing code left behind so we can resurrect later
**
case CPFP_CompMeterReset : {
CHECK_PARAM(DSFXCOMPRESSOR_COMPMETERRESET_MIN, DSFXCOMPRESSOR_COMPMETERRESET_MAX);
if(!value)
break; // return E_FAIL;
float InputPeak = m_EaxCompInputPeak;
float GainMin = m_EaxCompGainMin;
PUT_EAX_FVAL(CompInputPeak, 0);
PUT_EAX_FVAL(CompGainMin, 0.999999999);
InputPeak = (float)(186.0 * (InputPeak - 0.999999999)/0.999999999);
GainMin = - (float)(186.0 * (GainMin - 0.999999999)/0.999999999);
CParamsManager::SetParam(CPFP_CompMeterReset , 0);
if (!fSkipPasssingToParamManager)
CParamsManager::SetParam(CPFP_CompInputMeter , InputPeak);
if (!fSkipPasssingToParamManager)
CParamsManager::SetParam(CPFP_CompGainMeter , GainMin);
break;
}
*/
/* These values can't be set, only queried.
*/
/*
case CPFP_CompInputMeter :
CHECK_PARAM(DSFXCOMPRESSOR_COMPINPUTMETER_MIN, DSFXCOMPRESSOR_COMPINPUTMETER_MAX);
return E_FAIL;
case CPFP_CompGainMeter :
CHECK_PARAM(DSFXCOMPRESSOR_COMPGAINMETER_MIN, DSFXCOMPRESSOR_COMPGAINMETER_MAX);
return E_FAIL;
// } EAX
*/
default:
return E_FAIL;
}
// Let base class set this so it can handle all the rest of the param calls.
// Skip the base class if fSkipPasssingToParamManager. This indicates that we're calling the function
// internally using valuds that came from the base class -- thus there's no need to tell it values it
// already knows.
return fSkipPasssingToParamManager ? S_OK : CParamsManager::SetParam(dwParamIndex, value);
}
//////////////////////////////////////////////////////////////////////////////
//
// CDirectSoundCompressorDMO::SetAllParameters
//
STDMETHODIMP CDirectSoundCompressorDMO::SetAllParameters(LPCDSFXCompressor pComp)
{
HRESULT hr = S_OK;
// Check that the pointer is not NULL
if (pComp == NULL)
{
Trace(1,"ERROR: pComp is NULL\n");
hr = E_POINTER;
}
// Set the parameters
if (SUCCEEDED(hr)) hr = SetParam(CPFP_Gain, pComp->fGain);
if (SUCCEEDED(hr)) hr = SetParam(CPFP_Attack, pComp->fAttack);
if (SUCCEEDED(hr)) hr = SetParam(CPFP_Release, pComp->fRelease);
if (SUCCEEDED(hr)) hr = SetParam(CPFP_Threshold, pComp->fThreshold);
if (SUCCEEDED(hr)) hr = SetParam(CPFP_Ratio, pComp->fRatio);
if (SUCCEEDED(hr)) hr = SetParam(CPFP_Predelay, pComp->fPredelay);
/* These values can only be queried, not set. CPFP_CompMeterReset fills
* the values.
*/
// if (SUCCEEDED(hr)) hr = SetParam(CPFP_CompInputMeter, pComp->fCompInputMeter);
// if (SUCCEEDED(hr)) hr = SetParam(CPFP_CompGainMeter, pComp->fCompGainMeter);
m_fDirty = true;
return hr;
}
//////////////////////////////////////////////////////////////////////////////
//
// CDirectSoundCompressorDMO::GetAllParameters
//
STDMETHODIMP CDirectSoundCompressorDMO::GetAllParameters(LPDSFXCompressor pCompressor)
{
HRESULT hr = S_OK;
MP_DATA mpd;
if (pCompressor == NULL) return E_POINTER;
#define GET_PARAM(x,y) \
if (SUCCEEDED(hr)) { \
hr = GetParam(x, &mpd); \
if (SUCCEEDED(hr)) pCompressor->y = mpd; \
}
GET_PARAM(CPFP_Attack, fAttack);
GET_PARAM(CPFP_Release, fRelease);
GET_PARAM(CPFP_Threshold, fThreshold);
GET_PARAM(CPFP_Ratio, fRatio);
GET_PARAM(CPFP_Gain, fGain);
GET_PARAM(CPFP_Predelay, fPredelay);
return hr;
}
// GetClassID
//
// Part of the persistent file support. We must supply our class id
// which can be saved in a graph file and used on loading a graph with
// this fx in it to instantiate this filter via CoCreateInstance.
//
HRESULT CDirectSoundCompressorDMO::GetClassID(CLSID *pClsid)
{
if (pClsid==NULL) {
return E_POINTER;
}
*pClsid = GUID_DSFX_STANDARD_COMPRESSOR;
return NOERROR;
} // GetClassID