home *** CD-ROM | disk | FTP | other *** search
/ Visual Basic Game Programming for Teens / VBGPFT.cdr / DirectX8 / dx8a_sdk.exe / samples / multimedia / directshow / dmo / gargledmo / gargle.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2000-10-02  |  13.3 KB  |  405 lines

  1. //------------------------------------------------------------------------------
  2. // File: Gargle.cpp
  3. //
  4. // Desc: DirectShow sample code - implementation of CGargle class.
  5. //
  6. // Copyright (c) 2000, Microsoft Corporation.  All rights reserved.
  7. //------------------------------------------------------------------------------
  8.  
  9.  
  10. #include "stdafx.h"
  11. #define FIX_LOCK_NAME
  12. #include <dmo.h>
  13. #include <dmobase.h>
  14. #include <initguid.h> // needed to define GUID_TIME_REFERENCE from medparam.h
  15. #include <param.h>
  16. #include "Gargle.h"
  17. #include <uuids.h>    // DirectShow media type guids
  18.  
  19. #define DEFAULT_GARGLE_RATE 20
  20.  
  21. #define CHECK_PARAM(lo, hi) \
  22.     if (value < lo || value > hi) {return E_INVALIDARG;} ;
  23.  
  24. /////////////////////////////////////////////////////////////////////////////
  25. //
  26. // CGargle
  27. //
  28. CGargle::CGargle( ) :
  29.     m_ulShape(0),
  30.     m_ulGargleFreqHz(DEFAULT_GARGLE_RATE),
  31.     m_fDirty(true),
  32.     m_bInitialized(FALSE)
  33. {
  34.     m_pUnkMarshaler = NULL;
  35.  
  36.     HRESULT hr = Init();
  37.     assert( SUCCEEDED( hr ) );
  38. }
  39.  
  40. 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;
  41. static ParamInfo g_params[] =
  42. {
  43. //  index           type        caps        min,                        max,                        neutral,                    unit text,  label,          pwchText??
  44.     GFP_Rate,       MPT_INT,    g_capsAll,  GARGLE_FX_RATEHZ_MIN,      GARGLE_FX_RATEHZ_MAX,      20,                         L"Hz",      L"Rate",        L"",
  45.     GFP_Shape,      MPT_ENUM,   g_capsAll,  GARGLE_FX_WAVE_TRIANGLE,   GARGLE_FX_WAVE_SQUARE,     GARGLE_FX_WAVE_TRIANGLE,   L"",        L"WaveShape",   L"Triangle,Square",
  46. };
  47.  
  48. //////////////////////////////////////////////////////////////////////////////
  49. //
  50. // CGargle::Init
  51. //
  52. HRESULT CGargle::Init()
  53. {
  54.     HRESULT hr = S_OK;
  55.     if( !m_bInitialized )
  56.     {
  57.         hr = InitParams(1, &GUID_TIME_REFERENCE, 0, 0, sizeof(g_params)/sizeof(*g_params), g_params);
  58.     }
  59.  
  60.     if( SUCCEEDED( hr ) )
  61.     {
  62.         // compute the period
  63.         m_ulPeriod = m_ulSamplingRate / m_ulGargleFreqHz;
  64.         m_bInitialized = TRUE;
  65.     }
  66.     return hr;
  67. }
  68.  
  69. //////////////////////////////////////////////////////////////////////////////
  70. //
  71. // CGargle::Clone
  72. //
  73. HRESULT CGargle::Clone(IMediaObjectInPlace **ppCloned) 
  74. {
  75.     if (!ppCloned)
  76.         return E_POINTER;
  77.  
  78.     HRESULT hr = S_OK;
  79.     CGargle * pNewGargle = new CComObject<CGargle>;
  80.     if( !pNewGargle )
  81.         hr = E_OUTOFMEMORY;
  82.  
  83.     hr = pNewGargle->Init();
  84.  
  85.     IMediaObject * pCloned = NULL;
  86.     if( SUCCEEDED( hr ) )
  87.     {
  88.         IUnknown *pUnk;
  89.         hr = pNewGargle->QueryInterface( IID_IUnknown, (void **) &pUnk );
  90.         if( SUCCEEDED( hr ) )
  91.         {
  92.             hr = pUnk->QueryInterface( IID_IMediaObject, (void **) &pCloned );
  93.             pUnk->Release();
  94.         }
  95.     }
  96.     else
  97.     {
  98.         return hr;
  99.     }
  100.  
  101.     //
  102.     // Copy parameter control information
  103.     //
  104.     if (SUCCEEDED(hr))
  105.         hr = pNewGargle->CopyParamsFromSource((CParamsManager *) this);
  106.  
  107.     // Copy current parameter values
  108.     GargleFX params;
  109.     if (SUCCEEDED(hr))
  110.         hr = GetAllParameters(¶ms);
  111.  
  112.     if (SUCCEEDED(hr))
  113.         hr = pNewGargle->SetAllParameters(¶ms);
  114.  
  115.     if (SUCCEEDED(hr))
  116.     {
  117.         // Copy the input and output types
  118.         DMO_MEDIA_TYPE mt;
  119.         DWORD cInputStreams = 0;
  120.         DWORD cOutputStreams = 0;
  121.         GetStreamCount(&cInputStreams, &cOutputStreams);
  122.  
  123.         for (DWORD i = 0; i < cInputStreams && SUCCEEDED(hr); ++i)
  124.         {
  125.             hr = GetInputCurrentType(i, &mt);
  126.             if (hr == DMO_E_TYPE_NOT_SET)
  127.             {
  128.                 hr = S_OK; // great, don't need to set the cloned DMO
  129.             }
  130.             else if (SUCCEEDED(hr))
  131.             {
  132.                 hr = pCloned->SetInputType(i, &mt, 0);
  133.                 MoFreeMediaType( &mt );
  134.             }
  135.         }
  136.  
  137.         for (i = 0; i < cOutputStreams && SUCCEEDED(hr); ++i)
  138.         {
  139.             hr = GetOutputCurrentType(i, &mt);
  140.             if (hr == DMO_E_TYPE_NOT_SET)
  141.             {
  142.                 hr = S_OK; // great, don't need to set the cloned DMO
  143.             }
  144.             else if (SUCCEEDED(hr))
  145.             {
  146.                 hr = pCloned->SetOutputType(i, &mt, 0);
  147.                 MoFreeMediaType( &mt );
  148.             }
  149.         }
  150.  
  151.         if (SUCCEEDED(hr))
  152.             hr = pCloned->QueryInterface(IID_IMediaObjectInPlace, (void**)ppCloned);
  153.  
  154.         // Release the object's original ref.  If clone succeeded (made it through QI) then returned pointer
  155.         // has one ref.  If we failed, refs drop to zero, freeing the object.
  156.         pCloned->Release();
  157.     }
  158.     return hr;
  159. }
  160.  
  161.  
  162. //////////////////////////////////////////////////////////////////////////////
  163. //
  164. // CGargle::GetLatency
  165. //
  166. STDMETHODIMP CGargle::GetLatency(THIS_ REFERENCE_TIME *prt)
  167. {
  168.     *prt = 0;
  169.     return S_OK;
  170. }
  171.  
  172. //////////////////////////////////////////////////////////////////////////////
  173. //
  174. // CGargle::Discontinuity
  175. //
  176. HRESULT CGargle::Discontinuity() {
  177.     m_ulPhase = 0;
  178.     return NOERROR;
  179. }
  180.  
  181. //////////////////////////////////////////////////////////////////////////////
  182. //
  183. // CGargle::FBRProcess
  184. //
  185. HRESULT CGargle::FBRProcess(DWORD cQuanta, BYTE *pIn, BYTE *pOut) {
  186.     if (!m_bInitialized)
  187.         return DMO_E_TYPE_NOT_SET;
  188.  
  189.     DWORD cSample, cChannel;
  190.     for (cSample = 0; cSample < cQuanta; cSample++) 
  191.     {
  192.         // If m_Shape is 0 (triangle) then we multiply by a triangular waveform
  193.         // that runs 0..Period/2..0..Period/2..0... else by a square one that
  194.         // is either 0 or Period/2 (same maximum as the triangle) or zero.
  195.         //
  196.         // m_Phase is the number of samples from the start of the period.
  197.         // We keep this running from one call to the next,
  198.         // but if the period changes so as to make this more
  199.         // than Period then we reset to 0 with a bang.  This may cause
  200.         // an audible click or pop (but, hey! it's only a sample!)
  201.         //
  202.         ++m_ulPhase;
  203.         if (m_ulPhase > m_ulPeriod)
  204.             m_ulPhase = 0;
  205.  
  206.         ULONG ulM = m_ulPhase;      // m is what we modulate with
  207.  
  208.         if (m_ulShape == 0) {   // Triangle
  209.             if (ulM > m_ulPeriod / 2)
  210.                 ulM = m_ulPeriod - ulM;  // handle downslope
  211.         } else {             // Square wave
  212.             if (ulM <= m_ulPeriod / 2)
  213.                 ulM = m_ulPeriod / 2;
  214.             else
  215.                 ulM = 0;
  216.         }
  217.  
  218.         for (cChannel = 0; cChannel < m_cChannels; cChannel++) {
  219.             if (m_b8bit) {
  220.                 // sound sample, zero based
  221.                 int i = pIn[cSample * m_cChannels + cChannel] - 128;
  222.                 // modulate
  223.                 i = (i * (signed)ulM * 2) / (signed)m_ulPeriod;
  224.                 // 8 bit sound uses 0..255 representing -128..127
  225.                 // Any overflow, even by 1, would sound very bad.
  226.                 // so we clip paranoically after modulating.
  227.                 // I think it should never clip by more than 1
  228.                 //
  229.                 if (i > 127)
  230.                     i = 127;
  231.                 if (i < -128)
  232.                     i = -128;
  233.                 // reset zero offset to 128
  234.                 pOut[cSample * m_cChannels + cChannel] = (unsigned char)(i + 128);
  235.    
  236.             } else {
  237.                 // 16 bit sound uses 16 bits properly (0 means 0)
  238.                 // We still clip paranoically
  239.                 //
  240.                 int i = ((short*)pIn)[cSample * m_cChannels + cChannel];
  241.                 // modulate
  242.                 i = (i * (signed)ulM * 2) / (signed)m_ulPeriod;
  243.                 // clip
  244.                 if (i > 32767)
  245.                     i = 32767;
  246.                 if (i < -32768)
  247.                     i = -32768;
  248.                 ((short*)pOut)[cSample * m_cChannels + cChannel] = (short)i;
  249.             }
  250.         }
  251.    }
  252.    return NOERROR;
  253. }
  254.  
  255. //////////////////////////////////////////////////////////////////////////////
  256. //
  257. // GetClassID
  258. //
  259. HRESULT CGargle::GetClassID(CLSID *pClsid)
  260. {
  261.     if (pClsid==NULL) {
  262.         return E_POINTER;
  263.     }
  264.     *pClsid = CLSID_Gargle;
  265.     return NOERROR;
  266.  
  267. } // GetClassID
  268.  
  269. //////////////////////////////////////////////////////////////////////////////
  270. //
  271. // CGargle::GetPages
  272. //
  273. HRESULT CGargle::GetPages(CAUUID * pPages)
  274. {
  275.     pPages->cElems = 1;
  276.     pPages->pElems = static_cast<GUID *>(CoTaskMemAlloc(sizeof(GUID)));
  277.     if (pPages->pElems == NULL)
  278.         return E_OUTOFMEMORY;
  279.  
  280.     *(pPages->pElems) = CLSID_GargDMOProp;
  281.  
  282.     return S_OK;
  283. }
  284.  
  285. //////////////////////////////////////////////////////////////////////////////
  286. //
  287. // CGargle::SetAllParameters
  288. //
  289. STDMETHODIMP CGargle::SetAllParameters(THIS_ LPCGargleFX pParm)
  290. {
  291.     HRESULT hr = S_OK;
  292.     
  293.     // Check that the pointer is not NULL
  294.     if (pParm == NULL) hr = E_POINTER;
  295.  
  296.     // Set the parameters
  297.     if (SUCCEEDED(hr)) hr = SetParam(GFP_Rate, static_cast<MP_DATA>(pParm->dwRateHz));
  298.     if (SUCCEEDED(hr)) hr = SetParam(GFP_Shape, static_cast<MP_DATA>(pParm->dwWaveShape));
  299.             
  300.     m_fDirty = true;
  301.     return hr;
  302. }
  303.  
  304. //////////////////////////////////////////////////////////////////////////////
  305. //
  306. // CGargle::GetAllParameters
  307. //
  308. STDMETHODIMP CGargle::GetAllParameters(THIS_ LPGargleFX pParm)
  309. {   
  310.     HRESULT hr = S_OK;
  311.     MP_DATA var;
  312.  
  313.     if (pParm == NULL)
  314.     {
  315.         return E_POINTER;
  316.     }
  317.  
  318. #define GET_PARAM_DWORD(x,y) \
  319.     if (SUCCEEDED(hr)) { \
  320.         hr = GetParam(x, &var); \
  321.         if (SUCCEEDED(hr)) pParm->y = (DWORD)var; \
  322.     }
  323.     
  324.     GET_PARAM_DWORD(GFP_Rate, dwRateHz);
  325.     GET_PARAM_DWORD(GFP_Shape, dwWaveShape);
  326.     
  327.     return hr;
  328. }
  329.  
  330. //////////////////////////////////////////////////////////////////////////////
  331. //
  332. // CGargle::SetParam
  333. //
  334. HRESULT CGargle::SetParamInternal(DWORD dwParamIndex, MP_DATA value, bool fSkipPasssingToParamManager)
  335. {
  336.     switch (dwParamIndex)
  337.     {
  338.     case GFP_Rate:
  339.         CHECK_PARAM(GARGLE_FX_RATEHZ_MIN,GARGLE_FX_RATEHZ_MAX);
  340.         m_ulGargleFreqHz = (unsigned)value;
  341.         if (m_ulGargleFreqHz < 1) m_ulGargleFreqHz = 1;
  342.         if (m_ulGargleFreqHz > 1000) m_ulGargleFreqHz = 1000;
  343.         
  344.         // Init is where m_ulPeriod is updated, so call it here
  345.         // Would be better to do this outside of Init though
  346.         Init();          
  347.         break;
  348.  
  349.     case GFP_Shape:
  350.         CHECK_PARAM(GARGLE_FX_WAVE_TRIANGLE,GARGLE_FX_WAVE_SQUARE);
  351.         m_ulShape = (unsigned)value;
  352.         break;
  353.     }
  354.  
  355.     // Let base class set this so it can handle all the rest of the param calls.
  356.     // Skip the base class if fSkipPasssingToParamManager.  This indicates that we're calling the function
  357.     //    internally using values that came from the base class -- thus there's no need to tell it values it
  358.     //    already knows.
  359.     return fSkipPasssingToParamManager ? S_OK : CParamsManager::SetParam(dwParamIndex, value);
  360. }
  361.  
  362. //////////////////////////////////////////////////////////////////////////////
  363. //
  364. // CGargle::Process
  365. //
  366. HRESULT CGargle::Process(ULONG ulQuanta, LPBYTE pcbData, REFERENCE_TIME rtStart, DWORD dwFlags)
  367. {
  368.     // Update parameter values from any curves that may be in effect.
  369.     // We pick up the current values stored in the CParamsManager helper for time rtStart. 
  370.  
  371.     // Note that we are using IMediaParams in a less than
  372.     // perfect way. We update at the beginning of every time slice instead of smoothly over the curve.
  373.     // This is okay for an effect like gargle as long as the time slice is consistently small (which 
  374.     // it conveniently is when hosted in DSound.)
  375.     // However, in the future we will update this sample to use a more appropriate and accurate
  376.     // mechanism.
  377.     // Here are some suggestions of how it can be done, with increasing degree of accuracy. Different
  378.     // types of effects and effect parameters require different levels of accuracy, so no solution is the best 
  379.     // solution for all (especially if you are concerned about CPU cost.)
  380.     // 1) Break the time slice up into mini pieces of some number of milliseconds
  381.     // each and run through all the steps in Process for each sub slice. This guarantees the
  382.     // stair stepping is small enough not to be noticable. This approach will work well for parameters
  383.     // that don't create an audible stair stepping noise (or "zipper") noise when controled in this way. 
  384.     // Control over volume, for example, does not work well.
  385.     // 2) Use the above mechanism, but pass the start and end values for each parameter to the
  386.     // processing engine. It, in turn, applies linear interpolation to each parameter. This results
  387.     // in a smooth approximation of the parameter curve and removes all but the most subtle aliasing noise.
  388.     // 3) Pass the curves directly to the processing engine, which accurately calculates each sample
  389.     // mathematically. This is obviously the best, but most complex and CPU intensive.
  390.  
  391.     this->UpdateActiveParams(rtStart, *this);
  392.  
  393.     DMO_MEDIA_TYPE mt;
  394.     HRESULT hr = GetInputCurrentType(0, &mt);
  395.     if( FAILED( hr ) )
  396.         return hr;
  397.  
  398.     // convert bytes to samples for the FBRProcess call
  399.     assert(mt.formattype == FORMAT_WaveFormatEx);
  400.     ulQuanta /= LPWAVEFORMATEX(mt.pbFormat)->nBlockAlign;
  401.     MoFreeMediaType( &mt );
  402.     return FBRProcess(ulQuanta, pcbData, pcbData);
  403. }
  404.  
  405.