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