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