/*---------------------------------------------------------------------------* Project: Horizon File: SimplePlayer.cpp Copyright (C)2009 Nintendo Co., Ltd. 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. $Rev$ *---------------------------------------------------------------------------*/ #include #include #include #include #include #include #include #include "SimplePlayer.h" #include "wave.h" #include "HeapManager.h" namespace common { namespace { const int nFiles = 4; // 使用できる voice の最大値は 24 const char* apFileNames[nFiles] = { "rom:/ok.wav", "rom:/ng.wav", "rom:/cursor.wav", "rom:/annotation.wav" }; nn::snd::CTR::Voice* apVoice[nFiles]; nn::snd::CTR::WaveBuffer aBuffer[nFiles]; WaveFmt fmt[nFiles]; WaveData data[nFiles]; u8* apMemory[nFiles]; nn::os::CriticalSection s_CriticalSection; bool s_SoundThreadInitialized = false; // サウンドスレッド関連 const int SOUND_THREAD_STACK_SIZE = 4096; nn::os::StackBuffer s_SoundThreadStack; nn::os::Thread threadSound; bool threadSoundFlag; void SoundThreadFunc() { // サウンド出力をステレオに nn::snd::SetSoundOutputMode(nn::snd::OUTPUT_MODE_STEREO); // マスターボリュームを設定 nn::snd::SetMasterVolume( 1.0 ); NN_LOG("Loading wav files...\n"); // ファイルを開く for (int i = 0; i < nFiles; i++) { if (apFileNames[i] == NULL) continue; nn::snd::Voice* pVoice = NULL; nn::fs::FileReader fileReader; nn::Result result = fileReader.TryInitialize(apFileNames[i]); NN_UTIL_PANIC_IF_FAILED(result); if (::std::strcmp(::std::strrchr(apFileNames[i], '.'), ".wav") == false) { // 連続メモリ領域の取得、確認 apMemory[i] = reinterpret_cast(HeapManager::GetHeap()->Allocate(GetWaveLength(fileReader), 32)); if (apMemory[i] == NULL) { NN_LOG("Failed to allocate continuous memory\n"); continue; } // Wave データを読み込み、キャッシュを無効に data[i].buf = apMemory[i]; LoadWave(fileReader, &fmt[i], &data[i]); nn::snd::FlushDataCache(reinterpret_cast(apMemory[i]), data[i].size); NN_LOG("%s (%1dch, %5dHz, %2d-bit wav file)\n", apFileNames[i], fmt[i].channel, fmt[i].sample_rate, fmt[i].quantum_bits); pVoice = apVoice[i] = nn::snd::AllocVoice(128, NULL, NULL); NN_TASSERT_(pVoice); nn::snd::SampleFormat format = (fmt[i].quantum_bits == 8) ? nn::snd::SAMPLE_FORMAT_PCM8 : nn::snd::SAMPLE_FORMAT_PCM16; pVoice->SetChannelCount( fmt[i].channel ); pVoice->SetSampleFormat( format ); nn::snd::InitializeWaveBuffer(&aBuffer[i]); aBuffer[i].bufferAddress = apMemory[i]; aBuffer[i].sampleLength = nn::snd::GetSampleLength(data[i].size, format, fmt[i].channel); aBuffer[i].loopFlag = false; fileReader.Finalize(); // 音量の設定 nn::snd::MixParam mix; mix.mainBus[nn::snd::CHANNEL_INDEX_FRONT_LEFT ] = 0.707f; // メインボリューム (L) mix.mainBus[nn::snd::CHANNEL_INDEX_FRONT_RIGHT] = 0.707f; // メインボリューム (R) pVoice->SetMixParam(mix); pVoice->SetVolume(1.0f); // pitch の設定 pVoice->SetSampleRate(fmt[i].sample_rate); pVoice->SetPitch(1.0f); } } // 再生開始 for (int i = 0; i < nFiles; i++) { if (apFileNames[i] != NULL) { apVoice[i]->SetState( nn::snd::Voice::STATE_PLAY ); } } s_SoundThreadInitialized = true; threadSoundFlag = true; while (threadSoundFlag) { nn::snd::WaitForDspSync(); // DSP からのデータ受信を待つ。 s_CriticalSection.Enter(); // メインスレッドとの排他制御 nn::snd::SendParameterToDsp(); // パラメータを DSP に送信。 s_CriticalSection.Leave(); // メインスレッドとの排他制御 } // 再生終了 for (int i = 0; i < nFiles; i++) { if (apVoice[i]) { nn::snd::FreeVoice(apVoice[i]); } } } } void InitializeSimplePlayer() { NN_LOG("Initialize SimplePlayer\n"); nn::Result result; const size_t ROMFS_BUFFER_SIZE = 1024 * 64; static char buffer[ROMFS_BUFFER_SIZE]; NN_UTIL_PANIC_IF_FAILED( nn::fs::MountRom(16, 16, buffer, ROMFS_BUFFER_SIZE)); // dsp, snd の初期化 result = nn::dsp::Initialize(); NN_UTIL_PANIC_IF_FAILED(result); result = nn::dsp::LoadDefaultComponent(); NN_UTIL_PANIC_IF_FAILED(result); result = nn::snd::Initialize(); NN_UTIL_PANIC_IF_FAILED(result); s_CriticalSection.Initialize(); // サウンドスレッドを起動(DSP 割り込みイベント待ち) threadSound.Start(SoundThreadFunc, s_SoundThreadStack); } void PlaySound(u8 index) { NN_ASSERT(index <= nFiles); while(!s_SoundThreadInitialized) { nn::os::Thread::Sleep(nn::fnd::TimeSpan::FromMilliSeconds(1)); } nn::snd::CTR::Voice* pVoice = apVoice[index]; if (!pVoice->IsPlaying()) { nn::snd::InitializeWaveBuffer(&aBuffer[index]); aBuffer[index].bufferAddress = apMemory[index]; nn::snd::SampleFormat format = (fmt[index].quantum_bits == 8) ? nn::snd::SAMPLE_FORMAT_PCM8 : nn::snd::SAMPLE_FORMAT_PCM16; aBuffer[index].sampleLength = nn::snd::GetSampleLength(data[index].size, format, fmt[index].channel); aBuffer[index].loopFlag = false; pVoice->AppendWaveBuffer(&aBuffer[index]); NN_LOG("[voice%d] %s (pitch = %f)\n", index, (pVoice->GetState() == nn::snd::Voice::STATE_PAUSE ? "pause" : "play "), pVoice->GetPitch()); } } void FinalizeSimplePlayer() { nn::Result result; // サウンドスレッドの破棄 threadSoundFlag = false; threadSound.Join(); // SND の終了処理 result = nn::snd::Finalize(); NN_UTIL_PANIC_IF_FAILED(result); s_CriticalSection.Finalize(); // DSP の終了処理 result = nn::dsp::UnloadComponent(); NN_UTIL_PANIC_IF_FAILED(result); nn::dsp::Finalize(); NN_LOG("Finalize SimplePlayer\n"); } } // namespace ConsoleBackup /*---------------------------------------------------------------------------* End of file *---------------------------------------------------------------------------*/