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