1 /*---------------------------------------------------------------------------*
2 Project: Revolution WPAD AX demo
3 File: wpad_spdemo.c
4
5 Copyright (C)2006 Nintendo 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 $Log: wpad_spdemo.c,v $
14 Revision 1.2 08/03/2006 13:32:55 tojo
15 Removed old APIs.
16
17 Revision 1.1 07/27/2006 02:33:26 aka
18 Initial check-in.
19
20 $NoKeywords: $
21 *---------------------------------------------------------------------------*/
22
23 #include <string.h>
24
25 #include <demo.h>
26 #include <revolution/mem.h>
27 #include <revolution/mix.h>
28 #include <revolution/sp.h>
29 #include <revolution/wenc.h>
30 #include <revolution/wpad.h>
31
32 /*---------------------------------------------------------------------------*
33 SP
34 *---------------------------------------------------------------------------*/
35
36 // zero buffer
37 #define ZEROBUFFER_BYTES 512
38
39 // data
40 #define SPT_FILE "/spdemo/spdemo.spt"
41 #define SPD_FILE "/spdemo/spdemo.spd"
42
43 #define SFX_MENU 0 // not used
44 #define SFX_DRUM 1 // chan 0
45 #define SFX_GUNSHOT 2 // chan 1
46 #define VOICE_NGC_MAN 3 // chan 2
47 #define VOICE_NGC_WOMAN 4 // chan 3
48 #define SFX_HONK_LOOPED 5 // not used
49
50 static SPSoundTable *SpTable;
51
52 // application-layer voice abstraction
53 #define MAX_DEMO_VOICES 64
54
55 typedef struct
56 {
57 AXVPB *voice;
58 SPSoundEntry *entry;
59 s32 chan;
60
61 } VoiceInfo;
62
63 static VoiceInfo vInfo[MAX_DEMO_VOICES];
64
65 // function prototypes
66 static void AudioFrameCallback ( void );
67 static void *LoadFileIntoRam ( char *path );
68
69 static void InitVoice ( void );
70 static void PlaySfx ( s32 chan );
71 static void StopSfx ( s32 chan );
72 static VoiceInfo *GetVoice ( void );
73 static void DropVoiceCallback ( void *p );
74
75 /*---------------------------------------------------------------------------*
76 WPAD
77 *---------------------------------------------------------------------------*/
78
79 // allocator functions for WPAD
80 static void *myAlloc ( u32 size );
81 static u8 myFree ( void *ptr );
82
83 // function prototypes
84 static void UpdateSpeaker ( OSAlarm *alarm, OSContext *context );
85 static void SpeakerCallback ( s32 chan, s32 result );
86 static void SpeakerOnCallback ( s32 chan, s32 result );
87 static void ConnectCallback ( s32 chan, s32 reason );
88
89 // Speaker Status
90 typedef struct SpeakerInfo
91 {
92 u8 active;
93 WENCInfo encInfo;
94 BOOL first;
95 BOOL last;
96
97 } SpeakerInfo;
98
99 // Controller Status
100 typedef struct ContInfo
101 {
102 u32 type;
103 s32 status;
104 WPADStatus currStat;
105 WPADStatus prevStat;
106 SpeakerInfo Speakers;
107
108 u8 play;
109
110 } ContInfo;
111
112 static ContInfo info[WPAD_MAX_CONTROLLERS];
113
114 // Periodic Alarms for audio streaming
115 static OSAlarm SpeakerAlarm;
116
117 // Audio buffers
118 #define SAMPLES_PER_AUDIO_PACKET 40
119 #define AUDIO_PACKET_LEN 20 // SAMPLES_PER_AUDIO_PACKET / 2
120
121 /*---------------------------------------------------------------------------*
122 etc.
123 *---------------------------------------------------------------------------*/
124
125 // Exp Heap
126 static MEMHeapHandle hExpHeap;
127
128 // function prototypes
129 static void initialize();
130 static void RenderOperation();
131 static void RenderControllerStatus();
132
133 /*---------------------------------------------------------------------------*
134 * Name : main
135 * Description : main
136 * Arguments : None.
137 * Returns : None.
138 *---------------------------------------------------------------------------*/
main(void)139 void main (void)
140 {
141 s32 i;
142 s32 chan;
143 u16 button;
144
145 void *arenaMem2Lo;
146 void *arenaMem2Hi;
147
148 u8 *SpData;
149 u8 *ZeroBuffer;
150
151 // Initialize DEMO
152 initialize();
153
154 // initialize Exp Heap on MEM2
155 arenaMem2Lo = OSGetMEM2ArenaLo();
156 arenaMem2Hi = OSGetMEM2ArenaHi();
157 hExpHeap = MEMCreateExpHeap(arenaMem2Lo, (u32)arenaMem2Hi - (u32) arenaMem2Lo);
158 ASSERT(hExpHeap != NULL);
159
160 //
161 // Initialize Audio
162 //
163
164 AIInit(NULL);
165 AXInit();
166 MIXInit();
167
168 // Load Audio Data
169 SpTable = LoadFileIntoRam(SPT_FILE);
170 SpData = LoadFileIntoRam(SPD_FILE);
171
172 // Alloc Zero Buffer
173 ZeroBuffer = MEMAllocFromExpHeapEx(hExpHeap, ZEROBUFFER_BYTES, 8);
174 memset(ZeroBuffer, 0, ZEROBUFFER_BYTES);
175 DCFlushRange(ZeroBuffer, ZEROBUFFER_BYTES);
176
177 // Register Callback with AX for audio processing
178 AXRegisterCallback(&AudioFrameCallback);
179
180 // Initialize Sound Table
181 SPInitSoundTable(SpTable, SpData, ZeroBuffer);
182
183 // Application-layer Voice Abstraction
184 InitVoice();
185
186 //
187 // Initialize WPAD
188 //
189
190 WPADRegisterAllocator(myAlloc, myFree);
191
192 WPADInit();
193
194 while (WPADGetStatus() != WPAD_STATE_SETUP)
195 {
196 ;
197 }
198
199 for (i = 0; i < WPAD_MAX_CONTROLLERS; i++)
200 {
201 WPADSetConnectCallback((s32)i, ConnectCallback);
202 }
203
204 OSCreateAlarm(&SpeakerAlarm);
205 OSSetPeriodicAlarm(&SpeakerAlarm, OSGetTime(), WPAD_STRM_INTERVAL, UpdateSpeaker);
206
207
208 // Spin
209 while (1)
210 {
211 for (chan = 0; chan < WPAD_MAX_CONTROLLERS; chan++)
212 {
213 info[chan].status = WPADProbe(chan, &info[chan].type);
214
215 if (info[chan].status == WPAD_ERR_NONE)
216 {
217 WPADRead(chan, &info[chan].currStat);
218
219 button = WPADButtonDown(info[chan].prevStat.button, info[chan].currStat.button);
220 info[chan].prevStat = info[chan].currStat;
221
222 if (button & WPAD_BUTTON_A)
223 {
224 PlaySfx(chan);
225 }
226 }
227 }
228
229 DEMOBeforeRender();
230 RenderOperation();
231 RenderControllerStatus();
232 DEMODoneRender();
233 }
234
235 OSCancelAlarm(&SpeakerAlarm);
236 }
237
238 /*---------------------------------------------------------------------------*
239 * Name : UpdateSpeaker
240 * Description :
241 * Arguments :
242 * Returns : None.
243 *---------------------------------------------------------------------------*/
UpdateSpeaker(OSAlarm * alarm,OSContext * context)244 static void UpdateSpeaker(OSAlarm *alarm, OSContext *context)
245 {
246 #pragma unused(alarm, context)
247
248 s16 pcmData[SAMPLES_PER_AUDIO_PACKET];
249 u8 encData[AUDIO_PACKET_LEN];
250 u32 flag;
251 s32 chan;
252 BOOL adv = FALSE;
253
254 for (chan = 0; chan < WPAD_MAX_CONTROLLERS; chan++)
255 {
256 if (SAMPLES_PER_AUDIO_PACKET == AXRmtGetSamples(chan, pcmData, SAMPLES_PER_AUDIO_PACKET))
257 {
258 adv = TRUE;
259
260 if (info[chan].Speakers.active)
261 {
262 flag = (info[chan].Speakers.first) ? (u32)WENC_FLAG_FIRST : (u32)WENC_FLAG_CONT;
263 if (info[chan].Speakers.first)
264 {
265 info[chan].Speakers.first = FALSE;
266 }
267
268 WENCGetEncodeData(&info[chan].Speakers.encInfo,
269 flag,
270 pcmData,
271 SAMPLES_PER_AUDIO_PACKET,
272 encData);
273
274 WPADSendStreamData(chan, encData, AUDIO_PACKET_LEN);
275 }
276 }
277 }
278
279 if (adv)
280 {
281 AXRmtAdvancePtr(SAMPLES_PER_AUDIO_PACKET);
282 }
283 }
284
285 /*---------------------------------------------------------------------------*
286 * Name : SpeakerCallback
287 * Description :
288 * Arguments :
289 * Returns : None.
290 *---------------------------------------------------------------------------*/
SpeakerCallback(s32 chan,s32 result)291 static void SpeakerCallback( s32 chan, s32 result )
292 {
293 if (result == WPAD_ERR_NONE)
294 {
295 info[chan].Speakers.active = 1;
296 info[chan].Speakers.first = TRUE;
297 info[chan].Speakers.last = FALSE;
298 memset(&info[chan].Speakers.encInfo, 0, sizeof(WENCInfo));
299
300 OSReport("Chan[%d] is ready\n", chan);
301 }
302 }
303
304 /*---------------------------------------------------------------------------*
305 * Name : SpeakerOnCallback
306 * Description :
307 * Arguments :
308 * Returns : None.
309 *---------------------------------------------------------------------------*/
SpeakerOnCallback(s32 chan,s32 result)310 static void SpeakerOnCallback( s32 chan, s32 result )
311 {
312 if (result == WPAD_ERR_NONE)
313 {
314 WPADControlSpeaker(chan, WPAD_SPEAKER_PLAY, SpeakerCallback);
315 }
316 }
317
318 /*---------------------------------------------------------------------------*
319 * Name : ConnectCallback
320 * Description :
321 * Arguments :
322 * Returns : None.
323 *---------------------------------------------------------------------------*/
ConnectCallback(s32 chan,s32 reason)324 static void ConnectCallback( s32 chan, s32 reason )
325 {
326 OSReport("ConnectCallback(%d) : %s\n", chan, (reason < 0) ? "disconnect" : "connect");
327
328 info[chan].Speakers.active = 0;
329 if (reason >= 0)
330 {
331 WPADSetDataFormat(chan, WPAD_FMT_CORE);
332 WPADControlSpeaker(chan, WPAD_SPEAKER_ON, SpeakerOnCallback);
333 }
334 else
335 {
336 StopSfx(chan);
337 }
338 }
339
340 /*---------------------------------------------------------------------------*
341 * Name : myAlloc
342 * Description :
343 * Arguments : None.
344 * Returns : None.
345 *---------------------------------------------------------------------------*/
myAlloc(u32 size)346 static void *myAlloc(u32 size)
347 {
348 void *ptr;
349
350 ptr = MEMAllocFromExpHeap(hExpHeap, size);
351 ASSERTMSG(ptr, "Memory allocation failed\n");
352
353 return(ptr);
354 }
355
356 /*---------------------------------------------------------------------------*
357 * Name : myFree
358 * Description :
359 * Arguments : None.
360 * Returns : None.
361 *---------------------------------------------------------------------------*/
myFree(void * ptr)362 static u8 myFree(void *ptr)
363 {
364 MEMFreeToExpHeap(hExpHeap, ptr);
365 return(1);
366 }
367
368 /*---------------------------------------------------------------------------*
369 * Name : AudioFrameCallback
370 * Description : Callback that process audio data per 3ms audio frame.
371 * Arguments : None.
372 * Returns : : None.
373 *---------------------------------------------------------------------------*/
AudioFrameCallback(void)374 static void AudioFrameCallback(void)
375 {
376 u32 i;
377
378 info[0].play = info[1].play = info[2].play = info[3].play = 0;
379
380 for (i = 0; i < MAX_DEMO_VOICES; i++)
381 {
382 if (vInfo[i].voice)
383 {
384 if ( AX_PB_STATE_STOP == ((vInfo[i].voice)->pb.state))
385 {
386 MIXReleaseChannel(vInfo[i].voice);
387 AXFreeVoice(vInfo[i].voice);
388 vInfo[i].voice = NULL;
389 vInfo[i].entry = NULL;
390 vInfo[i].chan = -1;
391 }
392 else
393 {
394 info[vInfo[i].chan].play = 1;
395 }
396 }
397 }
398
399 // tell the mixer to update settings
400 MIXUpdateSettings();
401 }
402
403 /*---------------------------------------------------------------------------*
404 * Name : LoadFileIntoRam
405 * Description : Loads a file into memory. Memory is allocated.
406 * Arguments : path File to load into main memory
407 * Returns : : pointer to file in main memory or NULL if not opened
408 *---------------------------------------------------------------------------*/
LoadFileIntoRam(char * path)409 static void *LoadFileIntoRam(char *path)
410 {
411 DVDFileInfo handle;
412 u32 round_length;
413 s32 read_length;
414 void *buffer;
415
416 // Open File
417 if (!DVDOpen(path, &handle))
418 {
419 OSReport("WARNING! Failed to open %s\n", path);
420 return NULL;
421 }
422
423 // Make sure file length is not 0
424 if (DVDGetLength(&handle) == 0)
425 {
426 OSReport("WARNING! File length is 0\n");
427 return NULL;
428 }
429
430 round_length = OSRoundUp32B(DVDGetLength(&handle));
431 buffer = MEMAllocFromExpHeapEx(hExpHeap, round_length, 32);
432
433 // Make sure we got a buffer
434 if (buffer == NULL)
435 {
436 OSReport("WARNING! Unable to allocate buffer\n");
437 return NULL;
438 }
439
440 // Read Files
441 read_length = DVDRead(&handle, buffer, (s32)(round_length), 0);
442
443 // Make sure we read the file correctly
444 if (read_length <= 0)
445 {
446 OSReport("WARNING! File lenght is wrong\n");
447 return NULL;
448 }
449
450 return buffer;
451 }
452
453 /*---------------------------------------------------------------------------*
454 * Name : InitVoice
455 * Description :
456 * Arguments : None.
457 * Returns : : None.
458 *---------------------------------------------------------------------------*/
InitVoice(void)459 static void InitVoice(void)
460 {
461 s32 i;
462 for (i = 0; i < MAX_DEMO_VOICES; i++)
463 {
464 vInfo[i].voice = NULL;
465 vInfo[i].entry = NULL;
466 vInfo[i].chan = -1;
467 }
468 }
469
470 /*---------------------------------------------------------------------------*
471 * Name : PlaySfx
472 * Description :
473 * Arguments : chan
474 * Returns : : None.
475 *---------------------------------------------------------------------------*/
PlaySfx(s32 chan)476 static void PlaySfx(s32 chan)
477 {
478 VoiceInfo *v;
479 BOOL old;
480
481 old = OSDisableInterrupts();
482
483 v = GetVoice();
484 if (v)
485 {
486 v->voice = AXAcquireVoice(15, DropVoiceCallback, 0);
487 if (v->voice)
488 {
489 v->entry = SPGetSoundEntry(SpTable, (u32)(chan + 1));
490 v->chan = chan;
491
492 SPPrepareSound(v->entry, v->voice, (v->entry)->sampleRate);
493
494 MIXInitChannel(v->voice, 0, 0, -960, -960, -960, 64, 127, -960);
495 switch(chan)
496 {
497 case 0:
498 MIXRmtSetVolumes(v->voice, 0, 0, -960, -960, -960, -960, -960, -960, -960);
499 break;
500 case 1:
501 MIXRmtSetVolumes(v->voice, 0, -960, 0, -960, -960, -960, -960, -960, -960);
502 break;
503 case 2:
504 MIXRmtSetVolumes(v->voice, 0, -960, -960, 0, -960, -960, -960, -960, -960);
505 break;
506 default:
507 MIXRmtSetVolumes(v->voice, 0, -960, -960, -960, 0, -960, -960, -960, -960);
508 break;
509 }
510
511 AXSetVoiceRmtOn(v->voice, AX_PB_REMOTE_ON);
512 AXSetVoiceState(v->voice, AX_PB_STATE_RUN);
513
514 info[chan].play = 1;
515 }
516 }
517
518 OSRestoreInterrupts(old);
519 }
520
521 /*---------------------------------------------------------------------------*
522 * Name : StopSfx
523 * Description :
524 * Arguments : chan
525 * Returns : : None.
526 *---------------------------------------------------------------------------*/
StopSfx(s32 chan)527 static void StopSfx(s32 chan)
528 {
529 s32 i;
530 BOOL old;
531
532 old = OSDisableInterrupts();
533
534 for (i = 0; i < MAX_DEMO_VOICES; i++)
535 {
536 if (chan == vInfo[i].chan)
537 {
538 AXSetVoiceState(vInfo[i].voice, AX_PB_STATE_STOP);
539 }
540 }
541
542 OSRestoreInterrupts(old);
543 }
544
545 /*---------------------------------------------------------------------------*
546 * Name : GetVoice
547 * Description :
548 * Arguments : None.
549 * Returns : : pointer to VoiceInfo.
550 *---------------------------------------------------------------------------*/
GetVoice(void)551 static VoiceInfo *GetVoice(void)
552 {
553 s32 i;
554
555 for (i = 0; i < MAX_DEMO_VOICES; i++)
556 {
557 if (NULL == vInfo[i].voice)
558 {
559 return(&vInfo[i]);
560 }
561 }
562
563 return(NULL);
564 }
565
566 /*---------------------------------------------------------------------------*
567 * Name : DropVoiceCallback
568 * Description :
569 * Arguments :
570 * Returns : : None.
571 *---------------------------------------------------------------------------*/
DropVoiceCallback(void * p)572 static void DropVoiceCallback(void *p)
573 {
574 s32 i;
575
576 for (i = 0; i < MAX_DEMO_VOICES; i++)
577 {
578 if ( (AXVPB *)(p) == vInfo[i].voice)
579 {
580 MIXReleaseChannel(vInfo[i].voice);
581 vInfo[i].voice = NULL;
582 vInfo[i].entry = NULL;
583 vInfo[i].chan = -1;
584 break;
585 }
586 }
587 }
588
589 /*---------------------------------------------------------------------------*
590 * Name : RenderOperation
591 * Description :
592 * Arguments : None.
593 * Returns : : None.
594 *---------------------------------------------------------------------------*/
595 static const s16 HEIGHT = 10;
RenderOperation(void)596 static void RenderOperation(void)
597 {
598 s16 y = 64;
599
600 DEMOPrintf( 16, y += HEIGHT, 0, "button A : Start/Stop Sfx");
601 }
602
603 /*---------------------------------------------------------------------------*
604 * Name : RenderControllerStatus
605 * Description :
606 * Arguments : None.
607 * Returns : : None.
608 *---------------------------------------------------------------------------*/
RenderControllerStatus(void)609 static void RenderControllerStatus(void)
610 {
611 s16 y = 16;
612 int chan;
613
614 DEMOPrintf( 150, y, 0, "speaker");
615 DEMOPrintf( 220, y, 0, "sfx");
616 for( chan = 0; chan < WPAD_MAX_CONTROLLERS; chan++)
617 {
618 y += HEIGHT;
619 DEMOPrintf( 16, y, 0, "CHAN_%d [%s]",
620 chan,
621 (info[chan].status == WPAD_ERR_NO_CONTROLLER) ? "--" :
622 (info[chan].type == 0) ? "CORE" :
623 (info[chan].type == 1) ? "NUNCHAKU" :
624 (info[chan].type == 2) ? "CLASSIC" :
625 "UNKNOWN"
626 );
627 DEMOPrintf( 150, y, 0, "%s", (info[chan].Speakers.active == 1) ? "ON" :
628 (info[chan].Speakers.active == 2) ? "MUTE" :
629 "OFF");
630 DEMOPrintf( 220, y, 0, "%s", (info[chan].play == 0) ? "STOP" :
631 "PLAY");
632 }
633 }
634
635 /*---------------------------------------------------------------------------*
636 * Name : initialize
637 * Description : Initialize GX.
638 * Arguments : None.
639 * Returns : : None.
640 *---------------------------------------------------------------------------*/
initialize(void)641 static void initialize(void)
642 {
643 const GXColor DARKBLUE = { 0, 0, 64, 255 };
644 const int SCREEN_WIDTH = 320;
645 const int SCREEN_HEIGHT = 240;
646
647 DEMOInit( &GXNtsc480IntDf );
648 GXSetCopyClear( DARKBLUE, GX_MAX_Z24 );
649 GXCopyDisp( DEMOGetCurrentBuffer(), GX_TRUE );
650 DEMOInitCaption( DM_FT_XLU, SCREEN_WIDTH, SCREEN_HEIGHT );
651 GXSetZMode( GX_ENABLE, GX_ALWAYS, GX_ENABLE ); // Set pixel processing mode
652 GXSetBlendMode( GX_BM_BLEND, GX_BL_ONE, GX_BL_ONE, GX_LO_CLEAR ); // Translucent mode
653
654 DEMOPadInit();
655 }
656