/*---------------------------------------------------------------------------* Project: NintendoWare File: main.cpp Copyright (C)2009-2011 Nintendo/HAL Laboratory, Inc. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo and/or its licensed developers and are protected by national and international copyright laws. 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. The content herein is highly confidential and should be handled accordingly. $Revision: $ *---------------------------------------------------------------------------*/ #include "precompiled.h" #include #include // snd_Bcwav.h #include "demolib.h" #include "simple.csid" class WithSdkApp : 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[] = "WithSdk"; 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 が必要 const nn::snd::Bcwav::DspAdpcmInfo* m_sDspAdpcmInfo; int s_SdkVoicePan; // 0 = 左、100 = 右とする bool s_IsSdkVoicePause; bool s_IsSdkVoicePausePre; bool s_SdkVoicePauseTrigger; void* s_pMemoryForSdkVoice; void MySoundFrameProcess( uptr ) { // SetSoundFrameUserCallback で設定したコールバック関数の中なので、 // SoundThreadScopedLock でサウンドスレッドをロックする必要はありません。 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; } } } } // anonymous namespace void WithSdkApp::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 WithSdkApp::InitializeSoundSystem() { // サウンドシステムの初期化 { nw::snd::SoundSystem::SoundSystemParam param; 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 ); } // サウンドアーカイブの初期化 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 WithSdkApp::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 ) ); } // アプリケーションコアでサウンドスレッドを動作させている状況下で、 // SetSoundFrameUserCallback で設定したコールバック関数の外から s_pVoice を操作する場合は、 // SoundThreadScopedLock でサウンドスレッドをロックする必要があります。 { nw::snd::SoundSystem::SoundThreadScopedLock lock; // システムコア動作時は、優先度を nn::snd::VOICE_PRIORITY_NODROP にする必要がある s_pVoice = nn::snd::AllocVoice( 128, NULL, NULL ); // s_pVoice = nn::snd::AllocVoice( nn::snd::VOICE_PRIORITY_NODROP, 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 ); } } // nw::snd サウンドフレーム処理への処理追加 nw::snd::SoundSystem::SetSoundFrameUserCallback( MySoundFrameProcess, NULL ); } void WithSdkApp::OnFinalize() { nw::snd::SoundSystem::ClearSoundFrameUserCallback(); m_Heap.Destroy(); m_ArchivePlayer.Finalize(); m_DataManager.Finalize(); m_Archive.Close(); nw::snd::SoundSystem::Finalize(); MemFree( m_pMemoryForSoundSystem ); MemFree( m_pMemoryForInfoBlock ); MemFree( m_pMemoryForSoundDataManager ); MemFree( m_pMemoryForSoundArchivePlayer ); MemFree( m_pMemoryForSoundHeap ); MemFree( m_pMemoryForStreamBuffer ); } void WithSdkApp::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 WithSdkApp::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 WithSdkApp::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 WithSdkApp::OnUpdate() { m_ArchivePlayer.Update(); { if ( s_IsSdkVoicePausePre != s_IsSdkVoicePause ) { s_SdkVoicePauseTrigger = true; } else { s_SdkVoicePauseTrigger = false; } s_IsSdkVoicePausePre = s_IsSdkVoicePause; } } void nnMain() { WithSdkApp app; app.Initialize(); app.Run(); app.Finalize(); }