/*---------------------------------------------------------------------------* Project: NintendoWare File: main.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:$ *---------------------------------------------------------------------------*/ #include "precompiled.h" #include #include // snd_Bcwav.h #include "demolib.h" #include "simple.csid" class CreateSoundThreadManuallyApp : public nw::snd::demolib::AppBase { protected: virtual void OnInitialize(); virtual void OnFinalize(); virtual void OnDrawUpLCD( nw::font::TextWriter& ); virtual void OnDrawDownLCD( nw::font::TextWriter& ); virtual void OnUpdatePad( nw::demo::Pad& ); virtual void OnUpdate(); private: void InitializeSoundSystem(); void InitializeSdkVoice(); nw::snd::RomSoundArchive m_Archive; nw::snd::SoundArchivePlayer m_ArchivePlayer; nw::snd::SoundDataManager m_DataManager; nw::snd::SoundHeap m_Heap; nw::snd::SoundHandle m_Handle; void* m_pMemoryForSoundSystem; void* m_pMemoryForInfoBlock; void* m_pMemoryForSoundDataManager; void* m_pMemoryForSoundArchivePlayer; void* m_pMemoryForSoundHeap; void* m_pMemoryForStreamBuffer; }; namespace { const s32 SOUND_THREAD_PRIORITY = 4; const s32 LOAD_THREAD_PRIORITY = 3; const s32 SOUND_HEAP_SIZE = 1 * 1024 * 1024; const char SOUND_ARC_PATH[] = NW_SND_DEMO_PATH_PREFIX "simple.bcsar"; const char DEMO_TITLE[] = "CreateSoundThreadManually"; const char* SDK_WAV_NAME = NW_SND_DEMO_PATH_PREFIX "sin440.dspadpcm.bcwav"; void PanToMixParam( nn::snd::MixParam& mix, int pan ) { std::memset( &mix, 0, sizeof(mix) ); // ひとまずすべてゼロに初期化 pan = nw::ut::Clamp( pan, 0, 100 ); mix.mainBus[nn::snd::CHANNEL_INDEX_FRONT_LEFT] = 1.0f - pan / 100.f; mix.mainBus[nn::snd::CHANNEL_INDEX_FRONT_RIGHT] = pan / 100.f; } // for nn::snd // (ループ付きモノラル DSP ADPCM 波形を鳴らす) nn::snd::Voice* s_pVoice; nn::snd::WaveBuffer s_WaveBuffer[2]; // ループ波形再生には 2 つの WaveBuffer が必要 nn::snd::Bcwav::DspAdpcmInfo* m_sDspAdpcmInfo; // TODO: SDK-0.10.0 では const にする int s_SdkVoicePan; // 0 = 左、100 = 右とする bool s_IsSdkVoicePause; bool s_IsSdkVoicePausePre; bool s_SdkVoicePauseTrigger; void* s_pMemoryForSdkVoice; nn::os::Thread s_SoundThread; bool s_SoundThreadEnable; void MySoundFrameProcess() { // サウンドスレッドの中から呼ばれるため、ロックする必要はありません。 if ( s_pVoice ) { nn::snd::MixParam mix; PanToMixParam( mix, s_SdkVoicePan ); s_pVoice->SetMixParam( mix ); if ( s_SdkVoicePauseTrigger ) { nn::snd::Voice::State state = s_IsSdkVoicePause ? nn::snd::Voice::STATE_PAUSE : nn::snd::Voice::STATE_PLAY; s_pVoice->SetState( state ); s_SdkVoicePauseTrigger = false; } } } void SoundThreadFunc(uptr arg) { (void)( arg ); s_SoundThreadEnable = true; while ( s_SoundThreadEnable ) { nn::snd::WaitForDspSync(); nw::snd::SoundSystem::SoundFrameProcess(); MySoundFrameProcess(); nn::snd::SendParameterToDsp(); } } } // anonymous namespace void CreateSoundThreadManuallyApp::OnInitialize() { InitializeSoundSystem(); // サウンドデータのロード if ( ! m_DataManager.LoadData( SEQ_MARIOKART, &m_Heap ) ) { NW_ASSERTMSG( false, "LoadData(SEQ_MARIOKART) failed." ); } if ( ! m_DataManager.LoadData( SE_YOSHI, &m_Heap ) ) { NW_ASSERTMSG( false, "LoadData(SE_YOSHI) failed." ); } InitializeSdkVoice(); } void CreateSoundThreadManuallyApp::InitializeSoundSystem() { // サウンドシステムの初期化 { nw::snd::SoundSystem::SoundSystemParam param; param.autoCreateSoundThread = false; size_t workMemSize = nw::snd::SoundSystem::GetRequiredMemSize( param ); m_pMemoryForSoundSystem = MemAlloc( workMemSize ); // nn::snd::Voice で 1 ボイスだけ再生させるため、 // nw::snd で利用するボイス数を 1 つ減らす nw::snd::SoundSystem::SetMaxVoiceCount( 23 ); nw::snd::SoundSystem::Initialize( param, reinterpret_cast( m_pMemoryForSoundSystem ), workMemSize ); // ユーザーアプリ内でサウンドスレッド生成 (nw::snd 内では生成しない) s_SoundThread.StartUsingAutoStack( SoundThreadFunc, NULL, param.soundThreadStackSize, param.soundThreadPriority ); } // サウンドアーカイブの初期化 if ( ! m_Archive.Open( SOUND_ARC_PATH ) ) { NW_ASSERTMSG( 0, "cannot open bcsar(%s)\n", SOUND_ARC_PATH ); } // INFO ブロックのロード { size_t infoBlockSize = m_Archive.GetHeaderSize(); m_pMemoryForInfoBlock = MemAlloc( infoBlockSize ); if ( ! m_Archive.LoadHeader( m_pMemoryForInfoBlock, infoBlockSize ) ) { NW_ASSERTMSG( 0, "cannot load infoBlock(%s)", SOUND_ARC_PATH ); } } // サウンドデータマネージャーの初期化 { size_t setupSize = m_DataManager.GetRequiredMemSize( &m_Archive ); m_pMemoryForSoundDataManager = MemAlloc( setupSize ); m_DataManager.Initialize( &m_Archive, m_pMemoryForSoundDataManager, setupSize ); } // サウンドアーカイブプレイヤーの初期化 { size_t setupSize = m_ArchivePlayer.GetRequiredMemSize( &m_Archive ); m_pMemoryForSoundArchivePlayer = MemAlloc( setupSize, 32 ); size_t setupStrmBufferSize = m_ArchivePlayer.GetRequiredStreamBufferSize( &m_Archive ); m_pMemoryForStreamBuffer = MemAlloc( setupStrmBufferSize, 32 ); bool result = m_ArchivePlayer.Initialize( &m_Archive, &m_DataManager, m_pMemoryForSoundArchivePlayer, setupSize, m_pMemoryForStreamBuffer, setupStrmBufferSize ); NW_ASSERT( result ); } // サウンドヒープの構築 { m_pMemoryForSoundHeap = MemAlloc( SOUND_HEAP_SIZE ); bool result = m_Heap.Create( m_pMemoryForSoundHeap, SOUND_HEAP_SIZE ); NW_ASSERT( result ); } } void CreateSoundThreadManuallyApp::InitializeSdkVoice() { // パラメータ初期化 { s_pVoice = NULL; for ( int i = 0; i < 2; i++ ) { nn::snd::InitializeWaveBuffer( &s_WaveBuffer[i] ); } m_sDspAdpcmInfo = NULL; s_SdkVoicePan = 50; s_IsSdkVoicePause = s_IsSdkVoicePausePre = s_SdkVoicePauseTrigger = false; s_pMemoryForSdkVoice = NULL; } // ファイル読み込み { nn::fs::FileReader fileReader; nn::Result result = fileReader.TryInitialize( SDK_WAV_NAME ); NN_UTIL_PANIC_IF_FAILED( result ); size_t fileSize = fileReader.GetSize(); s_pMemoryForSdkVoice = MemAlloc( fileSize, 32 ); s32 readSize = fileReader.Read( s_pMemoryForSdkVoice, fileSize ); fileReader.Finalize(); nn::snd::FlushDataCache( reinterpret_cast(s_pMemoryForSdkVoice), readSize ); } // bcwav ファイル読み込み、ボイススタート { const nn::snd::Bcwav::WaveInfo& info = nn::snd::Bcwav::GetWaveInfo( s_pMemoryForSdkVoice ); const void* wave = nn::snd::Bcwav::GetWave( s_pMemoryForSdkVoice, 0 ); if ( info.encoding == 2 ) // DSP ADPCM { m_sDspAdpcmInfo = const_cast( nn::snd::Bcwav::GetDspAdpcmInfo( s_pMemoryForSdkVoice, 0 ) ); } // アプリケーションコアでサウンドスレッドを動作させている状況下で、 // サウンドスレッド以外のスレッドから s_pVoice を操作する場合は、 // SoundThreadScopedLock でサウンドスレッドをロックする必要があります。 // (nw::snd ライブラリでサウンドスレッドを起動させない場合も利用できます。 // 具体的には nw::snd::SoundSystem::SoundFrameProcess 内の処理をロックしています。) { nw::snd::SoundSystem::SoundThreadScopedLock lock; s_pVoice = nn::snd::AllocVoice( 128, NULL, NULL ); NN_TASSERT_(s_pVoice); if ( info.encoding == 2 ) { s_pVoice->SetAdpcmParam( m_sDspAdpcmInfo->param ); } s_pVoice->SetChannelCount( 1 ); s_pVoice->SetSampleFormat( static_cast( info.encoding ) ); s_pVoice->SetSampleRate( info.sampleRate ); s_pVoice->SetVolume( 1.0f ); s_pVoice->SetPitch( 1.0f ); nn::snd::MixParam mix; PanToMixParam( mix, s_SdkVoicePan ); s_pVoice->SetMixParam( mix ); s_WaveBuffer[0].bufferAddress = wave; s_WaveBuffer[0].sampleLength = info.loopEndFrame; s_WaveBuffer[0].loopFlag = false; if ( info.encoding == 2 ) { s_WaveBuffer[0].pAdpcmContext = &m_sDspAdpcmInfo->context; } s_pVoice->AppendWaveBuffer( &s_WaveBuffer[0] ); if ( info.isLoop ) { int offset = nn::snd::Bcwav::FrameToByte( info.encoding, info.loopStartFrame ); s_WaveBuffer[1].bufferAddress = nn::snd::Bcwav::AddOffsetToPtr( wave, offset ); s_WaveBuffer[1].sampleLength = info.loopEndFrame - info.loopStartFrame; s_WaveBuffer[1].loopFlag = true; if ( info.encoding == 2 ) { s_WaveBuffer[1].pAdpcmContext = &m_sDspAdpcmInfo->loopContext; } s_pVoice->AppendWaveBuffer( &s_WaveBuffer[1] ); } s_pVoice->SetState( nn::snd::Voice::STATE_PLAY ); } } } void CreateSoundThreadManuallyApp::OnFinalize() { m_Heap.Destroy(); m_ArchivePlayer.Finalize(); m_DataManager.Finalize(); m_Archive.Close(); s_SoundThreadEnable = false; s_SoundThread.Join(); s_SoundThread.Finalize(); nw::snd::SoundSystem::Finalize(); MemFree( m_pMemoryForSoundSystem ); MemFree( m_pMemoryForInfoBlock ); MemFree( m_pMemoryForSoundDataManager ); MemFree( m_pMemoryForSoundArchivePlayer ); MemFree( m_pMemoryForSoundHeap ); MemFree( m_pMemoryForStreamBuffer ); } void CreateSoundThreadManuallyApp::OnDrawUpLCD( nw::font::TextWriter& writer ) { writer.Printf(" DEMO nw::snd %s\n\n", DEMO_TITLE); writer.Print(" -- usage --\n\n"); writer.Print(" [A] Play Sequence Sound\n"); writer.Print(" [Y] Play Stream Sound\n"); writer.Print(" [B] Stop Sound\n\n"); writer.Print(" [X] SDK Voice Pause / Start\n"); writer.Print(" [R] SDK Voice Pan to Right\n"); writer.Print(" [L] SDK Voice Pan to Left\n"); writer.Print(" [START] Restart SoundSystem\n\n"); } void CreateSoundThreadManuallyApp::OnDrawDownLCD( nw::font::TextWriter& writer ) { writer.Print(" -- SDK Voice Status --\n\n"); writer.Printf(" isPause : %3d\n", s_IsSdkVoicePause ); writer.Printf(" pan : %3d (0:Left / 100:Right)\n", s_SdkVoicePan); } void CreateSoundThreadManuallyApp::OnUpdatePad( nw::demo::Pad& pad ) { // nw::snd 操作 if ( pad.IsButtonDown( nw::demo::Pad::BUTTON_A ) ) { m_Handle.Stop( 0 ); bool result = m_ArchivePlayer.StartSound( &m_Handle, SEQ_MARIOKART ).IsSuccess(); NN_LOG("[SEQ] SEQ_MARIOKART ... (%d)\n", result); } if ( pad.IsButtonDown( nw::demo::Pad::BUTTON_Y ) ) { m_Handle.Stop( 0 ); bool result = m_ArchivePlayer.StartSound( &m_Handle, STRM_MARIOKART ).IsSuccess(); NN_LOG("[STRM] STRM_MARIOKART ... (%d)\n", result ); } if ( pad.IsButtonDown( nw::demo::Pad::BUTTON_B ) ) { m_Handle.Stop( 3 ); } // SDK ボイス操作 if ( pad.IsButtonDown( nw::demo::Pad::BUTTON_X ) ) { s_IsSdkVoicePause = ! s_IsSdkVoicePause; } if ( pad.IsButtonRepeatFast( nw::demo::Pad::BUTTON_R ) ) { s_SdkVoicePan += 10; if ( s_SdkVoicePan > 100 ) s_SdkVoicePan = 100; } if ( pad.IsButtonRepeatFast( nw::demo::Pad::BUTTON_L ) ) { s_SdkVoicePan -= 10; if ( s_SdkVoicePan < 0 ) s_SdkVoicePan = 0; } // nn::snd, nw::snd 再起動 if ( pad.IsButtonDown( nw::demo::Pad::BUTTON_START ) ) { // 終了処理 { { nw::snd::SoundSystem::SoundThreadScopedLock(); s_pVoice->SetState( nn::snd::Voice::STATE_STOP ); } OnFinalize(); NN_LOG("nw::snd Finalized\n"); nn::Result result = nn::snd::Finalize(); NN_LOG("nn::snd::Finalize ... %d\n", result.IsSuccess()); NN_UTIL_PANIC_IF_FAILED( result ); result = nn::dsp::UnloadComponent(); NN_LOG("nn::dsp::UnloadComponent ... %d\n", result.IsSuccess()); NN_UTIL_PANIC_IF_FAILED( result ); } // 再初期化処理 { nn::Result result = nn::dsp::LoadDefaultComponent(); NN_LOG("nn::dsp::LoadDefaultComponent ... %d\n", result.IsSuccess()); NN_UTIL_PANIC_IF_FAILED( result ); result = nn::snd::Initialize(); NN_LOG("nn::snd::Initialize ... %d\n", result.IsSuccess()); NN_UTIL_PANIC_IF_FAILED( result ); OnInitialize(); NN_LOG("nw::snd Initialized\n"); } } } void CreateSoundThreadManuallyApp::OnUpdate() { m_ArchivePlayer.Update(); { if ( s_IsSdkVoicePausePre != s_IsSdkVoicePause ) { s_SdkVoicePauseTrigger = true; } else { s_SdkVoicePauseTrigger = false; } s_IsSdkVoicePausePre = s_IsSdkVoicePause; } } void nnMain() { CreateSoundThreadManuallyApp app; app.Initialize(); app.Run(); app.Finalize(); }