//==========================================================================;
//
// 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 );
}