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