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 WithSdkApp : 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[] = "WithSdk";
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 const nn::snd::Bcwav::DspAdpcmInfo* m_sDspAdpcmInfo;
79 int  s_SdkVoicePan;                      // 0 = 左、100 = 右とする
80 bool s_IsSdkVoicePause;
81 bool s_IsSdkVoicePausePre;
82 bool s_SdkVoicePauseTrigger;
83 void* s_pMemoryForSdkVoice;
84 
MySoundFrameProcess(uptr)85 void MySoundFrameProcess( uptr )
86 {
87     // SetSoundFrameUserCallback で設定したコールバック関数の中なので、
88     // SoundThreadScopedLock でサウンドスレッドをロックする必要はありません。
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 
105 } // anonymous namespace
106 
OnInitialize()107 void WithSdkApp::OnInitialize()
108 {
109     InitializeSoundSystem();
110 
111     // サウンドデータのロード
112     if ( ! m_DataManager.LoadData( SEQ_MARIOKART, &m_Heap ) )
113     {
114         NW_ASSERTMSG( false, "LoadData(SEQ_MARIOKART) failed." );
115     }
116     if ( ! m_DataManager.LoadData( SE_YOSHI, &m_Heap ) )
117     {
118         NW_ASSERTMSG( false, "LoadData(SE_YOSHI) failed." );
119     }
120 
121     InitializeSdkVoice();
122 }
123 
InitializeSoundSystem()124 void WithSdkApp::InitializeSoundSystem()
125 {
126     // サウンドシステムの初期化
127     {
128         nw::snd::SoundSystem::SoundSystemParam param;
129         size_t workMemSize = nw::snd::SoundSystem::GetRequiredMemSize( param );
130         m_pMemoryForSoundSystem = MemAlloc( workMemSize );
131 
132         // nn::snd::Voice で 1 ボイスだけ再生させるため、
133         // nw::snd で利用するボイス数を 1 つ減らす
134         nw::snd::SoundSystem::SetMaxVoiceCount( 23 );
135         nw::snd::SoundSystem::Initialize(
136                 param,
137                 reinterpret_cast<uptr>( m_pMemoryForSoundSystem ),
138                 workMemSize );
139     }
140 
141     // サウンドアーカイブの初期化
142     if ( ! m_Archive.Open( SOUND_ARC_PATH ) )
143     {
144         NW_ASSERTMSG( 0, "cannot open bcsar(%s)\n", SOUND_ARC_PATH );
145     }
146 
147     // INFO ブロックのロード
148     {
149         size_t infoBlockSize = m_Archive.GetHeaderSize();
150         m_pMemoryForInfoBlock = MemAlloc( infoBlockSize );
151         if ( ! m_Archive.LoadHeader( m_pMemoryForInfoBlock, infoBlockSize ) )
152         {
153             NW_ASSERTMSG( 0, "cannot load infoBlock(%s)", SOUND_ARC_PATH );
154         }
155     }
156 
157     // サウンドデータマネージャーの初期化
158     {
159         size_t setupSize = m_DataManager.GetRequiredMemSize( &m_Archive );
160         m_pMemoryForSoundDataManager = MemAlloc( setupSize );
161         m_DataManager.Initialize( &m_Archive, m_pMemoryForSoundDataManager, setupSize );
162     }
163 
164     // サウンドアーカイブプレイヤーの初期化
165     {
166         size_t setupSize = m_ArchivePlayer.GetRequiredMemSize( &m_Archive );
167         m_pMemoryForSoundArchivePlayer = MemAlloc( setupSize, 32 );
168         size_t setupStrmBufferSize =
169             m_ArchivePlayer.GetRequiredStreamBufferSize( &m_Archive );
170         m_pMemoryForStreamBuffer = MemAlloc( setupStrmBufferSize, 32 );
171         bool result = m_ArchivePlayer.Initialize(
172                 &m_Archive,
173                 &m_DataManager,
174                 m_pMemoryForSoundArchivePlayer, setupSize,
175                 m_pMemoryForStreamBuffer, setupStrmBufferSize );
176         NW_ASSERT( result );
177     }
178 
179     // サウンドヒープの構築
180     {
181         m_pMemoryForSoundHeap = MemAlloc( SOUND_HEAP_SIZE );
182         bool result = m_Heap.Create( m_pMemoryForSoundHeap, SOUND_HEAP_SIZE );
183         NW_ASSERT( result );
184     }
185 }
186 
InitializeSdkVoice()187 void WithSdkApp::InitializeSdkVoice()
188 {
189     // パラメータ初期化
190     {
191         s_pVoice = NULL;
192         for ( int i = 0; i < 2; i++ )
193         {
194             nn::snd::InitializeWaveBuffer( &s_WaveBuffer[i] );
195         }
196         m_sDspAdpcmInfo = NULL;
197         s_SdkVoicePan = 50;
198         s_IsSdkVoicePause = s_IsSdkVoicePausePre = s_SdkVoicePauseTrigger = false;
199         s_pMemoryForSdkVoice = NULL;
200     }
201 
202     // ファイル読み込み
203     {
204         nn::fs::FileReader fileReader;
205         nn::Result result = fileReader.TryInitialize( SDK_WAV_NAME );
206         NN_UTIL_PANIC_IF_FAILED( result );
207 
208         size_t fileSize = fileReader.GetSize();
209         s_pMemoryForSdkVoice = MemAlloc( fileSize, 32 );
210         s32 readSize = fileReader.Read( s_pMemoryForSdkVoice, fileSize );
211         fileReader.Finalize();
212 
213         nn::snd::FlushDataCache( reinterpret_cast<uptr>(s_pMemoryForSdkVoice), readSize );
214     }
215 
216     // bcwav ファイル読み込み、ボイススタート
217     {
218         const nn::snd::Bcwav::WaveInfo& info = nn::snd::Bcwav::GetWaveInfo( s_pMemoryForSdkVoice );
219         const void* wave = nn::snd::Bcwav::GetWave( s_pMemoryForSdkVoice, 0 );
220 
221         if ( info.encoding == 2 ) // DSP ADPCM
222         {
223             m_sDspAdpcmInfo = const_cast<nn::snd::Bcwav::DspAdpcmInfo*>(
224                     nn::snd::Bcwav::GetDspAdpcmInfo( s_pMemoryForSdkVoice, 0 ) );
225         }
226 
227         // アプリケーションコアでサウンドスレッドを動作させている状況下で、
228         // SetSoundFrameUserCallback で設定したコールバック関数の外から s_pVoice を操作する場合は、
229         // SoundThreadScopedLock でサウンドスレッドをロックする必要があります。
230         {
231             nw::snd::SoundSystem::SoundThreadScopedLock lock;
232 
233             // システムコア動作時は、優先度を nn::snd::VOICE_PRIORITY_NODROP にする必要がある
234             s_pVoice = nn::snd::AllocVoice( 128, NULL, NULL );
235             // s_pVoice = nn::snd::AllocVoice( nn::snd::VOICE_PRIORITY_NODROP, NULL, NULL );
236             NN_TASSERT_(s_pVoice);
237 
238             if ( info.encoding == 2 )
239             {
240                 s_pVoice->SetAdpcmParam( m_sDspAdpcmInfo->param );
241             }
242             s_pVoice->SetChannelCount( 1 );
243             s_pVoice->SetSampleFormat( static_cast<nn::snd::SampleFormat>( info.encoding ) );
244             s_pVoice->SetSampleRate( info.sampleRate );
245             s_pVoice->SetVolume( 1.0f );
246             s_pVoice->SetPitch( 1.0f );
247 
248             nn::snd::MixParam mix;
249             PanToMixParam( mix, s_SdkVoicePan );
250             s_pVoice->SetMixParam( mix );
251 
252             s_WaveBuffer[0].bufferAddress = wave;
253             s_WaveBuffer[0].sampleLength = info.loopEndFrame;
254             s_WaveBuffer[0].loopFlag = false;
255             if ( info.encoding == 2 )
256             {
257                 s_WaveBuffer[0].pAdpcmContext = &m_sDspAdpcmInfo->context;
258             }
259             s_pVoice->AppendWaveBuffer( &s_WaveBuffer[0] );
260 
261             if ( info.isLoop )
262             {
263                 int offset = nn::snd::Bcwav::FrameToByte( info.encoding, info.loopStartFrame );
264                 s_WaveBuffer[1].bufferAddress = nn::snd::Bcwav::AddOffsetToPtr( wave, offset );
265                 s_WaveBuffer[1].sampleLength = info.loopEndFrame - info.loopStartFrame;
266                 s_WaveBuffer[1].loopFlag = true;
267                 if ( info.encoding == 2 )
268                 {
269                     s_WaveBuffer[1].pAdpcmContext = &m_sDspAdpcmInfo->loopContext;
270                 }
271                 s_pVoice->AppendWaveBuffer( &s_WaveBuffer[1] );
272             }
273             s_pVoice->SetState( nn::snd::Voice::STATE_PLAY );
274         }
275     }
276 
277     // nw::snd サウンドフレーム処理への処理追加
278     nw::snd::SoundSystem::SetSoundFrameUserCallback( MySoundFrameProcess, NULL );
279 }
280 
OnFinalize()281 void WithSdkApp::OnFinalize()
282 {
283     nw::snd::SoundSystem::ClearSoundFrameUserCallback();
284 
285     m_Heap.Destroy();
286     m_ArchivePlayer.Finalize();
287     m_DataManager.Finalize();
288     m_Archive.Close();
289 
290     nw::snd::SoundSystem::Finalize();
291 
292     MemFree( m_pMemoryForSoundSystem );
293     MemFree( m_pMemoryForInfoBlock );
294     MemFree( m_pMemoryForSoundDataManager );
295     MemFree( m_pMemoryForSoundArchivePlayer );
296     MemFree( m_pMemoryForSoundHeap );
297     MemFree( m_pMemoryForStreamBuffer );
298 }
299 
OnDrawUpLCD(nw::font::TextWriter & writer)300 void WithSdkApp::OnDrawUpLCD( nw::font::TextWriter& writer )
301 {
302     writer.Printf(" DEMO nw::snd %s\n\n", DEMO_TITLE);
303 
304     writer.Print("    -- usage --\n\n");
305     writer.Print("    [A] Play Sequence Sound\n");
306     writer.Print("    [Y] Play Stream Sound\n");
307     writer.Print("    [B] Stop Sound\n\n");
308 
309     writer.Print("    [X] SDK Voice Pause / Start\n");
310     writer.Print("    [R] SDK Voice Pan to Right\n");
311     writer.Print("    [L] SDK Voice Pan to Left\n");
312 
313     writer.Print("    [START] Restart SoundSystem\n\n");
314 }
315 
OnDrawDownLCD(nw::font::TextWriter & writer)316 void WithSdkApp::OnDrawDownLCD( nw::font::TextWriter& writer )
317 {
318     writer.Print(" -- SDK Voice Status --\n\n");
319     writer.Printf(" isPause : %3d\n", s_IsSdkVoicePause );
320     writer.Printf(" pan     : %3d (0:Left / 100:Right)\n", s_SdkVoicePan);
321 }
322 
OnUpdatePad(nw::demo::Pad & pad)323 void WithSdkApp::OnUpdatePad( nw::demo::Pad& pad )
324 {
325     // nw::snd 操作
326     if ( pad.IsButtonDown( nw::demo::Pad::BUTTON_A ) )
327     {
328         m_Handle.Stop( 0 );
329         bool result = m_ArchivePlayer.StartSound( &m_Handle, SEQ_MARIOKART ).IsSuccess();
330         NN_LOG("[SEQ] SEQ_MARIOKART ... (%d)\n", result);
331     }
332 
333     if ( pad.IsButtonDown( nw::demo::Pad::BUTTON_Y ) )
334     {
335         m_Handle.Stop( 0 );
336         bool result = m_ArchivePlayer.StartSound( &m_Handle, STRM_MARIOKART ).IsSuccess();
337         NN_LOG("[STRM] STRM_MARIOKART ... (%d)\n", result );
338     }
339 
340     if ( pad.IsButtonDown( nw::demo::Pad::BUTTON_B ) )
341     {
342         m_Handle.Stop( 3 );
343     }
344 
345 
346     // SDK ボイス操作
347     if ( pad.IsButtonDown( nw::demo::Pad::BUTTON_X ) )
348     {
349         s_IsSdkVoicePause = ! s_IsSdkVoicePause;
350     }
351 
352 
353     if ( pad.IsButtonRepeatFast( nw::demo::Pad::BUTTON_R ) )
354     {
355         s_SdkVoicePan += 10;
356         if ( s_SdkVoicePan > 100 ) s_SdkVoicePan = 100;
357     }
358     if ( pad.IsButtonRepeatFast( nw::demo::Pad::BUTTON_L ) )
359     {
360         s_SdkVoicePan -= 10;
361         if ( s_SdkVoicePan < 0 ) s_SdkVoicePan = 0;
362     }
363 
364     // nn::snd, nw::snd 再起動
365     if ( pad.IsButtonDown( nw::demo::Pad::BUTTON_START ) )
366     {
367         // 終了処理
368         {
369             {
370                 nw::snd::SoundSystem::SoundThreadScopedLock();
371                 s_pVoice->SetState( nn::snd::Voice::STATE_STOP );
372             }
373 
374             OnFinalize();
375             NN_LOG("nw::snd Finalized\n");
376 
377             nn::Result result = nn::snd::Finalize();
378             NN_LOG("nn::snd::Finalize ... %d\n", result.IsSuccess());
379             NN_UTIL_PANIC_IF_FAILED( result );
380             result = nn::dsp::UnloadComponent();
381             NN_LOG("nn::dsp::UnloadComponent ... %d\n", result.IsSuccess());
382             NN_UTIL_PANIC_IF_FAILED( result );
383         }
384 
385         // 再初期化処理
386         {
387             nn::Result result = nn::dsp::LoadDefaultComponent();
388             NN_LOG("nn::dsp::LoadDefaultComponent ... %d\n", result.IsSuccess());
389             NN_UTIL_PANIC_IF_FAILED( result );
390             result = nn::snd::Initialize();
391             NN_LOG("nn::snd::Initialize ... %d\n", result.IsSuccess());
392             NN_UTIL_PANIC_IF_FAILED( result );
393 
394             OnInitialize();
395             NN_LOG("nw::snd Initialized\n");
396         }
397     }
398 }
399 
OnUpdate()400 void WithSdkApp::OnUpdate()
401 {
402     m_ArchivePlayer.Update();
403 
404     {
405         if ( s_IsSdkVoicePausePre != s_IsSdkVoicePause )
406         {
407             s_SdkVoicePauseTrigger = true;
408         }
409         else
410         {
411             s_SdkVoicePauseTrigger = false;
412         }
413         s_IsSdkVoicePausePre = s_IsSdkVoicePause;
414     }
415 }
416 
417 
418 
nnMain()419 void nnMain()
420 {
421     WithSdkApp app;
422 
423     app.Initialize();
424     app.Run();
425     app.Finalize();
426 }
427