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