#include #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(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