//==========================================================================; // // 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 - 1998 Microsoft Corporation. All Rights Reserved. // //--------------------------------------------------------------------------; // // synth.cpp // // Audio Signal Generator Source Filter #include #include #include #include #if (1100 > _MSC_VER) #include #else #include #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 ); }