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