#include #include "echop.h" #include "clone.h" STD_CREATE(Echo) ////////////////////////////////////////////////////////////////////////////// // // CDirectSoundEchoDMO::NDQueryInterface // // Subclass can override if it wants to implement more interfaces. // STDMETHODIMP CDirectSoundEchoDMO::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_IDirectSoundFXEcho) { return GetInterface((IDirectSoundFXEcho*)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); } ////////////////////////////////////////////////////////////////////////////// // // CDirectSoundEchoDMO::CDirectSoundEchoDMO // CDirectSoundEchoDMO::CDirectSoundEchoDMO( IUnknown *pUnk, HRESULT *phr ) : CComBase( pUnk, phr) , m_fDirty(true) // { EAX: put init data here if any (otherwise use Discontinuity). // } EAX { m_EaxSamplesPerSec = 22050; m_DelayL.Init(0); m_DelayR.Init(0); } ////////////////////////////////////////////////////////////////////////////// // // CDirectSoundEchoDMO::Init() // HRESULT CDirectSoundEchoDMO::Init() { DSFXEcho echo; HRESULT hr; // Force recalc of all internal parameters hr = GetAllParameters(&echo); if (SUCCEEDED(hr)) hr = SetAllParameters(&echo); if (SUCCEEDED(hr)) hr = m_DelayL.Init(m_EaxSamplesPerSec); if (SUCCEEDED(hr)) hr = m_DelayR.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 EFP_Wetdrymix, MPT_FLOAT, g_capsAll, DSFXECHO_WETDRYMIX_MIN, DSFXECHO_WETDRYMIX_MAX, 50, L"", L"WetDryMix", L"", EFP_Feedback, MPT_FLOAT, g_capsAll, DSFXECHO_FEEDBACK_MIN, DSFXECHO_FEEDBACK_MAX, 50, L"", L"Feedback", L"", EFP_DelayLeft, MPT_FLOAT, g_capsAll, DSFXECHO_LEFTDELAY_MIN, DSFXECHO_LEFTDELAY_MAX, 500, L"", L"LeftDelay", L"", EFP_DelayRight, MPT_FLOAT, g_capsAll, DSFXECHO_RIGHTDELAY_MIN, DSFXECHO_RIGHTDELAY_MAX, 500, L"", L"RightDelay", L"", EFP_PanDelay, MPT_BOOL, g_capsAll, DSFXECHO_PANDELAY_MIN, DSFXECHO_PANDELAY_MAX, 0, L"", L"PanDelay", L"", }; HRESULT CDirectSoundEchoDMO::InitOnCreation() { HRESULT hr = InitParams(1, &GUID_TIME_REFERENCE, 0, 0, sizeof(g_params)/sizeof(*g_params), g_params); return hr; } ////////////////////////////////////////////////////////////////////////////// // // CDirectSoundEchoDMO::~CDirectSoundEchoDMO // CDirectSoundEchoDMO::~CDirectSoundEchoDMO() { m_DelayL.Init(-1); m_DelayR.Init(-1); } ////////////////////////////////////////////////////////////////////////////// // // CDirectSoundEchoDMO::Clone // STDMETHODIMP CDirectSoundEchoDMO::Clone(IMediaObjectInPlace **pp) { return StandardDMOClone(this, pp); } // // Bump - bump the delay pointers. // void CDirectSoundEchoDMO::Bump(void) { // EAX { m_DelayL.Bump(); // Bump delay array pointers. m_DelayR.Bump(); // Bump delay array pointers. // } } HRESULT CDirectSoundEchoDMO::Discontinuity() { // { EAX m_EaxPan = 0; m_StateL = m_StateR = 0; m_DelayL.ZeroBuffer(); m_DelayR.ZeroBuffer(); // These values are set to be the defaults when the property page is activated. // m_EaxDelayLRead = m_DelayL.LastPos(-16); // m_EaxDelayRRead = m_DelayR.LastPos(-16); // These values have defined initial values. // } EAX return S_OK; } ////////////////////////////////////////////////////////////////////////////// __forceinline void CDirectSoundEchoDMO::DoOneSample(int *l, int *r) { float inPortL = (float)*l; float inPortR = (float)*r; float outPortL, outPortR; int pos; float tempvar, temp2; //LeftDelayRead: // tempvar = delayL[@-16] + 0 * 0; // tempvar = delayRread + 0 * 0; if (m_EaxPan) { pos = m_DelayR.Pos((int)m_EaxDelayRRead); tempvar = m_DelayR[pos]; } else { pos = m_DelayL.Pos((int)m_EaxDelayLRead); tempvar = m_DelayL[pos]; } temp2 = m_StateL + tempvar * m_EaxLpfb; // delayL[] = ACC + inPortL[0] * lpff; pos = m_DelayL.Pos(0); m_DelayL[pos] = temp2 + inPortL * m_EaxLpff; m_StateL = tempvar * m_EaxLpfb; // outPortL = wetlevel : inPortL[1] < tempvar; outPortL = Interpolate(inPortL, tempvar, m_EaxWetlevel); //RightDelayRead: // tempvar = delayR[@-16] + 0 * 0; // tempvar = delayRread + 0 * 0; if (m_EaxPan) { pos = m_DelayL.Pos((int)m_EaxDelayLRead); tempvar = m_DelayL[pos]; } else { pos = m_DelayR.Pos((int)m_EaxDelayRRead); tempvar = m_DelayR[pos]; } temp2 = m_StateR + tempvar * m_EaxLpfb; // delayR[]= ACC + inPortR[0] * lpff; pos = m_DelayR.Pos(0); m_DelayR[pos] = temp2 + inPortR * m_EaxLpff; m_StateR = tempvar * m_EaxLpfb; // outPortR = wetlevel : inPortR[1] < tempvar; outPortR = Interpolate(inPortR, tempvar, m_EaxWetlevel); *l = Saturate(outPortL); *r = Saturate(outPortR); Bump(); } ////////////////////////////////////////////////////////////////////////////// // // CDirectSoundEchoDMO::FBRProcess // HRESULT CDirectSoundEchoDMO::FBRProcess(DWORD cSamples, BYTE *pIn, BYTE *pOut) { // { EAX #define cb cSamples #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; DoOneSample(&i, &j); 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; DoOneSample(&i, &j); 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; } ////////////////////////////////////////////////////////////////////////////// // // CDirectSoundEchoDMO::ProcessInPlace // HRESULT CDirectSoundEchoDMO::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); } ////////////////////////////////////////////////////////////////////////////// // // CDirectSoundEchoDMO::SetParam // HRESULT CDirectSoundEchoDMO::SetParamInternal(DWORD dwParamIndex, MP_DATA value, bool fSkipPasssingToParamManager) { if (!m_EaxSamplesPerSec) return DMO_E_TYPE_NOT_ACCEPTED; // NO TYPE! switch (dwParamIndex) { // { EAX case EFP_Wetdrymix : CHECK_PARAM(DSFXECHO_WETDRYMIX_MIN, DSFXECHO_WETDRYMIX_MAX); PUT_EAX_VALUE(Wetlevel, value / 100); break; case EFP_Feedback : { CHECK_PARAM(DSFXECHO_FEEDBACK_MIN, DSFXECHO_FEEDBACK_MAX); MP_DATA valueFeedbackFactor = value / 100; // ratio out of one instead of 100 PUT_EAX_VALUE(Lpfb, TOFRACTION(valueFeedbackFactor/2)); PUT_EAX_VALUE(Lpff, TOFRACTION(sqrt(1.0 - valueFeedbackFactor*valueFeedbackFactor))); break; } case EFP_DelayLeft : { CHECK_PARAM(DSFXECHO_LEFTDELAY_MIN, DSFXECHO_LEFTDELAY_MAX); PUT_EAX_LVAL(DelayLRead, (value * FractMultiplier) /1000 * m_EaxSamplesPerSec); break; } case EFP_DelayRight : { CHECK_PARAM(DSFXECHO_RIGHTDELAY_MIN, DSFXECHO_RIGHTDELAY_MAX); PUT_EAX_LVAL(DelayRRead, (value * FractMultiplier) /1000 * m_EaxSamplesPerSec); break; case EFP_PanDelay : { CHECK_PARAM(DSFXECHO_PANDELAY_MIN, DSFXECHO_PANDELAY_MAX); PUT_EAX_LVAL(Pan, value); #if 0 if(value) { //Panned Delay float fval = m_EaxDelayRRead; m_EaxDelayRRead = m_EaxDelayLRead; m_EaxDelayLRead = fval; } else { //Unpanned Delay } #endif break; } } // } 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); } ////////////////////////////////////////////////////////////////////////////// // // CDirectSoundEchoDMO::SetAllParameters // STDMETHODIMP CDirectSoundEchoDMO::SetAllParameters(LPCDSFXEcho pEcho) { HRESULT hr = S_OK; // Check that the pointer is not NULL if (pEcho == NULL) { Trace(1,"ERROR: pEcho is NULL\n"); hr = E_POINTER; } // Set the parameters if (SUCCEEDED(hr)) hr = SetParam(EFP_Wetdrymix, pEcho->fWetDryMix); if (SUCCEEDED(hr)) hr = SetParam(EFP_Feedback, pEcho->fFeedback); if (SUCCEEDED(hr)) hr = SetParam(EFP_DelayLeft, pEcho->fLeftDelay); if (SUCCEEDED(hr)) hr = SetParam(EFP_DelayRight, pEcho->fRightDelay); if (SUCCEEDED(hr)) hr = SetParam(EFP_PanDelay, (float)pEcho->lPanDelay); m_fDirty = true; return hr; } ////////////////////////////////////////////////////////////////////////////// // // CDirectSoundEchoDMO::GetAllParameters // STDMETHODIMP CDirectSoundEchoDMO::GetAllParameters(LPDSFXEcho pEcho) { HRESULT hr = S_OK; MP_DATA mpd; if (pEcho == NULL) { return E_POINTER; } #define GET_PARAM(x,y) \ if (SUCCEEDED(hr)) { \ hr = GetParam(x, &mpd); \ if (SUCCEEDED(hr)) pEcho->y = mpd; \ } #define GET_PARAM_LONG(x,y) \ if (SUCCEEDED(hr)) { \ hr = GetParam(x, &mpd); \ if (SUCCEEDED(hr)) pEcho->y = (long)mpd; \ } GET_PARAM(EFP_Wetdrymix, fWetDryMix); GET_PARAM(EFP_Feedback, fFeedback); GET_PARAM(EFP_DelayLeft, fLeftDelay); GET_PARAM(EFP_DelayRight, fRightDelay); GET_PARAM_LONG(EFP_PanDelay, lPanDelay); 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 CDirectSoundEchoDMO::GetClassID(CLSID *pClsid) { if (pClsid==NULL) { return E_POINTER; } *pClsid = GUID_DSFX_STANDARD_ECHO; return NOERROR; } // GetClassID