#include #include #define DMO_NOATL // DMO base class needs this to work w/o ATL #include #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; }