/*---------------------------------------------------------------------------* Project: NintendoWare File: snd_AnimSoundImpl.cpp Copyright (C)2009-2011 Nintendo/HAL Laboratory, Inc. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo and/or its licensed developers and are protected by national and international copyright laws. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. The content herein is highly confidential and should be handled accordingly. $Revision: $ *---------------------------------------------------------------------------*/ #include "precompiled.h" #include #include #include #include namespace nw { namespace snd { namespace internal { AnimSoundImpl::AnimSoundImpl( SoundStartable& starter, AnimEventPlayer* eventPlayers, int eventPlayerCount ) : m_Starter( starter ), m_Reader(), m_pEventPlayers( eventPlayers ), m_EventCallback( NULL ), m_EventCallbackArg( NULL ), m_CurrentFrame( 0.0f ), m_EventPlayerCount( eventPlayerCount ), m_IsActive( false ), m_IsInitFrame( false ), m_IsReset( false ), m_LoopCounter( 0 ), m_BaseStep( 1.0f ), m_CurrentSpeed( 1.0f ) { } AnimSoundImpl::~AnimSoundImpl() { Finalize(); } bool AnimSoundImpl::Initialize( const void* bcasdFile ) { if ( m_IsActive ) { Finalize(); } NW_NULL_ASSERT( bcasdFile ); bool result = m_Reader.Initialize( bcasdFile ); if ( ! result ) { return false; } m_FrameSize = m_Reader.GetFrameSize(); NW_ASSERT( m_FrameSize != 0 ); m_IsInitFrame = true; m_IsActive = true; m_CurrentFrame = 0.0f; m_LoopCounter = 0; return true; } void AnimSoundImpl::Finalize() { if ( ! m_IsActive ) { return; } for ( int i = 0; i < m_EventPlayerCount; i++ ) { m_pEventPlayers[i].Finalize(); } m_Reader.Finalize(); m_IsActive = false; } bool AnimSoundImpl::ConvertSoundId( const SoundArchive& arc ) { if ( ! m_IsActive ) { return false; } u32 eventCount = m_Reader.GetEventCount(); for ( u32 i = 0; i < eventCount; i++ ) // TODO: Foreach 的なメソッドに置き換えたい { const AnimSoundFile::AnimEvent* event = m_Reader.GetAnimEvent( i ); if ( ! event ) { return false; } const AnimSoundFile::EventInfo* eventInfo = event->GetEventInfo(); if ( ! eventInfo ) { return false; } const char* soundLabel = eventInfo->GetSoundLabel(); if ( ! soundLabel ) { return false; } // ここだけ書き換える必要があるため、const を外す const_cast(eventInfo)->placeForSoundId = arc.GetItemId( soundLabel ); } return true; } void AnimSoundImpl::ResetFrame( f32 frame, int loopCounter ) { NW_MINMAXLT_ASSERT( frame, 0.0f, static_cast( m_FrameSize ) ); m_CurrentFrame = frame; m_LoopCounter = loopCounter; m_IsReset = true; m_IsInitFrame = false; } void AnimSoundImpl::UpdateFrame( f32 frame, PlayDirection direction ) { // 自動停止をチェック for ( int i = 0; i < m_EventPlayerCount; i++ ) { m_pEventPlayers[i].UpdateFrame(); } NW_MINMAXLT_ASSERT( frame, 0.0f, static_cast( m_FrameSize ) ); // 初期化直後の処理 if ( m_IsInitFrame ) { if ( direction == PLAY_DIRECTION_FORWARD ) { ResetFrame( 0.0f, 0 ); } else { ResetFrame( m_FrameSize - 1.0f, 0 ); } } // スピード更新 m_CurrentSpeed = ( frame - m_CurrentFrame ) / m_BaseStep; // 更新処理 if ( direction == PLAY_DIRECTION_FORWARD ) { UpdateForward( frame ); } else { UpdateBackward( frame ); } m_CurrentFrame = frame; m_IsReset = false; } void AnimSoundImpl::UpdateForward( float frame ) { s32 intBaseFrame = static_cast( std::floor( m_CurrentFrame ) ); s32 intTargetFrame = static_cast( std::floor( frame ) ); if ( m_IsReset ) { if ( m_CurrentFrame == static_cast( intBaseFrame ) ) { intBaseFrame -= 1; } } if ( intBaseFrame == intTargetFrame ) { return; } for ( s32 t = intBaseFrame + 1; ; t++ ) { // ループ判定 if ( t == m_FrameSize ) { t -= m_FrameSize; m_LoopCounter++; } // 現フレームのイベントを探して実行 UpdateOneFrame( t, PLAY_DIRECTION_FORWARD ); if ( t == intTargetFrame ) { break; } } } void AnimSoundImpl::UpdateBackward( float frame ) { s32 intBaseFrame = static_cast( std::ceil( m_CurrentFrame ) ); s32 intTargetFrame = static_cast( std::ceil( frame ) ); // TODO: 分かりやすい処理に書き換える if ( intBaseFrame >= m_FrameSize ) { intBaseFrame -= m_FrameSize; } if ( intTargetFrame >= m_FrameSize ) { intTargetFrame -= m_FrameSize; } if ( m_IsReset ) { if ( m_CurrentFrame == static_cast( intBaseFrame ) ) { intBaseFrame += 1; } } if ( intBaseFrame == intTargetFrame ) { return; } for ( s32 t = intBaseFrame - 1; ; t-- ) { // ループ判定 if ( t == -1 ) { t += m_FrameSize; m_LoopCounter--; } // 現フレームのイベントを探して実行 UpdateOneFrame( t, PLAY_DIRECTION_BACKWARD ); if ( t == intTargetFrame ) break; } } void AnimSoundImpl::UpdateOneFrame( s32 current, PlayDirection direction ) { // 現フレームのイベントを探して実行 u32 eventCount = m_Reader.GetEventCount(); for ( u32 i = 0; i < eventCount; i++ ) { // イベント取得 const AnimSoundFile::AnimEvent* event = m_Reader.GetAnimEvent( i ); if ( event == NULL ) { continue; } // ループカウンタ判定 if ( ! IsPlayableLoopCount( event->frameInfo ) ) continue; // イベント実行 if ( event->frameInfo.frameFlag & AnimSoundFile::FrameInfo::FRAME_FLAG_TRIGGER_EVENT ) { // トリガイベント UpdateTrigger( *event, current, direction ); } else { // レンジイベント UpdateRange( *event, current, direction ); } } } void AnimSoundImpl::UpdateTrigger( const AnimSoundFile::AnimEvent& event, s32 current, PlayDirection direction ) { if ( event.frameInfo.frameFlag & AnimSoundFile::FrameInfo::FRAME_FLAG_START_FRAME_INF ) { return; } const AnimSoundFile::EventInfo* eventInfo = event.GetEventInfo(); if ( eventInfo == NULL ) { return; } // 再生方向判定 bool isEnableForPlayDirection = false; if ( eventInfo->playDirection == AnimSoundFile::EventInfo::PLAY_DIRECTION_BOTH || ( direction == PLAY_DIRECTION_FORWARD && eventInfo->playDirection == AnimSoundFile::EventInfo::PLAY_DIRECTION_FORWARD ) || ( direction == PLAY_DIRECTION_BACKWARD && eventInfo->playDirection == AnimSoundFile::EventInfo::PLAY_DIRECTION_BACKWARD ) ) { isEnableForPlayDirection = true; } if ( ! isEnableForPlayDirection ) { return; } if ( event.frameInfo.frameFlag & AnimSoundFile::FrameInfo::FRAME_FLAG_END_FRAME_INF ) { // 終了フレームが無いワンショットのトリガイベント if ( event.frameInfo.startFrame == current ) { StopEvent( *eventInfo ); if ( m_EventCallback != NULL ) { m_EventCallback( EVENT_TYPE_TRIGGER_START, current, eventInfo->GetSoundLabel(), eventInfo->userParam, m_EventCallbackArg ); } StartEvent( *eventInfo, true ); } } else { // 終了フレームがあるトリガイベント // 開始判定 if ( event.frameInfo.startFrame == current ) { StopEvent( *eventInfo ); if ( m_EventCallback != NULL ) { m_EventCallback( EVENT_TYPE_TRIGGER_START, current, eventInfo->GetSoundLabel(), eventInfo->userParam, m_EventCallbackArg ); } StartEvent( *eventInfo, true ); } // 終了判定 long endFrame; if ( direction == PLAY_DIRECTION_FORWARD ) { endFrame = event.frameInfo.endFrame; } else // PLAY_BACKWARD { endFrame = event.frameInfo.startFrame - ( event.frameInfo.endFrame - event.frameInfo.startFrame ); endFrame = ut::Max( endFrame, 0L ); } if ( endFrame == current ) { if ( m_EventCallback != NULL ) { m_EventCallback( EVENT_TYPE_TRIGGER_STOP, current, eventInfo->GetSoundLabel(), eventInfo->userParam, m_EventCallbackArg ); } StopEvent( *eventInfo ); } } } void AnimSoundImpl::UpdateRange( const AnimSoundFile::AnimEvent& event, s32 current, PlayDirection direction ) { if ( direction == PLAY_DIRECTION_FORWARD ) { UpdateForwardRange( event, current ); } else // PLAY_BACKWARD { UpdateBackwardRange( event, current ); } } void AnimSoundImpl::UpdateForwardRange( const AnimSoundFile::AnimEvent& event, s32 current ) { const AnimSoundFile::EventInfo* eventInfo = event.GetEventInfo(); if ( eventInfo == NULL ) { return; } const AnimSoundFile::FrameInfo& frameInfo = event.frameInfo; if ( frameInfo.frameFlag & AnimSoundFile::FrameInfo::FRAME_FLAG_START_FRAME_INF ) { if ( frameInfo.frameFlag & AnimSoundFile::FrameInfo::FRAME_FLAG_END_FRAME_INF ) { // 開始フレームも終了フレームも無いレンジイベント HoldEvent( *eventInfo, true ); } else { // 開始フレームが無いレンジイベント if ( frameInfo.endFrame == current ) { if ( m_EventCallback != NULL ) { m_EventCallback( EVENT_TYPE_RANGE_STOP, current, eventInfo->GetSoundLabel(), eventInfo->userParam, m_EventCallbackArg ); } } if ( m_LoopCounter < frameInfo.loopOffset ) { HoldEvent( *eventInfo, true ); } else if ( m_LoopCounter == frameInfo.loopOffset ) { if ( current < frameInfo.endFrame ) { HoldEvent( *eventInfo, true ); } else { StopEvent( *eventInfo ); } } } } else if ( frameInfo.frameFlag & AnimSoundFile::FrameInfo::FRAME_FLAG_END_FRAME_INF ) { // 終了フレームが無いレンジイベント if ( frameInfo.startFrame == current ) { if ( m_EventCallback != NULL ) { m_EventCallback( EVENT_TYPE_RANGE_START, current, eventInfo->GetSoundLabel(), eventInfo->userParam, m_EventCallbackArg ); } } if ( m_LoopCounter > frameInfo.loopOffset ) { HoldEvent( *eventInfo, true ); } else if ( m_LoopCounter == frameInfo.loopOffset ) { if ( frameInfo.startFrame <= current ) { HoldEvent( *eventInfo, true ); } } } else { // 開始フレームも終了フレームもあるレンジイベント NW_ASSERT( frameInfo.startFrame < frameInfo.endFrame ); if ( frameInfo.startFrame == current ) { if ( m_EventCallback != NULL ) { m_EventCallback( EVENT_TYPE_RANGE_START, current, eventInfo->GetSoundLabel(), eventInfo->userParam, m_EventCallbackArg ); } } if ( frameInfo.endFrame == current ) { if ( m_EventCallback != NULL ) { m_EventCallback( EVENT_TYPE_RANGE_STOP, current, eventInfo->GetSoundLabel(), eventInfo->userParam, m_EventCallbackArg ); } } if ( ( frameInfo.startFrame <= current ) && ( current < frameInfo.endFrame ) ) { HoldEvent( *eventInfo, true ); } else { StopEvent( *eventInfo ); } } } void AnimSoundImpl::UpdateBackwardRange( const AnimSoundFile::AnimEvent& event, s32 current ) { const AnimSoundFile::EventInfo* eventInfo = event.GetEventInfo(); if ( eventInfo == NULL ) { return; } const AnimSoundFile::FrameInfo& frameInfo = event.frameInfo; if ( frameInfo.frameFlag & AnimSoundFile::FrameInfo::FRAME_FLAG_START_FRAME_INF ) { if ( frameInfo.frameFlag & AnimSoundFile::FrameInfo::FRAME_FLAG_END_FRAME_INF ) { // 開始フレームも終了フレームも無いレンジイベント HoldEvent( *eventInfo, true ); } else { // 開始フレームが無いレンジイベント if ( frameInfo.endFrame == current ) { if ( m_EventCallback != NULL ) { m_EventCallback( EVENT_TYPE_RANGE_START, current, eventInfo->GetSoundLabel(), eventInfo->userParam, m_EventCallbackArg ); } } if ( m_LoopCounter < frameInfo.loopOffset ) { HoldEvent( *eventInfo, true ); } else if ( m_LoopCounter == frameInfo.loopOffset ) { if ( current <= frameInfo.endFrame ) { HoldEvent( *eventInfo, true ); } } } } else if ( frameInfo.frameFlag & AnimSoundFile::FrameInfo::FRAME_FLAG_END_FRAME_INF ) { // 終了フレームが無いレンジイベント if ( frameInfo.startFrame == current ) { if ( m_EventCallback != NULL ) { m_EventCallback( EVENT_TYPE_RANGE_STOP, current, eventInfo->GetSoundLabel(), eventInfo->userParam, m_EventCallbackArg ); } } if ( m_LoopCounter > frameInfo.loopOffset ) { HoldEvent( *eventInfo, true ); } else if ( m_LoopCounter == frameInfo.loopOffset ) { if ( frameInfo.startFrame < current ) { HoldEvent( *eventInfo, true ); } else StopEvent( *eventInfo ); } } else { // 開始フレームも終了フレームもあるレンジイベント NW_ASSERT( frameInfo.startFrame < frameInfo.endFrame ); if ( frameInfo.startFrame == current ) { if ( m_EventCallback != NULL ) { m_EventCallback( EVENT_TYPE_RANGE_STOP, current, eventInfo->GetSoundLabel(), eventInfo->userParam, m_EventCallbackArg ); } } if ( frameInfo.endFrame == current ) { if ( m_EventCallback != NULL ) { m_EventCallback( EVENT_TYPE_RANGE_START, current, eventInfo->GetSoundLabel(), eventInfo->userParam, m_EventCallbackArg ); } } if ( ( frameInfo.startFrame < current ) && ( current <= frameInfo.endFrame ) ) { HoldEvent( *eventInfo, true ); } else { StopEvent( *eventInfo ); } } } void AnimSoundImpl::StartEvent( const AnimSoundFile::EventInfo& info, bool isStopWhenFinalize ) { // 空いているハンドルを探す // 全て埋まっている時は、一番プライオリティが低いサウンドを止める int lowestPriority = BasicSound::PRIORITY_MAX + 1; int lowestIndex = -1; int i; for ( i = 0; i < m_EventPlayerCount; i++ ) { if ( !m_pEventPlayers[i].IsAttachedSound() ) { break; } int priority = m_pEventPlayers[i].GetPlayingSoundPriority(); if ( lowestPriority > priority ) { lowestIndex = i; lowestPriority = priority; } } if ( i == m_EventPlayerCount ) { m_pEventPlayers[lowestIndex].ForceStop(); i = lowestIndex; } // 再生 m_pEventPlayers[i].StartEvent( info, m_Starter, isStopWhenFinalize ); WritePlaySpeedToSequenceVariable( i, info ); } void AnimSoundImpl::HoldEvent( const AnimSoundFile::EventInfo& info, bool isStopWhenFinalize ) { // イベントが再生中なら何もしない // 新規再生用に一番プライオリティが低いサウンドを探す int lowestPriority = BasicSound::PRIORITY_MAX + 1; int lowestIndex = -1; int i; for ( i = 0; i < m_EventPlayerCount; i++ ) { if ( m_pEventPlayers[i].IsPlaying( info ) ) { return; // 再生中なので何もしない } if ( !m_pEventPlayers[i].IsAttachedSound() ) { lowestIndex = i; lowestPriority = -1; } else { int priority = m_pEventPlayers[i].GetPlayingSoundPriority(); if ( lowestPriority > priority ) { lowestIndex = i; lowestPriority = priority; } } } // 再生中のイベントが無かったので新規に再生 if ( i == m_EventPlayerCount ) { m_pEventPlayers[lowestIndex].ForceStop(); i = lowestIndex; } // 再生 m_pEventPlayers[i].HoldEvent( info, m_Starter, isStopWhenFinalize ); WritePlaySpeedToSequenceVariable( i, info ); } void AnimSoundImpl::WritePlaySpeedToSequenceVariable( int eventPlayerNo, const AnimSoundFile::EventInfo& info ) { u8 sequenceVariableNo = 0; // とりあえず 0 で初期化 if ( info.GetSequenceVariable( &sequenceVariableNo ) ) { m_pEventPlayers[eventPlayerNo].WritePlaySpeedToSequenceVariable( sequenceVariableNo, m_CurrentSpeed ); } } void AnimSoundImpl::StopEvent( const AnimSoundFile::EventInfo& info ) { for ( int i=0; i( info.loopOffset ), 0 ); int loopCounter = ut::Abs( m_LoopCounter ); if ( info.loopInterval == 0 ) { if ( loopCounter < loopOffset ) { return false; } } else { if ( loopCounter < loopOffset ) { return false; } if ( ( loopCounter - loopOffset ) % info.loopInterval != 0 ) { return false; } } return true; } } // namespace nw::snd::internal } // namespace nw::snd } // namespace nw