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