1 /*---------------------------------------------------------------------------*
2   Project:  NintendoWare
3   File:     main.cpp
4 
5   Copyright (C)2009-2010 Nintendo Co., Ltd./HAL Laboratory, Inc.  All rights reserved.
6 
7   These coded instructions, statements, and computer programs contain
8   proprietary information of Nintendo of America Inc. and/or Nintendo
9   Company Ltd., and are protected by Federal copyright law.  They may
10   not be disclosed to third parties or copied or duplicated in any form,
11   in whole or in part, without the prior written consent of Nintendo.
12 
13   $Revision:$
14  *---------------------------------------------------------------------------*/
15 
16 #include "precompiled.h"
17 
18 #include <nw/snd.h>
19 #include <nn/snd.h> // snd_Bcwav.h
20 #include "demolib.h"
21 #include "simple.csid"
22 
23 class CreateSoundThreadManuallyApp : public nw::snd::demolib::AppBase
24 {
25 protected:
26     virtual void OnInitialize();
27     virtual void OnFinalize();
28     virtual void OnDrawUpLCD( nw::font::TextWriter& );
29     virtual void OnDrawDownLCD( nw::font::TextWriter& );
30     virtual void OnUpdatePad( nw::demo::Pad& );
31     virtual void OnUpdate();
32 
33 private:
34     void InitializeSoundSystem();
35     void InitializeSdkVoice();
36 
37     nw::snd::RomSoundArchive    m_Archive;
38     nw::snd::SoundArchivePlayer m_ArchivePlayer;
39     nw::snd::SoundDataManager   m_DataManager;
40     nw::snd::SoundHeap          m_Heap;
41     nw::snd::SoundHandle        m_Handle;
42 
43     void* m_pMemoryForSoundSystem;
44     void* m_pMemoryForInfoBlock;
45     void* m_pMemoryForSoundDataManager;
46     void* m_pMemoryForSoundArchivePlayer;
47     void* m_pMemoryForSoundHeap;
48     void* m_pMemoryForStreamBuffer;
49 };
50 
51 namespace
52 {
53 
54 const s32 SOUND_THREAD_PRIORITY = 4;
55 const s32 LOAD_THREAD_PRIORITY = 3;
56 const s32 SOUND_HEAP_SIZE = 1 * 1024 * 1024;
57 const char SOUND_ARC_PATH[] = NW_SND_DEMO_PATH_PREFIX "simple.bcsar";
58 const char DEMO_TITLE[] = "CreateSoundThreadManually";
59 
60 const char* SDK_WAV_NAME = NW_SND_DEMO_PATH_PREFIX "sin440.dspadpcm.bcwav";
61 
PanToMixParam(nn::snd::MixParam & mix,int pan)62 void PanToMixParam( nn::snd::MixParam& mix, int pan )
63 {
64     std::memset( &mix, 0, sizeof(mix) );     // ひとまずすべてゼロに初期化
65 
66     pan = nw::ut::Clamp( pan, 0, 100 );
67     mix.mainBus[nn::snd::CHANNEL_INDEX_FRONT_LEFT] = 1.0f - pan / 100.f;
68     mix.mainBus[nn::snd::CHANNEL_INDEX_FRONT_RIGHT] = pan / 100.f;
69 }
70 
71 
72 // for nn::snd
73 // (ループ付きモノラル DSP ADPCM 波形を鳴らす)
74 nn::snd::Voice* s_pVoice;
75 nn::snd::WaveBuffer s_WaveBuffer[2];    // ループ波形再生には 2 つの WaveBuffer が必要
76 nn::snd::Bcwav::DspAdpcmInfo* m_sDspAdpcmInfo;  // TODO: SDK-0.10.0 では const にする
77 int  s_SdkVoicePan;                      // 0 = 左、100 = 右とする
78 bool s_IsSdkVoicePause;
79 bool s_IsSdkVoicePausePre;
80 bool s_SdkVoicePauseTrigger;
81 void* s_pMemoryForSdkVoice;
82 
83 nn::os::Thread s_SoundThread;
84 bool s_SoundThreadEnable;
85 
MySoundFrameProcess()86 void MySoundFrameProcess()
87 {
88     // サウンドスレッドの中から呼ばれるため、ロックする必要はありません。
89     if ( s_pVoice )
90     {
91         nn::snd::MixParam mix;
92         PanToMixParam( mix, s_SdkVoicePan );
93         s_pVoice->SetMixParam( mix );
94 
95         if ( s_SdkVoicePauseTrigger )
96         {
97             nn::snd::Voice::State state =
98                 s_IsSdkVoicePause ? nn::snd::Voice::STATE_PAUSE : nn::snd::Voice::STATE_PLAY;
99             s_pVoice->SetState( state );
100             s_SdkVoicePauseTrigger = false;
101         }
102     }
103 }
104 
SoundThreadFunc(uptr arg)105 void SoundThreadFunc(uptr arg)
106 {
107     (void)( arg );
108     s_SoundThreadEnable = true;
109     while ( s_SoundThreadEnable )
110     {
111         nn::snd::WaitForDspSync();
112         nw::snd::SoundSystem::SoundFrameProcess();
113         MySoundFrameProcess();
114         nn::snd::SendParameterToDsp();
115     }
116 }
117 
118 
119 } // anonymous namespace
120 
OnInitialize()121 void CreateSoundThreadManuallyApp::OnInitialize()
122 {
123     InitializeSoundSystem();
124 
125     // サウンドデータのロード
126     if ( ! m_DataManager.LoadData( SEQ_MARIOKART, &m_Heap ) )
127     {
128         NW_ASSERTMSG( false, "LoadData(SEQ_MARIOKART) failed." );
129     }
130     if ( ! m_DataManager.LoadData( SE_YOSHI, &m_Heap ) )
131     {
132         NW_ASSERTMSG( false, "LoadData(SE_YOSHI) failed." );
133     }
134 
135     InitializeSdkVoice();
136 }
137 
InitializeSoundSystem()138 void CreateSoundThreadManuallyApp::InitializeSoundSystem()
139 {
140     // サウンドシステムの初期化
141     {
142         nw::snd::SoundSystem::SoundSystemParam param;
143         param.autoCreateSoundThread = false;
144         size_t workMemSize = nw::snd::SoundSystem::GetRequiredMemSize( param );
145         m_pMemoryForSoundSystem = MemAlloc( workMemSize );
146 
147         // nn::snd::Voice で 1 ボイスだけ再生させるため、
148         // nw::snd で利用するボイス数を 1 つ減らす
149         nw::snd::SoundSystem::SetMaxVoiceCount( 23 );
150         nw::snd::SoundSystem::Initialize(
151                 param,
152                 reinterpret_cast<uptr>( m_pMemoryForSoundSystem ),
153                 workMemSize );
154 
155 
156         // ユーザーアプリ内でサウンドスレッド生成 (nw::snd 内では生成しない)
157         s_SoundThread.StartUsingAutoStack(
158                 SoundThreadFunc,
159                 NULL,
160                 param.soundThreadStackSize,
161                 param.soundThreadPriority );
162     }
163 
164     // サウンドアーカイブの初期化
165     if ( ! m_Archive.Open( SOUND_ARC_PATH ) )
166     {
167         NW_ASSERTMSG( 0, "cannot open bcsar(%s)\n", SOUND_ARC_PATH );
168     }
169 
170     // INFO ブロックのロード
171     {
172         size_t infoBlockSize = m_Archive.GetHeaderSize();
173         m_pMemoryForInfoBlock = MemAlloc( infoBlockSize );
174         if ( ! m_Archive.LoadHeader( m_pMemoryForInfoBlock, infoBlockSize ) )
175         {
176             NW_ASSERTMSG( 0, "cannot load infoBlock(%s)", SOUND_ARC_PATH );
177         }
178     }
179 
180     // サウンドデータマネージャーの初期化
181     {
182         size_t setupSize = m_DataManager.GetRequiredMemSize( &m_Archive );
183         m_pMemoryForSoundDataManager = MemAlloc( setupSize );
184         m_DataManager.Initialize( &m_Archive, m_pMemoryForSoundDataManager, setupSize );
185     }
186 
187     // サウンドアーカイブプレイヤーの初期化
188     {
189         size_t setupSize = m_ArchivePlayer.GetRequiredMemSize( &m_Archive );
190         m_pMemoryForSoundArchivePlayer = MemAlloc( setupSize, 32 );
191         size_t setupStrmBufferSize =
192             m_ArchivePlayer.GetRequiredStreamBufferSize( &m_Archive );
193         m_pMemoryForStreamBuffer = MemAlloc( setupStrmBufferSize, 32 );
194         bool result = m_ArchivePlayer.Initialize(
195                 &m_Archive,
196                 &m_DataManager,
197                 m_pMemoryForSoundArchivePlayer, setupSize,
198                 m_pMemoryForStreamBuffer, setupStrmBufferSize );
199         NW_ASSERT( result );
200     }
201 
202     // サウンドヒープの構築
203     {
204         m_pMemoryForSoundHeap = MemAlloc( SOUND_HEAP_SIZE );
205         bool result = m_Heap.Create( m_pMemoryForSoundHeap, SOUND_HEAP_SIZE );
206         NW_ASSERT( result );
207     }
208 }
209 
InitializeSdkVoice()210 void CreateSoundThreadManuallyApp::InitializeSdkVoice()
211 {
212     // パラメータ初期化
213     {
214         s_pVoice = NULL;
215         for ( int i = 0; i < 2; i++ )
216         {
217             nn::snd::InitializeWaveBuffer( &s_WaveBuffer[i] );
218         }
219         m_sDspAdpcmInfo = NULL;
220         s_SdkVoicePan = 50;
221         s_IsSdkVoicePause = s_IsSdkVoicePausePre = s_SdkVoicePauseTrigger = false;
222         s_pMemoryForSdkVoice = NULL;
223     }
224 
225     // ファイル読み込み
226     {
227         nn::fs::FileReader fileReader;
228         nn::Result result = fileReader.TryInitialize( SDK_WAV_NAME );
229         NN_UTIL_PANIC_IF_FAILED( result );
230 
231         size_t fileSize = fileReader.GetSize();
232         s_pMemoryForSdkVoice = MemAlloc( fileSize, 32 );
233         s32 readSize = fileReader.Read( s_pMemoryForSdkVoice, fileSize );
234         fileReader.Finalize();
235 
236         nn::snd::FlushDataCache( reinterpret_cast<uptr>(s_pMemoryForSdkVoice), readSize );
237     }
238 
239     // bcwav ファイル読み込み、ボイススタート
240     {
241         const nn::snd::Bcwav::WaveInfo& info = nn::snd::Bcwav::GetWaveInfo( s_pMemoryForSdkVoice );
242         const void* wave = nn::snd::Bcwav::GetWave( s_pMemoryForSdkVoice, 0 );
243 
244         if ( info.encoding == 2 ) // DSP ADPCM
245         {
246             m_sDspAdpcmInfo = const_cast<nn::snd::Bcwav::DspAdpcmInfo*>(
247                     nn::snd::Bcwav::GetDspAdpcmInfo( s_pMemoryForSdkVoice, 0 ) );
248         }
249 
250         // アプリケーションコアでサウンドスレッドを動作させている状況下で、
251         // サウンドスレッド以外のスレッドから s_pVoice を操作する場合は、
252         // SoundThreadScopedLock でサウンドスレッドをロックする必要があります。
253         // (nw::snd ライブラリでサウンドスレッドを起動させない場合も利用できます。
254         //  具体的には nw::snd::SoundSystem::SoundFrameProcess 内の処理をロックしています。)
255         {
256             nw::snd::SoundSystem::SoundThreadScopedLock lock;
257 
258             s_pVoice = nn::snd::AllocVoice( 128, NULL, NULL );
259             NN_TASSERT_(s_pVoice);
260 
261             if ( info.encoding == 2 )
262             {
263                 s_pVoice->SetAdpcmParam( m_sDspAdpcmInfo->param );
264             }
265             s_pVoice->SetChannelCount( 1 );
266             s_pVoice->SetSampleFormat( static_cast<nn::snd::SampleFormat>( info.encoding ) );
267             s_pVoice->SetSampleRate( info.sampleRate );
268             s_pVoice->SetVolume( 1.0f );
269             s_pVoice->SetPitch( 1.0f );
270 
271             nn::snd::MixParam mix;
272             PanToMixParam( mix, s_SdkVoicePan );
273             s_pVoice->SetMixParam( mix );
274 
275             s_WaveBuffer[0].bufferAddress = wave;
276             s_WaveBuffer[0].sampleLength = info.loopEndFrame;
277             s_WaveBuffer[0].loopFlag = false;
278             if ( info.encoding == 2 )
279             {
280                 s_WaveBuffer[0].pAdpcmContext = &m_sDspAdpcmInfo->context;
281             }
282             s_pVoice->AppendWaveBuffer( &s_WaveBuffer[0] );
283 
284             if ( info.isLoop )
285             {
286                 int offset = nn::snd::Bcwav::FrameToByte( info.encoding, info.loopStartFrame );
287                 s_WaveBuffer[1].bufferAddress = nn::snd::Bcwav::AddOffsetToPtr( wave, offset );
288                 s_WaveBuffer[1].sampleLength = info.loopEndFrame - info.loopStartFrame;
289                 s_WaveBuffer[1].loopFlag = true;
290                 if ( info.encoding == 2 )
291                 {
292                     s_WaveBuffer[1].pAdpcmContext = &m_sDspAdpcmInfo->loopContext;
293                 }
294                 s_pVoice->AppendWaveBuffer( &s_WaveBuffer[1] );
295             }
296             s_pVoice->SetState( nn::snd::Voice::STATE_PLAY );
297         }
298     }
299 }
300 
OnFinalize()301 void CreateSoundThreadManuallyApp::OnFinalize()
302 {
303     m_Heap.Destroy();
304     m_ArchivePlayer.Finalize();
305     m_DataManager.Finalize();
306     m_Archive.Close();
307 
308     s_SoundThreadEnable = false;
309     s_SoundThread.Join();
310     s_SoundThread.Finalize();
311 
312     nw::snd::SoundSystem::Finalize();
313 
314     MemFree( m_pMemoryForSoundSystem );
315     MemFree( m_pMemoryForInfoBlock );
316     MemFree( m_pMemoryForSoundDataManager );
317     MemFree( m_pMemoryForSoundArchivePlayer );
318     MemFree( m_pMemoryForSoundHeap );
319     MemFree( m_pMemoryForStreamBuffer );
320 }
321 
OnDrawUpLCD(nw::font::TextWriter & writer)322 void CreateSoundThreadManuallyApp::OnDrawUpLCD( nw::font::TextWriter& writer )
323 {
324     writer.Printf(" DEMO nw::snd %s\n\n", DEMO_TITLE);
325 
326     writer.Print("    -- usage --\n\n");
327     writer.Print("    [A] Play Sequence Sound\n");
328     writer.Print("    [Y] Play Stream Sound\n");
329     writer.Print("    [B] Stop Sound\n\n");
330 
331     writer.Print("    [X] SDK Voice Pause / Start\n");
332     writer.Print("    [R] SDK Voice Pan to Right\n");
333     writer.Print("    [L] SDK Voice Pan to Left\n");
334 
335     writer.Print("    [START] Restart SoundSystem\n\n");
336 }
337 
OnDrawDownLCD(nw::font::TextWriter & writer)338 void CreateSoundThreadManuallyApp::OnDrawDownLCD( nw::font::TextWriter& writer )
339 {
340     writer.Print(" -- SDK Voice Status --\n\n");
341     writer.Printf(" isPause : %3d\n", s_IsSdkVoicePause );
342     writer.Printf(" pan     : %3d (0:Left / 100:Right)\n", s_SdkVoicePan);
343 }
344 
OnUpdatePad(nw::demo::Pad & pad)345 void CreateSoundThreadManuallyApp::OnUpdatePad( nw::demo::Pad& pad )
346 {
347     // nw::snd 操作
348     if ( pad.IsButtonDown( nw::demo::Pad::BUTTON_A ) )
349     {
350         m_Handle.Stop( 0 );
351         bool result = m_ArchivePlayer.StartSound( &m_Handle, SEQ_MARIOKART ).IsSuccess();
352         NN_LOG("[SEQ] SEQ_MARIOKART ... (%d)\n", result);
353     }
354 
355     if ( pad.IsButtonDown( nw::demo::Pad::BUTTON_Y ) )
356     {
357         m_Handle.Stop( 0 );
358         bool result = m_ArchivePlayer.StartSound( &m_Handle, STRM_MARIOKART ).IsSuccess();
359         NN_LOG("[STRM] STRM_MARIOKART ... (%d)\n", result );
360     }
361 
362     if ( pad.IsButtonDown( nw::demo::Pad::BUTTON_B ) )
363     {
364         m_Handle.Stop( 3 );
365     }
366 
367 
368     // SDK ボイス操作
369     if ( pad.IsButtonDown( nw::demo::Pad::BUTTON_X ) )
370     {
371         s_IsSdkVoicePause = ! s_IsSdkVoicePause;
372     }
373 
374 
375     if ( pad.IsButtonRepeatFast( nw::demo::Pad::BUTTON_R ) )
376     {
377         s_SdkVoicePan += 10;
378         if ( s_SdkVoicePan > 100 ) s_SdkVoicePan = 100;
379     }
380     if ( pad.IsButtonRepeatFast( nw::demo::Pad::BUTTON_L ) )
381     {
382         s_SdkVoicePan -= 10;
383         if ( s_SdkVoicePan < 0 ) s_SdkVoicePan = 0;
384     }
385 
386     // nn::snd, nw::snd 再起動
387     if ( pad.IsButtonDown( nw::demo::Pad::BUTTON_START ) )
388     {
389         // 終了処理
390         {
391             {
392                 nw::snd::SoundSystem::SoundThreadScopedLock();
393                 s_pVoice->SetState( nn::snd::Voice::STATE_STOP );
394             }
395 
396             OnFinalize();
397             NN_LOG("nw::snd Finalized\n");
398 
399             nn::Result result = nn::snd::Finalize();
400             NN_LOG("nn::snd::Finalize ... %d\n", result.IsSuccess());
401             NN_UTIL_PANIC_IF_FAILED( result );
402             result = nn::dsp::UnloadComponent();
403             NN_LOG("nn::dsp::UnloadComponent ... %d\n", result.IsSuccess());
404             NN_UTIL_PANIC_IF_FAILED( result );
405         }
406 
407         // 再初期化処理
408         {
409             nn::Result result = nn::dsp::LoadDefaultComponent();
410             NN_LOG("nn::dsp::LoadDefaultComponent ... %d\n", result.IsSuccess());
411             NN_UTIL_PANIC_IF_FAILED( result );
412             result = nn::snd::Initialize();
413             NN_LOG("nn::snd::Initialize ... %d\n", result.IsSuccess());
414             NN_UTIL_PANIC_IF_FAILED( result );
415 
416             OnInitialize();
417             NN_LOG("nw::snd Initialized\n");
418         }
419     }
420 }
421 
OnUpdate()422 void CreateSoundThreadManuallyApp::OnUpdate()
423 {
424     m_ArchivePlayer.Update();
425 
426     {
427         if ( s_IsSdkVoicePausePre != s_IsSdkVoicePause )
428         {
429             s_SdkVoicePauseTrigger = true;
430         }
431         else
432         {
433             s_SdkVoicePauseTrigger = false;
434         }
435         s_IsSdkVoicePausePre = s_IsSdkVoicePause;
436     }
437 }
438 
439 
440 
nnMain()441 void nnMain()
442 {
443     CreateSoundThreadManuallyApp app;
444 
445     app.Initialize();
446     app.Run();
447     app.Finalize();
448 }
449