mirror of
https://github.com/microsoft/Microsoft-3D-Movie-Maker.git
synced 2024-11-23 11:45:14 +00:00
957 lines
22 KiB
C++
957 lines
22 KiB
C++
/* Copyright (c) Microsoft Corporation.
|
|
Licensed under the MIT License. */
|
|
|
|
/***************************************************************************
|
|
|
|
msnd.cpp: Movie Sound class
|
|
|
|
Primary Authors: *****, *****
|
|
Status: Reviewed
|
|
|
|
A MSND (movie sound) owns a MIDI or WAVE child chunk, and also
|
|
specifies what sound type (sty) this sound is, and the default
|
|
volume for the sound.
|
|
|
|
Here's how the chunks look:
|
|
|
|
MSND
|
|
|
|
|
+---MIDI or WAVE (chid 0) // actual sound data
|
|
|
|
An MSND chunk with no child is a "no sound" or silent sound.
|
|
An MSND chunk with _fInvalid set requires no child.
|
|
|
|
***************************************************************************/
|
|
#include "soc.h"
|
|
#include "audioman.h"
|
|
|
|
ASSERTNAME
|
|
|
|
RTCLASS(MSND)
|
|
RTCLASS(MSQ)
|
|
|
|
BEGIN_CMD_MAP(MSQ, CMH)
|
|
ON_CID_ME(cidAlarm, &MSQ::FCmdAlarm, pvNil)
|
|
END_CMD_MAP_NIL()
|
|
|
|
// default sound import format
|
|
const WORD knSamplesPerSec = 11025;
|
|
const WORD knAvgBytesPerSec = 11025;
|
|
const WORD kwBitsPerSample = 8;
|
|
const WORD knChannels = 1;
|
|
const WORD knBlockAlign = 1;
|
|
|
|
/***************************************************************************
|
|
|
|
A PFNRPO to read a MSND from a file
|
|
|
|
***************************************************************************/
|
|
bool MSND::FReadMsnd(PCRF pcrf, CTG ctg, CNO cno, PBLCK pblck,
|
|
PBACO *ppbaco, long *pcb)
|
|
{
|
|
AssertPo(pcrf, 0);
|
|
AssertPo(pblck, 0);
|
|
AssertNilOrVarMem(ppbaco);
|
|
AssertVarMem(pcb);
|
|
|
|
MSND *pmsnd;
|
|
|
|
*pcb = size(MSND); // estimate MSND size
|
|
if (pvNil == ppbaco)
|
|
return fTrue;
|
|
|
|
pmsnd = NewObj MSND();
|
|
if (pvNil == pmsnd || !pmsnd->_FInit(pcrf->Pcfl(), ctg, cno))
|
|
{
|
|
TrashVar(ppbaco);
|
|
TrashVar(pcb);
|
|
ReleasePpo(&pmsnd);
|
|
return fFalse;
|
|
}
|
|
|
|
pmsnd->_prca = pcrf;
|
|
AssertPo(pmsnd, 0);
|
|
*ppbaco = pmsnd;
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Retrieve information contained in the msnd chunk
|
|
|
|
***************************************************************************/
|
|
bool MSND::FGetMsndInfo(PCFL pcfl, CTG ctg, CNO cno, bool *pfInvalid, long *psty, long *pvlm)
|
|
{
|
|
AssertPo(pcfl, 0);
|
|
|
|
PMSND pmsnd;
|
|
pmsnd = NewObj MSND();
|
|
if (pvNil == pmsnd)
|
|
return pvNil;
|
|
|
|
if (!pmsnd->_FInit(pcfl, ctg, cno))
|
|
{
|
|
ReleasePpo(&pmsnd);
|
|
return fFalse;
|
|
}
|
|
|
|
if (pvNil != pfInvalid)
|
|
*pfInvalid = pmsnd->_fInvalid;
|
|
if (pvNil != psty)
|
|
*psty = pmsnd->_sty;
|
|
if (pvNil != pvlm)
|
|
*pvlm = pmsnd->_vlm;
|
|
ReleasePpo(&pmsnd);
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Init a MSND from the given chunk of the given CFL
|
|
|
|
***************************************************************************/
|
|
bool MSND::_FInit(PCFL pcfl, CTG ctg, CNO cno)
|
|
{
|
|
AssertBaseThis(0);
|
|
AssertPo(pcfl, 0);
|
|
|
|
BLCK blck;
|
|
MSNDF msndf;
|
|
KID kid;
|
|
|
|
if (!pcfl->FFind(ctg, cno, &blck) || !blck.FUnpackData())
|
|
goto LFail;
|
|
if (blck.Cb() > size(MSNDF))
|
|
goto LFail;
|
|
if (!blck.FReadRgb(&msndf, size(MSNDF), 0))
|
|
goto LFail;
|
|
if (kboCur != msndf.bo)
|
|
SwapBytesBom(&msndf, kbomBkgdf);
|
|
Assert(kboCur == msndf.bo, "bad MSNDF");
|
|
|
|
if (!pcfl->FGetName(ctg, cno, &_stn))
|
|
return fFalse;
|
|
|
|
_sty = msndf.sty;
|
|
_vlm = msndf.vlmDefault;
|
|
_fInvalid = msndf.fInvalid;
|
|
if (_fInvalid)
|
|
return fTrue;
|
|
|
|
// If there is a SND child, it is not a "no sound"
|
|
if (pcfl->FGetKidChid(ctg, cno, kchidSnd, &kid))
|
|
{
|
|
_cnoSnd = kid.cki.cno;
|
|
_ctgSnd = kid.cki.ctg;
|
|
_fNoSound = fFalse;
|
|
}
|
|
else
|
|
_fNoSound = fTrue;
|
|
|
|
return fTrue;
|
|
LFail:
|
|
return fFalse;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Write an MSND MIDI chunk to file *pcfl
|
|
ie, write the MSND chunk, its name, and the midi child
|
|
|
|
***************************************************************************/
|
|
bool MSND::FWriteMidi(PCFL pcflDest, PMIDS pmids, STN *pstnName, CNO *pcno)
|
|
{
|
|
AssertPo(pcflDest, 0);
|
|
AssertPo(pmids, 0);
|
|
AssertVarMem(pstnName);
|
|
AssertVarMem(pcno);
|
|
|
|
MSNDF msndf;
|
|
BLCK blck;
|
|
CNO cno;
|
|
|
|
msndf.bo = kboCur;
|
|
msndf.osk = koskCur;
|
|
msndf.sty = styMidi;
|
|
msndf.vlmDefault = kvlmFull;
|
|
msndf.fInvalid = fFalse;
|
|
|
|
// Create the msnd chunk
|
|
if (!pcflDest->FAddPv(&msndf, size(MSNDF), kctgMsnd, pcno))
|
|
return fFalse;
|
|
|
|
// Create the midi chunk as a child of the msnd chunk
|
|
if (!pcflDest->FAddChild(kctgMsnd, *pcno, kchidSnd, pmids->CbOnFile(), kctgMidi, &cno, &blck))
|
|
goto LFail;
|
|
|
|
if (!pmids->FWrite(&blck))
|
|
goto LFail;
|
|
|
|
if (!pcflDest->FSetName(kctgMsnd, *pcno, pstnName))
|
|
goto LFail;
|
|
|
|
return fTrue;
|
|
|
|
LFail:
|
|
pcflDest->Delete(kctgMsnd, *pcno); // Deletes the midi chunk also
|
|
return fFalse;
|
|
}
|
|
|
|
/***************************************************************************
|
|
|
|
Write an MSND Wave file to a file
|
|
ie, write the MSND chunk, its name, and the midi child
|
|
|
|
***************************************************************************/
|
|
bool MSND::FWriteWave(PFIL pfilSrc, PCFL pcflDest, long sty, STN *pstnName, CNO *pcno)
|
|
{
|
|
AssertPo(pfilSrc, 0);
|
|
AssertIn(sty, 0, styLim);
|
|
AssertVarMem(pstnName);
|
|
AssertVarMem(pcno);
|
|
|
|
MSNDF msndf;
|
|
CNO cno;
|
|
FLO floSrc;
|
|
FLO floDest;
|
|
|
|
msndf.bo = kboCur;
|
|
msndf.osk = koskCur;
|
|
msndf.sty = sty;
|
|
msndf.vlmDefault = kvlmFull;
|
|
msndf.fInvalid = fFalse;
|
|
|
|
floSrc.pfil = pfilSrc;
|
|
floSrc.cb = pfilSrc->FpMac();
|
|
floSrc.fp = 0;
|
|
|
|
// Create the msnd chunk
|
|
if (!pcflDest->FAddPv(&msndf, size(MSNDF), kctgMsnd, pcno))
|
|
return fFalse;
|
|
|
|
// Create the wave chunk as a child of the msnd chunk
|
|
if (!pcflDest->FAddChild(kctgMsnd, *pcno, kchidSnd, floSrc.cb, kctgWave, &cno))
|
|
goto LFail;
|
|
|
|
if (!pcflDest->FFindFlo(kctgWave, cno, &floDest))
|
|
goto LFail;
|
|
|
|
if (!floSrc.FCopy(&floDest))
|
|
goto LFail;
|
|
|
|
if (!pcflDest->FSetName(kctgMsnd, *pcno, pstnName))
|
|
goto LFail;
|
|
|
|
return fTrue;
|
|
|
|
LFail:
|
|
pcflDest->Delete(kctgMsnd, *pcno); // Deletes the wave chunk also
|
|
return fFalse;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Copy the midi file to a chunk in the current movie
|
|
The *pcno is returned
|
|
|
|
***************************************************************************/
|
|
bool MSND::FCopyMidi(PFIL pfilSrc, PCFL pcflDest, CNO *pcno, PSTN pstn)
|
|
{
|
|
AssertPo(pfilSrc, 0);
|
|
AssertNilOrPo(pstn, 0);
|
|
|
|
PMIDS pmids = pvNil;
|
|
FNI fniSrc;
|
|
STN stnName;
|
|
|
|
pfilSrc->GetFni(&fniSrc);
|
|
if (pvNil == pstn)
|
|
fniSrc.GetLeaf(&stnName);
|
|
else
|
|
stnName = *pstn;
|
|
|
|
pmids = MIDS::PmidsReadNative(&fniSrc);
|
|
if (pmids == pvNil)
|
|
{
|
|
PushErc(ercSocBadSoundFile);
|
|
goto LFail;
|
|
}
|
|
|
|
// Create the chunk & write it to this movie
|
|
// Adopt it later as a child of kctgMvie
|
|
if (!MSND::FWriteMidi(pcflDest, pmids, &stnName, pcno))
|
|
goto LFail;
|
|
|
|
ReleasePpo(&pmids);
|
|
return fTrue;
|
|
|
|
LFail:
|
|
ReleasePpo(&pmids);
|
|
return fFalse;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Copy the wave file to a chunk in the current movie
|
|
|
|
***************************************************************************/
|
|
bool MSND::FCopyWave(PFIL pfilSrc, PCFL pcflDest, long sty, CNO *pcno, PSTN pstn)
|
|
{
|
|
AssertPo(pfilSrc, 0);
|
|
AssertPo(pcflDest, 0);
|
|
AssertIn(sty, 0, styLim);
|
|
AssertVarMem(pcno);
|
|
Assert(sty != styMidi, "Illegal sty argument");
|
|
AssertNilOrPo(pstn, 0);
|
|
|
|
FNI fniSrc;
|
|
STN stnName; // sound name
|
|
STN stn; // src file path name
|
|
WAVEFORMATEX wfxSrc;
|
|
LPSOUND psnd = pvNil;
|
|
LPSOUND psndTemp = pvNil;
|
|
FIL *pfilNew = pvNil;
|
|
STN stnNew;
|
|
FNI fniNew;
|
|
WAVEFORMATEX *pwfxDst = pvNil;
|
|
DWORD cbwfx; // size of waveformatex needed
|
|
DWORD csampSrc; // number of source samples
|
|
DWORD cbSrc; // size of source in bytes
|
|
LPBYTE pbSrc = pvNil; // src data buffer
|
|
DWORD cbDst; // size of dest in bytes
|
|
LPBYTE pbDst = pvNil; // dst data buffer
|
|
HACMSTREAM hacmstream = pvNil;
|
|
ACMSTREAMHEADER acmhdr;
|
|
DWORD dwTag;
|
|
DWORD dwLength;
|
|
long cbOriginalFile = 0;
|
|
long cbCompressedFile = 0;
|
|
FP fpNew;
|
|
|
|
pfilSrc->GetFni(&fniSrc);
|
|
if (pvNil == pstn)
|
|
fniSrc.GetLeaf(&stnName);
|
|
else
|
|
stnName = *pstn;
|
|
fniSrc.GetStnPath(&stn);
|
|
|
|
if (!fniNew.FGetTemp())
|
|
goto LFail;
|
|
|
|
// get the original file size
|
|
if (pvNil == (pfilNew = FIL::PfilOpen(&fniSrc)))
|
|
goto LFailPushError;
|
|
cbOriginalFile = pfilNew->FpMac();
|
|
ReleasePpo(&pfilNew);
|
|
|
|
// open the file as a pSound
|
|
if (FAILED(AllocSoundFromFile(&psnd, stn.Psz(), 0, fTrue, pvNil)))
|
|
goto LFailPushError;
|
|
if (FAILED(psnd->GetFormat((LPWAVEFORMATEX)&wfxSrc, size(WAVEFORMATEX))))
|
|
goto LFailPushError;
|
|
|
|
// if the file format is not 11M8, then create a converter to 11M8
|
|
if ((wfxSrc.nSamplesPerSec != knSamplesPerSec) ||
|
|
(wfxSrc.wBitsPerSample != kwBitsPerSample )||
|
|
(wfxSrc.nChannels != knChannels ) )
|
|
{
|
|
// Convert the wave import to 3mm's 11M8
|
|
wfxSrc.nSamplesPerSec = knSamplesPerSec ;
|
|
wfxSrc.nAvgBytesPerSec = knAvgBytesPerSec ;
|
|
wfxSrc.wBitsPerSample = kwBitsPerSample;
|
|
wfxSrc.nChannels = knChannels ;
|
|
wfxSrc.nBlockAlign = knBlockAlign ;
|
|
if (FAILED(AllocConvertFilter(&psndTemp, psnd, (LPWAVEFORMATEX)&wfxSrc)))
|
|
goto LFailPushError;
|
|
|
|
ReleasePpo(&psnd);
|
|
psnd = psndTemp;
|
|
psndTemp = pvNil;
|
|
}
|
|
|
|
// now figure out how many total samples there are
|
|
csampSrc = psnd->GetSamples();
|
|
if (csampSrc == -1) // Don't allow infinite sources to be written
|
|
goto LFailPushError;
|
|
cbSrc = csampSrc * (wfxSrc.wBitsPerSample>>3) * wfxSrc.nChannels;
|
|
|
|
if (FAILED(psnd->SetMode(TRUE,TRUE))) // Activate the Sound Audio Stream
|
|
goto LFailPushError;
|
|
|
|
// allocate a buffer big enough to hold them all
|
|
if (!FAllocPv((LPVOID *)&pbSrc, cbSrc, fmemNil, mprNormal))
|
|
goto LFail;
|
|
|
|
// get all of the samples
|
|
if (FAILED(psnd->GetSampleData(pbSrc, 0, &csampSrc, NULL)))
|
|
goto LFailPushError;
|
|
|
|
// -------------------------------------
|
|
// inplace compress the samples to ADPCM
|
|
|
|
// figure out the correct size for dest waveformatex (so we get the codec info)
|
|
if (acmMetrics(NULL, ACM_METRIC_MAX_SIZE_FORMAT, &cbwfx))
|
|
goto LFailPushError;
|
|
|
|
// allocate a buffer of this size
|
|
if (!FAllocPv((LPVOID *)&pwfxDst, cbwfx, fmemNil, mprNormal))
|
|
goto LFail;
|
|
|
|
// allocate a destination buffer big enough to hold them all
|
|
cbDst = cbSrc;
|
|
if (!FAllocPv((LPVOID *)&pbDst, cbDst, fmemNil, mprNormal))
|
|
goto LFail;
|
|
|
|
// set up the dest wfx
|
|
pwfxDst->wFormatTag = WAVE_FORMAT_ADPCM;
|
|
pwfxDst->nChannels = knChannels;
|
|
pwfxDst->nSamplesPerSec = knSamplesPerSec;
|
|
// pwfxDst->wBitsPerSample, pwfxDst->nAvgBytesPerSec, and
|
|
// pwfxDst->nBlockAlign are calced by the acmFormatSuggest()
|
|
pwfxDst->cbSize = (WORD)cbwfx - sizeof(WAVEFORMATEX);
|
|
|
|
// fill in the coeffiecients in the destination WFX by calling acmFormatSuggest
|
|
if (acmFormatSuggest(NULL, &wfxSrc, pwfxDst, cbwfx, ACM_FORMATSUGGESTF_WFORMATTAG |
|
|
ACM_FORMATSUGGESTF_NCHANNELS | ACM_FORMATSUGGESTF_NSAMPLESPERSEC))
|
|
goto LFailPushError;
|
|
|
|
// open convert stream
|
|
if (acmStreamOpen(&hacmstream, NULL, &wfxSrc, pwfxDst, NULL, 0, 0, ACM_STREAMOPENF_NONREALTIME))
|
|
goto LFailPushError;
|
|
|
|
acmhdr.cbStruct = sizeof(ACMSTREAMHEADER);
|
|
acmhdr.fdwStatus = 0;
|
|
acmhdr.dwUser = 0;
|
|
acmhdr.pbSrc = pbSrc;
|
|
acmhdr.cbSrcLength = cbSrc;
|
|
acmhdr.cbSrcLengthUsed = 0;
|
|
acmhdr.dwSrcUser = 0;
|
|
acmhdr.pbDst = pbDst;
|
|
acmhdr.cbDstLength = cbDst;
|
|
acmhdr.cbDstLengthUsed = 0;
|
|
acmhdr.dwDstUser = 0;
|
|
|
|
// prep acm convert header
|
|
if (acmStreamPrepareHeader(hacmstream, &acmhdr, NULL))
|
|
goto LFailPushError;
|
|
|
|
// now convert
|
|
if (acmStreamConvert(hacmstream, &acmhdr, ACM_STREAMCONVERTF_END))
|
|
goto LFailPushError;
|
|
|
|
// unprep header
|
|
if (acmStreamUnprepareHeader(hacmstream, &acmhdr, NULL))
|
|
goto LFailPushError;
|
|
|
|
// close stream
|
|
acmStreamClose(hacmstream, NULL);
|
|
hacmstream = pvNil;
|
|
|
|
// OK, now we have the sound file converted in memory, write out the data file...
|
|
// open file
|
|
pfilNew = FIL::PfilCreate(&fniNew);
|
|
if (pvNil == pfilNew)
|
|
goto LFail;
|
|
fpNew = 0;
|
|
|
|
// write out 'riff' header
|
|
dwTag = RIFF_TAG;
|
|
if (!pfilNew->FWriteRgbSeq(&dwTag, sizeof(dwTag), &fpNew))
|
|
goto LFail;
|
|
cbCompressedFile = sizeof(RIFF) + pwfxDst->cbSize + 12 - 8 + acmhdr.cbDstLengthUsed; // +12 is for fact chunk chunk, -8 for is riff chunk
|
|
dwLength = cbCompressedFile;
|
|
if (!pfilNew->FWriteRgbSeq(&dwLength, sizeof(dwLength), &fpNew))
|
|
goto LFail;
|
|
|
|
// write WAVE tag
|
|
dwTag = WAVE_TAG;
|
|
if (!pfilNew->FWriteRgbSeq(&dwTag, sizeof(dwTag), &fpNew))
|
|
goto LFail;
|
|
|
|
// write out 'fmt '
|
|
dwTag = FMT__TAG;
|
|
if (!pfilNew->FWriteRgbSeq(&dwTag, sizeof(dwTag), &fpNew))
|
|
goto LFail;
|
|
dwLength = sizeof(WAVEFORMATEX) + pwfxDst->cbSize;
|
|
if (!pfilNew->FWriteRgbSeq(&dwLength, sizeof(dwLength), &fpNew))
|
|
goto LFail;
|
|
if (!pfilNew->FWriteRgbSeq(pwfxDst, dwLength, &fpNew))
|
|
goto LFail;
|
|
|
|
// write out 'fact'
|
|
dwTag = FACT_TAG;
|
|
if (!pfilNew->FWriteRgbSeq(&dwTag, sizeof(dwTag), &fpNew))
|
|
goto LFail;
|
|
dwLength = sizeof(DWORD);
|
|
if (!pfilNew->FWriteRgbSeq(&dwLength, sizeof(dwLength), &fpNew))
|
|
goto LFail;
|
|
dwLength = csampSrc;
|
|
if (!pfilNew->FWriteRgbSeq(&dwLength, sizeof(dwLength), &fpNew))
|
|
goto LFail;
|
|
|
|
// write out 'data'
|
|
dwTag = DATA_TAG;
|
|
if (!pfilNew->FWriteRgbSeq(&dwTag, sizeof(dwTag), &fpNew))
|
|
goto LFail;
|
|
dwLength = acmhdr.cbDstLengthUsed;
|
|
if (!pfilNew->FWriteRgbSeq(&dwLength, sizeof(dwLength), &fpNew))
|
|
goto LFail;
|
|
if (!pfilNew->FWriteRgbSeq(pbDst, dwLength, &fpNew))
|
|
goto LFail;
|
|
|
|
// clean up conversion
|
|
FreePpv((LPVOID *)&pwfxDst);// free up destination waveformat
|
|
FreePpv((LPVOID *)&pbSrc); // free convert src buffer
|
|
FreePpv((LPVOID *)&pbDst); // free convert dest buffer
|
|
ReleasePpo(&pfilNew); // release open file
|
|
if (psnd)
|
|
psnd->SetMode(FALSE,TRUE);
|
|
ReleasePpo(&psnd); // release sound
|
|
|
|
// -------------------------------------------------------------------
|
|
// Copy the original or converted file to a chunk in the current movie
|
|
|
|
// if the compressed file is larger then the original
|
|
if (cbCompressedFile > cbOriginalFile)
|
|
{
|
|
// then just use original file
|
|
if (pvNil == (pfilNew = FIL::PfilOpen(&fniSrc)))
|
|
goto LFail;
|
|
}
|
|
else
|
|
{
|
|
// then we use the newly compressed file
|
|
if (pvNil == (pfilNew = FIL::PfilOpen(&fniNew)))
|
|
goto LFail;
|
|
}
|
|
|
|
if (!MSND::FWriteWave(pfilNew, pcflDest, sty, &stnName, pcno))
|
|
goto LFail;
|
|
|
|
ReleasePpo(&pfilNew);
|
|
if (tYes == fniNew.TExists())
|
|
fniNew.FDelete();
|
|
return fTrue;
|
|
|
|
LFailPushError:
|
|
PushErc(ercSocBadSoundFile);
|
|
LFail:
|
|
if (hacmstream)
|
|
{
|
|
acmStreamClose(hacmstream, NULL);
|
|
hacmstream = pvNil;
|
|
}
|
|
if (psnd)
|
|
psnd->SetMode(FALSE,TRUE);
|
|
|
|
FreePpv((LPVOID *)&pwfxDst);// free dest waveformatex struct
|
|
FreePpv((LPVOID *)&pbSrc); // free convert src buffer
|
|
FreePpv((LPVOID *)&pbDst); // free convert dest buffer
|
|
ReleasePpo(&pfilNew);
|
|
ReleasePpo(&psnd);
|
|
if (tYes == fniNew.TExists())
|
|
fniNew.FDelete();
|
|
return fFalse;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Invalidate a sound
|
|
|
|
***************************************************************************/
|
|
bool MSND::FInvalidate(void)
|
|
{
|
|
AssertThis(0);
|
|
|
|
KID kid;
|
|
MSNDF msndf;
|
|
|
|
// Invalidate the msnd on file
|
|
if (!Pcrf()->Pcfl()->FGetKidChid(kctgMsnd, Cno(), kchidSnd, &kid))
|
|
return fFalse;
|
|
msndf.bo = kboCur;
|
|
msndf.osk = koskCur;
|
|
msndf.sty = _sty;
|
|
msndf.vlmDefault = _vlm;
|
|
msndf.fInvalid = fTrue;
|
|
if (!Pcrf()->Pcfl()->FPutPv(&msndf, size(MSNDF), Ctg(), Cno()))
|
|
return fFalse;
|
|
Pcrf()->Pcfl()->DeleteChild(Ctg(), Cno(), kid.cki.ctg, kid.cki.cno);
|
|
|
|
// Invalidate the cache representation
|
|
_fInvalid = fTrue;
|
|
return fTrue;
|
|
}
|
|
|
|
/***************************************************************************
|
|
|
|
Clean up and delete this movie sound
|
|
|
|
***************************************************************************/
|
|
MSND::~MSND(void)
|
|
{
|
|
AssertBaseThis(0);
|
|
}
|
|
|
|
/***************************************************************************
|
|
|
|
Return the sqn for an msnd attached to an actor object of
|
|
id == objid
|
|
Static function
|
|
|
|
***************************************************************************/
|
|
long MSND::SqnActr(long sty, long objid)
|
|
{
|
|
AssertIn(sty, 0, styLim);
|
|
|
|
long sqnsty = sty << ksqnStyShift;
|
|
return (sqnActr | sqnsty | SwLow(objid));
|
|
}
|
|
|
|
/***************************************************************************
|
|
|
|
Return the sqn for an msnd attached to an actor object of
|
|
id == objid
|
|
Static function
|
|
|
|
***************************************************************************/
|
|
long MSND::SqnBkgd(long sty, long objid)
|
|
{
|
|
long sqnsty = sty << ksqnStyShift;
|
|
return (sqnBkgd | sqnsty | SwLow(objid));
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Return the priority for a tool,sty combination
|
|
|
|
***************************************************************************/
|
|
long MSND::Spr(long tool)
|
|
{
|
|
AssertThis(0);
|
|
Assert(tool == toolMatcher ||
|
|
tool == toolSounder ||
|
|
tool == toolLooper, "Invalid tool");
|
|
|
|
switch(_sty)
|
|
{
|
|
case styMidi:
|
|
Assert(tool != toolMatcher, "No midi motion matching");
|
|
return 1;
|
|
break;
|
|
case stySpeech:
|
|
if (tool == toolSounder)
|
|
return 1;
|
|
if (tool == toolMatcher)
|
|
return 5;
|
|
return 3;
|
|
break;
|
|
case stySfx:
|
|
if (tool == toolSounder)
|
|
return 2;
|
|
if (tool == toolMatcher)
|
|
return 6;
|
|
return 4;
|
|
default:
|
|
Assert(0, "Invalid sty in MSND: Spr");
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Play this sound
|
|
|
|
***************************************************************************/
|
|
void MSND::Play(long objID, bool fLoop, bool fQueue, long vlm,
|
|
long spr, bool fActr, ulong dtsStart)
|
|
{
|
|
AssertThis(0);
|
|
|
|
long cactRepeat;
|
|
long sqn; // sound queue
|
|
long scl; // sound class
|
|
long sii;
|
|
|
|
static long _siiLastMidi;
|
|
static CTG _ctgLastMidi;
|
|
static CNO _cnoLastMidi;
|
|
|
|
if (_fInvalid)
|
|
return;
|
|
|
|
sqn = fActr ? SqnActr(objID) : SqnBkgd(objID);
|
|
cactRepeat = fLoop ? klwMax : 1;
|
|
scl = Scl(fLoop);
|
|
|
|
if (_sty == styMidi && _ctgSnd == _ctgLastMidi &&
|
|
_cnoSnd == _cnoLastMidi && vpsndm->FPlaying(_siiLastMidi))
|
|
{
|
|
// Don't restart midi if the same sound is still playing
|
|
return;
|
|
}
|
|
|
|
if (!fQueue || _fNoSound)
|
|
vpsndm->StopAll(sqn, sclNil);
|
|
|
|
if (!_fNoSound)
|
|
{
|
|
sii = vpsndm->SiiPlay(_prca, _ctgSnd, _cnoSnd, sqn, vlm, cactRepeat,
|
|
dtsStart, spr, scl);
|
|
}
|
|
|
|
if (_sty == styMidi)
|
|
{
|
|
_siiLastMidi = sii;
|
|
_ctgLastMidi = _ctgSnd;
|
|
_cnoLastMidi = _cnoSnd;
|
|
}
|
|
|
|
}
|
|
|
|
/***************************************************************************
|
|
|
|
New MSQ
|
|
|
|
***************************************************************************/
|
|
PMSQ MSQ::PmsqNew(void)
|
|
{
|
|
PMSQ pmsq;
|
|
if (pvNil == (pmsq = NewObj MSQ(khidMsq)))
|
|
return pvNil;
|
|
|
|
if (pvNil == (pmsq->_pglsqe = GL::PglNew(size(SQE), kcsqeGrow)))
|
|
{
|
|
ReleasePpo(&pmsq);
|
|
return pvNil;
|
|
}
|
|
|
|
if (pvNil == (pmsq->_pclok = NewObj CLOK(khidMsqClock)))
|
|
{
|
|
ReleasePpo(&pmsq);
|
|
return pvNil;
|
|
}
|
|
|
|
pmsq->_dtim = kdtim2Msq;
|
|
return pmsq;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Enqueue a sound in the MSQ. Overwrites sounds of the same type.
|
|
|
|
***************************************************************************/
|
|
bool MSQ::FEnqueue(PMSND pmsnd, long objID, bool fLoop, bool fQueue,
|
|
long vlm, long spr, bool fActr, ulong dtsStart, bool fLowPri)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(pmsnd, 0);
|
|
|
|
SQE sqe;
|
|
SQE *psqe;
|
|
long sqn;
|
|
long sqnT;
|
|
long isqe;
|
|
|
|
if (_dtim == kdtimOffMsq)
|
|
return fTrue;
|
|
|
|
sqn = fActr ? MSND::SqnActr(pmsnd->Sty(), objID) : MSND::SqnBkgd(pmsnd->Sty(), objID);
|
|
|
|
if (!fQueue)
|
|
for (isqe = 0; isqe < _pglsqe->IvMac(); isqe++)
|
|
{
|
|
psqe = (SQE *)_pglsqe->QvGet(isqe);
|
|
sqnT = psqe->fActr ?
|
|
MSND::SqnActr(psqe->pmsnd->Sty(), psqe->objID) : MSND::SqnBkgd(psqe->pmsnd->Sty(), psqe->objID);
|
|
if (sqnT == sqn)
|
|
{
|
|
if (fLowPri)
|
|
return fTrue; // Nothing to enqueue; same type already taken
|
|
// Hi priority. Get rid of lower priority sound.
|
|
ReleasePpo(&psqe->pmsnd);
|
|
_pglsqe->Delete(isqe);
|
|
break;
|
|
}
|
|
}
|
|
|
|
sqe.pmsnd = pmsnd;
|
|
pmsnd->AddRef();
|
|
sqe.objID = objID;
|
|
sqe.fLoop = fLoop;
|
|
sqe.fQueue = fQueue;
|
|
sqe.vlmMod = vlm;
|
|
sqe.spr = spr;
|
|
sqe.fActr = fActr;
|
|
sqe.dtsStart = dtsStart;
|
|
|
|
if (fLowPri)
|
|
{
|
|
if (!_pglsqe->FPush(&sqe))
|
|
{
|
|
ReleasePpo(&sqe.pmsnd);
|
|
return fFalse;
|
|
}
|
|
}
|
|
else if (!_pglsqe->FEnqueue(&sqe))
|
|
{
|
|
ReleasePpo(&sqe.pmsnd);
|
|
return fFalse;
|
|
}
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Dequeue and Play the MSQ sounds
|
|
If _dtim == kdtimOff, empty the queue
|
|
|
|
***************************************************************************/
|
|
void MSQ::PlayMsq(void)
|
|
{
|
|
AssertThis(0);
|
|
|
|
SQE sqe;
|
|
|
|
if (_pglsqe->IvMac() == 0)
|
|
return;
|
|
|
|
if (_dtim == kdtimOffMsq)
|
|
{
|
|
FlushMsq();
|
|
return;
|
|
}
|
|
|
|
if (0 < _pglsqe->IvMac())
|
|
{
|
|
vpsndm->BeginSynch();
|
|
|
|
while(_pglsqe->FDequeue(&sqe))
|
|
{
|
|
sqe.pmsnd->Play(sqe.objID, sqe.fLoop, sqe.fQueue, sqe.vlmMod,
|
|
sqe.spr, sqe.fActr, sqe.dtsStart);
|
|
ReleasePpo(&sqe.pmsnd);
|
|
}
|
|
|
|
vpsndm->EndSynch();
|
|
}
|
|
|
|
if (_dtim < kdtimLongMsq)
|
|
{
|
|
_pclok->Start(0);
|
|
if (!_pclok->FSetAlarm(_dtim, this))
|
|
{
|
|
StopAll();
|
|
return;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Flush Queue - without playing the sounds
|
|
|
|
***************************************************************************/
|
|
void MSQ::FlushMsq(void)
|
|
{
|
|
AssertThis(0);
|
|
SQE sqe;
|
|
|
|
while( _pglsqe->FDequeue(&sqe))
|
|
{
|
|
ReleasePpo(&sqe.pmsnd);
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
FCmdAlarm - Timeout has elapsed. Stop all sounds
|
|
|
|
***************************************************************************/
|
|
bool MSQ::FCmdAlarm(PCMD pcmd)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(pcmd);
|
|
|
|
_pclok->Stop();
|
|
StopAll();
|
|
return fTrue;
|
|
}
|
|
|
|
/***************************************************************************
|
|
|
|
Clean up and delete this movie sound queue
|
|
|
|
***************************************************************************/
|
|
MSQ::~MSQ(void)
|
|
{
|
|
AssertBaseThis(0);
|
|
StopAll();
|
|
FlushMsq();
|
|
ReleasePpo(&_pglsqe);
|
|
ReleasePpo(&_pclok);
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
/***************************************************************************
|
|
Assert the validity of the MSND.
|
|
***************************************************************************/
|
|
void MSND::AssertValid(ulong grf)
|
|
{
|
|
MSND_PAR::AssertValid(fobjAllocated);
|
|
AssertNilOrPo(_prca, 0);
|
|
AssertIn(_sty, 0, styLim);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Mark memory used by the MSND
|
|
***************************************************************************/
|
|
void MSND::MarkMem(void)
|
|
{
|
|
AssertThis(0);
|
|
MSND_PAR::MarkMem();
|
|
// Note: don't mark _prca, because _prca marks us, and would cause
|
|
// an infinite recursive loop.
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Assert the validity of the MSQ.
|
|
***************************************************************************/
|
|
void MSQ::AssertValid(ulong grf)
|
|
{
|
|
MSQ_PAR::AssertValid(fobjAllocated);
|
|
AssertPo(_pglsqe, 0);
|
|
AssertPo(_pclok, 0);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Mark memory used by the MSND
|
|
***************************************************************************/
|
|
void MSQ::MarkMem(void)
|
|
{
|
|
AssertThis(0);
|
|
MSQ_PAR::MarkMem();
|
|
MarkMemObj(_pglsqe);
|
|
MarkMemObj(_pclok);
|
|
}
|
|
|
|
#endif //DEBUG
|