home *** CD-ROM | disk | FTP | other *** search
/ The Net: Ultimate Internet Guide / WWLCD1.ISO / mac / SiteBldr / AMOVIE / SDK / _SETUP / COMMON.Z / gargle.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1996-05-17  |  16.6 KB  |  553 lines

  1. //==========================================================================;
  2. //
  3. //  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
  4. //  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  5. //  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
  6. //  PURPOSE.
  7. //
  8. //  Copyright (c) 1992 - 1996  Microsoft Corporation.  All Rights Reserved.
  9. //
  10. //--------------------------------------------------------------------------;
  11.  
  12. /*
  13.  * Gargle Filter - A Transform filter that turns humans into daleks!!!
  14.  *
  15.  * Known problems:
  16.  * 1. The properties sheet does NOT give real-time control.
  17.  *    It affects the samples that are being processed. This
  18.  *    means that there can be a long latency between moving the knob
  19.  *    and anything actually happening
  20.  * 2. It doesn't handle read-only streams.
  21.  * 3. The track bar doesn't get initialised with the current setting
  22.  *
  23.  */
  24.  
  25.  /* An example of a transform-in-place filter.
  26.     This filter has one input pin, one output pin and
  27.     does its transform in-place (i.e. without copying the data)
  28.     on the push thread (i.e. it is called with a buffer, which it
  29.     transforms and gives to the next filter downstream.  It is
  30.     then blocked until that filter returns.  It then returns
  31.     to its own caller.
  32.  
  33.     The filter modulates sound by multiplying the value of each sample
  34.     by a relatively slowly varying triangular waveform.  Depending on
  35.     the frequency of the modulation it will sound like recurent fading,
  36.     like a tremolo or like a sort of distortion.
  37.  
  38.     It has a properties page which allows control of one property
  39.     (the frequency of the modulating waveform).  It exports a private
  40.     interface (IGargle) which the properties page uses to get or set
  41.     the frequency.
  42.  
  43.     As far as possible the properties page code has been separated into
  44.     is implemented in the files GargProp.* whereas the basic filter
  45.     code is in this file.
  46.  
  47.     The word "sample" is used in two senses.  It means either a sound sample
  48.     which is 8 or 16 bits of data representing the instantanious sound pressure
  49.     or else it means a quartz sample which is the unit of data that is passed
  50.     between filters, i.e. a buffer full of sound samples.
  51. */
  52.  
  53.  
  54. #include <streams.h>              // quartz, includes windows
  55.  
  56. #include <initguid.h>
  57. #include <olectl.h>
  58. #include <olectlid.h>
  59.  
  60. #include "GargUids.h"             // our own uuids
  61.  
  62. // The next two are only concerned with properties.
  63. #include "IGargle.h"              // IGargle (properties)
  64. #include "GargProp.h"             // CGargleProperties
  65. #include "Gargle.h"               // CGargle
  66.  
  67. // Put out the name of the function and instance on the debugger.
  68. #define DbgFunc(a) DbgLog(( LOG_TRACE                        \
  69.                           , 2                                \
  70.                           , TEXT("CGargle(Instance %d)::%s") \
  71.                           , m_nThisInstance                  \
  72.                           , TEXT(a)                          \
  73.                          ));
  74.  
  75. // setup data
  76.  
  77. AMOVIESETUP_MEDIATYPE sudPinTypes =   { &MEDIATYPE_Audio                   // clsMajorType
  78.                                            , &MEDIASUBTYPE_NULL }  ;       // clsMinorType
  79.  
  80. AMOVIESETUP_PIN psudPins[] = { { L"Input"            // strName
  81.                                , FALSE               // bRendered
  82.                                , FALSE               // bOutput
  83.                                , FALSE               // bZero
  84.                                , FALSE               // bMany
  85.                                , &CLSID_NULL         // clsConnectsToFilter
  86.                                , L"Output"           // strConnectsToPin
  87.                                , 1                   // nTypes
  88.                                , &sudPinTypes }      // lpTypes
  89.                              , { L"Output"           // strName
  90.                                , FALSE               // bRendered
  91.                                , TRUE                // bOutput
  92.                                , FALSE               // bZero
  93.                                , FALSE               // bMany
  94.                                , &CLSID_NULL         // clsConnectsToFilter
  95.                                , L"Input"            // strConnectsToPin
  96.                                , 1                   // nTypes
  97.                                , &sudPinTypes } };   // lpTypes
  98.  
  99.  
  100. AMOVIESETUP_FILTER sudGargle = { &CLSID_Gargle                      // clsID
  101.                                   , L"Gargle Filter"                // strName
  102.                                   , MERIT_DO_NOT_USE                // dwMerit
  103.                                   , 2                               // nPins
  104.                                   , psudPins };                     // lpPin
  105.  
  106.  
  107. // Needed for the CreateInstance mechanism
  108. CFactoryTemplate g_Templates[2]=
  109.     { { L"Gargle filter"              , &CLSID_Gargle , CGargle::CreateInstance          }
  110.     , { L"Gargle filter Property Page", &CLSID_GargProp, CGargleProperties::CreateInstance}
  111.     };
  112.  
  113. int g_cTemplates = sizeof(g_Templates)/sizeof(g_Templates[0]);
  114.  
  115. // initialise the static instance count.
  116. int CGargle::m_nInstanceCount = 0;
  117.  
  118. //
  119. // CGargle::Constructor
  120. //
  121. CGargle::CGargle(TCHAR *tszName, LPUNKNOWN punk, HRESULT *phr)
  122.     : CTransInPlaceFilter (tszName, punk, CLSID_Gargle, phr)
  123.     , CPersistStream(punk, phr)
  124.     , m_DefaultGargleRate (DefaultGargleRate)
  125.     , m_GargleRate        (DefaultGargleRate)
  126.     , m_SamplesPerSec     (0)
  127.     , m_BytesPerSample    (0)
  128.     , m_Channels          (0)
  129.     , m_Phase             (0)
  130.     , m_Shape             (0)
  131. {
  132.     m_nThisInstance = ++m_nInstanceCount;
  133.  
  134.     DbgFunc("CGargle");
  135. } // (CGargle constructor)
  136.  
  137.  
  138. //
  139. // CreateInstance
  140. //
  141. // Provide the way for COM to create a CGargle object
  142. CUnknown *CGargle::CreateInstance(LPUNKNOWN punk, HRESULT *phr) {
  143.  
  144.     CGargle *pNewObject = new CGargle(NAME("Gargle Filter"), punk, phr);
  145.     if (pNewObject == NULL) {
  146.         *phr = E_OUTOFMEMORY;
  147.     }
  148.  
  149.     return pNewObject;
  150. } // CreateInstance
  151.  
  152.  
  153. //
  154. // GetSetupData
  155. //
  156. LPAMOVIESETUP_FILTER CGargle::GetSetupData()
  157. {
  158.   return &sudGargle;
  159. }
  160.  
  161. //
  162. // NonDelegatingQueryInterface
  163. //
  164. // Reveal our persistent stream, property pages and IGargle interfaces
  165. STDMETHODIMP CGargle::NonDelegatingQueryInterface(REFIID riid, void **ppv) {
  166.  
  167.     if (riid == IID_IGargle) {
  168.         return GetInterface((IGargle *) this, ppv);
  169.     } else if (riid == IID_ISpecifyPropertyPages) {
  170.         return GetInterface((ISpecifyPropertyPages *) this, ppv);
  171.     } else if (riid == IID_IPersistStream) {
  172.         AddRef();     // Add a reference count to ourselves
  173.         *ppv = (void *)(IPersistStream *)this;
  174.         return NOERROR;
  175.  
  176.         // was: return GetInterface((IPersistStream *) this, ppv);
  177.         // but after reading combase.cpp GetInterface, I didn't understand that.
  178.     } else {
  179.         return CTransInPlaceFilter::NonDelegatingQueryInterface(riid, ppv);
  180.     }
  181. } // NonDelegatingQueryInterface
  182.  
  183.  
  184. STDMETHODIMP CGargle::GetClassID(CLSID *pClsid)
  185. {
  186.     if (pClsid==NULL) {
  187.         return E_POINTER;
  188.     }
  189.     *pClsid = CLSID_Gargle;
  190.     return NOERROR;
  191. }
  192.  
  193.  
  194. int CGargle::SizeMax()
  195. {
  196.     return 24;
  197. }
  198.  
  199. HRESULT CGargle::WriteToStream(IStream *pStream)
  200. {
  201.     HRESULT hr;
  202.     hr = WriteInt(pStream, m_GargleRate);
  203.     if (FAILED(hr)) return hr;
  204.     hr = WriteInt(pStream, m_Shape);
  205.     if (FAILED(hr)) return hr;
  206.     return NOERROR;
  207. }
  208.  
  209.  
  210. HRESULT CGargle::ReadFromStream(IStream *pStream)
  211. {
  212.     HRESULT hr;
  213.     m_GargleRate = ReadInt(pStream, hr);
  214.     if (FAILED(hr)) return hr;
  215.     m_Shape = ReadInt(pStream, hr);
  216.     if (FAILED(hr)) return hr;
  217.     return NOERROR;
  218. }
  219.  
  220.  
  221. // We make a total mess of the sound by modulating it with a waveform.
  222. // We know the frequency of the modulation (from the slider setting).
  223. // Uses and updates m_Phase
  224. // Uses m_SamplesPerSec, m_Channels, m_GargleRate, m_Shape
  225. void CGargle::MessItAbout(PBYTE pb, int cb)
  226. {
  227.     // We know how many samples per sec and how
  228.     // many channels so we can calculate the modulation period in samples.
  229.     CAutoLock foo(&m_GargleLock);
  230.  
  231.     int Period = (m_SamplesPerSec * m_Channels) / m_GargleRate;
  232.  
  233.  
  234.     while (cb>0) {
  235.        --cb;
  236.  
  237.        // multiply by a triangular waveform
  238.        // that runs 0..Period/2..0..Period/2..0...
  239.        // or a square wave that is either 0 or Period/2
  240.        {
  241.            // m_Phase is the number of samples from the start of the period.
  242.            // We keep this running from one call to the next,
  243.            // but if the period changes so as to make this more
  244.            // than Period then we reset to 0 with a bang.
  245.            ++m_Phase;
  246.            if (m_Phase>Period) m_Phase = 0;
  247.  
  248.            int M = m_Phase;      // m is what we modulate with
  249.            if (m_Shape ==0 ) {
  250.                // Triangle
  251.                if (M>Period/2) M = Period-M;  // downslope
  252.            } else {
  253.                // Square wave
  254.                if (M<=Period/2) M = Period/2; else M = 0;
  255.            }
  256.  
  257.            if (m_BytesPerSample==1) {
  258.                // 8 bit sound uses 0..255 representing -128..127
  259.                int i = *pb-128;               // sound sample, zero based
  260.                i = (i*M*2)/Period;
  261.                if (i>127) i = 127;
  262.                if (i<-128) i = -128;
  263.                *pb = (unsigned char)(i+128);                   // reset zero
  264.            } else if (m_BytesPerSample==2) {
  265.                // 16 bit sound uses 16 bits properly (0 means 0)
  266.                short int *psi = (short int *)pb;
  267.                int i = *psi;
  268.                i = (i*M*2)/Period;
  269.                if (i>32767) i = 32767;
  270.                if (i<-32768) i = -32768;
  271.                *psi = (short)i;
  272.                ++pb;  // nudge it on another 8 bits here
  273.                --cb;  // and nudge the count too.
  274.            } else {
  275.                DbgBreak("Too many bytes per sample");
  276.                // just leave it alone!
  277.            }
  278.        }
  279.        ++pb;   // move on 8 bits to next sound sample
  280.     }
  281. } // MessItAbout
  282.  
  283.  
  284.  
  285. //
  286. // Transform
  287. //
  288. // Convert the input quartz sample into the output quartz sample.
  289. //
  290. HRESULT CGargle::Transform(IMediaSample *pSample) {
  291.  
  292.     DbgFunc("Transform");
  293.  
  294.     // Get the details of the data (address, length)
  295.  
  296.     BYTE *pSampleBuffer;
  297.     int iSize = pSample->GetSize();
  298.     pSample->GetPointer(&pSampleBuffer);
  299.  
  300.  
  301.     // Actually transform the data
  302.  
  303.     MessItAbout(pSampleBuffer, iSize );
  304.  
  305.     return NOERROR;
  306.  
  307. } // Transform
  308.  
  309.  
  310.  
  311. //
  312. // CheckInputType
  313. //
  314. // We only work for wave audio, 8 or 16 bit, uncompressed.
  315. //
  316. HRESULT CGargle::CheckInputType(const CMediaType *pmt) {
  317.     DbgFunc("CheckInputType");
  318.  
  319.     WAVEFORMATEX *pwfx = (WAVEFORMATEX *) pmt->pbFormat;
  320.     DbgLog((LOG_TRACE,1,TEXT("Format length %d"),pmt->cbFormat));
  321.  
  322. #ifdef DEBUG
  323.  
  324.     const int iGUID_STRING = 128;
  325.  
  326.     // Dump the GUID types
  327.  
  328.     DbgLog((LOG_TRACE,2,TEXT("Major type %s"),GuidNames[pmt->majortype]));
  329.     DbgLog((LOG_TRACE,2,TEXT("Subtype %s"),GuidNames[pmt->subtype]));
  330.  
  331.     // Dump the generic media types
  332.  
  333.     DbgLog((LOG_TRACE,2,TEXT("Fixed size sample %d"),pmt->bFixedSizeSamples));
  334.     DbgLog((LOG_TRACE,2,TEXT("Temporal compression %d"),pmt->bTemporalCompression));
  335.     DbgLog((LOG_TRACE,2,TEXT("Sample size %d"),pmt->lSampleSize));
  336.     DbgLog((LOG_TRACE,2,TEXT("Format size %d"),pmt->cbFormat));
  337.  
  338. #endif
  339.  
  340.  
  341.     // reject non-Audio type
  342.     if (pmt->majortype != MEDIATYPE_Audio) {
  343.         return E_INVALIDARG;
  344.     }
  345.  
  346.  
  347. #ifdef DEBUG
  348.  
  349.     // Now that we know it's audio we can
  350.     // Dump the contents of the WAVEFORMATEX type-specific format structure
  351.  
  352.     DbgLog((LOG_TRACE,2,TEXT("wFormatTag %u"), pwfx->wFormatTag));
  353.     DbgLog((LOG_TRACE,2,TEXT("nChannels %u"), pwfx->nChannels));
  354.     DbgLog((LOG_TRACE,2,TEXT("nSamplesPerSec %lu"), pwfx->nSamplesPerSec));
  355.     DbgLog((LOG_TRACE,2,TEXT("nAvgBytesPerSec %lu"), pwfx->nAvgBytesPerSec));
  356.     DbgLog((LOG_TRACE,2,TEXT("nBlockAlign %u"), pwfx->nBlockAlign));
  357.     DbgLog((LOG_TRACE,2,TEXT("wBitsPerSample %u"), pwfx->wBitsPerSample));
  358.  
  359.     // PCM uses a WAVEFORMAT and does not have the extra size field
  360.  
  361.     if (pmt->cbFormat >= sizeof(WAVEFORMATEX)) {
  362.         DbgLog((LOG_TRACE,2,TEXT("cbSize %u"), pwfx->cbSize));
  363.     }
  364.  
  365. #endif
  366.  
  367.     // Reject invalid format blocks
  368.     if (pmt->cbFormat < sizeof(PCMWAVEFORMAT))
  369.         return E_INVALIDARG;
  370.  
  371.     // Reject compressed audio
  372.     if (pmt->bTemporalCompression) {
  373.         return E_INVALIDARG;
  374.     }
  375.  
  376.     // Accept only 8 or 16 bit
  377.     if (pwfx->wBitsPerSample!=8 && pwfx->wBitsPerSample!=16) {
  378.         return E_INVALIDARG;
  379.     }
  380.  
  381.     return NOERROR;
  382. } // CheckInputType
  383.  
  384.  
  385. //
  386. // SetMediaType
  387. //
  388. // This is called when a connection attempt has succeeded. If the output pin
  389. // is being connected and the input pin's media type does not agree then we
  390. // reconnect the input (thus allowing its media type to change,) and vice versa.
  391. //
  392. HRESULT CGargle::SetMediaType(PIN_DIRECTION direction,const CMediaType *pmt){
  393.  
  394.     DbgFunc("SetMediaType");
  395.  
  396.     // Record what we need for doing the actual transform
  397.  
  398.     WAVEFORMATEX *pwfx = (WAVEFORMATEX *) pmt->Format();
  399.     m_Channels =  pwfx->nChannels;
  400.     m_SamplesPerSec = pwfx->nSamplesPerSec;
  401.     // Ignored: pwfx->nAvgBytesPerSec;
  402.     // Ignored: pwfx->nBlockAlign;
  403.     m_BytesPerSample = pwfx->wBitsPerSample/8;
  404.  
  405.     // Call the base class to do its thing
  406.     CTransInPlaceFilter::SetMediaType(direction, pmt);
  407.  
  408.     if( m_pInput->IsConnected() && m_pOutput->IsConnected() ){
  409.         FILTER_INFO fInfo;
  410.  
  411.         QueryFilterInfo( &fInfo );
  412.  
  413.         if (direction == PINDIR_OUTPUT && *pmt != m_pInput->CurrentMediaType() )
  414.             fInfo.pGraph->Reconnect( m_pInput );
  415.  
  416.         QueryFilterInfoReleaseGraph( fInfo );
  417.  
  418.         ASSERT(!(direction == PINDIR_INPUT && *pmt != m_pOutput->CurrentMediaType()));
  419.     }
  420.  
  421.     return NOERROR;
  422.  
  423. } // SetMediaType
  424.  
  425.  
  426. // ==============Implementation of the private IGargle Interface ==========
  427. // ==================== neded to support the property page ===============
  428.  
  429.  
  430. //
  431. // get_GargleRate
  432. //
  433. // Set *GargleRate to our current rate (Hz)
  434. //
  435. STDMETHODIMP CGargle::get_GargleRate(int *GargleRate) {
  436.  
  437.     CAutoLock foo(&m_GargleLock);
  438.  
  439.     *GargleRate = m_GargleRate;
  440.  
  441.     DbgLog((LOG_TRACE, 1, TEXT("get_GargleRate: %d"), *GargleRate));
  442.  
  443.     return NOERROR;
  444. } // get_GargleRate
  445.  
  446.  
  447.  
  448.  
  449. //
  450. // put_GargleRate
  451. //
  452. // set the current rate from GargleRate
  453. //
  454. STDMETHODIMP CGargle::put_GargleRate(int GargleRate) {
  455.  
  456.     CAutoLock foo(&m_GargleLock);
  457.  
  458.     m_GargleRate = GargleRate;
  459.     SetDirty(TRUE);                     // Need to scribble
  460.  
  461.     DbgLog((LOG_TRACE, 1, TEXT("put_GargleRate: %x"), m_GargleRate));
  462.  
  463.     return NOERROR;
  464. } // put_GargleRate
  465.  
  466.  
  467. //
  468. // put_DefaultGargleRate
  469. //
  470. // Setthe current gargle rate to the default
  471. //
  472. STDMETHODIMP CGargle::put_DefaultGargleRate(void) {
  473.  
  474.     CAutoLock foo(&m_GargleLock);
  475.  
  476.     DbgLog((LOG_TRACE, 1, TEXT("put_DefaultGargleRate")));
  477.  
  478.     m_GargleRate = m_DefaultGargleRate;
  479.     SetDirty(TRUE);                     // Need to scribble
  480.  
  481.     return NOERROR;
  482. } // put_DefaultGargleRate
  483.  
  484.  
  485. //
  486. // put_GargleShape
  487. //
  488. // Alter the waveform between triangle and square
  489. //
  490. STDMETHODIMP CGargle::put_GargleShape(int iGargleShape) {
  491.     if (iGargleShape<0 || iGargleShape>1)
  492.         return E_INVALIDARG;
  493.     m_Shape = iGargleShape;
  494.     SetDirty(TRUE);                     // Need to scribble
  495.     return NOERROR;
  496. }
  497.  
  498.  
  499. //
  500. // get_GargleShape
  501. //
  502. // Return 0 if the current shape is triangle, 1 if it's square
  503. //
  504. STDMETHODIMP CGargle::get_GargleShape(int *GargleShape) {
  505.     *GargleShape = m_Shape;
  506.     return NOERROR;
  507. }
  508.  
  509.  
  510. // ==============Implementation of the IPropertypages Interface ===========
  511.  
  512. //
  513. // GetPages
  514. //
  515.  
  516. STDMETHODIMP CGargle::GetPages(CAUUID * pPages) {
  517.  
  518.     pPages->cElems = 1;
  519.     pPages->pElems = (GUID *) CoTaskMemAlloc(sizeof(GUID));
  520.     if (pPages->pElems == NULL) {
  521.         return E_OUTOFMEMORY;
  522.     }
  523.     *(pPages->pElems) = CLSID_GargProp;
  524.  
  525.     return NOERROR;
  526.  
  527. } // GetPages
  528.  
  529. /******************************Public*Routine******************************\
  530. * exported entry points for registration and
  531. * unregistration (in this case they only call
  532. * through to default implmentations).
  533. *
  534. *
  535. *
  536. * History:
  537. *
  538. \**************************************************************************/
  539. HRESULT
  540. DllRegisterServer()
  541. {
  542.   return AMovieDllRegisterServer();
  543. }
  544.  
  545. HRESULT
  546. DllUnregisterServer()
  547. {
  548.   return AMovieDllUnregisterServer();
  549. }
  550.  
  551. #pragma warning(disable: 4514) // "unreferenced inline function has been removed"
  552.  
  553.