Microsoft-3D-Movie-Maker/SRC/ENGINE/ACTRSND.CPP

758 lines
16 KiB
C++

/* Copyright (c) Microsoft Corporation.
Licensed under the MIT License. */
/***************************************************************************
Actor Sound.
Primary Author: ******
Status: Reviewed
***************************************************************************/
#include "soc.h"
ASSERTNAME
/***************************************************************************
Add a sound event to the actor event list, and create an undo object
***************************************************************************/
bool ACTR::FSetSnd(PTAG ptag, bool fLoop, bool fQueue, bool fMotionMatch, long vlm, long sty)
{
AssertThis(0);
AssertVarMem(ptag);
AssertIn(sty, 0, styLim);
PACTR pactrDup;
if (!FDup(&pactrDup))
{
return fFalse;
}
if (!FSetSndCore(ptag, fLoop, fQueue, fMotionMatch, vlm, sty))
{
Restore(pactrDup);
ReleasePpo(&pactrDup);
return fFalse;
}
if (!FCreateUndo(pactrDup, fTrue))
{
Restore(pactrDup);
ReleasePpo(&pactrDup);
return fFalse;
}
ReleasePpo(&pactrDup);
return fTrue;
}
/***************************************************************************
Add a sound event to the actor event list
For user sounds, the chid must also be fetched for the argument *ptag
and stored with the event, since for user sounds, the cno can change
(eg on scene import)
Note: ptag is current because the sound browser has just completed building
a current tag.
***************************************************************************/
bool ACTR::FSetSndCore(PTAG ptag, bool fLoop, bool fQueue, bool fMotionMatch, long vlm, long sty)
{
AssertThis(0);
AssertVarMem(ptag);
AssertIn(sty, 0, styLim);
PMSND pmsnd = pvNil;
AEVSND aevsnd;
long ccel;
long iaev;
AEV *paev;
long sqn;
long cbVar;
// Verify sound before including in the event list
pmsnd = (PMSND)vptagm->PbacoFetch(ptag, MSND::FReadMsnd);
if (pvNil == pmsnd)
goto LFail;
if (pmsnd->Sty() == styMidi)
{
// There are no midi actor sounds in the product
PushErc(ercSocNoActrMidi);
goto LFail;
}
// Verify space up front
cbVar = kcbVarStep + kcbVarSnd;
if (!_pggaev->FEnsureSpace(2, cbVar, fgrpNil))
return fFalse;
aevsnd.fLoop = fLoop;
aevsnd.fQueue = fQueue;
aevsnd.vlm = (vlm == vlmNil) ? pmsnd->Vlm() : vlm;
aevsnd.tag = *ptag;
aevsnd.fNoSound = pmsnd->FNoSound();
aevsnd.sty = (sty == styNil) ? pmsnd->Sty() : sty;
sty = aevsnd.sty;
vlm = aevsnd.vlm;
if (ptag->sid != ksidUseCrf)
TrashVar(&aevsnd.chid);
else
{
if (!_pscen->Pmvie()->FChidFromUserSndCno(aevsnd.tag.cno, &aevsnd.chid))
goto LFail;
}
if (!fMotionMatch)
aevsnd.celn = smmNil; // Not a motion match sound
else
{
if (!_ptmpl->FGetCcelActn(_anidCur, &ccel))
goto LFail;
aevsnd.celn = _celnCur % ccel;
}
if (!_FAddDoAev(aetSnd, kcbVarSnd, &aevsnd))
goto LFail;
ReleasePpo(&pmsnd);
// Remove "no sounds" of this sqn between here and the next
// sound of this sqn
sqn = MSND::SqnActr(sty, _arid);
for (iaev = _iaevCur; iaev < _pggaev->IvMac(); iaev++)
{
AEVSND aevsndT;
paev = (AEV *)_pggaev->QvFixedGet(iaev);
if (aetAdd == paev->aet)
break;
if (aetSnd != paev->aet)
continue;
_pggaev->Get(iaev, &aevsndT);
if (MSND::SqnActr(aevsndT.sty, _arid) != sqn)
continue;
// Quit when reach real sound of same sqn
if (!aevsndT.fNoSound)
break;
_RemoveAev(iaev);
iaev--;
}
// Enqueue the motion match sounds in the Msq
// Allow the edit even if all sounds do not play
if (!(_pscen->GrfScen() & fscenSounds))
_FEnqueueSmmInMsq(); //Ignore errors : other sounds involved
Pscen()->Pmvie()->Pmcc()->SetSndFrame(fTrue);
return fTrue;
LFail:
ReleasePpo(&pmsnd);
return fFalse;
}
/***************************************************************************
Enqueue the sound at event iaev.
Enter motion match sounds in the smm gl.
Enter non-motion match sounds in the Msq
***************************************************************************/
bool ACTR::_FEnqueueSnd(long iaev)
{
AssertThis(0);
AssertIn(iaev, 0, _pggaev->IvMac());
AEVSND aevsnd;
PMSND pmsnd = pvNil;
long tool;
_pggaev->Get(iaev, &aevsnd);
//
// Motion match sounds are not enqueued when event iaev is seen
// Otherwise the sound would be played twice
//
if (aevsnd.celn != smmNil)
{
// Insert Smm for this cel in the gl
return _FInsertSmm(iaev);
}
if (aevsnd.fLoop)
tool = toolLooper;
else
tool = toolSounder;
// If the scene was imported, the sound will need to be resolved
if (aevsnd.tag.sid == ksidUseCrf)
{
if (!_pscen->Pmvie()->FResolveSndTag(&aevsnd.tag, aevsnd.chid))
{
Bug("Actrsnd: Expected to resolve snd tag");
goto LFail;
}
_pggaev->Put(iaev, &aevsnd); // Update event
}
pmsnd = (PMSND)vptagm->PbacoFetch(&aevsnd.tag, MSND::FReadMsnd);
if (pvNil == pmsnd)
goto LFail;
if (!_pscen->Pmvie()->Pmsq()->FEnqueue(pmsnd, _arid, aevsnd.fLoop, aevsnd.fQueue,
aevsnd.vlm, pmsnd->Spr(tool), fTrue))
{
goto LFail;
}
ReleasePpo(&pmsnd);
return fTrue;
LFail:
ReleasePpo(&pmsnd);
return fFalse;
}
/***************************************************************************
Play the motion match sounds appropriate to the current cel
***************************************************************************/
bool ACTR::_FEnqueueSmmInMsq(void)
{
AssertThis(0);
long celn;
long ccel;
long ismm;
SMM *psmm;
bool fSuccess = fTrue;
PMSND pmsnd = pvNil;
if (!_ptmpl->FGetCcelActn(_anidCur, &ccel))
return fFalse;
celn = _celnCur % ccel;
for (ismm = 0; ismm < _pglsmm->IvMac(); ismm++)
{
psmm = (SMM *)(_pglsmm->QvGet(ismm));
Assert(psmm->aevsnd.celn != smmNil, "Logic error in smm");
if (psmm->aevsnd.celn != celn)
{
continue;
}
if (psmm->aevsnd.tag.sid == ksidUseCrf)
{
if (!_pscen->Pmvie()->FResolveSndTag(&psmm->aevsnd.tag, psmm->aevsnd.chid))
{
Bug("Actrsnd: Expected to resolve snd tag");
goto LFail;
}
_pglsmm->Put(ismm, psmm); // Update event
}
pmsnd = (PMSND)vptagm->PbacoFetch(&psmm->aevsnd.tag, MSND::FReadMsnd);
if (pvNil == pmsnd)
{
fSuccess = fFalse;
continue;
}
// Motion match sounds need to be entered at low priority as sounds of the
// same type take precedence over them
if (!_pscen->Pmvie()->Pmsq()->FEnqueue(pmsnd, _arid, psmm->aevsnd.fLoop,
psmm->aevsnd.fQueue, psmm->aevsnd.vlm, pmsnd->Spr(toolMatcher), fTrue, 0, fTrue))
{
// Continuing would result in additional error
goto LFail;
}
ReleasePpo(&pmsnd);
}
return fSuccess;
LFail:
ReleasePpo(&pmsnd);
return fFalse;
}
/***************************************************************************
Insert a motion match sound in the smm queue
***************************************************************************/
bool ACTR::_FInsertSmm(long iaev)
{
AssertThis(0);
AssertIn(iaev, 0, _pggaev->IvMac());
long ismm;
SMM smm;
SMM *psmm;
AEV *paev;
AEVSND *paevsnd;
paev = (AEV *)_pggaev->QvFixedGet(iaev);
paevsnd = (AEVSND *)_pggaev->QvGet(iaev);
smm.aev = *paev;
smm.aevsnd = *paevsnd;
for (ismm = 0; ismm < _pglsmm->IvMac(); ismm++)
{
psmm = (SMM *)_pglsmm->QvGet(ismm);
if (psmm->aevsnd.celn != paevsnd->celn)
continue;
if (psmm->aevsnd.sty != paevsnd->sty)
continue;
// Replace the current smm with the new
_pglsmm->Put(ismm, &smm);
return fTrue;
}
// No sound for this cel was found
// Add a new mm sound
return _pglsmm->FAdd(&smm);
}
/***************************************************************************
Delete old motion sounds in event list
***************************************************************************/
bool ACTR::_FRemoveAevMm(long anid)
{
AssertThis(0);
long iaev;
AEV *paev;
AEVSND aevsnd;
AEVACTN aevactn;
// Remove motion match sounds from the previous action
for (iaev = _iaevCur; iaev < _pggaev->IvMac(); iaev++)
{
paev = (AEV *)_pggaev->QvFixedGet(iaev);
if (aetAdd == paev->aet)
break;
if (aetActn == paev->aet)
{
_pggaev->Get(iaev, &aevactn);
if (aevactn.anid != anid)
break;
continue;
}
if (aetSnd != paev->aet)
continue;
// Test if sound is motion match
_pggaev->Get(iaev, &aevsnd);
if (smmNil == aevsnd.celn)
continue;
_RemoveAev(iaev);
iaev--;
}
return fTrue;
}
/***************************************************************************
Insert default motion match sounds in event list
***************************************************************************/
bool ACTR::_FAddAevDefMm(long anid)
{
AssertThis(0);
TAG tag;
PMSND pmsnd;
long iceln;
long ccel;
long vlm;
long sty;
bool fSoundExists;
// Insert new motion match sounds
if (!_ptmpl->FGetCcelActn(anid, &ccel))
return fFalse;
for (iceln = 0; iceln < ccel; iceln++)
{
if (!_ptmpl->FGetSndActnCel(anid, iceln, &fSoundExists, &tag))
continue; //Ignore failure
if (!fSoundExists)
continue;
pmsnd = (PMSND)vptagm->PbacoFetch(&tag, MSND::FReadMsnd);
if (pvNil == pmsnd)
continue; //Ignore failure
vlm = pmsnd->Vlm();
sty = pmsnd->Sty();
if (!FSetSndCore(&tag, fFalse, vlm, fFalse, sty, fTrue))
{
goto LFail; // Continuing pointless
}
ReleasePpo(&pmsnd);
}
return fTrue;
LFail:
ReleasePpo(&pmsnd);
return fFalse;
}
/***************************************************************************
Set the Volume for sounds in the current frame of the specified sty
***************************************************************************/
bool ACTR::FSetVlmSnd(long sty, bool fMotionMatch, long vlm)
{
AssertThis(0);
AssertIn(sty, 0, styLim);
AssertIn(vlm, 0, kvlmFull + 1);
AEVSND aevsnd;
bool fActnThisFrame = fFalse;
long celn;
long ccel;
long iaev;
long ismm;
AEV aev;
SMM smm;
long nfrmMM = ivNil;
if (!_ptmpl->FGetCcelActn(_anidCur, &ccel))
return fFalse;
celn = _celnCur % ccel;
// Set the volume for any events in the actor list for this frame
for (iaev = _iaevFrmMin; iaev < _iaevCur; iaev++)
{
_pggaev->GetFixed(iaev, &aev);
if (aev.aet == aetActn)
fActnThisFrame = fTrue;
if (aev.aet != aetSnd)
continue;
_pggaev->Get(iaev, &aevsnd);
if (FPure(aevsnd.celn != smmNil) != fMotionMatch)
continue;
if (aevsnd.sty != sty)
continue;
aevsnd.vlm = vlm;
_pggaev->Put(iaev, &aevsnd);
}
// Set the volume for any earlier motion match sounds
// which land in this cel
if (fMotionMatch && !fActnThisFrame)
{
for (iaev = _iaevFrmMin - 1; iaev > 0; iaev--)
{
_pggaev->GetFixed(iaev, &aev);
// Quit when the action changes
if (aev.aet == aetActn || (nfrmMM != ivNil && nfrmMM > aev.nfrm))
break;
if (aev.aet != aetSnd)
continue;
_pggaev->Get(iaev, &aevsnd);
if (aevsnd.sty != sty)
continue;
// Only adjust sounds that match this cel
if (aevsnd.celn != celn)
continue;
aevsnd.vlm = vlm;
_pggaev->Put(iaev, &aevsnd);
nfrmMM = aev.nfrm;
// There are no queued motion match sounds by spec
break;
}
}
if (fMotionMatch)
{
// Adjust the volume in the smm also
for (ismm = 0; ismm < _pglsmm->IvMac(); ismm++)
{
_pglsmm->Get(ismm, &smm);
Assert(smm.aevsnd.celn != smmNil, "Logic error in smm");
if (smm.aevsnd.celn != celn)
continue;
if (smm.aevsnd.sty != sty)
continue;
// Set the volume in the smm
smm.aevsnd.vlm = vlm;
_pglsmm->Put(ismm, &smm);
}
}
_pscen->MarkDirty();
return fTrue;
}
/***************************************************************************
Query actor for current frame sounds
***************************************************************************/
bool ACTR::FQuerySnd(long sty, bool fMotionMatch, PGL *pglTagSnd, long *pvlm, bool *pfLoop)
{
AssertThis(0);
AssertIn(sty, 0, styLim);
AssertVarMem(pvlm);
AssertVarMem(pfLoop);
PGL pgltag = pvNil;
AEVSND aevsnd;
long ccel;
long celn;
AEV aev;
long iaev;
long ismm;
SMM smm;
if (pvNil == (pgltag = GL::PglNew(size(TAG), kctagSndGrow)))
return fFalse;
pgltag->SetMinGrow(kctagSndGrow);
if (!fMotionMatch)
{
// Non-motion match sounds
for (iaev = _iaevFrmMin; iaev < _iaevCur; iaev++)
{
_pggaev->GetFixed(iaev, &aev);
if (aev.aet != aetSnd)
continue;
_pggaev->Get(iaev, &aevsnd);
if (aevsnd.sty != sty)
continue;
// Handle motion match sounds later
if (aevsnd.celn != smmNil)
continue;
if (aevsnd.tag.sid == ksidUseCrf)
{
if (!_pscen->Pmvie()->FResolveSndTag(&aevsnd.tag, aevsnd.chid))
goto LFail;
}
if (!pgltag->FAdd(&aevsnd.tag))
goto LFail;
*pvlm = aevsnd.vlm;
*pfLoop = aevsnd.fLoop;
}
}
if (fMotionMatch)
{
if (!_ptmpl->FGetCcelActn(_anidCur, &ccel))
goto LFail;
celn = _celnCur % ccel;
// Motion match sounds
for (ismm = 0; ismm < _pglsmm->IvMac(); ismm++)
{
_pglsmm->Get(ismm, &smm);
if (smm.aevsnd.sty != sty)
continue;
if (smm.aevsnd.celn != celn)
continue;
if (smm.aevsnd.tag.sid == ksidUseCrf)
{
if (!_pscen->Pmvie()->FResolveSndTag(&smm.aevsnd.tag, smm.aevsnd.chid))
goto LFail;
}
if (!pgltag->FAdd(&smm.aevsnd.tag))
goto LFail;
*pvlm = smm.aevsnd.vlm;
*pfLoop = smm.aevsnd.fLoop;
break;
}
}
if (pgltag->IvMac() == 0)
{
ReleasePpo(&pgltag);
*pglTagSnd = pvNil;
return fTrue;
}
*pglTagSnd = pgltag;
return fTrue;
LFail:
ReleasePpo(&pgltag);
return fFalse;
}
/***************************************************************************
Delete a sound in this frame
***************************************************************************/
bool ACTR::FDeleteSndCore(long sty, bool fMotionMatch)
{
AssertThis(0);
AssertIn(sty, 0, styLim);
AEVSND aevsnd;
long iaevFirst;
long iaev;
long ismm;
long ccel;
long celn;
AEV aev;
SMM smm;
long nfrmMM = ivNil; // For motion match, this may be an earlier frame
// First the actor event list
if (!_ptmpl->FGetCcelActn(_anidCur, &ccel))
return fFalse;
celn = _celnCur % ccel;
iaevFirst = (fMotionMatch) ? _iaevActnCur : _iaevFrmMin;
for (iaev = _iaevCur - 1; iaev >= iaevFirst; iaev--)
{
_pggaev->GetFixed(iaev, &aev);
if (fMotionMatch && nfrmMM != ivNil && nfrmMM > aev.nfrm)
break;
if (aev.aet != aetSnd)
continue;
_pggaev->Get(iaev, &aevsnd);
if (aevsnd.sty != sty)
continue;
if ((aevsnd.celn != smmNil) != fMotionMatch)
continue;
// Only delete the sound that match this cel
if (fMotionMatch && aevsnd.celn != celn)
continue;
// Delete this sound & continue to remove any
// other sounds which are possibly chained
_RemoveAev(iaev);
nfrmMM = aev.nfrm;
}
if (fMotionMatch)
{
// Also the motion match sound list : smm
if (!_ptmpl->FGetCcelActn(_anidCur, &ccel))
return fFalse;
celn = _celnCur % ccel;
// Motion match sounds
for (ismm = 0; ismm < _pglsmm->IvMac(); ismm++)
{
_pglsmm->Get(ismm, &smm);
if (smm.aevsnd.sty != sty)
continue;
if (smm.aevsnd.celn != celn)
continue;
_pglsmm->Delete(ismm);
ismm--;
}
}
_pscen->MarkDirty();
return fTrue;
}
/******************************************************************************
FSoundInFrm
Enumerates all actor events for current frame looking for a sound
event.
Returns: fTrue if a sound event was found, fFalse otherwise
************************************************************ PETED ***********/
bool ACTR::FSoundInFrm(void)
{
AssertThis(0);
AssertPo(Pscen(), 0);
/* Can't provide useful info if we're not at the current scene frame */
if (Pscen()->Nfrm() != _nfrmCur)
return fFalse;
for (long iaev = _iaevFrmMin; iaev < _iaevCur; iaev++)
{
AEV aev;
_pggaev->GetFixed(iaev, &aev);
if (aev.aet == aetSnd)
return fTrue;
}
return fFalse;
}
/******************************************************************************
Resolve all sound tags
******************************************************************************/
bool ACTR::FResolveAllSndTags(CNO cnoScen)
{
AssertThis(0);
long iaev;
AEV *paev;
AEVSND aevsnd;
bool fSuccess = fTrue;
for (iaev = 0; iaev < _pggaev->IvMac(); iaev++)
{
paev = (AEV *)_pggaev->QvFixedGet(iaev);
if (paev->aet != aetSnd)
continue;
_pggaev->Get(iaev, &aevsnd);
if (aevsnd.tag.sid == ksidUseCrf)
{
if (!_pscen->Pmvie()->FResolveSndTag(&aevsnd.tag, aevsnd.chid, cnoScen))
{
fSuccess = fFalse;
continue;
}
_pggaev->Put(iaev, &aevsnd);
}
}
return fSuccess;
}