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.2.1 2007/12/11 01:17:50 tojo
15 Modified to handle the error code of WPADStatus.
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_INVALID)
282 {
283 button = WPADButtonDown(info[chan].prevStat.cr.button, info[chan].currStat.cr.button);
284 info[chan].prevStat.cr = info[chan].currStat.cr;
285 }
286 if (button & WPAD_BUTTON_A)
287 {
288 switch (info[chan].Speakers.active)
289 {
290 case 0: WPADControlSpeaker(chan, WPAD_SPEAKER_ON, SpeakerOnCallback); break;
291 case 1: WPADControlSpeaker(chan, WPAD_SPEAKER_OFF, SpeakerOffCallback); break;
292 default: break;
293 }
294 }
295
296 if (SEQGetState(&info[chan].Sequence) == SEQ_STATE_RUN)
297 {
298 info[chan].play = 1;
299 }
300 else
301 {
302 info[chan].play = 0;
303
304 if (info[chan].Speakers.active == 1)
305 {
306 info[chan].Speakers.active = 2;
307 WPADControlSpeaker(chan, WPAD_SPEAKER_OFF, SpeakerOffCallback);
308 }
309 }
310 }
311 }
312
313 DEMOBeforeRender();
314
315 DEMOPrintf( 16, 16, 0, "WPAD Demo -- WPAD Seqdemo");
316 RenderOperation();
317 RenderControllerStatus();
318 DEMODoneRender();
319 }
320
321 OSCancelAlarm(&SpeakerAlarm);
322 }
323
324 /*---------------------------------------------------------------------------*
325 * Name : UpdateSpeaker
326 * Description :
327 * Arguments :
328 * Returns : None.
329 *---------------------------------------------------------------------------*/
UpdateSpeaker(OSAlarm * alarm,OSContext * context)330 static void UpdateSpeaker( OSAlarm *alarm, OSContext *context )
331 {
332 #pragma unused(alarm, context)
333
334 u32 flag;
335 s32 chan;
336 BOOL intr;
337
338 if (SAMPLES_PER_AUDIO_PACKET <= AXRmtGetSamplesLeft())
339 {
340
341 for(chan = 0; chan < WPAD_MAX_CONTROLLERS; chan++)
342 {
343
344 AXRmtGetSamples(chan, info[chan].Speakers.pcmData, SAMPLES_PER_AUDIO_PACKET);
345
346 if (info[chan].Speakers.active)
347 {
348 intr = OSDisableInterrupts();
349
350 if (WPADCanSendStreamData(chan))
351 {
352 flag = (info[chan].Speakers.first) ? (u32)WENC_FLAG_FIRST : (u32)WENC_FLAG_CONT;
353 if (info[chan].Speakers.first)
354 {
355 info[chan].Speakers.first = FALSE;
356 }
357
358 WENCGetEncodeData(&info[chan].Speakers.encInfo,
359 flag,
360 info[chan].Speakers.pcmData,
361 SAMPLES_PER_AUDIO_PACKET,
362 info[chan].Speakers.encData);
363 info[chan].Speakers.status = WPADSendStreamData(chan, info[chan].Speakers.encData, AUDIO_PACKET_LEN);
364 }
365
366 OSRestoreInterrupts(intr);
367 }
368 }
369
370 AXRmtAdvancePtr(SAMPLES_PER_AUDIO_PACKET);
371 }
372 }
373
374 /*---------------------------------------------------------------------------*
375 * Name : SpeakerCallback
376 * Description :
377 * Arguments :
378 * Returns : None.
379 *---------------------------------------------------------------------------*/
SpeakerCallback(s32 chan,s32 result)380 static void SpeakerCallback( s32 chan, s32 result )
381 {
382 if (result == WPAD_ERR_NONE)
383 {
384 info[chan].Speakers.active = 1;
385 info[chan].Speakers.first = TRUE;
386 info[chan].Speakers.last = FALSE;
387 memset(&info[chan].Speakers.encInfo, 0, sizeof(WENCInfo));
388
389 info[chan].Speakers.status = WPAD_ERR_NONE;
390 memset(info[chan].Speakers.pcmData, 0, SAMPLES_PER_AUDIO_PACKET);
391 memset(info[chan].Speakers.encData, 0, AUDIO_PACKET_LEN);
392 SEQSetState(&info[chan].Sequence, SEQ_STATE_RUN);
393
394 OSReport("Chan[%d] is ready\n", chan);
395 }
396 }
397
398 /*---------------------------------------------------------------------------*
399 * Name : SpeakerOffCallback
400 * Description :
401 * Arguments :
402 * Returns : None.
403 *---------------------------------------------------------------------------*/
SpeakerOffCallback(s32 chan,s32 result)404 static void SpeakerOffCallback( s32 chan, s32 result )
405 {
406 #pragma unused(result)
407
408 SEQSetState(&info[chan].Sequence, SEQ_STATE_STOP);
409 info[chan].Speakers.active = 0;
410
411 OSReport("Chan[%d] is stopped.\n", chan);
412 }
413
414 /*---------------------------------------------------------------------------*
415 * Name : SpeakerOnCallback
416 * Description :
417 * Arguments :
418 * Returns : None.
419 *---------------------------------------------------------------------------*/
SpeakerOnCallback(s32 chan,s32 result)420 static void SpeakerOnCallback( s32 chan, s32 result )
421 {
422 if (result == WPAD_ERR_NONE)
423 {
424 WPADControlSpeaker(chan, WPAD_SPEAKER_PLAY, SpeakerCallback);
425 }
426 }
427
428 /*---------------------------------------------------------------------------*
429 * Name : ExtensionCallback
430 * Description :
431 * Arguments :
432 * Returns : None.
433 *---------------------------------------------------------------------------*/
ExtensionCallback(s32 chan,s32 result)434 static void ExtensionCallback( s32 chan, s32 result )
435 {
436 switch(result)
437 {
438 case WPAD_DEV_CORE:
439 case WPAD_DEV_FUTURE:
440 case WPAD_DEV_NOT_SUPPORTED: WPADSetDataFormat(chan, WPAD_FMT_CORE); break;
441 case WPAD_DEV_FREESTYLE: WPADSetDataFormat(chan, WPAD_FMT_FREESTYLE); break;
442 case WPAD_DEV_CLASSIC: WPADSetDataFormat(chan, WPAD_FMT_CLASSIC); break;
443 }
444 }
445
446 /*---------------------------------------------------------------------------*
447 * Name : ConnectCallback
448 * Description :
449 * Arguments :
450 * Returns : None.
451 *---------------------------------------------------------------------------*/
ConnectCallback(s32 chan,s32 reason)452 static void ConnectCallback( s32 chan, s32 reason )
453 {
454 OSReport("ConnectCallback(%d) : %s\n", chan, (reason < 0) ? "disconnect" : "connect");
455
456 info[chan].Speakers.active = 0;
457 if (reason >= 0)
458 {
459 WPADSetDataFormat(chan, WPAD_FMT_CORE);
460 WPADSetExtensionCallback(chan, ExtensionCallback);
461 }
462 else
463 {
464 SEQSetState(&info[chan].Sequence, SEQ_STATE_STOP);
465 info[chan].play = 0;
466 }
467 }
468
469 /*---------------------------------------------------------------------------*
470 * Name : myAlloc
471 * Description :
472 * Arguments : None.
473 * Returns : None.
474 *---------------------------------------------------------------------------*/
myAlloc(u32 size)475 static void *myAlloc( u32 size )
476 {
477 void *ptr;
478
479 ptr = MEMAllocFromExpHeap(hExpHeap, size);
480 ASSERTMSG(ptr, "Memory allocation failed\n");
481
482 return(ptr);
483 }
484
485 /*---------------------------------------------------------------------------*
486 * Name : myFree
487 * Description :
488 * Arguments : None.
489 * Returns : None.
490 *---------------------------------------------------------------------------*/
myFree(void * ptr)491 static u8 myFree( void *ptr )
492 {
493 MEMFreeToExpHeap(hExpHeap, ptr);
494 return(1);
495 }
496
497 /*---------------------------------------------------------------------------*
498 * Name : AudioFrameCallback
499 * Description : Callback that process audio data per 3ms audio frame.
500 * Arguments : None.
501 * Returns: : None.
502 *---------------------------------------------------------------------------*/
AudioFrameCallback(void)503 static void AudioFrameCallback( void )
504 {
505 // run the sequencer
506 SEQRunAudioFrame();
507
508 // run the synth
509 SYNRunAudioFrame();
510
511 // tell the mixer to update settings
512 MIXUpdateSettings();
513 }
514
515 /*---------------------------------------------------------------------------*
516 * Name : LoadFileIntoRam
517 * Description : Loads a file into memory. Memory is allocated.
518 * Arguments : path File to load into main memory
519 * Returns: : pointer to file in main memory or NULL if not opened
520 *---------------------------------------------------------------------------*/
LoadFileIntoRam(char * path)521 static void *LoadFileIntoRam( char *path )
522 {
523 DVDFileInfo handle;
524 u32 round_length;
525 s32 read_length;
526 void *buffer;
527
528 // Open File
529 if (!DVDOpen(path, &handle))
530 {
531 OSReport("WARNING! Failed to open %s\n", path);
532 return NULL;
533 }
534
535 // Make sure file length is not 0
536 if (DVDGetLength(&handle) == 0)
537 {
538 OSReport("WARNING! File length is 0\n");
539 return NULL;
540 }
541
542 round_length = OSRoundUp32B(DVDGetLength(&handle));
543 buffer = MEMAllocFromExpHeapEx(hExpHeap, round_length, 32);
544
545 // Make sure we got a buffer
546 if (buffer == NULL)
547 {
548 OSReport("WARNING! Unable to allocate buffer\n");
549 return NULL;
550 }
551
552 // Read Files
553 read_length = DVDRead(&handle, buffer, (s32)(round_length), 0);
554
555 // Make sure we read the file correctly
556 if (read_length <= 0)
557 {
558 OSReport("WARNING! File lenght is wrong\n");
559 return NULL;
560 }
561
562 return buffer;
563 }
564
565 /*---------------------------------------------------------------------------*
566 * Name : RenderOperation
567 * Description :
568 * Arguments : None.
569 * Returns: : None.
570 *---------------------------------------------------------------------------*/
571 static const s16 HEIGHT = 10;
RenderOperation(void)572 static void RenderOperation( void )
573 {
574 s16 y = 80;
575
576 DEMOPrintf( 16, y += HEIGHT, 0, "button A : Start/Stop Sequence");
577 }
578
579 /*---------------------------------------------------------------------------*
580 * Name : RenderControllerStatus
581 * Description :
582 * Arguments : None.
583 * Returns: : None.
584 *---------------------------------------------------------------------------*/
RenderControllerStatus(void)585 static void RenderControllerStatus( void )
586 {
587 s16 y = 32;
588 int chan;
589
590 DEMOPrintf( 150, y, 0, "speaker");
591 DEMOPrintf( 220, y, 0, "sequence");
592 for( chan = 0; chan < WPAD_MAX_CONTROLLERS; chan++)
593 {
594 y += HEIGHT;
595 DEMOPrintf( 16, y, 0, "CHAN_%d [%s]",
596 chan,
597 (info[chan].status == WPAD_ERR_NO_CONTROLLER) ? "--" :
598 (info[chan].type == 0) ? "CORE" :
599 (info[chan].type == 1) ? "NUNCHAKU" :
600 (info[chan].type == 2) ? "CLASSIC" :
601 "UNKNOWN"
602 );
603 DEMOPrintf( 150, y, 0, "%s", (info[chan].Speakers.active == 1) ? "ON" :
604 (info[chan].Speakers.active == 2) ? "MUTE" :
605 "OFF");
606 DEMOPrintf( 220, y, 0, "%s", (info[chan].play == 0) ? "STOP" :
607 "PLAY");
608 }
609 }
610
611 /*---------------------------------------------------------------------------*
612 * Name : initialize
613 * Description : Initialize GX.
614 * Arguments : None.
615 * Returns: : None.
616 *---------------------------------------------------------------------------*/
initialize(void)617 static void initialize( void )
618 {
619 const GXColor DARKBLUE = { 0, 0, 64, 255 };
620 const int SCREEN_WIDTH = 320;
621 const int SCREEN_HEIGHT = 240;
622
623 DEMOInit( &GXNtsc480IntDf );
624 GXSetCopyClear( DARKBLUE, GX_MAX_Z24 );
625 GXCopyDisp( DEMOGetCurrentBuffer(), GX_TRUE );
626 DEMOInitCaption( DM_FT_XLU, SCREEN_WIDTH, SCREEN_HEIGHT );
627 GXSetZMode( GX_ENABLE, GX_ALWAYS, GX_ENABLE ); // Set pixel processing mode
628 GXSetBlendMode( GX_BM_BLEND, GX_BL_ONE, GX_BL_ONE, GX_LO_CLEAR ); // Translucent mode
629
630 DEMOPadInit();
631 }
632
633 /*---------------------------------------------------------------------------*
634 * Name : VoiceInitCallback
635 * Description :
636 * Arguments :
637 * Returns: : None.
638 *---------------------------------------------------------------------------*/
VoiceInitCallback(AXVPB * axvpb,SYNSYNTH * synth,u8 midiChannel)639 static void VoiceInitCallback( AXVPB *axvpb, SYNSYNTH *synth, u8 midiChannel )
640 {
641 #pragma unused(midiChannel)
642
643 s32 i;
644
645 for (i = 0; i < WPAD_MAX_CONTROLLERS; i++)
646 {
647 if (synth == &info[i].Sequence.synth)
648 {
649 break;
650 }
651 }
652
653 switch(i)
654 {
655 case 0:
656 MIXRmtSetVolumes(axvpb, 0, 0, -960, -960, -960, -960, -960, -960, -960);
657 break;
658 case 1:
659 MIXRmtSetVolumes(axvpb, 0, -960, 0, -960, -960, -960, -960, -960, -960);
660 break;
661 case 2:
662 MIXRmtSetVolumes(axvpb, 0, -960, -960, 0, -960, -960, -960, -960, -960);
663 break;
664 default:
665 MIXRmtSetVolumes(axvpb, 0, -960, -960, -960, 0, -960, -960, -960, -960);
666 break;
667 }
668
669 AXSetVoiceRmtOn(axvpb, AX_PB_REMOTE_ON);
670 }
671
672 /*---------------------------------------------------------------------------*
673 * Name : VoiceUpdateCallback
674 * Description :
675 * Arguments :
676 * Returns: : None.
677 *---------------------------------------------------------------------------*/
VoiceUpdateCallback(AXVPB * axvpb,SYNSYNTH * synth,u8 midiChannel)678 static void VoiceUpdateCallback( AXVPB *axvpb, SYNSYNTH *synth, u8 midiChannel )
679 {
680 #pragma unused(axvpb)
681 #pragma unused(synth)
682 #pragma unused(midiChannel)
683 }
684