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