180 lines
5 KiB
C++
180 lines
5 KiB
C++
#include <windows.h>
|
|
#include <dmocom.h>
|
|
#define DMO_NOATL // DMO base class needs this to work w/o ATL
|
|
#include <dmobase.h>
|
|
|
|
#include "initguid.h"
|
|
DEFINE_GUID(CLSID_GargleDMO, 0xdafd8210,0x5711,0x4b91,0x9f,0xe3,0xf7,0x5b,0x7a,0xe2,0x79,0xbf);
|
|
|
|
class CGargle : public CComBase,
|
|
public CPCMDMO
|
|
{
|
|
public:
|
|
DECLARE_IUNKNOWN;
|
|
STDMETHODIMP NDQueryInterface(REFIID riid, void **ppv);
|
|
static CComBase* WINAPI CreateInstance(IUnknown *pUnk, HRESULT *phr);
|
|
CGargle(IUnknown *pUnk, HRESULT *phr);
|
|
|
|
// All of these methods are called by the base class
|
|
HRESULT FBRProcess(DWORD cQuanta, BYTE *pIn, BYTE *pOut);
|
|
HRESULT Init();
|
|
HRESULT Discontinuity();
|
|
void GetWindowParams(DWORD *pdwMaxLookahead,DWORD *pdwMaxLookbehind);
|
|
private:
|
|
// gargle params
|
|
ULONG m_ulPeriod;
|
|
ULONG m_ulShape;
|
|
ULONG m_ulGargleFreqHz;
|
|
|
|
// gargle state
|
|
ULONG m_ulPhase;
|
|
|
|
BOOL m_bInitialized;
|
|
};
|
|
|
|
CGargle::CGargle(IUnknown *pUnk, HRESULT *phr)
|
|
: CComBase(pUnk, phr),
|
|
m_ulShape(0),
|
|
m_ulGargleFreqHz(20),
|
|
m_bInitialized(FALSE)
|
|
{
|
|
}
|
|
|
|
void CGargle::GetWindowParams(
|
|
DWORD *pdwMaxLookahead, // in input quanta, 0 means no lookahead
|
|
DWORD *pdwMaxLookbehind
|
|
) {
|
|
*pdwMaxLookahead = 0;
|
|
*pdwMaxLookbehind = 0;
|
|
}
|
|
|
|
HRESULT CGargle::Init() {
|
|
// compute the period
|
|
m_ulPeriod = m_ulSamplingRate / m_ulGargleFreqHz;
|
|
|
|
m_bInitialized = TRUE;
|
|
return NOERROR;
|
|
}
|
|
|
|
HRESULT CGargle::Discontinuity() {
|
|
m_ulPhase = 0;
|
|
return NOERROR;
|
|
}
|
|
|
|
HRESULT CGargle::FBRProcess(DWORD cSamples, BYTE *pIn, BYTE *pOut) {
|
|
if (!m_bInitialized)
|
|
return DMO_E_TYPE_NOT_SET;
|
|
|
|
// test code
|
|
//memcpy(pOut, pIn, cSamples * m_cChannels * (m_b8bit ? 1 : 2));
|
|
//return NOERROR;
|
|
|
|
DWORD cSample, cChannel;
|
|
for (cSample = 0; cSample < cSamples; cSample++) {
|
|
// If m_Shape is 0 (triangle) then we multiply by a triangular waveform
|
|
// that runs 0..Period/2..0..Period/2..0... else by a square one that
|
|
// is either 0 or Period/2 (same maximum as the triangle) or zero.
|
|
//
|
|
// m_Phase is the number of samples from the start of the period.
|
|
// We keep this running from one call to the next,
|
|
// but if the period changes so as to make this more
|
|
// than Period then we reset to 0 with a bang. This may cause
|
|
// an audible click or pop (but, hey! it's only a sample!)
|
|
//
|
|
++m_ulPhase;
|
|
if (m_ulPhase > m_ulPeriod)
|
|
m_ulPhase = 0;
|
|
|
|
ULONG ulM = m_ulPhase; // m is what we modulate with
|
|
|
|
if (m_ulShape == 0) { // Triangle
|
|
if (ulM > m_ulPeriod / 2)
|
|
ulM = m_ulPeriod - ulM; // handle downslope
|
|
} else { // Square wave
|
|
if (ulM <= m_ulPeriod / 2)
|
|
ulM = m_ulPeriod / 2;
|
|
else
|
|
ulM = 0;
|
|
}
|
|
|
|
for (cChannel = 0; cChannel < m_cChannels; cChannel++) {
|
|
if (m_b8bit) {
|
|
// sound sample, zero based
|
|
int i = pIn[cSample * m_cChannels + cChannel] - 128;
|
|
// modulate
|
|
i = (i * (signed)ulM * 2) / (signed)m_ulPeriod;
|
|
// 8 bit sound uses 0..255 representing -128..127
|
|
// Any overflow, even by 1, would sound very bad.
|
|
// so we clip paranoically after modulating.
|
|
// I think it should never clip by more than 1
|
|
//
|
|
if (i > 127)
|
|
i = 127;
|
|
if (i < -128)
|
|
i = -128;
|
|
// reset zero offset to 128
|
|
pOut[cSample * m_cChannels + cChannel] = (unsigned char)(i + 128);
|
|
|
|
} else {
|
|
// 16 bit sound uses 16 bits properly (0 means 0)
|
|
// We still clip paranoically
|
|
//
|
|
int i = ((short*)pIn)[cSample * m_cChannels + cChannel];
|
|
// modulate
|
|
i = (i * (signed)ulM * 2) / (signed)m_ulPeriod;
|
|
// clip
|
|
if (i > 32767)
|
|
i = 32767;
|
|
if (i < -32768)
|
|
i = -32768;
|
|
((short*)pOut)[cSample * m_cChannels + cChannel] = (short)i;
|
|
}
|
|
}
|
|
}
|
|
return NOERROR;
|
|
}
|
|
|
|
//
|
|
// COM stuff
|
|
//
|
|
CComBase* WINAPI CGargle::CreateInstance(IUnknown *pUnk, HRESULT *phr) {
|
|
return new CGargle(pUnk, phr);
|
|
}
|
|
|
|
HRESULT CGargle::NDQueryInterface(REFIID riid, void **ppv) {
|
|
if (riid == IID_IMediaObject)
|
|
return GetInterface((IMediaObject*)this, ppv);
|
|
else
|
|
return CComBase::NDQueryInterface(riid, ppv);
|
|
}
|
|
|
|
struct CComClassTemplate g_ComClassTemplates[] = {
|
|
{
|
|
&CLSID_GargleDMO,
|
|
CGargle::CreateInstance
|
|
}
|
|
};
|
|
|
|
int g_cComClassTemplates = 1;
|
|
|
|
STDAPI DllRegisterServer(void) {
|
|
HRESULT hr;
|
|
|
|
// Register as a COM class
|
|
hr = CreateCLSIDRegKey(CLSID_GargleDMO, "Gargle media object");
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
// Now register as a DMO
|
|
DMO_PARTIAL_MEDIATYPE mt;
|
|
mt.type = MEDIATYPE_Audio;
|
|
mt.subtype = MEDIASUBTYPE_PCM;
|
|
return DMORegister(L"gargle", CLSID_GargleDMO, DMOCATEGORY_AUDIO_EFFECT, 0, 1, &mt, 1, &mt);
|
|
}
|
|
|
|
STDAPI DllUnregisterServer(void) {
|
|
// BUGBUG - implement !
|
|
return S_OK;
|
|
}
|
|
|