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