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