SYNTH.CPP
//==========================================================================; 
// 
//  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY 
//  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 
//  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR 
//  PURPOSE. 
// 
//  Copyright (c) 1992 - 1997  Microsoft Corporation.  All Rights Reserved. 
// 
//--------------------------------------------------------------------------; 
// 
// synth.cpp 
// 
// Audio Signal Generator Source Filter 
 
 
#include <windows.h> 
#include <streams.h> 
#include <math.h> 
 
#include <initguid.h> 
#if (1100 > _MSC_VER) 
#include <olectlid.h> 
#else 
#include <olectl.h> 
#endif 
 
 
#define _AUDIOSYNTH_IMPLEMENTATION_ 
 
#include "isynth.h" 
#include "synth.h" 
#include "synthprp.h" 
 
// setup data 
 
const AMOVIESETUP_MEDIATYPE sudOpPinTypes = 
{ &MEDIATYPE_Audio      // clsMajorType 
, &MEDIASUBTYPE_NULL }; // clsMinorType 
 
const AMOVIESETUP_PIN sudOpPin = 
{ L"Output"          // strName 
, FALSE              // bRendered 
, TRUE               // bOutput 
, FALSE              // bZero 
, FALSE              // bMany 
, &CLSID_NULL        // clsConnectsToFilter 
, L"Input"           // strConnectsToPin 
, 1                  // nTypes 
, &sudOpPinTypes };  // lpTypes 
 
const AMOVIESETUP_FILTER sudSynth = 
{ &CLSID_SynthFilter     // clsID 
, L"Audio Synthesizer" // strName 
, MERIT_UNLIKELY       // dwMerit 
, 1                    // nPins 
, &sudOpPin };         // lpPin 
 
// ------------------------------------------------------------------------- 
// g_Templates 
// ------------------------------------------------------------------------- 
// COM global table of objects in this dll 
 
CFactoryTemplate g_Templates[] = { 
 
    { L"Audio Synthesizer" 
    , &CLSID_SynthFilter 
    , CSynthFilter::CreateInstance 
    , NULL 
    , &sudSynth } 
  , 
    { L"Audio Synthesizer Property Page" 
    , &CLSID_SynthPropertyPage 
    , CSynthProperties::CreateInstance } 
 
}; 
int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]); 
 
// ------------------------------------------------------------------------- 
// CSynthFilter, the main filter object 
// ------------------------------------------------------------------------- 
// 
// CreateInstance 
// 
// The only allowed way to create Synthesizers 
 
CUnknown * WINAPI CSynthFilter::CreateInstance(LPUNKNOWN lpunk, HRESULT *phr) { 
 
    CUnknown *punk = new CSynthFilter(lpunk, phr); 
    if (punk == NULL) { 
        *phr = E_OUTOFMEMORY; 
    } 
 
    return punk; 
} 
 
// 
// CSynthFilter::Constructor 
// 
// initialise a CSynthStream object so that we have a pin. 
 
CSynthFilter::CSynthFilter(LPUNKNOWN lpunk, HRESULT *phr) 
    : CSource(NAME("Audio Synthesizer Filter"),lpunk, CLSID_SynthFilter) 
    , CPersistStream(lpunk, phr) 
{ 
    CAutoLock l(&m_cStateLock); 
 
    m_paStreams    = (CSourceStream **) new CSynthStream*[1]; 
    if (m_paStreams == NULL) { 
        *phr = E_OUTOFMEMORY; 
        return; 
    } 
 
    m_paStreams[0] = new CSynthStream(phr, this, L"Audio Synth Stream"); 
    if (m_paStreams[0] == NULL) { 
        *phr = E_OUTOFMEMORY; 
        return; 
    } 
 
    if (SUCCEEDED(*phr)) { 
ASSERT(m_Synth); 
m_Channels = 1; 
m_SamplesPerSec = 11025; 
m_BitsPerSample = 8; 
m_Synth->put_SynthFormat(m_Channels, m_BitsPerSample, m_SamplesPerSec); 
    } 
} 
 
// 
// CSynthFilter::Destructor 
// 
CSynthFilter::~CSynthFilter(void) { 
 
    // 
    //  Base class will free our pins 
    // 
} 
 
// 
// NonDelegatingQueryInterface 
// 
// Reveal our property page, persistance, and control interfaces 
 
STDMETHODIMP CSynthFilter::NonDelegatingQueryInterface(REFIID riid, void **ppv) 
{ 
    CAutoLock l(&m_cStateLock); 
 
    if (riid == IID_ISynth) { 
        return GetInterface((ISynth *) this, ppv); 
    } 
    else if (riid == IID_IPersistStream) { 
        return GetInterface((IPersistStream *) this, ppv); 
    } 
    else if (riid == IID_ISpecifyPropertyPages) { 
        return GetInterface((ISpecifyPropertyPages *) this, ppv); 
    } else { 
        return CSource::NonDelegatingQueryInterface(riid, ppv); 
    } 
} 
 
 
// 
// GetPages 
// 
STDMETHODIMP CSynthFilter::GetPages(CAUUID * pPages) { 
 
    CAutoLock l(&m_cStateLock); 
 
    pPages->cElems = 1; 
    pPages->pElems = (GUID *) CoTaskMemAlloc(sizeof(GUID)); 
    if (pPages->pElems == NULL) { 
        return E_OUTOFMEMORY; 
    } 
    *(pPages->pElems) = CLSID_SynthPropertyPage; 
 
    return NOERROR; 
 
} 
 
// ------------------------------------------------------------------------- 
// --- IPersistStream --- 
// ------------------------------------------------------------------------- 
 
#define WRITEOUT(var)   hr = pStream->Write(&var, sizeof(var), NULL); \ 
                        if (FAILED(hr)) return hr; 
 
#define READIN(var)     hr = pStream->Read(&var, sizeof(var), NULL); \ 
                        if (FAILED(hr)) return hr; 
 
STDMETHODIMP CSynthFilter::GetClassID(CLSID *pClsid) 
{ 
    return CBaseFilter::GetClassID(pClsid); 
} 
 
int CSynthFilter::SizeMax () 
{ 
    return sizeof (int) * 8; 
} 
 
HRESULT CSynthFilter::WriteToStream(IStream *pStream) 
{ 
    HRESULT hr; 
    int i, k; 
 
    get_Frequency (&i);  // don't we wish we'd used a structure, now? 
    WRITEOUT(i); 
    get_Waveform (&i); 
    WRITEOUT(i); 
    get_Channels (&i); 
    WRITEOUT(i); 
    get_BitsPerSample (&i); 
    WRITEOUT(i); 
    get_SamplesPerSec (&i); 
    WRITEOUT(i); 
    get_Amplitude (&i); 
    WRITEOUT(i); 
    get_SweepRange (&i, &k); 
    WRITEOUT(i); 
    WRITEOUT(k); 
 
    return hr; 
} 
 
 
HRESULT CSynthFilter::ReadFromStream(IStream *pStream) 
{ 
    HRESULT hr; 
    int i, k; 
 
    READIN(i); 
    put_Frequency(i); 
    READIN(i); 
    put_Waveform (i); 
    READIN(i); 
    put_Channels (i); 
    READIN(i); 
    put_BitsPerSample (i); 
    READIN(i); 
    put_SamplesPerSec (i); 
    READIN(i); 
    put_Amplitude (i); 
    READIN(i); 
    READIN(k); 
    put_SweepRange (i, k); 
 
    return hr; 
} 
 
// ------------------------------------------------------------------------- 
// ISynth, the control interface for the synthesizer 
// ------------------------------------------------------------------------- 
 
// 
// get_Frequency 
// 
STDMETHODIMP CSynthFilter::get_Frequency(int *Frequency) { 
 
    m_Synth->get_Frequency(Frequency); 
 
    DbgLog((LOG_TRACE, 3, TEXT("get_Frequency: %d"), *Frequency)); 
 
    return NOERROR; 
} 
 
 
// 
// put_Frequency 
// 
STDMETHODIMP CSynthFilter::put_Frequency(int Frequency) { 
 
    m_Synth->put_Frequency (Frequency); 
 
    DbgLog((LOG_TRACE, 3, TEXT("put_Frequency: %d"), Frequency)); 
 
    return NOERROR; 
} 
 
// 
// get_Waveform 
// 
STDMETHODIMP CSynthFilter::get_Waveform(int *Waveform) { 
 
    m_Synth->get_Waveform (Waveform); 
 
    DbgLog((LOG_TRACE, 3, TEXT("get_Waveform: %d"), *Waveform)); 
 
    return NOERROR; 
} 
 
 
// 
// put_Waveform 
// 
STDMETHODIMP CSynthFilter::put_Waveform(int Waveform) { 
 
    m_Synth->put_Waveform (Waveform); 
 
    DbgLog((LOG_TRACE, 3, TEXT("put_Waveform: %d"), Waveform)); 
 
    return NOERROR; 
} 
 
// 
// get_Channels 
// 
STDMETHODIMP CSynthFilter::get_Channels(int *Channels) { 
 
    *Channels = m_Channels; 
 
    DbgLog((LOG_TRACE, 3, TEXT("get_Channels: %d"), *Channels)); 
 
    return NOERROR; 
} 
 
// 
// If the format changes, we need to reconnect 
// 
void CSynthFilter::ReconnectWithNewFormat(void) { 
 
//    CAutoLock l(&m_SynthLock); 
 
    HRESULT hr; 
    FILTER_STATE  State; 
 
    CBasePin *pPin = GetPin(0); 
 
    // Get the state and confirm that the graph is stopped 
    GetState (0, &State); 
    if (State != State_Stopped && pPin->GetConnected()) { 
// let's attempt a dynamic connection 
CMediaType mtNew; 
 
// !!! better way to get back to a CSynthStream??? 
CSynthStream * pStream = (CSynthStream *) pPin; 
pStream->GetMediaType(&mtNew); 
 
// !!! does this really mean they'll accept a dynamic format change? 
hr = pPin->GetConnected()->QueryAccept(&mtNew); 
 
DbgLog((LOG_TRACE,2,TEXT("Attempting format change: queryAccept returned %x"), hr)); 
 
if (hr == S_OK) { 
    // actually change what's being pushed 
    m_Synth->put_SynthFormat(m_Channels, m_BitsPerSample, m_SamplesPerSec); 
} else { 
    // !!! couldn't change right now, we should really schedule a reconnect 
    // for the next time the graph is stopped. 
} 
 
        return; 
    } 
 
    if (!m_pGraph) 
        return; 
 
    hr = GetFilterGraph()->Reconnect (pPin);       // Renegotiate the format 
    if (FAILED(hr)) { 
        DbgLog((LOG_TRACE, 1, TEXT("Reconnect failed, err=%x"), hr)); 
        return; 
    } 
} 
 
 
// 
// put_Channels 
// 
STDMETHODIMP CSynthFilter::put_Channels(int Channels) { 
 
    m_Channels = Channels; 
 
    ReconnectWithNewFormat (); 
 
    DbgLog((LOG_TRACE, 3, TEXT("put_Channels: %d"), Channels)); 
 
    return NOERROR; 
} 
 
// 
// get_BitsPerSample 
// 
STDMETHODIMP CSynthFilter::get_BitsPerSample(int *BitsPerSample) { 
 
    *BitsPerSample = m_BitsPerSample; 
 
    DbgLog((LOG_TRACE, 3, TEXT("get_BitsPerSample: %d"), *BitsPerSample)); 
 
    return NOERROR; 
} 
 
 
// 
// put_BitsPerSample 
// 
STDMETHODIMP CSynthFilter::put_BitsPerSample(int BitsPerSample) { 
 
    m_BitsPerSample = BitsPerSample; 
 
    ReconnectWithNewFormat (); 
 
    DbgLog((LOG_TRACE, 3, TEXT("put_BitsPerSample: %d"), BitsPerSample)); 
 
    return NOERROR; 
} 
 
// 
// get_SamplesPerSec 
// 
STDMETHODIMP CSynthFilter::get_SamplesPerSec(int *SamplesPerSec) { 
 
    *SamplesPerSec = m_SamplesPerSec; 
 
    DbgLog((LOG_TRACE, 3, TEXT("get_SamplesPerSec: %d"), *SamplesPerSec)); 
 
    return NOERROR; 
} 
 
 
// 
// put_SamplesPerSec 
// 
STDMETHODIMP CSynthFilter::put_SamplesPerSec(int SamplesPerSec) { 
 
    m_SamplesPerSec = SamplesPerSec; 
 
    ReconnectWithNewFormat (); 
 
    DbgLog((LOG_TRACE, 3, TEXT("put_SamplesPerSec: %d"), SamplesPerSec)); 
 
    return NOERROR; 
} 
 
// 
// get_Amplitude 
// 
STDMETHODIMP CSynthFilter::get_Amplitude(int *Amplitude) { 
 
    m_Synth->get_Amplitude (Amplitude); 
 
    DbgLog((LOG_TRACE, 3, TEXT("get_Amplitude: %d"), *Amplitude)); 
 
    return NOERROR; 
} 
 
 
// 
// put_Amplitude 
// 
STDMETHODIMP CSynthFilter::put_Amplitude(int Amplitude) { 
 
    m_Synth->put_Amplitude (Amplitude); 
 
    DbgLog((LOG_TRACE, 3, TEXT("put_Amplitude: %d"), Amplitude)); 
 
    return NOERROR; 
} 
 
 
// 
// get_SweepRange 
// 
STDMETHODIMP CSynthFilter::get_SweepRange(int *SweepStart, int *SweepEnd) { 
 
    m_Synth->get_SweepRange (SweepStart, SweepEnd); 
 
    DbgLog((LOG_TRACE, 3, TEXT("get_SweepStart: %d %d"), *SweepStart, *SweepEnd)); 
 
    return NOERROR; 
} 
 
 
// 
// put_SweepRange 
// 
STDMETHODIMP CSynthFilter::put_SweepRange(int SweepStart, int SweepEnd) { 
 
    m_Synth->put_SweepRange (SweepStart, SweepEnd); 
 
    DbgLog((LOG_TRACE, 3, TEXT("put_SweepRange: %d %d"), SweepStart, SweepEnd)); 
 
    return NOERROR; 
} 
 
 
// ------------------------------------------------------------------------- 
// CSynthStream, the output pin 
// ------------------------------------------------------------------------- 
 
// 
// CSynthStream::Constructor 
// 
 
CSynthStream::CSynthStream(HRESULT *phr, CSynthFilter *pParent, LPCWSTR pName) 
    : CSourceStream(NAME("Audio Synth output pin"),phr, pParent, pName) { 
 
    CAutoLock l(m_pFilter->pStateLock()); 
 
    { 
        CAutoLock l(&m_cSharedState); 
 
        m_Synth = new CAudioSynth( ); 
        pParent->m_Synth = m_Synth; 
        if (m_Synth == NULL) { 
            *phr = E_OUTOFMEMORY; 
            return; 
        } 
m_pParent = pParent; 
    } 
} 
 
 
// 
// CSynthStream::Destructor 
// 
CSynthStream::~CSynthStream(void) { 
 
    CAutoLock l(&m_cSharedState); 
 
    delete m_Synth; 
} 
 
 
// 
// FillBuffer 
// 
// Stuffs the buffer with data 
HRESULT CSynthStream::FillBuffer(IMediaSample *pms) { 
 
    BYTE *pData; 
    long lDataLen; 
    int nSamplesPerSec; 
    int nBitsPerSample; 
    int nChannels; 
    BOOL fNewFormat = FALSE; 
 
    pms->GetPointer(&pData); 
    lDataLen = pms->GetSize(); 
 
    CAutoLock lShared(&m_cSharedState); 
    m_Synth->FillAudioBuffer (pData, lDataLen, &fNewFormat); 
 
    if (fNewFormat) { 
CMediaType mtNew; 
GetMediaType(&mtNew); 
 
pms->SetMediaType(&mtNew); 
 
DbgLog((LOG_TRACE,2,TEXT("Sending buffer with new media type"))); 
    } 
 
    CRefTime rtStart  = m_rtSampleTime;  // the current time is the sample's start 
 
    m_Synth->get_SamplesPerSec (&nSamplesPerSec); 
    m_Synth->get_BitsPerSample (&nBitsPerSample); 
    m_Synth->get_Channels (&nChannels); 
 
    m_rtSampleTime += (UNITS * lDataLen / 
            (nSamplesPerSec * nChannels * nBitsPerSample / 8)); 
 
    pms->SetTime((REFERENCE_TIME*)&rtStart, 
                 (REFERENCE_TIME*)&m_rtSampleTime); 
 
    return NOERROR; 
} 
 
 
// 
// Format Support 
// 
 
// 
// GetMediaType 
// 
HRESULT CSynthStream::GetMediaType(CMediaType *pmt) { 
 
    CAutoLock l(m_pFilter->pStateLock()); 
 
    WAVEFORMATEX *pwf = (WAVEFORMATEX *) pmt->AllocFormatBuffer(sizeof(WAVEFORMATEX)); 
 
    pwf->wFormatTag = WAVE_FORMAT_PCM; 
    pwf->nChannels = (WORD) m_pParent->m_Channels; 
    pwf->nSamplesPerSec = (DWORD) m_pParent->m_SamplesPerSec; 
    pwf->wBitsPerSample = (WORD) m_pParent->m_BitsPerSample; 
    pwf->nBlockAlign = pwf->wBitsPerSample * pwf->nChannels / 8; 
    pwf->nAvgBytesPerSec = (int) ((DWORD) pwf->nBlockAlign * 
                           pwf->nSamplesPerSec); 
    pwf->cbSize = 0; 
 
    return CreateAudioMediaType(pwf, pmt, FALSE); 
} 
 
 
// 
// CheckMediaType 
// 
// Returns E_INVALIDARG if the mediatype is not acceptable, S_OK if it is 
HRESULT CSynthStream::CheckMediaType(const CMediaType *pMediaType) { 
 
    CAutoLock l(m_pFilter->pStateLock()); 
 
    //  Check that's Audio and that the format block 
    //  has the WAVEFORMATEX structure (indicated by a format type 
    //  GUID of FORMAT_WaveFormatEx) 
 
    if ((*pMediaType->Type() != MEDIATYPE_Audio) || 
        (*pMediaType->FormatType() != FORMAT_WaveFormatEx)) 
        return E_INVALIDARG; 
 
    WAVEFORMATEX * pwfx  = (WAVEFORMATEX *)pMediaType->Format(); 
 
    if (pwfx->wFormatTag != WAVE_FORMAT_PCM) 
return E_INVALIDARG; 
 
    // !!! check 8/16, 1/2 channel 
 
    // Check for the subtypes we support 
 
    // Get the format area of the media type 
 
    // !!! if we're going to allow arbitrary media types here, we have to actually 
    // look at SetMediaType to see what we've agreed on! 
 
    return S_OK;  // This format is acceptable. 
} 
 
// 
// DecideBufferSize 
// 
// This will always be called after the format has been sucessfully 
// negotiated. So we have a look at m_mt to see what format we agreed to. 
// Then we can ask for buffers of the correct size to contain them. 
HRESULT CSynthStream::DecideBufferSize(IMemAllocator *pAlloc, 
                                       ALLOCATOR_PROPERTIES *pProperties) 
{ 
    CAutoLock l(m_pFilter->pStateLock()); 
    ASSERT(pAlloc); 
    ASSERT(pProperties); 
    HRESULT hr = NOERROR; 
 
    pProperties->cbBuffer = WaveBufferSize; 
 
    int nBitsPerSample; 
    int nSamplesPerSec; 
    int nChannels; 
 
    m_Synth->get_SamplesPerSec (&nSamplesPerSec); 
    m_Synth->get_BitsPerSample (&nBitsPerSample); 
    m_Synth->get_Channels (&nChannels); 
 
    pProperties->cBuffers = nChannels * (nSamplesPerSec / pProperties->cbBuffer) * (nBitsPerSample / 8); 
    // Get 1/2 second worth of buffers 
    pProperties->cBuffers /= 2; 
    if (pProperties->cBuffers < 1) 
        pProperties->cBuffers = 1 ; 
 
    // Ask the allocator to reserve us the memory 
 
    ALLOCATOR_PROPERTIES Actual; 
    hr = pAlloc->SetProperties(pProperties,&Actual); 
    if (FAILED(hr)) { 
        return hr; 
    } 
 
    // Is this allocator unsuitable 
 
    if (Actual.cbBuffer < pProperties->cbBuffer) { 
        return E_FAIL; 
    } 
    return NOERROR; 
} 
 
 
// 
// SetMediaType 
// 
// Overriden from CBasePin. 
HRESULT CSynthStream::SetMediaType(const CMediaType *pMediaType) { 
 
    CAutoLock l(m_pFilter->pStateLock()); 
 
    HRESULT hr;         // return code from base class calls 
 
    // Pass the call up to my base class 
    hr = CSourceStream::SetMediaType(pMediaType); 
    if (SUCCEEDED(hr)) 
        return NOERROR; 
    else 
        return hr; 
 
} 
 
 
// 
// OnThreadCreate 
// 
// as we go active reset the stream time to zero 
HRESULT CSynthStream::OnThreadCreate(void) { 
 
    CAutoLock lShared(&m_cSharedState); 
 
    m_rtSampleTime = 0; 
 
    return NOERROR; 
} 
 
// 
// Active 
// 
// Send a message to the property page telling it to disable 
// buttons which change the format when the graph starts running 
HRESULT CSynthStream::Active  (void) { 
    m_Synth->AllocWaveCache(); 
 
    return CSourceStream::Active(); 
} 
 
 
// 
// Inactive 
// 
// Send a message to the property page telling it to enable 
// buttons which change the format when the graph stops running 
HRESULT CSynthStream::Inactive  (void) { 
    return CSourceStream::Inactive(); 
} 
 
// ------------------------------------------------------------------------- 
// CAudioSynth 
// ------------------------------------------------------------------------- 
// Object that knows nothing about ActiveMovie, but just synthesizes 
// waveforms 
 
CAudioSynth::CAudioSynth( 
                int Frequency, 
                int Waveform, 
                int iBitsPerSample, 
                int iChannels, 
                int iSamplesPerSec, 
                int iAmplitude 
                ) 
    : m_bWaveCache(NULL), 
      m_wWaveCache(NULL) 
{ 
 
    ASSERT(Waveform >= WAVE_SINE); 
    ASSERT(Waveform <  WAVE_LAST); 
 
    m_iFrequency = Frequency; 
    m_iWaveform = Waveform; 
    m_iAmplitude = iAmplitude; 
    m_iSweepStart = DefaultSweepStart; 
    m_iSweepEnd = DefaultSweepEnd; 
 
    // init our WAVEFORMATEX structure 
    wfex.wFormatTag = WAVE_FORMAT_PCM; 
    wfex.wBitsPerSample = iBitsPerSample; 
    wfex.nChannels = iChannels; 
    wfex.nSamplesPerSec = iSamplesPerSec; 
    wfex.nBlockAlign = wfex.wBitsPerSample * wfex.nChannels / 8; 
    wfex.nAvgBytesPerSec = ((DWORD) wfex.nBlockAlign * 
                           wfex.nSamplesPerSec); 
    wfex.cbSize = 0; 
} 
 
CAudioSynth::~CAudioSynth() 
{ 
    if (m_bWaveCache) { 
        delete[] m_bWaveCache; 
    } 
 
    if (m_wWaveCache) { 
        delete[] m_wWaveCache; 
    } 
} 
 
// 
// AllocWaveCache 
// 
// 
void CAudioSynth::AllocWaveCache (void) { 
    wfexLast = wfex; 
 
    m_iWaveCacheCycles = m_iFrequency; 
    m_iWaveCacheSize = (int) wfex.nSamplesPerSec; 
 
    m_iFrequencyLast = 0;// force cache contents invalid 
 
    if (m_bWaveCache) { 
        delete[] m_bWaveCache; 
        m_bWaveCache = NULL; 
    } 
    if (m_wWaveCache) { 
        delete[] m_wWaveCache; 
        m_wWaveCache = NULL; 
    } 
 
    if (wfex.wBitsPerSample == 8) 
        m_bWaveCache = new BYTE [m_iWaveCacheSize]; 
    else 
        m_wWaveCache = new WORD [m_iWaveCacheSize]; 
} 
 
// 
// FillAudioBuffer 
// 
// 
// 
void CAudioSynth::FillAudioBuffer (BYTE pBuf[], int iSize, BOOL *pfNewFormat) { 
    BOOL fCalcCache = FALSE; 
 
    CAutoLock l(&m_SynthLock); 
 
    // Only realloc the cache if the format has changed ! 
 
    if ((wfex.nChannels != wfexLast.nChannels) || 
        (wfex.wBitsPerSample != wfexLast.wBitsPerSample) || 
        (wfex.nSamplesPerSec != wfexLast.nSamplesPerSec)) { 
    *pfNewFormat = TRUE; 
            fCalcCache = TRUE; 
    AllocWaveCache(); 
    } 
 
    if (m_iFrequency != m_iFrequencyLast) { 
fCalcCache = TRUE; 
m_iFrequencyLast = m_iFrequency; 
    } 
    if (m_iWaveform != m_iWaveformLast) { 
fCalcCache = TRUE; 
m_iWaveformLast = m_iWaveform; 
    } 
    if (m_iAmplitude != m_iAmplitudeLast) { 
fCalcCache = TRUE; 
m_iAmplitudeLast = m_iAmplitude; 
    } 
 
    if (fCalcCache) { 
        switch (m_iWaveform) { 
 
        case WAVE_SINE: 
                CalcCacheSine (); 
                break; 
 
        case WAVE_SQUARE: 
                CalcCacheSquare (); 
                break; 
 
        case WAVE_SAWTOOTH: 
                CalcCacheSawtooth (); 
                break; 
 
        case WAVE_SINESWEEP: 
                CalcCacheSweep (); 
                break; 
 
        } 
    } 
 
    // Copy cache to output buffers 
    if (wfex.wBitsPerSample == 8 && wfex.nChannels == 1) { 
        while (iSize--) { 
            *pBuf++ = m_bWaveCache[m_iWaveCacheIndex++]; 
            if (m_iWaveCacheIndex >= m_iWaveCacheSize) 
                m_iWaveCacheIndex = 0; 
        } 
    } 
    else if (wfex.wBitsPerSample == 8 && wfex.nChannels == 2) { 
        iSize /= 2; 
        while (iSize--) { 
            *pBuf++ = m_bWaveCache[m_iWaveCacheIndex]; 
            *pBuf++ = m_bWaveCache[m_iWaveCacheIndex++]; 
            if (m_iWaveCacheIndex >= m_iWaveCacheSize) 
                m_iWaveCacheIndex = 0; 
        } 
    } 
    else if (wfex.wBitsPerSample == 16 && wfex.nChannels == 1) { 
        WORD * pW = (WORD *) pBuf; 
        iSize /= 2; 
        while (iSize--) { 
            *pW++ = m_wWaveCache[m_iWaveCacheIndex++]; 
            if (m_iWaveCacheIndex >= m_iWaveCacheSize) 
                m_iWaveCacheIndex = 0; 
        } 
    } 
    else if (wfex.wBitsPerSample == 16 && wfex.nChannels == 2) { 
        WORD * pW = (WORD *) pBuf; 
        iSize /= 4; 
        while (iSize--) { 
            *pW++ = m_wWaveCache[m_iWaveCacheIndex]; 
            *pW++ = m_wWaveCache[m_iWaveCacheIndex++]; 
            if (m_iWaveCacheIndex >= m_iWaveCacheSize) 
                m_iWaveCacheIndex = 0; 
        } 
    } 
 
} 
 
// 
// CalcCacheSine 
// 
// 
void CAudioSynth::CalcCacheSine (void) { 
 
    int i; 
    double d; 
    double amplitude; 
    double FTwoPIDivSpS; 
 
    amplitude = ((wfex.wBitsPerSample == 8) ? 127 : 32767 ) 
                    * m_iAmplitude / 100; 
 
    FTwoPIDivSpS = m_iFrequency * TWOPI / wfex.nSamplesPerSec; 
 
    m_iWaveCacheIndex = 0; 
    m_iCurrentSample = 0; 
 
    if (wfex.wBitsPerSample == 8) { 
        BYTE * pB = m_bWaveCache; 
 
        for (i = 0; i < m_iWaveCacheSize; i++) { 
            d = FTwoPIDivSpS * i; 
            *pB++ = (BYTE) (sin (d) * amplitude) + 128; 
        } 
    } 
    else { 
        PWORD pW = (PWORD) m_wWaveCache; 
 
        for (i = 0; i < m_iWaveCacheSize; i++) { 
            d = FTwoPIDivSpS * i; 
            *pW++ = (WORD) (sin (d) * amplitude); 
        } 
    } 
 
} 
 
// 
// CalcCacheSquare 
// 
// 
void CAudioSynth::CalcCacheSquare (void) { 
 
    int i; 
    double d; 
    double FTwoPIDivSpS; 
    BYTE b0, b1; 
    WORD w0, w1; 
 
    b0 = (BYTE) 128 - (127 * m_iAmplitude / 100); 
    b1 = (BYTE) 128 + (127 * m_iAmplitude / 100); 
    w0 = (WORD) (32767. * m_iAmplitude / 100); 
    w1 = (WORD) - (32767. * m_iAmplitude / 100); 
 
    FTwoPIDivSpS = m_iFrequency * TWOPI / wfex.nSamplesPerSec; 
 
    m_iWaveCacheIndex = 0; 
    m_iCurrentSample = 0; 
 
    if (wfex.wBitsPerSample == 8) { 
        BYTE * pB = m_bWaveCache; 
 
        for (i = 0; i < m_iWaveCacheSize; i++) { 
            d = FTwoPIDivSpS * i; 
            *pB++ = (BYTE) ((sin (d) >= 0) ? b1 : b0); 
        } 
    } 
    else { 
        PWORD pW = (PWORD) m_wWaveCache; 
 
        for (i = 0; i < m_iWaveCacheSize; i++) { 
            d = FTwoPIDivSpS * i; 
            *pW++ = (WORD) ((sin (d) >= 0) ? w1 : w0); 
        } 
    } 
} 
 
// 
// CalcCacheSawtooth 
// 
void CAudioSynth::CalcCacheSawtooth (void) { 
 
    int i; 
    double d; 
    double amplitude; 
    double FTwoPIDivSpS; 
    double step; 
    double curstep; 
    BOOL fLastWasNeg = TRUE; 
    BOOL fPositive; 
 
    amplitude = ((wfex.wBitsPerSample == 8) ? 255 : 65535 ) 
                    * m_iAmplitude / 100; 
 
    FTwoPIDivSpS = m_iFrequency * TWOPI / wfex.nSamplesPerSec; 
    step = amplitude * m_iFrequency / wfex.nSamplesPerSec; 
 
    m_iWaveCacheIndex = 0; 
    m_iCurrentSample = 0; 
 
    BYTE * pB = m_bWaveCache; 
    PWORD pW = (PWORD) m_wWaveCache; 
 
    for (i = 0; i < m_iWaveCacheSize; i++) { 
        d = FTwoPIDivSpS * i; 
 
        // OneShot triggered on positive zero crossing 
        fPositive = (sin (d) >= 0); 
 
        if (fLastWasNeg && fPositive) { 
            if (wfex.wBitsPerSample == 8) 
                curstep = 128 - amplitude / 2; 
            else 
                curstep = 32768 - amplitude / 2; 
        } 
        fLastWasNeg = !fPositive; 
 
        if (wfex.wBitsPerSample == 8) 
            *pB++ = (BYTE) curstep; 
        else 
            *pW++ = (WORD) (-32767 + curstep); 
 
        curstep += step; 
    } 
} 
 
// 
// CalcCacheSweep 
// 
void CAudioSynth::CalcCacheSweep (void) { 
 
    int i; 
    double d; 
    double amplitude; 
    double FTwoPIDivSpS; 
    double CurrentFreq; 
    double DeltaFreq; 
 
    amplitude = ((wfex.wBitsPerSample == 8) ? 127 : 32767 ) 
                    * m_iAmplitude / 100; 
 
    DeltaFreq = ((double) m_iSweepEnd - m_iSweepStart) / m_iWaveCacheSize; 
    CurrentFreq = m_iSweepStart; 
 
    m_iWaveCacheIndex = 0; 
    m_iCurrentSample = 0; 
 
    if (wfex.wBitsPerSample == 8) { 
        BYTE * pB = m_bWaveCache; 
        d = 0.0; 
 
        for (i = 0; i < m_iWaveCacheSize; i++) { 
            FTwoPIDivSpS = (int) CurrentFreq * TWOPI / wfex.nSamplesPerSec; 
            CurrentFreq += DeltaFreq; 
            d += FTwoPIDivSpS; 
            *pB++ = (BYTE) (sin (d) * amplitude) + 128; 
        } 
    } 
    else { 
        PWORD pW = (PWORD) m_wWaveCache; 
        d = 0.0; 
 
        for (i = 0; i < m_iWaveCacheSize; i++) { 
            FTwoPIDivSpS = (int) CurrentFreq * TWOPI / wfex.nSamplesPerSec; 
            CurrentFreq += DeltaFreq; 
            d += FTwoPIDivSpS; 
            *pW++ = (WORD) (sin (d) * amplitude); 
        } 
    } 
} 
 
// 
// get_Frequency 
// 
STDMETHODIMP CAudioSynth::get_Frequency(int *Frequency) { 
 
    *Frequency = m_iFrequency; 
 
    DbgLog((LOG_TRACE, 3, TEXT("get_Frequency: %d"), *Frequency)); 
 
    return NOERROR; 
} 
 
 
// 
// put_Frequency 
// 
STDMETHODIMP CAudioSynth::put_Frequency(int Frequency) { 
 
    CAutoLock l(&m_SynthLock); 
 
    m_iFrequency = Frequency; 
 
    DbgLog((LOG_TRACE, 3, TEXT("put_Frequency: %d"), Frequency)); 
 
    return NOERROR; 
} 
 
// 
// get_Waveform 
// 
STDMETHODIMP CAudioSynth::get_Waveform(int *Waveform) { 
 
    *Waveform = m_iWaveform; 
 
    DbgLog((LOG_TRACE, 3, TEXT("get_Waveform: %d"), *Waveform)); 
 
    return NOERROR; 
} 
 
 
// 
// put_Waveform 
// 
STDMETHODIMP CAudioSynth::put_Waveform(int Waveform) { 
 
    CAutoLock l(&m_SynthLock); 
 
    m_iWaveform = Waveform; 
 
    DbgLog((LOG_TRACE, 3, TEXT("put_Waveform: %d"), Waveform)); 
 
    return NOERROR; 
} 
 
// 
// get_Channels 
// 
STDMETHODIMP CAudioSynth::get_Channels(int *Channels) { 
 
    *Channels = wfex.nChannels; 
 
    DbgLog((LOG_TRACE, 3, TEXT("get_Channels: %d"), *Channels)); 
 
    return NOERROR; 
} 
 
 
// 
// get_BitsPerSample 
// 
STDMETHODIMP CAudioSynth::get_BitsPerSample(int *BitsPerSample) { 
 
    *BitsPerSample = wfex.wBitsPerSample; 
 
    DbgLog((LOG_TRACE, 3, TEXT("get_BitsPerSample: %d"), *BitsPerSample)); 
 
    return NOERROR; 
} 
 
 
// 
// get_SamplesPerSec 
// 
STDMETHODIMP CAudioSynth::get_SamplesPerSec(int *SamplesPerSec) { 
 
    *SamplesPerSec = wfex.nSamplesPerSec; 
 
    DbgLog((LOG_TRACE, 3, TEXT("get_SamplesPerSec: %d"), *SamplesPerSec)); 
 
    return NOERROR; 
} 
 
// 
// put_SynthFormat 
// 
STDMETHODIMP CAudioSynth::put_SynthFormat(int Channels, int BitsPerSample, 
  int SamplesPerSec) { 
 
    CAutoLock l(&m_SynthLock); 
 
    wfex.nChannels = Channels; 
 
    wfex.wBitsPerSample = BitsPerSample; 
 
    wfex.nSamplesPerSec = SamplesPerSec; 
 
    DbgLog((LOG_TRACE, 1, TEXT("put_SynthFormat: %d-bit %d-channel %dHz"), 
    BitsPerSample, Channels, SamplesPerSec)); 
 
    return NOERROR; 
} 
 
 
// 
// get_Amplitude 
// 
STDMETHODIMP CAudioSynth::get_Amplitude(int *Amplitude) { 
 
    *Amplitude =  m_iAmplitude; 
 
    DbgLog((LOG_TRACE, 3, TEXT("get_Amplitude: %d"), *Amplitude)); 
 
    return NOERROR; 
} 
 
 
// 
// put_Amplitude 
// 
STDMETHODIMP CAudioSynth::put_Amplitude(int Amplitude) { 
 
    CAutoLock l(&m_SynthLock); 
 
    if (Amplitude > MaxAmplitude || Amplitude < MinAmplitude) 
        return E_INVALIDARG; 
 
    m_iAmplitude = Amplitude; 
 
    DbgLog((LOG_TRACE, 3, TEXT("put_Amplitude: %d"), Amplitude)); 
 
    return NOERROR; 
} 
 
 
// 
// get_SweepRange 
// 
STDMETHODIMP CAudioSynth::get_SweepRange(int *SweepStart, int *SweepEnd) { 
 
    *SweepStart = m_iSweepStart; 
    *SweepEnd = m_iSweepEnd; 
 
    DbgLog((LOG_TRACE, 3, TEXT("get_SweepStart: %d %d"), *SweepStart, *SweepEnd)); 
 
    return NOERROR; 
} 
 
 
// 
// put_SweepRange 
// 
STDMETHODIMP CAudioSynth::put_SweepRange(int SweepStart, int SweepEnd) { 
 
    CAutoLock l(&m_SynthLock); 
 
    m_iSweepStart = SweepStart; 
    m_iSweepEnd = SweepEnd; 
 
    DbgLog((LOG_TRACE, 3, TEXT("put_SweepRange: %d %d"), SweepStart, SweepEnd)); 
 
    return NOERROR; 
} 
 
/******************************Public*Routine******************************\ 
* exported entry points for registration and 
* unregistration (in this case they only call 
* through to default implmentations). 
* 
* 
* 
* History: 
* 
\**************************************************************************/ 
STDAPI 
DllRegisterServer() 
{ 
  return AMovieDllRegisterServer2( TRUE ); 
} 
 
STDAPI 
DllUnregisterServer() 
{ 
  return AMovieDllRegisterServer2( FALSE ); 
}