/* Only implemented to the extent necessary to match data sections. */ #include "nw4r/snd/snd_AnimSound.h" #include "common.h" // nullptr #include "nw4r/snd/snd_BasicSound.h" #include "nw4r/snd/snd_SeqSound.h" #include "nw4r/snd/snd_SeqSoundHandle.h" #include "nw4r/snd/snd_SoundHandle.h" #include "nw4r/snd/snd_SoundStartable.h" #include namespace nw4r { namespace snd { namespace detail { AnimSoundImpl::AnimSoundImpl(SoundStartable &startable, AnimEventPlayer *player, int numSounds) : mStartable(startable), mpSounds(player), mNumSounds(numSounds), mCurrentFrame(0.0f), mIsActive(false), mNeedFrameReset(false), mNeedTriggerEventsAtCurrentFrame(false), mCycleCounter(0), mCallback(NULL), field_0x28(1.0f) {} AnimSoundImpl::~AnimSoundImpl() { Shutdown(); } bool AnimSoundImpl::Setup(const void *data) { Shutdown(); if (!mReader.Setup(data)) { return false; } mNeedFrameReset = true; mIsActive = true; mCurrentFrame = 0.0f; mCycleCounter = 0; return true; } void AnimSoundImpl::Shutdown() { if (mIsActive) { for (int i = 0; i < mNumSounds; i++) { if (mpSounds[i].GetHandle()->IsAttachedSound() && mpSounds[i].IsRunning()) { mpSounds[i].GetHandle()->Stop(0); } } mReader.Shutdown(); mIsActive = false; } } void AnimSoundImpl::ResetFrame(f32 f, int i) { mCurrentFrame = f; mCycleCounter = i; mNeedTriggerEventsAtCurrentFrame = true; mNeedFrameReset = false; } void AnimSoundImpl::UpdateFrame(f32 frame, PlayDirection dir) { for (int i = 0; i < mNumSounds; i++) { mpSounds[i].UpdateFrame(); } if (mNeedFrameReset) { if (dir == FORWARD) { ResetFrame(0.0f, 0); } else { u32 duration = mReader.GetAnimDuration(); ResetFrame(duration - 1.0f, 0); } mNeedFrameReset = false; } mVariableValue = (frame - mCurrentFrame) / field_0x28; if (dir == FORWARD) { UpdateForward(frame); } else { UpdateBackward(frame); } mCurrentFrame = frame; mNeedTriggerEventsAtCurrentFrame = false; } void AnimSoundImpl::UpdateForward(f32 frame) { s32 currFrameFloor = std::floor(mCurrentFrame); s32 targetFrame = std::floor(frame); if (mNeedTriggerEventsAtCurrentFrame && mCurrentFrame == currFrameFloor) { currFrameFloor -= 1; } if (currFrameFloor == targetFrame) { return; } s32 loopFrame = currFrameFloor + 1; while (true) { if (loopFrame == mReader.GetAnimDuration()) { loopFrame -= mReader.GetAnimDuration(); mCycleCounter++; } UpdateOneFrame(loopFrame, FORWARD); if (loopFrame == targetFrame) { break; } loopFrame++; } } void AnimSoundImpl::UpdateBackward(f32 frame) { s32 currFrameCeil = std::ceil(mCurrentFrame); s32 targetFrame = std::ceil(frame); if (currFrameCeil >= mReader.GetAnimDuration()) { currFrameCeil -= mReader.GetAnimDuration(); } if (targetFrame >= mReader.GetAnimDuration()) { targetFrame -= mReader.GetAnimDuration(); } if (mNeedTriggerEventsAtCurrentFrame && mCurrentFrame == currFrameCeil) { currFrameCeil += 1; } if (currFrameCeil == targetFrame) { return; } s32 loopFrame = currFrameCeil - 1; while (true) { if (loopFrame == -1) { loopFrame += mReader.GetAnimDuration(); mCycleCounter--; } UpdateOneFrame(loopFrame, BACKWARD); if (loopFrame == targetFrame) { break; } loopFrame--; } } void AnimSoundImpl::UpdateOneFrame(s32 duration, PlayDirection direction) { for (u32 eventIdx = 0; eventIdx < mReader.GetEventCount(); eventIdx++) { const AnimEventRef *ref = mReader.GetEventRef(eventIdx); if (ref == NULL) { continue; } if (IsPlayableLoopCount(ref->mInfo)) { if ((ref->mInfo.flags) & 1) { UpdateTrigger(ref, duration, direction); } else { if (direction == FORWARD) { UpdateForwardRange(ref, duration); } else { UpdateBackwardRange(ref, duration); } } } } } void AnimSoundImpl::UpdateTrigger(const AnimEventRef *ref, s32 frame, PlayDirection direction) { const AnimEvent *event = mReader.GetEvent(ref); if (event == NULL) { return; } if (ref->mInfo.flags & 4) { return; } if (!event->MatchesDirection(direction)) { return; } if ((ref->mInfo.flags & 2)) { if (ref->mInfo.field_0x00 == frame) { StopEvent(event); if (mCallback != NULL) { (mCallback)(0, frame, event->GetSoundLabel(), event->field_0x1C, mUserData); } StartEvent(event, true); } } else { if (ref->mInfo.field_0x00 == frame) { StopEvent(event); if (mCallback != NULL) { (mCallback)(0, frame, event->GetSoundLabel(), event->field_0x1C, mUserData); } StartEvent(event, true); } s32 u; if (direction == FORWARD) { u = ref->mInfo.field_0x04; } else { u = ut::Max(ref->mInfo.field_0x00 * 2 - ref->mInfo.field_0x04, 0); } if (u == frame) { if (mCallback != NULL) { (mCallback)(1, frame, event->GetSoundLabel(), event->field_0x1C, mUserData); } StopEvent(event); } } } void AnimSoundImpl::UpdateForwardRange(const AnimEventRef *ref, s32 frame) { const AnimEvent *event = mReader.GetEvent(ref); if (event == NULL) { return; } if (ref->mInfo.flags & 4 && ref->mInfo.flags & 2) { HoldEvent(event, true); return; } if (ref->mInfo.flags & 4) { if (ref->mInfo.field_0x04 == frame && mCallback != NULL) { (mCallback)(3, frame, event->GetSoundLabel(), event->field_0x1C, mUserData); } if (mCycleCounter < ref->mInfo.field_0x09) { HoldEvent(event, true); } else if (mCycleCounter == ref->mInfo.field_0x09) { if (frame < ref->mInfo.field_0x04) { HoldEvent(event, true); } else { StopEvent(event); } } } else if ((ref->mInfo.flags & 2)) { if (ref->mInfo.field_0x00 == frame && mCallback != NULL) { (mCallback)(2, frame, event->GetSoundLabel(), event->field_0x1C, mUserData); } if (mCycleCounter > ref->mInfo.field_0x09) { HoldEvent(event, true); } else if (mCycleCounter == ref->mInfo.field_0x09 && ref->mInfo.field_0x00 <= frame) { HoldEvent(event, true); } } else { if (ref->mInfo.field_0x00 == frame && mCallback != NULL) { (mCallback)(2, frame, event->GetSoundLabel(), event->field_0x1C, mUserData); } if (ref->mInfo.field_0x04 == frame && mCallback != NULL) { (mCallback)(3, frame, event->GetSoundLabel(), event->field_0x1C, mUserData); } if (ref->mInfo.field_0x00 <= frame && frame < ref->mInfo.field_0x04) { HoldEvent(event, true); } else { StopEvent(event); } } } void AnimSoundImpl::UpdateBackwardRange(const AnimEventRef *ref, s32 frame) { const AnimEvent *event = mReader.GetEvent(ref); if (event == NULL) { return; } if (ref->mInfo.flags & 4 && ref->mInfo.flags & 2) { HoldEvent(event, true); return; } if (ref->mInfo.flags & 4) { if (ref->mInfo.field_0x04 == frame && mCallback != NULL) { (mCallback)(2, frame, event->GetSoundLabel(), event->field_0x1C, mUserData); } if (mCycleCounter < ref->mInfo.field_0x09) { HoldEvent(event, true); } else if (mCycleCounter == ref->mInfo.field_0x09 && frame <= ref->mInfo.field_0x04) { HoldEvent(event, true); } } else if ((ref->mInfo.flags & 2)) { if (ref->mInfo.field_0x00 == frame && mCallback != NULL) { (mCallback)(3, frame, event->GetSoundLabel(), event->field_0x1C, mUserData); } if (mCycleCounter > ref->mInfo.field_0x09) { HoldEvent(event, true); } else if (mCycleCounter == ref->mInfo.field_0x09) { if (ref->mInfo.field_0x00 < frame) { HoldEvent(event, true); } else { StopEvent(event); } } } else { if (ref->mInfo.field_0x00 == frame && mCallback != NULL) { (mCallback)(3, frame, event->GetSoundLabel(), event->field_0x1C, mUserData); } if (ref->mInfo.field_0x04 == frame && mCallback != NULL) { (mCallback)(2, frame, event->GetSoundLabel(), event->field_0x1C, mUserData); } if (ref->mInfo.field_0x00 < frame && frame <= ref->mInfo.field_0x04) { HoldEvent(event, true); } else { StopEvent(event); } } } void AnimSoundImpl::StartEvent(const AnimEvent *event, bool b) { int leastPriority = 0x80; int leastPriorityIdx = -1; int i = 0; for (; i < mNumSounds; i++) { if (!mpSounds[i].IsAttachedSound()) { break; } int priority = mpSounds[i].GetPriority(); if (leastPriority > priority) { leastPriorityIdx = i; leastPriority = priority; } } if (i == mNumSounds) { mpSounds[leastPriorityIdx].ForceStop(); i = leastPriorityIdx; } mpSounds[i].StartEvent(event, &mStartable, b); u32 varNo = 0; if (event->GetVarNo(&varNo)) { mpSounds[i].SetVariable(event, varNo, mVariableValue); } } void AnimSoundImpl::HoldEvent(const AnimEvent *event, bool b) { int leastPriority = 0x80; int leastPriorityIdx = -1; int i = 0; for (; i < mNumSounds; i++) { if (mpSounds[i].IsCurrentEvent(event)) { return; } if (!mpSounds[i].IsAttachedSound()) { leastPriorityIdx = i; leastPriority = -1; } else { int priority = mpSounds[i].GetPriority(); if (leastPriority > priority) { leastPriorityIdx = i; leastPriority = priority; } } } if (i == mNumSounds) { mpSounds[leastPriorityIdx].ForceStop(); i = leastPriorityIdx; } mpSounds[i].HoldEvent(event, &mStartable, b); u32 varNo = 0; if (event->GetVarNo(&varNo)) { mpSounds[i].SetVariable(event, varNo, mVariableValue); } } void AnimSoundImpl::StopEvent(const AnimEvent *event) { for (int i = 0; i < mNumSounds; i++) { mpSounds[i].StopEvent(event); } } bool AnimSoundImpl::IsPlayableLoopCount(const nw4r::snd::detail::AnimEventFrameInfo &ref) { if (!(ref.flags & 1) && (ref.flags & 4 || ref.flags & 2)) { return true; } int i1 = ut::Max((int)ref.field_0x09, 0); int i2 = ut::Abs(mCycleCounter); if (ref.field_0x0A == 0) { if (i2 < i1) { return false; } } else { if (i2 < i1) { return false; } if ((i2 - i1) % ref.field_0x0A != 0) { return false; } } return true; } void AnimEventPlayer::SetVolumePitch(const AnimEvent *event, bool b) { if (event->volume < 128) { SetVolume(event->volume / 128.0f); } if (event->pitch != 1.0f) { SetPitch(event->pitch); } mpEvent = event; if (b) { b = !event->ShouldPreventStart(); } mIsRunning = b; } void AnimEventPlayer::SetVariable(const AnimEvent *event, u32 varNo, f32 f) { if (varNo < 16) { if (GetHandle()->IsAttachedSound()) { SeqSoundHandle handle(GetHandle()); int iVal = f * 100.0f; handle.WriteVariable(varNo, ut::Clamp(iVal, -0x8000, 0x7FFF)); } } else if (varNo < 32) { int iVal = f * 100.0f; SeqSound::WriteGlobalVariable(varNo - 16, ut::Clamp(iVal, -0x8000, 0x7FFF)); } } AnimEventPlayer::AnimEventPlayer() : mpEvent(NULL), mIsRunning(false) {} AnimEventPlayer::~AnimEventPlayer() { if (mHandle.IsAttachedSound() && IsRunning()) { mHandle.Stop(0); } } void AnimEventPlayer::StartEvent(const AnimEvent *event, SoundStartable *startable, bool b) { if (event->soundId == 0xFFFFFFFF) { if (!startable->StartSound(GetHandle(), event->GetSoundLabel())) { return; } } else { if (!startable->StartSound(GetHandle(), event->soundId)) { return; } } SetVolumePitch(event, b); } void AnimEventPlayer::HoldEvent(const AnimEvent *event, SoundStartable *startable, bool b) { if (event->soundId == 0xFFFFFFFF) { if (!startable->HoldSound(GetHandle(), event->GetSoundLabel())) { return; } } else { if (!startable->HoldSound(GetHandle(), event->soundId)) { return; } } GetHandle()->detail_GetAttachedSound()->SetAutoStopCounter(0); SetVolumePitch(event, b); } void AnimEventPlayer::ForceStop() { if (IsAttachedSound()) { mHandle.detail_GetAttachedSound()->Stop(0); } mpEvent = 0; } void AnimEventPlayer::UpdateFrame() { if (!mHandle.IsAttachedSound()) { mpEvent = 0; } } } // namespace detail } // namespace snd } // namespace nw4r