mirror of
https://github.com/microsoft/Microsoft-3D-Movie-Maker.git
synced 2024-11-22 02:12:33 +01:00
1023 lines
24 KiB
C++
1023 lines
24 KiB
C++
/* Copyright (c) Microsoft Corporation.
|
|
Licensed under the MIT License. */
|
|
|
|
/***************************************************************************
|
|
Author: ShonK
|
|
Project: Kauai
|
|
Copyright (c) Microsoft Corporation
|
|
|
|
Audioman based sound classes.
|
|
|
|
***************************************************************************/
|
|
#include "frame.h"
|
|
#include "audioman.h"
|
|
#include "sndampri.h"
|
|
ASSERTNAME
|
|
|
|
// CHECK_AUDIO_DEVCAPS turns on using waveOutGetDevCaps to check for
|
|
// audio device characteristics, to workaround AudioMan 1.0 always returning
|
|
// whatever device was asked for, regardless if device actually supports format.
|
|
// In case of asking for a 16 bit device, and there is only an 8 bit device, the
|
|
// open will succeed, causing sub-optimal audioman performance on an 8 bit card.
|
|
// With AudioMan 1.5, this should go away.
|
|
#define CHECK_AUDIO_DEVCAPS
|
|
|
|
// Initialize the maximum mem footprint of wave sounds.
|
|
long SDAM::vcbMaxMemWave = 40 * 1024;
|
|
|
|
static IAMMixer *_pamix; // the audioman mixer
|
|
static ulong _luGroup; // the group number
|
|
static bool _fGrouped; // whether new sounds are grouped
|
|
static long _cactGroup; // group nesting count
|
|
|
|
static ulong _luFormat; // format mixer is in
|
|
static ulong _luCacheTime; // buffer size for mixer
|
|
|
|
|
|
RTCLASS(SDAM)
|
|
RTCLASS(CAMS)
|
|
RTCLASS(AMQUE)
|
|
|
|
|
|
/***************************************************************************
|
|
Constructor for a streamed block.
|
|
***************************************************************************/
|
|
STBL::STBL(void)
|
|
{
|
|
AssertThisMem();
|
|
|
|
// WARNING: this is not allocated using our NewObj because STBL is not
|
|
// based on BASE. So fields are not automatically initialized to 0.
|
|
_cactRef = 1;
|
|
_ib = 0;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Destructor for a streamed block.
|
|
***************************************************************************/
|
|
STBL::~STBL(void)
|
|
{
|
|
AssertThisMem();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
QueryInterface for STBL.
|
|
***************************************************************************/
|
|
STDMETHODIMP STBL::QueryInterface(REFIID riid, void ** ppv)
|
|
{
|
|
AssertThis(0);
|
|
|
|
if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IStream))
|
|
{
|
|
*ppv = (void *)this;
|
|
AddRef();
|
|
return S_OK;
|
|
}
|
|
|
|
*ppv = pvNil;
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Increment the reference count.
|
|
***************************************************************************/
|
|
STDMETHODIMP_(ULONG) STBL::AddRef(void)
|
|
{
|
|
AssertThis(0);
|
|
return ++_cactRef;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Decrement the reference count.
|
|
***************************************************************************/
|
|
STDMETHODIMP_(ULONG) STBL::Release(void)
|
|
{
|
|
AssertThis(0);
|
|
long cactRef;
|
|
|
|
if ((cactRef = --_cactRef) == 0)
|
|
delete this;
|
|
return cactRef;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Read some stuff.
|
|
***************************************************************************/
|
|
STDMETHODIMP STBL::Read(void * pv, ULONG cb, ULONG * pcb)
|
|
{
|
|
AssertThis(0);
|
|
AssertPvCb(pv, cb);
|
|
AssertNilOrVarMem(pcb);
|
|
|
|
cb = LwMin(_blck.Cb() - _ib, cb);
|
|
if (_blck.FReadRgb(pv, cb, _ib))
|
|
{
|
|
_ib += cb;
|
|
if (pvNil != pcb)
|
|
*pcb = cb;
|
|
return NOERROR;
|
|
}
|
|
|
|
if (pvNil != pcb)
|
|
*pcb = 0;
|
|
return ResultFromScode(STG_E_READFAULT);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Seek to a place.
|
|
***************************************************************************/
|
|
STDMETHODIMP STBL::Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin,
|
|
ULARGE_INTEGER * plibNewPosition)
|
|
{
|
|
AssertThis(0);
|
|
AssertNilOrVarMem(plibNewPosition);
|
|
|
|
switch (dwOrigin)
|
|
{
|
|
case STREAM_SEEK_SET:
|
|
break;
|
|
|
|
case STREAM_SEEK_CUR:
|
|
dlibMove.QuadPart += _ib;
|
|
break;
|
|
|
|
case STREAM_SEEK_END:
|
|
dlibMove.QuadPart += _blck.Cb();
|
|
break;
|
|
}
|
|
|
|
if (dlibMove.QuadPart < 0 || dlibMove.QuadPart > _blck.Cb())
|
|
{
|
|
if (pvNil != plibNewPosition)
|
|
plibNewPosition->QuadPart = _ib;
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
_ib = (long)dlibMove.QuadPart;
|
|
if (pvNil != plibNewPosition)
|
|
plibNewPosition->QuadPart = _ib;
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Static method to create a new stream wrapper around a flo.
|
|
***************************************************************************/
|
|
PSTBL STBL::PstblNew(FLO *pflo, bool fPacked)
|
|
{
|
|
AssertPo(pflo, ffloReadable);
|
|
PSTBL pstbl;
|
|
BLCK blck;
|
|
PBLCK pblck;
|
|
|
|
if (pvNil == (pstbl = new STBL))
|
|
return pvNil;
|
|
|
|
pblck = &pstbl->_blck;
|
|
if (fPacked)
|
|
{
|
|
// unpack the block
|
|
pblck->Set(pflo, fPacked);
|
|
if (!pblck->FUnpackData())
|
|
{
|
|
delete pstbl;
|
|
return pvNil;
|
|
}
|
|
|
|
// see if it's too big to keep in memory
|
|
if (pstbl->CbMem() > SDAM::vcbMaxMemWave)
|
|
{
|
|
// try to put the sound on disk
|
|
HQ hq = pblck->HqFree();
|
|
|
|
AssertHq(hq);
|
|
if (pblck->FSetTemp(CbOfHq(hq), fTrue) &&
|
|
pblck->FWriteHq(hq, 0))
|
|
{
|
|
FreePhq(&hq);
|
|
}
|
|
else
|
|
pblck->SetHq(&hq);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// see if it's on a removeable disk
|
|
FNI fni;
|
|
|
|
pflo->pfil->GetFni(&fni);
|
|
if (fni.Grfvk() & (fvkFloppy | fvkCD | fvkRemovable))
|
|
{
|
|
// cache to the hard drive or memory, depending on the size
|
|
BLCK blck(pflo);
|
|
|
|
if (!pblck->FSetTemp(pflo->cb,
|
|
blck.Cb() + size(STBL) > SDAM::vcbMaxMemWave) ||
|
|
!blck.FWriteToBlck(pblck))
|
|
{
|
|
delete pstbl;
|
|
return pvNil;
|
|
}
|
|
}
|
|
else
|
|
pblck->Set(pflo);
|
|
}
|
|
|
|
AssertPo(pstbl, 0);
|
|
return pstbl;
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
/***************************************************************************
|
|
Assert the validity of a STBL.
|
|
***************************************************************************/
|
|
void STBL::AssertValid(ulong grf)
|
|
{
|
|
AssertThisMem();
|
|
AssertPo(&_blck, 0);
|
|
AssertIn(_ib, 0, _blck.Cb() + 1);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Mark memory for the STBL.
|
|
***************************************************************************/
|
|
void STBL::MarkMem(void)
|
|
{
|
|
AssertValid(0);
|
|
MarkMemObj(&_blck);
|
|
}
|
|
#endif //DEBUG
|
|
|
|
|
|
/***************************************************************************
|
|
Constructor for a cached AudioMan sound.
|
|
***************************************************************************/
|
|
CAMS::CAMS(void)
|
|
{
|
|
AssertBaseThis(fobjAllocated);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Destructor for a cached AudioMan sound.
|
|
***************************************************************************/
|
|
CAMS::~CAMS(void)
|
|
{
|
|
AssertBaseThis(fobjAllocated);
|
|
ReleasePpo(&psnd);
|
|
ReleasePpo(&_pstbl);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Static BACO reader method to put together a Cached AudioMan sound.
|
|
***************************************************************************/
|
|
bool CAMS::FReadCams(PCRF pcrf, CTG ctg, CNO cno, PBLCK pblck,
|
|
PBACO *ppbaco, long *pcb)
|
|
{
|
|
AssertPo(pcrf, 0);
|
|
AssertPo(pblck, 0);
|
|
AssertNilOrVarMem(ppbaco);
|
|
AssertVarMem(pcb);
|
|
FLO flo;
|
|
bool fPacked;
|
|
PCAMS pcams = pvNil;
|
|
PSTBL pstbl = pvNil;
|
|
|
|
*pcb = size(CAMS) + size(STBL);
|
|
if (pvNil == ppbaco)
|
|
return fTrue;
|
|
|
|
*ppbaco = pvNil;
|
|
if (!pcrf->Pcfl()->FFindFlo(ctg, cno, &flo))
|
|
return fFalse;
|
|
|
|
fPacked = pcrf->Pcfl()->FPacked(ctg, cno);
|
|
if (pvNil == (pstbl = STBL::PstblNew(&flo, fPacked)))
|
|
return fFalse;
|
|
|
|
*pcb = size(CAMS) + pstbl->CbMem();
|
|
if (pvNil == (pcams = NewObj CAMS) ||
|
|
FAILED(AllocSoundFromStream(&pcams->psnd, pstbl, fTrue, pvNil)))
|
|
{
|
|
ReleasePpo(&pcams);
|
|
}
|
|
|
|
if (pvNil != pcams)
|
|
pcams->_pstbl = pstbl;
|
|
else
|
|
ReleasePpo(&pstbl);
|
|
|
|
AssertNilOrPo(pcams, 0);
|
|
*ppbaco = pcams;
|
|
|
|
return pvNil != *ppbaco;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Static BACO reader method to put together a Cached AudioMan sound.
|
|
***************************************************************************/
|
|
PCAMS CAMS::PcamsNewLoop(PCAMS pcamsSrc, long cactPlay)
|
|
{
|
|
AssertPo(pcamsSrc, 0);
|
|
Assert(cactPlay != 1, "bad loop count");
|
|
PCAMS pcams = pvNil;
|
|
|
|
if (pvNil == (pcams = NewObj CAMS) ||
|
|
FAILED(AllocLoopFilter(&pcams->psnd, pcamsSrc->psnd,
|
|
cactPlay - 1)))
|
|
{
|
|
ReleasePpo(&pcams);
|
|
}
|
|
|
|
if (pvNil != pcams)
|
|
{
|
|
pcams->_pstbl = pcamsSrc->_pstbl;
|
|
pcams->_pstbl->AddRef();
|
|
}
|
|
|
|
AssertNilOrPo(pcams, 0);
|
|
return pcams;
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
/***************************************************************************
|
|
Assert the validity of a CAMS.
|
|
***************************************************************************/
|
|
void CAMS::AssertValid(ulong grf)
|
|
{
|
|
CAMS_PAR::AssertValid(0);
|
|
AssertPo(_pstbl, 0);
|
|
Assert(psnd != pvNil, 0);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Mark memory for the CAMS.
|
|
***************************************************************************/
|
|
void CAMS::MarkMem(void)
|
|
{
|
|
AssertValid(0);
|
|
CAMS_PAR::MarkMem();
|
|
if (pvNil != _pstbl)
|
|
_pstbl->MarkMem();
|
|
}
|
|
#endif //DEBUG
|
|
|
|
|
|
/***************************************************************************
|
|
Constructor for our notify sink.
|
|
***************************************************************************/
|
|
AMNOT::AMNOT(void)
|
|
{
|
|
_cactRef = 1;
|
|
_pamque = pvNil;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Set the AMQUE that we're to notify.
|
|
***************************************************************************/
|
|
void AMNOT::Set(PAMQUE pamque)
|
|
{
|
|
AssertNilOrVarMem(pamque);
|
|
_pamque = pamque;
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
/***************************************************************************
|
|
Assert the validity of a AMNOT.
|
|
***************************************************************************/
|
|
void AMNOT::AssertValid(ulong grf)
|
|
{
|
|
AssertThisMem();
|
|
AssertNilOrVarMem(_pamque);
|
|
}
|
|
#endif //DEBUG
|
|
|
|
|
|
/***************************************************************************
|
|
QueryInterface for AMNOT.
|
|
***************************************************************************/
|
|
STDMETHODIMP AMNOT::QueryInterface(REFIID riid, void ** ppv)
|
|
{
|
|
AssertThis(0);
|
|
|
|
if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IAMNotifySink))
|
|
{
|
|
*ppv = (void *)this;
|
|
AddRef();
|
|
return S_OK;
|
|
}
|
|
|
|
*ppv = pvNil;
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Increment the reference count.
|
|
***************************************************************************/
|
|
STDMETHODIMP_(ULONG) AMNOT::AddRef(void)
|
|
{
|
|
AssertThis(0);
|
|
return ++_cactRef;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Decrement the reference count.
|
|
***************************************************************************/
|
|
STDMETHODIMP_(ULONG) AMNOT::Release(void)
|
|
{
|
|
AssertThis(0);
|
|
long cactRef;
|
|
|
|
if ((cactRef = --_cactRef) == 0)
|
|
delete this;
|
|
return cactRef;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
The indicated sound is done. Just tell the AMQUE that we got a notify.
|
|
***************************************************************************/
|
|
STDMETHODIMP_(void) AMNOT::OnCompletion(LPSOUND pSound, DWORD dwPosition)
|
|
{
|
|
AssertThis(0);
|
|
|
|
if (pvNil != _pamque)
|
|
_pamque->Notify(pSound);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Constructor for an audioman queue.
|
|
***************************************************************************/
|
|
AMQUE::AMQUE(void)
|
|
{
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Destructor for an audioman queue.
|
|
***************************************************************************/
|
|
AMQUE::~AMQUE(void)
|
|
{
|
|
if (pvNil != _pchan)
|
|
StopAll();
|
|
ReleasePpo(&_pchan);
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
/***************************************************************************
|
|
Assert the validity of a AMQUE.
|
|
***************************************************************************/
|
|
void AMQUE::AssertValid(ulong grf)
|
|
{
|
|
AMQUE_PAR::AssertValid(0);
|
|
Assert(pvNil != _pchan, 0);
|
|
}
|
|
#endif //DEBUG
|
|
|
|
|
|
/***************************************************************************
|
|
Static method to create a new audioman queue.
|
|
***************************************************************************/
|
|
PAMQUE AMQUE::PamqueNew(void)
|
|
{
|
|
PAMQUE pamque;
|
|
|
|
if (pvNil == (pamque = NewObj AMQUE))
|
|
return pvNil;
|
|
|
|
if (!pamque->_FInit())
|
|
ReleasePpo(&pamque);
|
|
|
|
AssertNilOrPo(pamque, 0);
|
|
return pamque;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Initialize the audioman queue. Allocate the audioman channel and the
|
|
_pglsndin.
|
|
***************************************************************************/
|
|
bool AMQUE::_FInit(void)
|
|
{
|
|
AssertBaseThis(0);
|
|
|
|
if (!AMQUE_PAR::_FInit())
|
|
return fFalse;
|
|
|
|
if (FAILED(_pamix->AllocChannel(&_pchan)))
|
|
{
|
|
_pchan = pvNil;
|
|
return fFalse;
|
|
}
|
|
if (pvNil == _pchan)
|
|
{
|
|
Bug("Audioman messed up!");
|
|
return fFalse;
|
|
}
|
|
|
|
_amnot.Set(this);
|
|
if (FAILED(_pchan->RegisterNotify(&_amnot, NOTIFYSINK_ONCOMPLETION)))
|
|
return fFalse;
|
|
|
|
AssertThis(0);
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Enter the critical section protecting member variables.
|
|
***************************************************************************/
|
|
void AMQUE::_Enter(void)
|
|
{
|
|
_mutx.Enter();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Leave the critical section protecting member variables.
|
|
***************************************************************************/
|
|
void AMQUE::_Leave(void)
|
|
{
|
|
_mutx.Leave();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Fetch the given sound chunk as a CAMS.
|
|
***************************************************************************/
|
|
PBACO AMQUE::_PbacoFetch(PRCA prca, CTG ctg, CNO cno)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(prca, 0);
|
|
|
|
return prca->PbacoFetch(ctg, cno, &CAMS::FReadCams);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
An item was added to or deleted from the queue.
|
|
***************************************************************************/
|
|
void AMQUE::_Queue(long isndinMin)
|
|
{
|
|
AssertThis(0);
|
|
SNDIN sndin;
|
|
long isndin;
|
|
|
|
_Enter();
|
|
|
|
if (pvNil != _pglsndin)
|
|
{
|
|
PCAMS pcams;
|
|
|
|
for (isndin = isndinMin; isndin < _pglsndin->IvMac(); isndin++)
|
|
{
|
|
_pglsndin->Get(isndin, &sndin);
|
|
|
|
if (1 == sndin.cactPlay)
|
|
continue;
|
|
|
|
// put a loop filter around it to get seamless sample based looping
|
|
if (pvNil != (pcams = CAMS::PcamsNewLoop((PCAMS)sndin.pbaco,
|
|
sndin.cactPlay)))
|
|
{
|
|
sndin.cactPlay = 1; // now it's just one sound
|
|
ReleasePpo(&sndin.pbaco);
|
|
sndin.pbaco = pcams;
|
|
_pglsndin->Put(isndin, &sndin);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (_isndinCur == isndinMin && pvNil != _pglsndin)
|
|
{
|
|
for ( ; _isndinCur < _pglsndin->IvMac(); _isndinCur++)
|
|
{
|
|
_pglsndin->Get(_isndinCur, &sndin);
|
|
if (0 <= sndin.cactPause)
|
|
break;
|
|
}
|
|
|
|
if (_isndinCur < _pglsndin->IvMac() && 0 == sndin.cactPause)
|
|
{
|
|
// stop the channel
|
|
_pchan->Stop();
|
|
|
|
// set the volume
|
|
_pchan->SetVolume(LuVolScale((ulong)(-1), sndin.vlm));
|
|
|
|
// if the sound is in memory
|
|
if (((PCAMS)sndin.pbaco)->FInMemory())
|
|
{
|
|
// set the sound source, with no cache (Since it's in memory)
|
|
_pchan->SetSoundSrc(((PCAMS)sndin.pbaco)->psnd);
|
|
}
|
|
else
|
|
{
|
|
CacheConfig cc;
|
|
cc.dwSize = size(cc);
|
|
cc.fSrcFormat = fTrue;
|
|
cc.lpFormat = pvNil;
|
|
cc.dwFormat = _luFormat;
|
|
cc.dwCacheTime = 2*_luCacheTime;
|
|
|
|
// set the sound src, using cache cause it's not in memory
|
|
_pchan->SetCachedSrc(((PCAMS)sndin.pbaco)->psnd, &cc);
|
|
}
|
|
|
|
// if there is a starting offset, apply it
|
|
if (sndin.dtsStart != 0)
|
|
_pchan->SetTimePos(sndin.dtsStart);
|
|
|
|
if (!_fGrouped || FAILED(_pamix->EnlistGroup(_pchan, _luGroup)))
|
|
{
|
|
// start the channel
|
|
_pchan->Play();
|
|
}
|
|
|
|
_tsStart = TsCurrentSystem() - sndin.dtsStart;
|
|
}
|
|
else
|
|
{
|
|
_pchan->Stop();
|
|
_pchan->SetSoundSrc(pvNil);
|
|
}
|
|
}
|
|
|
|
_Leave();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
One or more items in the queue were paused.
|
|
***************************************************************************/
|
|
void AMQUE::_PauseQueue(long isndinMin)
|
|
{
|
|
AssertThis(0);
|
|
SNDIN sndin;
|
|
|
|
_mutx.Enter();
|
|
|
|
if (_isndinCur == isndinMin && _pglsndin->IvMac() > _isndinCur)
|
|
{
|
|
_pglsndin->Get(_isndinCur, &sndin);
|
|
sndin.dtsStart = TsCurrentSystem() - _tsStart;
|
|
_pglsndin->Put(_isndinCur, &sndin);
|
|
|
|
_Queue(isndinMin);
|
|
}
|
|
|
|
_mutx.Leave();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
One or more items in the queue were resumed.
|
|
***************************************************************************/
|
|
void AMQUE::_ResumeQueue(long isndinMin)
|
|
{
|
|
AssertThis(0);
|
|
|
|
_Queue(isndinMin);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Called by our notify sink to tell us that the indicated sound is done.
|
|
WARNING: this is called in an auxillary thread.
|
|
***************************************************************************/
|
|
void AMQUE::Notify(LPSOUND psnd)
|
|
{
|
|
AssertThis(0);
|
|
SNDIN sndin;
|
|
|
|
_Enter();
|
|
|
|
if (pvNil != _pglsndin && _pglsndin->IvMac() > _isndinCur)
|
|
{
|
|
_pglsndin->Get(_isndinCur, &sndin);
|
|
if (psnd == ((PCAMS)sndin.pbaco)->psnd)
|
|
{
|
|
if (--sndin.cactPlay == 0)
|
|
{
|
|
_isndinCur++;
|
|
_Queue(_isndinCur);
|
|
}
|
|
else
|
|
{
|
|
// play the sound again
|
|
_pglsndin->Put(_isndinCur, &sndin);
|
|
_pchan->SetSoundSrc(((PCAMS)sndin.pbaco)->psnd);
|
|
_tsStart = TsCurrentSystem();
|
|
}
|
|
}
|
|
}
|
|
|
|
_Leave();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Constructor for the audioman device.
|
|
***************************************************************************/
|
|
SDAM::SDAM(void)
|
|
{
|
|
_vlm = kvlmFull;
|
|
_luVolSys = (ulong)(-1);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Destructor for the audioman device.
|
|
***************************************************************************/
|
|
SDAM::~SDAM(void)
|
|
{
|
|
AssertBaseThis(0);
|
|
|
|
if (_fAudioManInited && 0 == _pamix->Release())
|
|
_pamix = pvNil;
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
/***************************************************************************
|
|
Assert the validity of a SDAM.
|
|
***************************************************************************/
|
|
void SDAM::AssertValid(ulong grf)
|
|
{
|
|
SDAM_PAR::AssertValid(0);
|
|
Assert(_pamix != pvNil, 0);
|
|
}
|
|
#endif //DEBUG
|
|
|
|
|
|
/***************************************************************************
|
|
Static method to create the audioman device.
|
|
***************************************************************************/
|
|
PSDAM SDAM::PsdamNew(long wav)
|
|
{
|
|
PSDAM psdam;
|
|
|
|
if (pvNil == (psdam = NewObj SDAM))
|
|
return pvNil;
|
|
|
|
if (!psdam->_FInit(wav))
|
|
ReleasePpo(&psdam);
|
|
|
|
AssertNilOrPo(psdam, 0);
|
|
return psdam;
|
|
}
|
|
|
|
|
|
static long _mpwavfmt[] =
|
|
{
|
|
WAVE_FORMAT_1M08,
|
|
WAVE_FORMAT_2M08,
|
|
WAVE_FORMAT_4M08,
|
|
WAVE_FORMAT_1S08,
|
|
WAVE_FORMAT_2S08,
|
|
WAVE_FORMAT_4S08,
|
|
WAVE_FORMAT_1M16,
|
|
WAVE_FORMAT_2M16,
|
|
WAVE_FORMAT_4M16,
|
|
WAVE_FORMAT_1S16,
|
|
WAVE_FORMAT_2S16,
|
|
WAVE_FORMAT_4S16,
|
|
};
|
|
|
|
#ifdef CHECK_AUDIO_DEVCAPS
|
|
/******************************************************************************
|
|
|
|
@func WORD | wHaveWaveDevice |
|
|
|
|
Do we have a wave device capable of playing the passed PCM format(s).
|
|
|
|
@parm DWORD | dwFormats | WAVE formats needed to be supported. These can be
|
|
a bitwise combination of WAVE_FORMAT_???? flags
|
|
which are defined in mmsystem.h. If you don't
|
|
care what formats are supported you can pass zero.
|
|
|
|
******************************************************************************/
|
|
|
|
bool FHaveWaveDevice(DWORD dwReqFormats)
|
|
{
|
|
WORD wNumWavDev;
|
|
WAVEOUTCAPS WOC;
|
|
WORD wDevID;
|
|
WORD wErr;
|
|
|
|
// Determine how many WAVE devices are in the user's system
|
|
wNumWavDev = waveOutGetNumDevs();
|
|
|
|
// If there are none, return indicating that
|
|
if (0 == wNumWavDev)
|
|
return(fFalse);
|
|
|
|
// Cycle through the WAVE devices to determine if any support
|
|
// the desired format.
|
|
for (wDevID = 0; wDevID < wNumWavDev; wDevID++)
|
|
{
|
|
wErr = waveOutGetDevCaps(wDevID, &WOC, sizeof(WAVEOUTCAPS));
|
|
|
|
// If we obtain a WAVE device's capabilities OK
|
|
// and it supports the desired format
|
|
if ((0 == wErr) && ((WOC.dwFormats & dwReqFormats) == dwReqFormats))
|
|
{
|
|
// then return success - we have a device that supports what we want
|
|
return fTrue;
|
|
}
|
|
}
|
|
|
|
// it doesn't support this device
|
|
return fFalse;
|
|
}
|
|
#endif
|
|
|
|
|
|
/***************************************************************************
|
|
Initialize the audioman device.
|
|
***************************************************************************/
|
|
bool SDAM::_FInit(long wav)
|
|
{
|
|
AssertBaseThis(0);
|
|
MIXERCONFIG mixc;
|
|
ADVMIXCONFIG amxc;
|
|
|
|
if (!SDAM_PAR::_FInit())
|
|
return fFalse;
|
|
|
|
// get IAMMixer interface
|
|
if (pvNil != _pamix)
|
|
{
|
|
_pamix->AddRef();
|
|
_fAudioManInited = fTrue;
|
|
}
|
|
else
|
|
{
|
|
if (pvNil == (_pamix = GetAudioManMixer()))
|
|
return fFalse;
|
|
_fAudioManInited = fTrue;
|
|
|
|
// REVIEW shonk: what values should we use?
|
|
mixc.dwSize = size(mixc);
|
|
mixc.lpFormat = pvNil;
|
|
if (!FIn(wav, 0, kwavLim))
|
|
wav = kwav22M16;
|
|
mixc.dwFormat = _mpwavfmt[wav];
|
|
amxc.dwSize = size(amxc);
|
|
amxc.uVoices = 12;
|
|
amxc.fRemixEnabled = fTrue;
|
|
amxc.uBufferTime = 600;
|
|
|
|
#ifdef CHECK_AUDIO_DEVCAPS
|
|
// if we don't have a device of this format...
|
|
if (!FHaveWaveDevice(mixc.dwFormat))
|
|
{
|
|
// failed, so try dropping to 8 bit
|
|
wav += kwav22M8 - kwav22M16;
|
|
if (!FIn(wav, 0, kwavLim))
|
|
return fFalse;
|
|
mixc.dwFormat = _mpwavfmt[wav];
|
|
// we'll try to open at 8, cause if the card doesn't
|
|
// support it, then WAVE_MAPPER will actually convert
|
|
// to the 8 bit format.
|
|
}
|
|
|
|
_luFormat = mixc.dwFormat;
|
|
_luCacheTime = amxc.uBufferTime;
|
|
|
|
// initialize it (done only once...)
|
|
if (FAILED(_pamix->Init(vwig.hinst, pvNil, &mixc, &amxc)))
|
|
return fFalse;
|
|
#else
|
|
_luFormat = mixc.dwFormat;
|
|
_luCacheTime = amxc.uBufferTime;
|
|
|
|
// initialize it (done only once...)
|
|
if (FAILED(_pamix->Init(vwig.hinst, pvNil, &mixc, &amxc)))
|
|
{
|
|
// failed, so try at 8 bit
|
|
wav += kwav22M8 - kwav22M16;
|
|
if (!FIn(wav, 0, kwavLim))
|
|
return fFalse;
|
|
if (FAILED(_pamix->Init(vwig.hinst, pvNil, &mixc, &amxc)))
|
|
return fFalse;
|
|
}
|
|
#endif
|
|
if (FAILED(_pamix->Activate(fTrue)))
|
|
return fFalse;
|
|
}
|
|
|
|
_Suspend(_cactSuspend > 0 || !_fActive);
|
|
|
|
AssertThis(0);
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Allocate a new audioman queue.
|
|
***************************************************************************/
|
|
PSNQUE SDAM::_PsnqueNew(void)
|
|
{
|
|
AssertThis(0);
|
|
|
|
return AMQUE::PamqueNew();
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Activate or deactivate audioman.
|
|
***************************************************************************/
|
|
void SDAM::_Suspend(bool fSuspend)
|
|
{
|
|
AssertThis(0);
|
|
|
|
if (fSuspend)
|
|
_pamix->SetMixerVolume(_luVolSys);
|
|
|
|
if (FAILED(_pamix->Suspend(fSuspend)) && !fSuspend)
|
|
PushErc(ercSndamWaveDeviceBusy);
|
|
else if (!fSuspend)
|
|
{
|
|
// becoming active
|
|
_pamix->GetMixerVolume(&_luVolSys);
|
|
vluSysVolFake = _luVolSys;
|
|
_pamix->SetMixerVolume(LuVolScale(_luVolSys, _vlm));
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Set the volume.
|
|
***************************************************************************/
|
|
void SDAM::SetVlm(long vlm)
|
|
{
|
|
AssertThis(0);
|
|
|
|
if (_vlm != vlm)
|
|
{
|
|
_vlm = vlm;
|
|
if (_cactSuspend <= 0 && _fActive)
|
|
_pamix->SetMixerVolume(LuVolScale(_luVolSys, vlm));
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Get the current volume.
|
|
***************************************************************************/
|
|
long SDAM::VlmCur(void)
|
|
{
|
|
AssertThis(0);
|
|
|
|
return _vlm;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Begin a synchronization group.
|
|
***************************************************************************/
|
|
void SDAM::BeginSynch(void)
|
|
{
|
|
AssertThis(0);
|
|
|
|
if (0 == _cactGroup++)
|
|
_fGrouped = SUCCEEDED(_pamix->AllocGroup(&_luGroup));
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
End a synchronization group.
|
|
***************************************************************************/
|
|
void SDAM::EndSynch(void)
|
|
{
|
|
AssertThis(0);
|
|
|
|
if ((0 == --_cactGroup) && _fGrouped)
|
|
{
|
|
_pamix->StartGroup(_luGroup, fTrue);
|
|
_pamix->FreeGroup(_luGroup);
|
|
_fGrouped = fFalse;
|
|
}
|
|
}
|
|
|
|
|