481 lines
12 KiB
C++
481 lines
12 KiB
C++
#include <windows.h>
|
|
|
|
#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<CDirectSoundEchoDMO, DSFXEcho>(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
|
|
|