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