1 /*---------------------------------------------------------------------------*
2   Project:  NintendoWare
3   File:     snd_AnimSoundImpl.cpp
4 
5   Copyright (C)2009-2011 Nintendo/HAL Laboratory, Inc.  All rights reserved.
6 
7   These coded instructions, statements, and computer programs contain proprietary
8   information of Nintendo and/or its licensed developers and are protected by
9   national and international copyright laws. They may not be disclosed to third
10   parties or copied or duplicated in any form, in whole or in part, without the
11   prior written consent of Nintendo.
12 
13   The content herein is highly confidential and should be handled accordingly.
14 
15   $Revision: $
16  *---------------------------------------------------------------------------*/
17 
18 #include "precompiled.h"
19 
20 #include <nw/snd/snd_AnimSoundImpl.h>
21 #include <nw/snd/snd_AnimEventPlayer.h>
22 #include <nw/snd/snd_BasicSound.h>
23 #include <nw/ut/ut_Inlines.h>
24 
25 namespace nw {
26 namespace snd {
27 namespace internal {
28 
AnimSoundImpl(SoundStartable & starter,AnimEventPlayer * eventPlayers,int eventPlayerCount)29 AnimSoundImpl::AnimSoundImpl(
30     SoundStartable& starter,
31     AnimEventPlayer* eventPlayers,
32     int eventPlayerCount )
33 : m_Starter( starter ),
34   m_Reader(),
35   m_pEventPlayers( eventPlayers ),
36   m_EventCallback( NULL ),
37   m_EventCallbackArg( NULL ),
38   m_CurrentFrame( 0.0f ),
39   m_EventPlayerCount( eventPlayerCount ),
40   m_IsActive( false ),
41   m_IsInitFrame( false ),
42   m_IsReset( false ),
43   m_LoopCounter( 0 ),
44   m_BaseStep( 1.0f ),
45   m_CurrentSpeed( 1.0f )
46 {
47 }
48 
~AnimSoundImpl()49 AnimSoundImpl::~AnimSoundImpl()
50 {
51     Finalize();
52 }
53 
Initialize(const void * bcasdFile)54 bool AnimSoundImpl::Initialize( const void* bcasdFile )
55 {
56     if ( m_IsActive )
57     {
58         Finalize();
59     }
60 
61     NW_NULL_ASSERT( bcasdFile );
62 
63     bool result = m_Reader.Initialize( bcasdFile );
64     if ( ! result )
65     {
66         return false;
67     }
68     m_FrameSize = m_Reader.GetFrameSize();
69     NW_ASSERT( m_FrameSize != 0 );
70 
71     m_IsInitFrame = true;
72     m_IsActive = true;
73 
74     m_CurrentFrame = 0.0f;
75     m_LoopCounter = 0;
76 
77     return true;
78 }
79 
Finalize()80 void AnimSoundImpl::Finalize()
81 {
82     if ( ! m_IsActive )
83     {
84         return;
85     }
86 
87     for ( int i = 0; i < m_EventPlayerCount; i++ )
88     {
89         m_pEventPlayers[i].Finalize();
90     }
91 
92     m_Reader.Finalize();
93     m_IsActive = false;
94 }
95 
ConvertSoundId(const SoundArchive & arc)96 bool AnimSoundImpl::ConvertSoundId( const SoundArchive& arc )
97 {
98     if ( ! m_IsActive )
99     {
100         return false;
101     }
102 
103     u32 eventCount = m_Reader.GetEventCount();
104     for ( u32 i = 0; i < eventCount; i++ )  // TODO: Foreach 的なメソッドに置き換えたい
105     {
106         const AnimSoundFile::AnimEvent* event = m_Reader.GetAnimEvent( i );
107         if ( ! event )
108         {
109             return false;
110         }
111 
112         const AnimSoundFile::EventInfo* eventInfo = event->GetEventInfo();
113         if ( ! eventInfo )
114         {
115             return false;
116         }
117 
118         const char* soundLabel = eventInfo->GetSoundLabel();
119         if ( ! soundLabel )
120         {
121             return false;
122         }
123 
124         // ここだけ書き換える必要があるため、const を外す
125         const_cast<AnimSoundFile::EventInfo*>(eventInfo)->placeForSoundId =
126             arc.GetItemId( soundLabel );
127     }
128 
129     return true;
130 }
131 
ResetFrame(f32 frame,int loopCounter)132 void AnimSoundImpl::ResetFrame( f32 frame, int loopCounter )
133 {
134     NW_MINMAXLT_ASSERT( frame, 0.0f, static_cast<f32>( m_FrameSize ) );
135 
136     m_CurrentFrame = frame;
137     m_LoopCounter = loopCounter;
138 
139     m_IsReset = true;
140     m_IsInitFrame = false;
141 }
142 
UpdateFrame(f32 frame,PlayDirection direction)143 void AnimSoundImpl::UpdateFrame( f32 frame, PlayDirection direction )
144 {
145     // 自動停止をチェック
146     for ( int i = 0; i < m_EventPlayerCount; i++ )
147     {
148         m_pEventPlayers[i].UpdateFrame();
149     }
150 
151     NW_MINMAXLT_ASSERT( frame, 0.0f, static_cast<f32>( m_FrameSize ) );
152 
153     // 初期化直後の処理
154     if ( m_IsInitFrame )
155     {
156         if ( direction == PLAY_DIRECTION_FORWARD )
157         {
158             ResetFrame( 0.0f, 0 );
159         }
160         else
161         {
162             ResetFrame( m_FrameSize - 1.0f, 0 );
163         }
164     }
165 
166     // スピード更新
167     m_CurrentSpeed = ( frame - m_CurrentFrame ) / m_BaseStep;
168 
169     // 更新処理
170     if ( direction == PLAY_DIRECTION_FORWARD )
171     {
172         UpdateForward( frame );
173     }
174     else
175     {
176         UpdateBackward( frame );
177     }
178 
179     m_CurrentFrame = frame;
180     m_IsReset = false;
181 }
182 
UpdateForward(float frame)183 void AnimSoundImpl::UpdateForward( float frame )
184 {
185     s32 intBaseFrame = static_cast<s32>( std::floor( m_CurrentFrame ) );
186     s32 intTargetFrame = static_cast<s32>( std::floor( frame ) );
187 
188     if ( m_IsReset )
189     {
190         if ( m_CurrentFrame == static_cast<f32>( intBaseFrame ) )
191         {
192             intBaseFrame -= 1;
193         }
194     }
195 
196     if ( intBaseFrame == intTargetFrame )
197     {
198         return;
199     }
200 
201     for ( s32 t = intBaseFrame + 1; ; t++ )
202     {
203         // ループ判定
204         if ( t == m_FrameSize )
205         {
206             t -= m_FrameSize;
207             m_LoopCounter++;
208         }
209 
210         // 現フレームのイベントを探して実行
211         UpdateOneFrame( t, PLAY_DIRECTION_FORWARD );
212 
213         if ( t == intTargetFrame )
214         {
215             break;
216         }
217     }
218 }
UpdateBackward(float frame)219 void AnimSoundImpl::UpdateBackward( float frame )
220 {
221     s32 intBaseFrame = static_cast<s32>( std::ceil( m_CurrentFrame ) );
222     s32 intTargetFrame = static_cast<s32>( std::ceil( frame ) );
223 
224     // TODO: 分かりやすい処理に書き換える
225     if ( intBaseFrame >= m_FrameSize )
226     {
227         intBaseFrame -= m_FrameSize;
228     }
229     if ( intTargetFrame >= m_FrameSize )
230     {
231         intTargetFrame -= m_FrameSize;
232     }
233 
234     if ( m_IsReset )
235     {
236         if ( m_CurrentFrame == static_cast<f32>( intBaseFrame ) )
237         {
238             intBaseFrame += 1;
239         }
240     }
241 
242     if ( intBaseFrame == intTargetFrame )
243     {
244         return;
245     }
246 
247     for ( s32 t = intBaseFrame - 1; ; t-- )
248     {
249         // ループ判定
250         if ( t == -1 )
251         {
252             t += m_FrameSize;
253             m_LoopCounter--;
254         }
255 
256         // 現フレームのイベントを探して実行
257         UpdateOneFrame( t, PLAY_DIRECTION_BACKWARD );
258 
259         if ( t == intTargetFrame ) break;
260     }
261 }
262 
UpdateOneFrame(s32 current,PlayDirection direction)263 void AnimSoundImpl::UpdateOneFrame( s32 current, PlayDirection direction )
264 {
265     // 現フレームのイベントを探して実行
266     u32 eventCount = m_Reader.GetEventCount();
267     for ( u32 i = 0; i < eventCount; i++ )
268     {
269         // イベント取得
270         const AnimSoundFile::AnimEvent* event = m_Reader.GetAnimEvent( i );
271         if ( event == NULL )
272         {
273             continue;
274         }
275 
276         // ループカウンタ判定
277         if ( ! IsPlayableLoopCount( event->frameInfo ) ) continue;
278 
279         // イベント実行
280         if ( event->frameInfo.frameFlag & AnimSoundFile::FrameInfo::FRAME_FLAG_TRIGGER_EVENT )
281         {
282             // トリガイベント
283             UpdateTrigger( *event, current, direction );
284         }
285         else
286         {
287             // レンジイベント
288             UpdateRange( *event, current, direction );
289         }
290     }
291 }
292 
UpdateTrigger(const AnimSoundFile::AnimEvent & event,s32 current,PlayDirection direction)293 void AnimSoundImpl::UpdateTrigger(
294     const AnimSoundFile::AnimEvent& event,
295     s32 current,
296     PlayDirection direction
297 )
298 {
299     if ( event.frameInfo.frameFlag & AnimSoundFile::FrameInfo::FRAME_FLAG_START_FRAME_INF )
300     {
301         return;
302     }
303     const AnimSoundFile::EventInfo* eventInfo = event.GetEventInfo();
304     if ( eventInfo == NULL )
305     {
306         return;
307     }
308 
309     // 再生方向判定
310     bool isEnableForPlayDirection = false;
311     if ( eventInfo->playDirection == AnimSoundFile::EventInfo::PLAY_DIRECTION_BOTH ||
312          ( direction == PLAY_DIRECTION_FORWARD  &&
313            eventInfo->playDirection == AnimSoundFile::EventInfo::PLAY_DIRECTION_FORWARD  ) ||
314          ( direction == PLAY_DIRECTION_BACKWARD  &&
315            eventInfo->playDirection == AnimSoundFile::EventInfo::PLAY_DIRECTION_BACKWARD  )
316        )
317     {
318         isEnableForPlayDirection = true;
319     }
320     if ( ! isEnableForPlayDirection )
321     {
322         return;
323     }
324 
325     if ( event.frameInfo.frameFlag & AnimSoundFile::FrameInfo::FRAME_FLAG_END_FRAME_INF )
326     {
327         // 終了フレームが無いワンショットのトリガイベント
328         if ( event.frameInfo.startFrame == current )
329         {
330             StopEvent( *eventInfo );
331             if ( m_EventCallback != NULL )
332             {
333                 m_EventCallback(
334                     EVENT_TYPE_TRIGGER_START,
335                     current,
336                     eventInfo->GetSoundLabel(),
337                     eventInfo->userParam,
338                     m_EventCallbackArg
339                 );
340             }
341             StartEvent( *eventInfo, true );
342         }
343     }
344     else
345     {
346         // 終了フレームがあるトリガイベント
347 
348         // 開始判定
349         if ( event.frameInfo.startFrame == current )
350         {
351             StopEvent( *eventInfo );
352             if ( m_EventCallback != NULL )
353             {
354                 m_EventCallback(
355                     EVENT_TYPE_TRIGGER_START,
356                     current,
357                     eventInfo->GetSoundLabel(),
358                     eventInfo->userParam,
359                     m_EventCallbackArg
360                 );
361             }
362             StartEvent( *eventInfo, true );
363         }
364 
365         // 終了判定
366         long endFrame;
367         if ( direction == PLAY_DIRECTION_FORWARD )
368         {
369             endFrame = event.frameInfo.endFrame;
370         }
371         else // PLAY_BACKWARD
372         {
373             endFrame = event.frameInfo.startFrame
374                 - ( event.frameInfo.endFrame - event.frameInfo.startFrame );
375             endFrame = ut::Max( endFrame, 0L );
376         }
377         if ( endFrame == current )
378         {
379             if ( m_EventCallback != NULL )
380             {
381                 m_EventCallback(
382                     EVENT_TYPE_TRIGGER_STOP,
383                     current,
384                     eventInfo->GetSoundLabel(),
385                     eventInfo->userParam,
386                     m_EventCallbackArg
387                 );
388             }
389             StopEvent( *eventInfo );
390         }
391     }
392 }
393 
UpdateRange(const AnimSoundFile::AnimEvent & event,s32 current,PlayDirection direction)394 void AnimSoundImpl::UpdateRange(
395     const AnimSoundFile::AnimEvent& event,
396     s32 current,
397     PlayDirection direction
398 )
399 {
400     if ( direction == PLAY_DIRECTION_FORWARD )
401     {
402         UpdateForwardRange( event, current );
403     }
404     else // PLAY_BACKWARD
405     {
406         UpdateBackwardRange( event, current );
407     }
408 }
409 
UpdateForwardRange(const AnimSoundFile::AnimEvent & event,s32 current)410 void AnimSoundImpl::UpdateForwardRange(
411     const AnimSoundFile::AnimEvent& event,
412     s32 current
413 )
414 {
415     const AnimSoundFile::EventInfo* eventInfo = event.GetEventInfo();
416     if ( eventInfo == NULL )
417     {
418         return;
419     }
420 
421     const AnimSoundFile::FrameInfo& frameInfo = event.frameInfo;
422     if ( frameInfo.frameFlag & AnimSoundFile::FrameInfo::FRAME_FLAG_START_FRAME_INF )
423     {
424         if ( frameInfo.frameFlag & AnimSoundFile::FrameInfo::FRAME_FLAG_END_FRAME_INF )
425         {
426             // 開始フレームも終了フレームも無いレンジイベント
427             HoldEvent( *eventInfo, true );
428         }
429         else
430         {
431             // 開始フレームが無いレンジイベント
432             if ( frameInfo.endFrame == current )
433             {
434                 if ( m_EventCallback != NULL )
435                 {
436                     m_EventCallback(
437                         EVENT_TYPE_RANGE_STOP,
438                         current,
439                         eventInfo->GetSoundLabel(),
440                         eventInfo->userParam,
441                         m_EventCallbackArg );
442                 }
443             }
444             if ( m_LoopCounter < frameInfo.loopOffset )
445             {
446                 HoldEvent( *eventInfo, true );
447             }
448             else if ( m_LoopCounter == frameInfo.loopOffset )
449             {
450                 if ( current < frameInfo.endFrame )
451                 {
452                     HoldEvent( *eventInfo, true );
453                 }
454                 else
455                 {
456                     StopEvent( *eventInfo );
457                 }
458             }
459         }
460     }
461     else if ( frameInfo.frameFlag & AnimSoundFile::FrameInfo::FRAME_FLAG_END_FRAME_INF )
462     {
463         // 終了フレームが無いレンジイベント
464         if ( frameInfo.startFrame == current )
465         {
466             if ( m_EventCallback != NULL )
467             {
468                 m_EventCallback(
469                     EVENT_TYPE_RANGE_START,
470                     current,
471                     eventInfo->GetSoundLabel(),
472                     eventInfo->userParam,
473                     m_EventCallbackArg
474                 );
475             }
476         }
477         if ( m_LoopCounter > frameInfo.loopOffset )
478         {
479             HoldEvent( *eventInfo, true );
480         }
481         else if ( m_LoopCounter == frameInfo.loopOffset )
482         {
483             if ( frameInfo.startFrame <= current )
484             {
485                 HoldEvent( *eventInfo, true );
486             }
487         }
488     }
489     else
490     {
491         // 開始フレームも終了フレームもあるレンジイベント
492         NW_ASSERT( frameInfo.startFrame < frameInfo.endFrame );
493         if ( frameInfo.startFrame == current )
494         {
495             if ( m_EventCallback != NULL )
496             {
497                 m_EventCallback(
498                     EVENT_TYPE_RANGE_START,
499                     current,
500                     eventInfo->GetSoundLabel(),
501                     eventInfo->userParam,
502                     m_EventCallbackArg
503                 );
504             }
505         }
506         if ( frameInfo.endFrame == current )
507         {
508             if ( m_EventCallback != NULL )
509             {
510                 m_EventCallback(
511                     EVENT_TYPE_RANGE_STOP,
512                     current,
513                     eventInfo->GetSoundLabel(),
514                     eventInfo->userParam,
515                     m_EventCallbackArg
516                 );
517             }
518         }
519         if ( ( frameInfo.startFrame <= current ) &&
520              ( current < frameInfo.endFrame ) )
521         {
522             HoldEvent( *eventInfo, true );
523         }
524         else
525         {
526             StopEvent( *eventInfo );
527         }
528     }
529 }
530 
UpdateBackwardRange(const AnimSoundFile::AnimEvent & event,s32 current)531 void AnimSoundImpl::UpdateBackwardRange(
532     const AnimSoundFile::AnimEvent& event,
533     s32 current
534 )
535 {
536     const AnimSoundFile::EventInfo* eventInfo = event.GetEventInfo();
537     if ( eventInfo == NULL )
538     {
539         return;
540     }
541 
542     const AnimSoundFile::FrameInfo& frameInfo = event.frameInfo;
543     if ( frameInfo.frameFlag & AnimSoundFile::FrameInfo::FRAME_FLAG_START_FRAME_INF )
544     {
545         if ( frameInfo.frameFlag & AnimSoundFile::FrameInfo::FRAME_FLAG_END_FRAME_INF )
546         {
547             // 開始フレームも終了フレームも無いレンジイベント
548             HoldEvent( *eventInfo, true );
549         }
550         else
551         {
552             // 開始フレームが無いレンジイベント
553             if ( frameInfo.endFrame == current )
554             {
555                 if ( m_EventCallback != NULL )
556                 {
557                     m_EventCallback(
558                         EVENT_TYPE_RANGE_START,
559                         current,
560                         eventInfo->GetSoundLabel(),
561                         eventInfo->userParam,
562                         m_EventCallbackArg
563                     );
564                 }
565             }
566             if ( m_LoopCounter < frameInfo.loopOffset )
567             {
568                 HoldEvent( *eventInfo, true );
569             }
570             else if ( m_LoopCounter == frameInfo.loopOffset )
571             {
572                 if ( current <= frameInfo.endFrame )
573                 {
574                     HoldEvent( *eventInfo, true );
575                 }
576             }
577         }
578     }
579     else if ( frameInfo.frameFlag & AnimSoundFile::FrameInfo::FRAME_FLAG_END_FRAME_INF )
580     {
581         // 終了フレームが無いレンジイベント
582         if ( frameInfo.startFrame == current )
583         {
584             if ( m_EventCallback != NULL )
585             {
586                 m_EventCallback(
587                     EVENT_TYPE_RANGE_STOP,
588                     current,
589                     eventInfo->GetSoundLabel(),
590                     eventInfo->userParam,
591                     m_EventCallbackArg
592                 );
593             }
594         }
595         if ( m_LoopCounter > frameInfo.loopOffset )
596         {
597             HoldEvent( *eventInfo, true );
598         }
599         else if ( m_LoopCounter == frameInfo.loopOffset )
600         {
601             if ( frameInfo.startFrame < current )
602             {
603                 HoldEvent( *eventInfo, true );
604             }
605             else StopEvent( *eventInfo );
606         }
607     }
608     else
609     {
610         // 開始フレームも終了フレームもあるレンジイベント
611         NW_ASSERT( frameInfo.startFrame < frameInfo.endFrame );
612         if ( frameInfo.startFrame == current )
613         {
614             if ( m_EventCallback != NULL )
615             {
616                 m_EventCallback(
617                     EVENT_TYPE_RANGE_STOP,
618                     current,
619                     eventInfo->GetSoundLabel(),
620                     eventInfo->userParam,
621                     m_EventCallbackArg
622                 );
623             }
624         }
625         if ( frameInfo.endFrame == current )
626         {
627             if ( m_EventCallback != NULL )
628             {
629                 m_EventCallback(
630                     EVENT_TYPE_RANGE_START,
631                     current,
632                     eventInfo->GetSoundLabel(),
633                     eventInfo->userParam,
634                     m_EventCallbackArg
635                 );
636             }
637         }
638         if ( ( frameInfo.startFrame < current ) && ( current <= frameInfo.endFrame ) )
639         {
640             HoldEvent( *eventInfo, true );
641         }
642         else
643         {
644             StopEvent( *eventInfo );
645         }
646     }
647 }
648 
StartEvent(const AnimSoundFile::EventInfo & info,bool isStopWhenFinalize)649 void AnimSoundImpl::StartEvent( const AnimSoundFile::EventInfo& info, bool isStopWhenFinalize )
650 {
651     // 空いているハンドルを探す
652     // 全て埋まっている時は、一番プライオリティが低いサウンドを止める
653     int lowestPriority = BasicSound::PRIORITY_MAX + 1;
654     int lowestIndex = -1;
655     int i;
656     for ( i = 0; i < m_EventPlayerCount; i++ )
657     {
658         if ( !m_pEventPlayers[i].IsAttachedSound() )
659         {
660             break;
661         }
662         int priority = m_pEventPlayers[i].GetPlayingSoundPriority();
663         if ( lowestPriority > priority )
664         {
665             lowestIndex = i;
666             lowestPriority = priority;
667         }
668     }
669     if ( i == m_EventPlayerCount )
670     {
671         m_pEventPlayers[lowestIndex].ForceStop();
672         i = lowestIndex;
673     }
674 
675     // 再生
676     m_pEventPlayers[i].StartEvent( info, m_Starter, isStopWhenFinalize );
677     WritePlaySpeedToSequenceVariable( i, info );
678 }
679 
HoldEvent(const AnimSoundFile::EventInfo & info,bool isStopWhenFinalize)680 void AnimSoundImpl::HoldEvent( const AnimSoundFile::EventInfo& info, bool isStopWhenFinalize )
681 {
682     // イベントが再生中なら何もしない
683     // 新規再生用に一番プライオリティが低いサウンドを探す
684     int lowestPriority = BasicSound::PRIORITY_MAX + 1;
685     int lowestIndex = -1;
686     int i;
687     for ( i = 0; i < m_EventPlayerCount; i++ )
688     {
689         if ( m_pEventPlayers[i].IsPlaying( info ) )
690         {
691             return; // 再生中なので何もしない
692         }
693         if ( !m_pEventPlayers[i].IsAttachedSound() )
694         {
695             lowestIndex = i;
696             lowestPriority = -1;
697         }
698         else
699         {
700             int priority = m_pEventPlayers[i].GetPlayingSoundPriority();
701             if ( lowestPriority > priority )
702             {
703                 lowestIndex = i;
704                 lowestPriority = priority;
705             }
706         }
707     }
708 
709     // 再生中のイベントが無かったので新規に再生
710     if ( i == m_EventPlayerCount )
711     {
712         m_pEventPlayers[lowestIndex].ForceStop();
713         i = lowestIndex;
714     }
715 
716     // 再生
717     m_pEventPlayers[i].HoldEvent( info, m_Starter, isStopWhenFinalize );
718     WritePlaySpeedToSequenceVariable( i, info );
719 }
720 
WritePlaySpeedToSequenceVariable(int eventPlayerNo,const AnimSoundFile::EventInfo & info)721 void AnimSoundImpl::WritePlaySpeedToSequenceVariable(
722         int eventPlayerNo, const AnimSoundFile::EventInfo& info )
723 {
724     u8 sequenceVariableNo = 0;  // とりあえず 0 で初期化
725     if ( info.GetSequenceVariable( &sequenceVariableNo ) )
726     {
727         m_pEventPlayers[eventPlayerNo].WritePlaySpeedToSequenceVariable(
728                 sequenceVariableNo,
729                 m_CurrentSpeed );
730     }
731 }
732 
StopEvent(const AnimSoundFile::EventInfo & info)733 void AnimSoundImpl::StopEvent( const AnimSoundFile::EventInfo& info )
734 {
735     for ( int i=0; i<m_EventPlayerCount; i++ )
736     {
737         m_pEventPlayers[i].StopEvent( info );
738     }
739 }
740 
StopAllSound()741 void AnimSoundImpl::StopAllSound()
742 {
743     for ( int i = 0; i < m_EventPlayerCount; i++ )
744     {
745         m_pEventPlayers[i].ForceStop();
746     }
747 }
748 
IsPlayableLoopCount(const AnimSoundFile::FrameInfo & info)749 bool AnimSoundImpl::IsPlayableLoopCount( const AnimSoundFile::FrameInfo& info )
750 {
751     // レンジイベントで、開始フレームもしくは終了フレームがinfの場合は
752     // ループカウントが反映されない
753     if ( ! ( info.frameFlag & AnimSoundFile::FrameInfo::FRAME_FLAG_TRIGGER_EVENT ) )
754     {
755         if ( ( info.frameFlag & AnimSoundFile::FrameInfo::FRAME_FLAG_START_FRAME_INF ) ||
756              ( info.frameFlag & AnimSoundFile::FrameInfo::FRAME_FLAG_END_FRAME_INF )
757            )
758         {
759             return true;
760         }
761     }
762 
763     int loopOffset = ut::Max( static_cast<int>( info.loopOffset ), 0 );
764     int loopCounter = ut::Abs( m_LoopCounter );
765     if ( info.loopInterval == 0 )
766     {
767         if ( loopCounter < loopOffset )
768         {
769             return false;
770         }
771     }
772     else
773     {
774         if ( loopCounter < loopOffset )
775         {
776             return false;
777         }
778         if ( ( loopCounter - loopOffset ) % info.loopInterval != 0 )
779         {
780             return false;
781         }
782     }
783     return true;
784 }
785 
786 } // namespace nw::snd::internal
787 } // namespace nw::snd
788 } // namespace nw
789 
790