/*---------------------------------------------------------------------------* Project: NintendoWare File: snd_SoundThread.cpp Copyright (C)2009-2010 Nintendo Co., Ltd./HAL Laboratory, Inc. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo of America Inc. and/or Nintendo Company Ltd., and are protected by Federal copyright law. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. $Revision: 26995 $ *---------------------------------------------------------------------------*/ #include "precompiled.h" #include #ifdef NW_PLATFORM_CTRWIN #include #endif #include #include #include #include #include #include #ifdef NW_PLATFORM_CTR // パフォーマンス計測 #if NN_SND_SAMPLES_PER_FRAME == 96 #define SND_DENOM (29.33f) #else #define SND_DENOM (48.89f) #endif #endif namespace { #ifdef NW_PLATFORM_CTR // // コア1で動かす // const s32 TEST_CPU_NUM = 2; void TestDefaultValue() { // スレッド ID NN_LOG("[CurrendId] 0x%04X default(%d)\n", nn::os::Thread::GetCurrentId(), nn::os::CORE_NO_USE_PROCESS_VALUE ); // スレッド優先度 NN_LOG("[CurrentPrio] 0x%04X\n", nn::os::Thread::GetCurrentPriority() ); // アフィニティマスク bit8 affinityMask; nn::os::Thread::GetCurrentAffinityMask( &affinityMask, TEST_CPU_NUM ); NN_LOG("[AffinityMask] 0x%04X\n", affinityMask, TEST_CPU_NUM ); nn::os::Thread::GetDefaultAffinityMask( &affinityMask, TEST_CPU_NUM ); NN_LOG("[...(Default)] 0x%04X\n", affinityMask, TEST_CPU_NUM ); // 優先プロセッサ番号 NN_LOG("[IdealCore] 0x%04X\n", nn::os::Thread::GetCurrentIdealProcessor() ); NN_LOG("[...(Default)] 0x%04X\n", nn::os::Thread::GetDefaultIdealProcessor() ); // カレントスレッドの動作プロセッサ番号 NN_LOG("[CurrProcCore] 0x%04X\n", nn::os::Thread::GetCurrentProcessorNumber() ); } #endif } namespace nw { namespace snd { namespace internal { namespace driver { /* ======================================================================== member function ======================================================================== */ /*---------------------------------------------------------------------------* Name: SoundThread Description: コンストラクタ Arguments: なし Returns: なし *---------------------------------------------------------------------------*/ SoundThread::SoundThread() : #ifdef NW_PLATFORM_CTR m_SoundThreadSumTick( nn::os::Tick( 0 ) ), m_SoundThreadCount( 0 ), m_DspCycles( 0 ), m_IsBeginCalcPerf( false ), m_SoundThreadCoreNo( 0 ), #endif m_CreateFlag( false ), m_PauseFlag( false ), m_IsEnableGetTick( false ), m_IsFinalizing( false ), m_pPerfData(NULL), m_NoteOnCount(0), m_PerfDataNum(0), m_PerfDataCount(0) { #ifdef NW_PLATFORM_CTRWIN m_BlockingQueue.Initialize( m_MsgBuffer, THREAD_MESSAGE_BUFSIZE ); #endif } /*---------------------------------------------------------------------------* Name: GetInstance Description: シングルトンのインスタンスを取得します Arguments: 無し Returns: インスタンスへの参照 *---------------------------------------------------------------------------*/ SoundThread& SoundThread::GetInstance() { static SoundThread instance; return instance; } bool SoundThread::CreateImpl( bool enableGetTick ) { NW_ASSERTMSG( HardwareManager::GetInstance().IsInitialized(), "note initialized nw::snd::internal::HardwareManager.\n" ); if ( m_CreateFlag ) { return true; } m_CreateFlag = true; m_IsFinalizing = false; m_IsEnableGetTick = enableGetTick; return true; } #if defined ( NW_SND_AVAILABLE_NN_SND_STARTSOUNDTHREAD ) #if defined ( NW_SND_AVAILABLE_NN_SND_STARTSOUNDTHREAD2 ) bool SoundThread::CreateSoundThread( uptr stackBufferAddress, size_t stackBufferSize, s32 priority, uptr userThreadStackBufferAddress, size_t userThreadStackBufferSize, s32 userThreadPriority, s32 coreNo, bool isEnableGetTick ) { bool createImplResult = CreateImpl( isEnableGetTick ); if ( createImplResult == false ) { return false; } m_SoundThreadCoreNo = coreNo; nn::snd::ThreadParameter mainThread; mainThread.stackBuffer = stackBufferAddress; mainThread.stackSize = stackBufferSize; mainThread.priority = (coreNo == 1) ? 0 : priority; nn::snd::ThreadParameter userThread; userThread.stackBuffer = userThreadStackBufferAddress; userThread.stackSize = userThreadStackBufferSize; userThread.priority = userThreadPriority; nn::Result result = nn::snd::StartSoundThread( &mainThread, SoundThreadFunc, reinterpret_cast( &GetInstance() ), coreNo == 1 ? &userThread : NULL, UserThreadCallback, reinterpret_cast( &GetInstance() ), coreNo ); nn::snd::EnableSoundThreadTickCounter( isEnableGetTick ); return result.IsSuccess(); } #else bool SoundThread::CreateSoundThread( uptr stackBufferAddress, size_t stackBufferSize, s32 priority, s32 coreNo, bool isEnableGetTick ) { bool createImplResult = CreateImpl( isEnableGetTick ); if ( createImplResult == false ) { return false; } m_SoundThreadCoreNo = coreNo; nn::Result result = nn::snd::StartSoundThread( SoundThreadFunc, reinterpret_cast( &GetInstance() ), stackBufferAddress, stackBufferSize, priority, coreNo ); #ifdef NW_SND_CONFIG_AVOID_NN_SND_AUXCALLBACK_BUG if ( coreNo == 0 ) { // AUX コールバックが呼ばれないのを防ぐ回避策 // TODO: SDK-0.14.0 以降は呼ばないようにする nn::snd::EnableAuxBusProcessingOnCore1( true ); } #endif nn::snd::EnableSoundThreadTickCounter( isEnableGetTick ); return result.IsSuccess(); } bool SoundThread::CreateUserSoundThread( uptr stackBufferAddress, size_t stackBufferSize, s32 priority ) { // ただのラップ関数。Finalize 関連を nw::snd SoundThread クラス内で行うため。 nn::Result result = nn::snd::StartUserSoundThread( stackBufferAddress, stackBufferSize, priority ); return result.IsSuccess(); } #endif #else bool SoundThread::Create( s32 priority, ThreadStack& stack, s32 coreNo, bool enableGetTick ) { bool createImplResult = CreateImpl( enableGetTick ); if ( createImplResult == false ) { return false; } m_SoundThreadCoreNo = coreNo; nn::Result result = m_Thread.TryStart( SoundThreadFunc, reinterpret_cast( &GetInstance() ), stack, priority, coreNo ); return result.IsSuccess(); } #endif void SoundThread::Initialize() { m_CriticalSection.Initialize(); } void SoundThread::Destroy() { if ( ! m_CreateFlag ) return; #ifdef NW_PLATFORM_CTRWIN // サウンドスレッド終了メッセージ送信 m_BlockingQueue.Jam( static_cast( MESSAGE_SHUTDOWN ) ); #else m_IsFinalizing = true; #endif // スレッドの終了を待つ #if defined( NW_SND_AVAILABLE_NN_SND_STARTSOUNDTHREAD ) if ( m_SoundThreadCoreNo == 1 ) { nn::snd::FinalizeUserSoundThread(); } nn::snd::FinalizeSoundThread(); #else m_Thread.Join(); m_Thread.Finalize(); #endif m_CreateFlag = false; } /*---------------------------------------------------------------------------* Name: Shutdown Description: サウンドスレッドを終了する Arguments: None. Returns: None. *---------------------------------------------------------------------------*/ void SoundThread::Finalize() { Destroy(); // プレイヤーを全て停止する for ( PlayerCallbackList::Iterator itr = m_PlayerCallbackList.GetBeginIter(); itr != m_PlayerCallbackList.GetEndIter(); ) { PlayerCallbackList::Iterator curItr = itr++; curItr->OnShutdownSoundThread(); } m_CriticalSection.Finalize(); } #ifdef NW_PLATFORM_CTRWIN /*---------------------------------------------------------------------------* Name: HwCallbackFunc Description: アラーム周期で呼びだされるコールバック Arguments: Returns: None. *---------------------------------------------------------------------------*/ void SoundThread::HwCallbackFunc() { SoundThread* soundThread = &GetInstance(); soundThread->HwCallbackProc(); } /*---------------------------------------------------------------------------* Name: HwCallbackProc Description: アラーム周期で呼びだされるコールバック Arguments: Returns: None. *---------------------------------------------------------------------------*/ void SoundThread::HwCallbackProc() { // サウンドスレッド起動メッセージ送信 if ( ! m_PauseFlag ) { bool result = m_BlockingQueue.TryEnqueue( static_cast( MESSAGE_HW_CALLBACK ) ); // NOTE: 割り込み禁止中なのでメッセージは出せない // NW_WARNING( result, "Failed TryEnqueue to wakeup sound thread"); } } #endif // NW_PLATFORM_CTRWIN /*---------------------------------------------------------------------------* Name: SoundThreadProc Description: サウンドスレッド関数 Arguments: arg - スレッドインスタンス Returns: None. *---------------------------------------------------------------------------*/ void SoundThread::SoundThreadFunc( uptr arg ) { SoundThread* th = reinterpret_cast< SoundThread* >( arg ); // TestDefaultValue(); #ifdef NW_PLATFORM_CTRWIN HardwareManager::GetInstance().RegisterCallback( &th->m_HwCallbackNode, HwCallbackFunc ); #endif // スレッドプロシージャ th->SoundThreadProc(); #ifdef NW_PLATFORM_CTRWIN HardwareManager::GetInstance().UnregisterCallback( &th->m_HwCallbackNode ); #endif } void SoundThread::UserThreadCallback( uptr arg ) { SoundThread* th = reinterpret_cast< SoundThread* >( arg ); // ユーザー処理 if ( th->m_UserCallback != NULL ) { th->m_UserCallback( th->m_UserCallbackArg ); } } void SoundThread::RegisterSoundFrameCallback( SoundFrameCallback* callback ) { CriticalSection::ScopedLock lock( m_CriticalSection ); m_SoundFrameCallbackList.PushBack( callback ); } void SoundThread::UnregisterSoundFrameCallback( SoundFrameCallback* callback ) { CriticalSection::ScopedLock lock( m_CriticalSection ); m_SoundFrameCallbackList.Erase( callback ); } void SoundThread::RegisterPlayerCallback( PlayerCallback* callback ) { m_PlayerCallbackList.PushBack( callback ); } void SoundThread::UnregisterPlayerCallback( PlayerCallback* callback ) { m_PlayerCallbackList.Erase( callback ); } /*---------------------------------------------------------------------------* Name: FrameProcess Description: サウンドフレーム処理 Arguments: None. Returns: None. *---------------------------------------------------------------------------*/ void SoundThread::FrameProcess() { CriticalSection::ScopedLock lock( m_CriticalSection ); #ifndef NW_SND_AVAILABLE_NN_SND_STARTSOUNDTHREAD2 // ユーザー処理 if ( m_UserCallback != NULL ) { m_UserCallback( m_UserCallbackArg ); } #endif // サウンドフレーム開始コールバック { for ( SoundFrameCallbackList::Iterator itr = m_SoundFrameCallbackList.GetBeginIter(); itr != m_SoundFrameCallbackList.GetEndIter(); ) { SoundFrameCallbackList::Iterator curItr = itr++; curItr->OnBeginSoundFrame(); } } // サウンドスレッドのメイン処理 { // ボイス更新 { // NW_PROFILE("SoundThread::VoiceUpdate"); VoiceUpdate(); } #ifdef NW_PLATFORM_CTRWIN // Free予約されているAxVoiceをFreeする HardwareChannelManager::GetInstance().FreeAllReservedHardwareChannel(); #endif // HardwareManager 更新 HardwareManager::GetInstance().Update(); // コマンド処理 { // NW_PROFILE("SoundThread::ProcessCommand"); DriverCommandManager::GetInstance().ProcessCommand(); DriverCommandManager::GetInstanceForTaskThread().ProcessCommand(); } // プレイヤー更新 { // NW_PROFILE("SoundThread::UpdatePlayer"); for ( PlayerCallbackList::Iterator itr = m_PlayerCallbackList.GetBeginIter(); itr != m_PlayerCallbackList.GetEndIter(); ) { PlayerCallbackList::Iterator curItr = itr++; curItr->OnUpdateFrameSoundThread(); } } // チャンネル更新 { // NW_PROFILE("SoundThread::UpdateChannel"); ChannelManager::GetInstance().UpdateAllChannel(); } // 乱数列更新 (void)Util::CalcRandom(); // ボイスの更新 VoiceManager::GetInstance().UpdateAllVoices(); } // サウンドフレーム終了コールバック { for ( SoundFrameCallbackList::Iterator itr = m_SoundFrameCallbackList.GetBeginIter(); itr != m_SoundFrameCallbackList.GetEndIter(); ) { SoundFrameCallbackList::Iterator curItr = itr++; curItr->OnEndSoundFrame(); } } } /*---------------------------------------------------------------------------* Name: SoundThreadProc Description: サウンドスレッドプロシージャ Arguments: None. Returns: None. *---------------------------------------------------------------------------*/ void SoundThread::SoundThreadProc() { #ifdef NW_PLATFORM_CTRWIN uptr message; while ( true ) { // Wait for DSP Callback message = m_BlockingQueue.Dequeue(); if ( static_cast( message ) == MESSAGE_HW_CALLBACK ) { FrameProcess(); } else if ( static_cast( message ) == MESSAGE_SHUTDOWN ) { break; } } #else // #ifdef NW_PLATFORM_CTRWIN #if defined( NW_SND_AVAILABLE_NN_SND_STARTSOUNDTHREAD ) if ( m_IsEnableGetTick == true ) { nn::os::Tick processTick; processTick = nn::snd::GetSoundThreadTick(); CalcProcessCost( processTick ); // 1 つ前 (現フレームではないことに注意) の処理時間を取得 } FrameProcess(); #else // if defined( NW_SND_AVAILABLE_NN_SND_STARTSOUNDTHREAD ) while ( m_IsFinalizing == false ) { nn::os::Tick sum, wait, etc; nn::snd::WaitForDspSync( &wait ); { Util::AutoStopWatch watch( etc ); FrameProcess(); nn::snd::SendParameterToDsp(); } sum = wait + etc; CalcProcessCost( sum ); } #endif // if defined( NW_SND_AVAILABLE_NN_SND_STARTSOUNDTHREAD ) #endif // #ifdef NW_PLATFORM_CTRWIN } void SoundThread::VoiceUpdate() { for ( PlayerCallbackList::Iterator itr = m_PlayerCallbackList.GetBeginIter(); itr != m_PlayerCallbackList.GetEndIter(); ) { PlayerCallbackList::Iterator curItr = itr++; curItr->OnUpdateVoiceSoundThread(); } } #ifdef NW_PLATFORM_CTR void SoundThread::CalcProcessCost( const nn::os::Tick& tick ) { if ( m_IsEnableGetTick == true ) { m_SoundThreadSumTick += tick; m_SoundThreadCount += 1; m_DspCycles += nn::snd::GetDspCycles(); // パフォーマンス計測用コード if ( m_IsBeginCalcPerf == true ) { f32 load = tick.ToTimeSpan().GetMicroSeconds() / SND_DENOM; m_PerfHistogram.SetLoad( load ); if ( m_pPerfData != NULL ) { if ( m_PerfDataCount < m_PerfDataNum ) { PerfData& data = m_pPerfData[m_PerfDataCount]; data.voice = SoundSystem::GetVoiceCount(); data.noteOn = m_NoteOnCount; data.load = load; m_PerfDataCount += 1; } } m_NoteOnCount = 0; } } } void SoundThread::GetTickCount( nn::os::Tick& tick, int& count ) const { CriticalSection::ScopedLock lock( m_CriticalSection ); tick = m_SoundThreadSumTick; count = m_SoundThreadCount; } void SoundThread::ClearTickCount() { CriticalSection::ScopedLock lock( m_CriticalSection ); m_SoundThreadSumTick = nn::os::Tick( 0 ); m_SoundThreadCount = 0; } #endif } // namespace nw::snd::internal::driver } // namespace nw::snd::internal } // namespace nw::snd } // namespace nw