1 /*---------------------------------------------------------------------------*
2   Project:  NintendoWare
3   File:     snd_SoundThread.cpp
4 
5   Copyright (C)2009-2010 Nintendo Co., Ltd./HAL Laboratory, Inc.  All rights reserved.
6 
7   These coded instructions, statements, and computer programs contain
8   proprietary information of Nintendo of America Inc. and/or Nintendo
9   Company Ltd., and are protected by Federal copyright law.  They may
10   not be disclosed to third parties or copied or duplicated in any form,
11   in whole or in part, without the prior written consent of Nintendo.
12 
13   $Revision: 26995 $
14  *---------------------------------------------------------------------------*/
15 
16 #include "precompiled.h"
17 
18 #include <nw/snd/snd_SoundThread.h>
19 
20 #ifdef NW_PLATFORM_CTRWIN
21     #include <nw/snd/snd_HardwareChannelManager.h>
22 #endif
23 #include <nw/snd/snd_HardwareManager.h>
24 #include <nw/snd/snd_VoiceManager.h>
25 #include <nw/snd/snd_ChannelManager.h>
26 #include <nw/snd/snd_DriverCommandManager.h>
27 #include <nw/snd/snd_Util.h>
28 #include <nw/snd/snd_Config.h>
29 
30 #ifdef NW_PLATFORM_CTR
31 // パフォーマンス計測
32 #if NN_SND_SAMPLES_PER_FRAME == 96
33     #define SND_DENOM (29.33f)
34 #else
35     #define SND_DENOM (48.89f)
36 #endif
37 #endif
38 
39 namespace
40 {
41 #ifdef NW_PLATFORM_CTR
42 
43 //
44 // コア1で動かす
45 //
46 const s32 TEST_CPU_NUM = 2;
TestDefaultValue()47 void TestDefaultValue()
48 {
49     // スレッド ID
50     NN_LOG("[CurrendId]    0x%04X default(%d)\n",
51             nn::os::Thread::GetCurrentId(), nn::os::CORE_NO_USE_PROCESS_VALUE );
52 
53     // スレッド優先度
54     NN_LOG("[CurrentPrio]  0x%04X\n", nn::os::Thread::GetCurrentPriority() );
55 
56     // アフィニティマスク
57     bit8 affinityMask;
58     nn::os::Thread::GetCurrentAffinityMask( &affinityMask, TEST_CPU_NUM );
59     NN_LOG("[AffinityMask] 0x%04X\n", affinityMask, TEST_CPU_NUM );
60     nn::os::Thread::GetDefaultAffinityMask( &affinityMask, TEST_CPU_NUM );
61     NN_LOG("[...(Default)] 0x%04X\n", affinityMask, TEST_CPU_NUM );
62 
63     // 優先プロセッサ番号
64     NN_LOG("[IdealCore]    0x%04X\n", nn::os::Thread::GetCurrentIdealProcessor() );
65     NN_LOG("[...(Default)] 0x%04X\n", nn::os::Thread::GetDefaultIdealProcessor() );
66 
67     // カレントスレッドの動作プロセッサ番号
68     NN_LOG("[CurrProcCore] 0x%04X\n", nn::os::Thread::GetCurrentProcessorNumber() );
69 }
70 
71 
72 #endif
73 }
74 
75 namespace nw {
76 namespace snd {
77 namespace internal {
78 namespace driver {
79 
80 /* ========================================================================
81         member function
82    ======================================================================== */
83 
84 /*---------------------------------------------------------------------------*
85   Name:         SoundThread
86 
87   Description:  コンストラクタ
88 
89   Arguments:    なし
90 
91   Returns:      なし
92  *---------------------------------------------------------------------------*/
SoundThread()93 SoundThread::SoundThread()
94 :
95 #ifdef NW_PLATFORM_CTR
96   m_SoundThreadSumTick( nn::os::Tick( 0 ) ),
97   m_SoundThreadCount( 0 ),
98   m_DspCycles( 0 ),
99   m_IsBeginCalcPerf( false ),
100   m_SoundThreadCoreNo( 0 ),
101 #endif
102   m_CreateFlag( false ),
103   m_PauseFlag( false ),
104   m_IsEnableGetTick( false ),
105   m_IsFinalizing( false ),
106   m_pPerfData(NULL),
107   m_NoteOnCount(0),
108   m_PerfDataNum(0),
109   m_PerfDataCount(0)
110 {
111 #ifdef NW_PLATFORM_CTRWIN
112     m_BlockingQueue.Initialize( m_MsgBuffer, THREAD_MESSAGE_BUFSIZE );
113 #endif
114 }
115 
116 /*---------------------------------------------------------------------------*
117   Name:         GetInstance
118 
119   Description:  シングルトンのインスタンスを取得します
120 
121   Arguments:    無し
122 
123   Returns:      インスタンスへの参照
124  *---------------------------------------------------------------------------*/
GetInstance()125 SoundThread& SoundThread::GetInstance()
126 {
127     static SoundThread instance;
128     return instance;
129 }
130 
CreateImpl(bool enableGetTick)131 bool SoundThread::CreateImpl( bool enableGetTick )
132 {
133     NW_ASSERTMSG( HardwareManager::GetInstance().IsInitialized(),
134             "note initialized nw::snd::internal::HardwareManager.\n" );
135     if ( m_CreateFlag )
136     {
137         return true;
138     }
139     m_CreateFlag = true;
140     m_IsFinalizing = false;
141     m_IsEnableGetTick = enableGetTick;
142     return true;
143 }
144 
145 #if defined ( NW_SND_AVAILABLE_NN_SND_STARTSOUNDTHREAD )
146 
147 #if defined ( NW_SND_AVAILABLE_NN_SND_STARTSOUNDTHREAD2 )
148 
CreateSoundThread(uptr stackBufferAddress,size_t stackBufferSize,s32 priority,uptr userThreadStackBufferAddress,size_t userThreadStackBufferSize,s32 userThreadPriority,s32 coreNo,bool isEnableGetTick)149 bool SoundThread::CreateSoundThread(
150     uptr stackBufferAddress,
151     size_t stackBufferSize,
152     s32 priority,
153     uptr userThreadStackBufferAddress,
154     size_t userThreadStackBufferSize,
155     s32 userThreadPriority,
156     s32 coreNo,
157     bool isEnableGetTick
158 )
159 {
160     bool createImplResult = CreateImpl( isEnableGetTick );
161     if ( createImplResult == false )
162     {
163         return false;
164     }
165 
166     m_SoundThreadCoreNo = coreNo;
167 
168     nn::snd::ThreadParameter mainThread;
169     mainThread.stackBuffer = stackBufferAddress;
170     mainThread.stackSize = stackBufferSize;
171     mainThread.priority = (coreNo == 1) ? 0 : priority;
172 
173     nn::snd::ThreadParameter userThread;
174     userThread.stackBuffer = userThreadStackBufferAddress;
175     userThread.stackSize = userThreadStackBufferSize;
176     userThread.priority = userThreadPriority;
177 
178     nn::Result result = nn::snd::StartSoundThread(
179         &mainThread,
180         SoundThreadFunc,
181         reinterpret_cast<uptr>( &GetInstance() ),
182 
183         coreNo == 1 ? &userThread : NULL,
184         UserThreadCallback,
185         reinterpret_cast<uptr>( &GetInstance() ),
186 
187         coreNo
188     );
189     nn::snd::EnableSoundThreadTickCounter( isEnableGetTick );
190     return result.IsSuccess();
191 }
192 
193 #else
194 
CreateSoundThread(uptr stackBufferAddress,size_t stackBufferSize,s32 priority,s32 coreNo,bool isEnableGetTick)195 bool SoundThread::CreateSoundThread(
196         uptr stackBufferAddress,
197         size_t stackBufferSize,
198         s32 priority,
199         s32 coreNo,
200         bool isEnableGetTick )
201 {
202     bool createImplResult = CreateImpl( isEnableGetTick );
203     if ( createImplResult == false )
204     {
205         return false;
206     }
207 
208     m_SoundThreadCoreNo = coreNo;
209     nn::Result result = nn::snd::StartSoundThread(
210             SoundThreadFunc,
211             reinterpret_cast<uptr>( &GetInstance() ),
212             stackBufferAddress,
213             stackBufferSize,
214             priority,
215             coreNo );
216 #ifdef NW_SND_CONFIG_AVOID_NN_SND_AUXCALLBACK_BUG
217     if ( coreNo == 0 )
218     {
219         // AUX コールバックが呼ばれないのを防ぐ回避策
220         // TODO: SDK-0.14.0 以降は呼ばないようにする
221         nn::snd::EnableAuxBusProcessingOnCore1( true );
222     }
223 #endif
224     nn::snd::EnableSoundThreadTickCounter( isEnableGetTick );
225     return result.IsSuccess();
226 }
CreateUserSoundThread(uptr stackBufferAddress,size_t stackBufferSize,s32 priority)227 bool SoundThread::CreateUserSoundThread(
228         uptr stackBufferAddress,
229         size_t stackBufferSize,
230         s32 priority )
231 {
232     // ただのラップ関数。Finalize 関連を nw::snd SoundThread クラス内で行うため。
233     nn::Result result = nn::snd::StartUserSoundThread(
234             stackBufferAddress,
235             stackBufferSize,
236             priority );
237     return result.IsSuccess();
238 }
239 
240 #endif
241 
242 #else
Create(s32 priority,ThreadStack & stack,s32 coreNo,bool enableGetTick)243 bool SoundThread::Create(
244         s32 priority,
245         ThreadStack& stack,
246         s32 coreNo,
247         bool enableGetTick )
248 {
249     bool createImplResult = CreateImpl( enableGetTick );
250     if ( createImplResult == false )
251     {
252         return false;
253     }
254 
255     m_SoundThreadCoreNo = coreNo;
256     nn::Result result = m_Thread.TryStart(
257             SoundThreadFunc,
258             reinterpret_cast<uptr>( &GetInstance() ),
259             stack,
260             priority,
261             coreNo );
262     return result.IsSuccess();
263 }
264 #endif
265 
Initialize()266 void SoundThread::Initialize()
267 {
268     m_CriticalSection.Initialize();
269 }
270 
Destroy()271 void SoundThread::Destroy()
272 {
273     if ( ! m_CreateFlag ) return;
274 
275 #ifdef NW_PLATFORM_CTRWIN
276     // サウンドスレッド終了メッセージ送信
277     m_BlockingQueue.Jam( static_cast<uptr>( MESSAGE_SHUTDOWN ) );
278 #else
279     m_IsFinalizing = true;
280 #endif
281 
282     // スレッドの終了を待つ
283 #if defined( NW_SND_AVAILABLE_NN_SND_STARTSOUNDTHREAD )
284     if ( m_SoundThreadCoreNo == 1 )
285     {
286         nn::snd::FinalizeUserSoundThread();
287     }
288     nn::snd::FinalizeSoundThread();
289 #else
290     m_Thread.Join();
291     m_Thread.Finalize();
292 #endif
293 
294     m_CreateFlag = false;
295 }
296 
297 /*---------------------------------------------------------------------------*
298   Name:         Shutdown
299 
300   Description:  サウンドスレッドを終了する
301 
302   Arguments:    None.
303 
304   Returns:      None.
305  *---------------------------------------------------------------------------*/
Finalize()306 void SoundThread::Finalize()
307 {
308     Destroy();
309 
310     // プレイヤーを全て停止する
311     for ( PlayerCallbackList::Iterator itr = m_PlayerCallbackList.GetBeginIter();
312             itr != m_PlayerCallbackList.GetEndIter();
313         )
314     {
315         PlayerCallbackList::Iterator curItr = itr++;
316         curItr->OnShutdownSoundThread();
317     }
318 
319     m_CriticalSection.Finalize();
320 }
321 
322 #ifdef NW_PLATFORM_CTRWIN
323 /*---------------------------------------------------------------------------*
324   Name:         HwCallbackFunc
325 
326   Description:  アラーム周期で呼びだされるコールバック
327 
328   Arguments:
329 
330   Returns:      None.
331  *---------------------------------------------------------------------------*/
HwCallbackFunc()332 void SoundThread::HwCallbackFunc()
333 {
334     SoundThread* soundThread = &GetInstance();
335     soundThread->HwCallbackProc();
336 }
337 
338 /*---------------------------------------------------------------------------*
339   Name:         HwCallbackProc
340 
341   Description:  アラーム周期で呼びだされるコールバック
342 
343   Arguments:
344 
345   Returns:      None.
346  *---------------------------------------------------------------------------*/
HwCallbackProc()347 void SoundThread::HwCallbackProc()
348 {
349     // サウンドスレッド起動メッセージ送信
350     if ( ! m_PauseFlag )
351     {
352         bool result =
353             m_BlockingQueue.TryEnqueue( static_cast<uptr>( MESSAGE_HW_CALLBACK ) );
354         // NOTE: 割り込み禁止中なのでメッセージは出せない
355         // NW_WARNING( result, "Failed TryEnqueue to wakeup sound thread");
356     }
357 }
358 #endif // NW_PLATFORM_CTRWIN
359 
360 /*---------------------------------------------------------------------------*
361   Name:         SoundThreadProc
362 
363   Description:  サウンドスレッド関数
364 
365   Arguments:    arg - スレッドインスタンス
366 
367   Returns:      None.
368  *---------------------------------------------------------------------------*/
SoundThreadFunc(uptr arg)369 void SoundThread::SoundThreadFunc( uptr arg )
370 {
371     SoundThread* th = reinterpret_cast< SoundThread* >( arg );
372 
373 //    TestDefaultValue();
374 
375 #ifdef NW_PLATFORM_CTRWIN
376     HardwareManager::GetInstance().RegisterCallback( &th->m_HwCallbackNode, HwCallbackFunc );
377 #endif
378 
379     // スレッドプロシージャ
380     th->SoundThreadProc();
381 
382 #ifdef NW_PLATFORM_CTRWIN
383     HardwareManager::GetInstance().UnregisterCallback( &th->m_HwCallbackNode );
384 #endif
385 }
386 
UserThreadCallback(uptr arg)387 void SoundThread::UserThreadCallback( uptr arg )
388 {
389     SoundThread* th = reinterpret_cast< SoundThread* >( arg );
390 
391     // ユーザー処理
392     if ( th->m_UserCallback != NULL )
393     {
394         th->m_UserCallback( th->m_UserCallbackArg );
395     }
396 }
397 
RegisterSoundFrameCallback(SoundFrameCallback * callback)398 void SoundThread::RegisterSoundFrameCallback( SoundFrameCallback* callback )
399 {
400 	CriticalSection::ScopedLock lock( m_CriticalSection );
401     m_SoundFrameCallbackList.PushBack( callback );
402 }
403 
UnregisterSoundFrameCallback(SoundFrameCallback * callback)404 void SoundThread::UnregisterSoundFrameCallback( SoundFrameCallback* callback )
405 {
406     CriticalSection::ScopedLock lock( m_CriticalSection );
407     m_SoundFrameCallbackList.Erase( callback );
408 }
409 
RegisterPlayerCallback(PlayerCallback * callback)410 void SoundThread::RegisterPlayerCallback( PlayerCallback* callback )
411 {
412     m_PlayerCallbackList.PushBack( callback );
413 }
414 
UnregisterPlayerCallback(PlayerCallback * callback)415 void SoundThread::UnregisterPlayerCallback( PlayerCallback* callback )
416 {
417     m_PlayerCallbackList.Erase( callback );
418 }
419 
420 /*---------------------------------------------------------------------------*
421   Name:         FrameProcess
422 
423   Description:  サウンドフレーム処理
424 
425   Arguments:    None.
426 
427   Returns:      None.
428   *---------------------------------------------------------------------------*/
FrameProcess()429 void SoundThread::FrameProcess()
430 {
431 	CriticalSection::ScopedLock lock( m_CriticalSection );
432 
433 #ifndef NW_SND_AVAILABLE_NN_SND_STARTSOUNDTHREAD2
434     // ユーザー処理
435     if ( m_UserCallback != NULL )
436     {
437         m_UserCallback( m_UserCallbackArg );
438     }
439 #endif
440 
441     // サウンドフレーム開始コールバック
442     {
443         for ( SoundFrameCallbackList::Iterator itr = m_SoundFrameCallbackList.GetBeginIter();
444               itr != m_SoundFrameCallbackList.GetEndIter();
445         )
446         {
447             SoundFrameCallbackList::Iterator curItr = itr++;
448             curItr->OnBeginSoundFrame();
449         }
450     }
451 
452     // サウンドスレッドのメイン処理
453     {
454         // ボイス更新
455         {
456             // NW_PROFILE("SoundThread::VoiceUpdate");
457             VoiceUpdate();
458         }
459 
460     #ifdef NW_PLATFORM_CTRWIN
461         // Free予約されているAxVoiceをFreeする
462         HardwareChannelManager::GetInstance().FreeAllReservedHardwareChannel();
463     #endif
464 
465         // HardwareManager 更新
466         HardwareManager::GetInstance().Update();
467 
468         // コマンド処理
469         {
470             // NW_PROFILE("SoundThread::ProcessCommand");
471             DriverCommandManager::GetInstance().ProcessCommand();
472             DriverCommandManager::GetInstanceForTaskThread().ProcessCommand();
473         }
474 
475         // プレイヤー更新
476         {
477             // NW_PROFILE("SoundThread::UpdatePlayer");
478             for ( PlayerCallbackList::Iterator itr = m_PlayerCallbackList.GetBeginIter();
479                   itr != m_PlayerCallbackList.GetEndIter();
480             )
481             {
482                 PlayerCallbackList::Iterator curItr = itr++;
483                 curItr->OnUpdateFrameSoundThread();
484             }
485         }
486 
487         // チャンネル更新
488         {
489             // NW_PROFILE("SoundThread::UpdateChannel");
490             ChannelManager::GetInstance().UpdateAllChannel();
491         }
492 
493         // 乱数列更新
494         (void)Util::CalcRandom();
495 
496         // ボイスの更新
497         VoiceManager::GetInstance().UpdateAllVoices();
498     }
499 
500     // サウンドフレーム終了コールバック
501     {
502         for ( SoundFrameCallbackList::Iterator itr = m_SoundFrameCallbackList.GetBeginIter();
503               itr != m_SoundFrameCallbackList.GetEndIter();
504         )
505         {
506             SoundFrameCallbackList::Iterator curItr = itr++;
507             curItr->OnEndSoundFrame();
508         }
509     }
510 }
511 
512 /*---------------------------------------------------------------------------*
513   Name:         SoundThreadProc
514 
515   Description:  サウンドスレッドプロシージャ
516 
517   Arguments:    None.
518 
519   Returns:      None.
520  *---------------------------------------------------------------------------*/
SoundThreadProc()521 void SoundThread::SoundThreadProc()
522 {
523 #ifdef NW_PLATFORM_CTRWIN
524     uptr message;
525 
526     while ( true )
527     {
528         // Wait for DSP Callback
529         message = m_BlockingQueue.Dequeue();
530 
531         if ( static_cast<u32>( message ) == MESSAGE_HW_CALLBACK )
532         {
533             FrameProcess();
534         }
535         else if ( static_cast<u32>( message ) == MESSAGE_SHUTDOWN )
536         {
537             break;
538         }
539     }
540 
541 #else   // #ifdef NW_PLATFORM_CTRWIN
542 
543 
544   #if defined( NW_SND_AVAILABLE_NN_SND_STARTSOUNDTHREAD )
545     if ( m_IsEnableGetTick == true )
546     {
547         nn::os::Tick processTick;
548         processTick = nn::snd::GetSoundThreadTick();
549         CalcProcessCost( processTick );
550         // 1 つ前 (現フレームではないことに注意) の処理時間を取得
551     }
552     FrameProcess();
553   #else // if defined( NW_SND_AVAILABLE_NN_SND_STARTSOUNDTHREAD )
554     while ( m_IsFinalizing == false )
555     {
556         nn::os::Tick sum, wait, etc;
557         nn::snd::WaitForDspSync( &wait );
558 
559         {
560             Util::AutoStopWatch watch( etc );
561             FrameProcess();
562             nn::snd::SendParameterToDsp();
563         }
564         sum = wait + etc;
565 
566         CalcProcessCost( sum );
567     }
568   #endif // if defined( NW_SND_AVAILABLE_NN_SND_STARTSOUNDTHREAD )
569 #endif  // #ifdef NW_PLATFORM_CTRWIN
570 }
571 
572 
VoiceUpdate()573 void SoundThread::VoiceUpdate()
574 {
575     for ( PlayerCallbackList::Iterator itr = m_PlayerCallbackList.GetBeginIter();
576           itr != m_PlayerCallbackList.GetEndIter();
577     )
578     {
579         PlayerCallbackList::Iterator curItr = itr++;
580         curItr->OnUpdateVoiceSoundThread();
581     }
582 }
583 
584 #ifdef NW_PLATFORM_CTR
CalcProcessCost(const nn::os::Tick & tick)585 void SoundThread::CalcProcessCost( const nn::os::Tick& tick )
586 {
587     if ( m_IsEnableGetTick == true )
588     {
589         m_SoundThreadSumTick += tick;
590         m_SoundThreadCount += 1;
591         m_DspCycles += nn::snd::GetDspCycles();
592 
593         // パフォーマンス計測用コード
594         if ( m_IsBeginCalcPerf == true )
595         {
596             f32 load = tick.ToTimeSpan().GetMicroSeconds() / SND_DENOM;
597             m_PerfHistogram.SetLoad( load );
598 
599             if ( m_pPerfData != NULL )
600             {
601                 if ( m_PerfDataCount < m_PerfDataNum )
602                 {
603                     PerfData& data = m_pPerfData[m_PerfDataCount];
604                     data.voice     = SoundSystem::GetVoiceCount();
605                     data.noteOn    = m_NoteOnCount;
606                     data.load      = load;
607 
608                     m_PerfDataCount += 1;
609                 }
610             }
611             m_NoteOnCount = 0;
612         }
613     }
614 }
615 
GetTickCount(nn::os::Tick & tick,int & count) const616 void SoundThread::GetTickCount( nn::os::Tick& tick, int& count ) const
617 {
618     CriticalSection::ScopedLock lock( m_CriticalSection );
619 
620     tick = m_SoundThreadSumTick;
621     count = m_SoundThreadCount;
622 }
623 
ClearTickCount()624 void SoundThread::ClearTickCount()
625 {
626     CriticalSection::ScopedLock lock( m_CriticalSection );
627 
628     m_SoundThreadSumTick = nn::os::Tick( 0 );
629     m_SoundThreadCount = 0;
630 }
631 #endif
632 
633 } // namespace nw::snd::internal::driver
634 } // namespace nw::snd::internal
635 } // namespace nw::snd
636 } // namespace nw
637 
638