1 /*---------------------------------------------------------------------------*
2 Project: Revolution WPAD AX demo
3 File: wpad_axdemo.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_axdemo.c,v $
14 Revision 1.19.2.1 2010/04/02 02:58:32 tojo
15 Fixed a race condition issue to check speaker is off.
16
17 Revision 1.19 2008/12/24 06:28:38 tojo
18 Modified the error handling of controller data.
19
20 Revision 1.18 2007/07/10 12:14:46 tojo
21 (none)
22
23 Revision 1.17 2007/05/10 04:17:34 aka
24 Revised to use AXRmtGetSamplesLeft().
25
26 Revision 1.16 2007/04/23 09:16:08 tojo
27 (none)
28
29 Revision 1.15 2007/02/13 04:35:17 tojo
30 Changed the button assignment of MUTE/MUTE OFF.
31
32 Revision 1.14 2007/02/09 07:22:07 tojo
33 Modifed to follow the guideline.
34
35 Revision 1.13 2006/11/21 08:38:15 aka
36 Removed the zero buffer.
37
38 Revision 1.12 2006/10/23 02:16:23 aka
39 Changed from AXInit() to AXInitSpecifyMem().
40 Changed from MIXInit() to MIXInitSpecifyMem().
41 Changed from SYNInit() to SYNInitSpecifyMem().
42
43 Revision 1.11 2006/10/23 01:17:30 tojo
44 Called WPADSetConnectCallback before initialization complete.
45
46 Revision 1.10 2006/10/11 03:03:50 aka
47 Revised AXInit(), MIXInit() and SYNInit().
48
49 Revision 1.9 2006/09/19 06:35:07 tojo
50 (none)
51
52 Revision 1.8 2006/09/18 12:13:54 tojo
53 (none)
54
55 Revision 1.7 2006/08/15 08:46:04 tojo
56 (none)
57
58 Revision 1.6 2006/08/11 09:33:13 tojo
59 (none)
60
61 Revision 1.5 2006/08/10 00:41:21 aka
62 Changed comments.
63
64 Revision 1.4 2006/08/03 13:32:55 tojo
65 Removed old apis.
66
67 Revision 1.3 2006/07/24 02:01:07 aka
68 Changed in relation to modification of AX.
69
70 Revision 1.2 2006/07/21 03:00:11 tojo
71 Implemented gui.
72
73 Revision 1.1 2006/07/19 09:31:24 tojo
74 Initial check in.
75
76 $NoKeywords: $
77 *---------------------------------------------------------------------------*/
78
79 /*---------------------------------------------------------------------------*
80 This program plays audio streaming on Wii remote controller using AX.
81 The audio streaming is encoded by WENC in runtime.
82 *---------------------------------------------------------------------------*/
83
84 #include <demo.h>
85 #include <revolution/mix.h>
86 #include <revolution/seq.h>
87 #include <revolution/syn.h>
88 #include <string.h>
89 #include <revolution/mem.h>
90
91 #include <revolution/wenc.h>
92 #include <revolution/wpad.h>
93
94 // User defines
95 #define NUM_SAMPLES 8
96 #define NUM_BUTTON_MAPS 8
97 // Filenames for samples to be played
98 char SampleFiles [NUM_SAMPLES][256] =
99 {
100 "axdemo/simple/snare.dsp",
101 "axdemo/simple/tom.dsp",
102 "axdemo/simple/cymbal.dsp",
103 "axdemo/simple/ride.dsp",
104 "axdemo/simple/bongo1.dsp",
105 "axdemo/simple/bongo2.dsp",
106 "axdemo/simple/bongo3.dsp",
107 "axdemo/simple/bongo4.dsp",
108 };
109 // Button mappings for samples {Button, index in file table}
110 u32 ButtonMap [NUM_BUTTON_MAPS][2] =
111 {
112 {WPAD_BUTTON_A, 0},
113 {WPAD_BUTTON_B, 1},
114 {WPAD_BUTTON_1, 2},
115 {WPAD_BUTTON_2, 3},
116 {WPAD_BUTTON_DOWN, 4},
117 {WPAD_BUTTON_UP, 5},
118 {WPAD_BUTTON_LEFT, 6},
119 {WPAD_BUTTON_RIGHT, 7}
120 };
121
122 // This demo uses a very simple mixing paradigm. All sounds are played at
123 // a volume of 1.0 (0x8000). Please see the MIX library for a more
124 // comprehensive mixing library
125 AXPBMIX g_mix = {
126 0x0000, // volume left
127 0x0000, // volume ramp left
128 0x0000, // volume right
129 0x0000, // volume ramp right
130
131 0x0000, // volume AUX A left
132 0x0000, // volume ramp AUX A left
133 0x0000, // volume AUX A right
134 0x0000, // volume ramp AUX A right
135
136 0x0000, // volume AUX B left
137 0x0000, // volume ramp AUX B left
138 0x0000, // volume AUX B right
139 0x0000, // volume ramp AUX B right
140
141 0x0000, // volume AUX C left
142 0x0000, // volume ramp AUX C left
143 0x0000, // volume AUX C right
144 0x0000, // volume ramp AUX C right
145
146 0x0000, // volume surround
147 0x0000, // volume ramp surround
148 0x0000, // volume AUX A surround
149 0x0000, // volume ramp AUX A surround
150 0x0000, // volume AUX B surround
151 0x0000, // volume ramp AUX B surround
152 0x0000, // volume AUX C surround
153 0x0000, // volume ramp AUX C surround
154 };
155
156 AXPBVE g_ve = {
157 0x8000, // volume at start of frame, 0x8000 = 1.0
158 0 // signed per sample delta (160 samples per frame)
159 };
160
161 AXPBRMTMIX g_rmix = {
162 0x0000, // volume main0
163 0x0000, // volume ramp main0
164 0x0000, // volume aux0
165 0x0000, // volume ramp aux0
166
167 0x0000, // volume main1
168 0x0000, // volume ramp main1
169 0x0000, // volume aux1
170 0x0000, // volume ramp aux1
171
172 0x0000, // volume main2
173 0x0000, // volume ramp main2
174 0x0000, // volume aux2
175 0x0000, // volume ramp aux2
176
177 0x0000, // volume main3
178 0x0000, // volume ramp main3
179 0x0000, // volume aux3
180 0x0000 // volume ramp aux3
181 };
182
183 // User Structures to keep track of data
184 typedef struct
185 {
186 void *mramAddr;
187
188 } SampleInfo;
189
190 typedef struct
191 {
192 AXVPB *voice;
193 u32 state;
194 } VoiceInfo;
195
196 // Voice Defines
197 #define VOICE_PRIO_HIGH 31
198 #define VOICE_PRIO_MED 15
199 #define VOICE_PRIO_LOW 1
200
201 #define VOICE_STATE_STOPPED 0
202 #define VOICE_STATE_START 1
203 #define VOICE_STATE_STARTED 2
204 #define VOICE_STATE_PLAYING 3
205 #define VOICE_STATE_STOP 4
206
207 // Exp Heap
208 static MEMHeapHandle hExpHeap;
209
210 // Utility Macro Functions
211 #define RoundUp64(x) (((u32)(x) + 64 - 1) & ~(64 - 1))
212 #define Bytes2Nibbles(n) (n << 1)
213 #define Nibbles2Bytes(n) (n >> 1)
214 #define GetDSPADPCMDataAddress(a) ((void*)((u32)a + sizeof(DSPADPCM)))
215 #define GetDSPADPCMDataSize32B(a) (RoundUp64(((DSPADPCM*)a)->num_adpcm_nibbles) >> 1)
216 #define GetVoiceCurrentAddr32(v) (*(u32 *)(&((v)->pb.addr.currentAddressHi)))
217 #define GetVoiceLoopAddr32(v) (*(u32 *)(&((v)->pb.addr.loopAddressHi)))
218 #define GetVoiceEndAddr32(v) (*(u32 *)(&((v)->pb.addr.endAddressHi)))
219
220 // Sample Info
221 static SampleInfo Samples[NUM_SAMPLES];
222 // AX Voice info
223 static VoiceInfo Voices[AX_MAX_VOICES];
224
225 // function Prototypes
226 static void * LoadFileIntoRam ( char *path );
227 static void AudioFrameCallback ( void );
228 static AXVPB* AquireVoiceADPCM ( s32 chan, void *pDSPADPCMData );
229 static void LoadSamples ( void );
230 static void PlaySample ( s32 chan, SampleInfo *sample );
231 static void VoiceCallback ( void * voiceIn );
232
233 #define SAMPLES_PER_AUDIO_FRAME 96
234 #define BYTES_PER_AUDIO_FRAME 384
235 static s16 SoundBuffer[2][SAMPLES_PER_AUDIO_FRAME * 2] ATTRIBUTE_ALIGN(32);
236 static s32 SoundBufferIndex = 0;
237 static AIDCallback OldAIDCallback = NULL;
238 static s16* LastAudioBuffer = NULL;
239 static s16* CurAudioBuffer = NULL;
240
241 #define GM_WT "/axdemo/synth/gm16adpcm.wt"
242 #define GM_PCM "/axdemo/synth/gm16adpcm.pcm"
243 #define MIDI_FILE "/axdemo/midi/2nd_time.mid"
244
245 // allocator functions for WPAD
246 void* myAlloc ( u32 size );
247 u8 myFree ( void *ptr );
248
249 static void ConnectCallback ( s32 chan, s32 reason );
250 static void ExtensionCallback ( s32 chan, s32 result );
251 static void SpeakerCallback ( s32 chan, s32 result );
252 static void SpeakerOnCallback ( s32 chna, s32 result );
253 static void SpeakerOffCallback ( s32 chan, s32 result );
254 static void MuteOnCallback ( s32 chan, s32 result );
255 static void MuteOffCallback ( s32 chan, s32 result );
256 static void UpdateSpeaker ( OSAlarm *alarm, OSContext *context );
257
258 #define SAMPLES_PER_AUDIO_PACKET 40
259 #define AUDIO_PACKET_MAX_LEN 20
260
261 // Speaker Status
262 typedef struct SpeakerInfo
263 {
264 u8 active;
265 BOOL muteReq;
266 WENCInfo encInfo;
267 BOOL first;
268 BOOL last;
269 } SpeakerInfo;
270
271 // Periodic Alarms for audio streaming
272 static OSAlarm SpeakerAlarm;
273 // Audio buffers
274 static s16 AudioBuffer[WPAD_MAX_CONTROLLERS][SAMPLES_PER_AUDIO_PACKET];
275
276 static void initialize ( void );
277 static void RenderOperation ( void );
278 static void RenderControllerStatus ( void );
279
280 typedef union Status
281 {
282 WPADStatus cr;
283 WPADFSStatus fs;
284 WPADCLStatus cl;
285 } Status;
286
287 typedef struct ContInfo
288 {
289 u32 type;
290 s32 status;
291 u16 keepBtnMap;
292 u16 button;
293 Status currStat;
294 Status prevStat;
295 SpeakerInfo Speakers;
296 } ContInfo;
297
298 static ContInfo info[WPAD_MAX_CONTROLLERS];
299
300 /*---------------------------------------------------------------------------*
301 * Name : main
302 * Description : main
303 * Arguments : None.
304 * Returns : None.
305 *---------------------------------------------------------------------------*/
main(void)306 void main( void )
307 {
308 s32 i;
309 s32 chan;
310
311 void *arenaMem2Lo;
312 void *arenaMem2Hi;
313
314 SEQSEQUENCE Sequence;
315 u8 *Wavetable;
316 u8 *Pcm;
317 u8 *MidiFile;
318 void *axBuffer;
319 void *mixBuffer;
320 void *synBuffer;
321
322 // Initialize DEMO
323 initialize();
324
325 // initialize Exp Heap on MEM2
326 arenaMem2Lo = OSGetMEM2ArenaLo();
327 arenaMem2Hi = OSGetMEM2ArenaHi();
328 hExpHeap = MEMCreateExpHeap(arenaMem2Lo, (u32)arenaMem2Hi - (u32) arenaMem2Lo);
329 ASSERT(hExpHeap != NULL);
330
331 // Initialize Audio
332 axBuffer = OSAlloc(AXGetMemorySize(AX_MAX_VOICES));
333 mixBuffer = MEMAllocFromExpHeap(hExpHeap, MIXGetMemorySize(AX_MAX_VOICES));
334 synBuffer = MEMAllocFromExpHeap(hExpHeap, SYNGetMemorySize(AX_MAX_VOICES));
335
336 AIInit(NULL);
337 AXInitSpecifyMem(AX_MAX_VOICES, axBuffer);
338 MIXInitSpecifyMem(mixBuffer);
339 SYNInitSpecifyMem(synBuffer);
340 SEQInit();
341
342 // Load Voice data into MRAM
343 LoadSamples();
344
345 Wavetable = LoadFileIntoRam(GM_WT);
346 Pcm = LoadFileIntoRam(GM_PCM);
347 MidiFile = LoadFileIntoRam(MIDI_FILE);
348
349 // Register Callback with AX for audio processing
350 AXRegisterCallback(&AudioFrameCallback);
351
352 SEQAddSequence( &Sequence,
353 MidiFile,
354 Wavetable,
355 Pcm,
356 NULL,
357 16,
358 15,
359 1
360 );
361 SEQSetState(&Sequence, SEQ_STATE_RUNLOOPED);
362
363 // Initialize WPAD
364 WPADRegisterAllocator(myAlloc, myFree);
365
366 WPADInit();
367
368 for(i=0;i<WPAD_MAX_CONTROLLERS; i++)
369 {
370 WPADSetConnectCallback((s32)i, ConnectCallback);
371 }
372
373 while(WPADGetStatus() != WPAD_STATE_SETUP)
374 {
375 ;
376 }
377
378 OSCreateAlarm(&SpeakerAlarm);
379 OSSetPeriodicAlarm(&SpeakerAlarm, OSGetTime(), WPAD_STRM_INTERVAL, UpdateSpeaker);
380
381 // Spin
382 while (1)
383 {
384 for(chan=0; chan<WPAD_MAX_CONTROLLERS; chan++)
385 {
386 info[chan].status = WPADProbe(chan, &info[chan].type);
387
388 if (info[chan].status != WPAD_ERR_NO_CONTROLLER)
389 {
390 WPADRead(chan, &info[chan].currStat.cr);
391
392 if (info[chan].currStat.cr.err == WPAD_ERR_NONE
393 || info[chan].currStat.cr.err == WPAD_ERR_CORRUPTED)
394 {
395 info[chan].button = WPADButtonDown(info[chan].prevStat.cr.button, info[chan].currStat.cr.button);
396 info[chan].prevStat.cr = info[chan].currStat.cr;
397 }
398 else
399 {
400 info[chan].button = 0;
401 }
402 if (info[chan].Speakers.active > 0)
403 {
404 BOOL active = FALSE;
405
406 // Stop all sounds when START/PAUSE is pressed
407 if (info[chan].button & WPAD_BUTTON_HOME)
408 {
409 // Stop all voices
410 for (i = 0; i < AX_MAX_VOICES; i++)
411 {
412 Voices[i].state = VOICE_STATE_STOP;
413 }
414 continue;
415 }
416 if (info[chan].button & WPAD_BUTTON_PLUS)
417 {
418 if (info[chan].Speakers.active == 1)
419 {
420 WPADControlSpeaker(chan, WPAD_SPEAKER_MUTE, MuteOnCallback);
421 }
422 info[chan].Speakers.muteReq = TRUE;
423 continue;
424 }
425 if (info[chan].button & WPAD_BUTTON_MINUS)
426 {
427 if (info[chan].Speakers.active == 2)
428 {
429 WPADControlSpeaker(chan, WPAD_SPEAKER_MUTE_OFF, MuteOffCallback);
430 }
431 info[chan].Speakers.muteReq = FALSE;
432 continue;
433 }
434
435 // Use Button map to start sounds
436 for (i = 0; i < NUM_BUTTON_MAPS; i++)
437 {
438 if (info[chan].button & ButtonMap[i][0])
439 {
440 PlaySample(chan, &Samples[ButtonMap[i][1]]);
441 }
442 }
443
444 // Check whether one voice at least is active
445 for (i = 0; i < AX_MAX_VOICES; i++)
446 {
447 if (Voices[i].state != VOICE_STATE_STOPPED && Voices[i].state != VOICE_STATE_STOP)
448 {
449 active = TRUE;
450 }
451 }
452
453 if (!active && info[chan].Speakers.active != 3)
454 {
455 // start shutdown speaker.
456 info[chan].Speakers.active = 3;
457 WPADControlSpeaker(chan, WPAD_SPEAKER_OFF, SpeakerOffCallback);
458 }
459 }
460 else
461 {
462 if (info[chan].button & WPAD_BUTTON_PLUS)
463 {
464 info[chan].Speakers.muteReq = TRUE;
465 }
466 else if (info[chan].button & WPAD_BUTTON_MINUS)
467 {
468 info[chan].Speakers.muteReq = FALSE;
469 }
470 else if (!info[chan].Speakers.muteReq && !(info[chan].button & WPAD_BUTTON_HOME) && info[chan].button)
471 {
472 WPADControlSpeaker(chan, WPAD_SPEAKER_ON, SpeakerOnCallback);
473 info[chan].keepBtnMap = info[chan].button;
474 }
475 }
476 }
477 }
478
479 DEMOBeforeRender();
480
481 DEMOPrintf( 16, 16, 0, "WPAD Demo -- WPAD Axdemo");
482
483 RenderOperation();
484 RenderControllerStatus();
485
486 DEMODoneRender();
487
488 }
489
490 OSCancelAlarm(&SpeakerAlarm);
491
492 }
493
494 /*---------------------------------------------------------------------------*
495 * Name : UpdateSpeaker
496 * Description :
497 * Arguments :
498 * Returns : None.
499 *---------------------------------------------------------------------------*/
UpdateSpeaker(OSAlarm * alarm,OSContext * context)500 static void UpdateSpeaker( OSAlarm *alarm, OSContext *context )
501 {
502 #pragma unused(alarm, context)
503
504 u8 data[24];
505 u32 flag;
506 s32 chan;
507 BOOL intr;
508
509 if (SAMPLES_PER_AUDIO_PACKET <= AXRmtGetSamplesLeft())
510 {
511
512 for(chan = 0; chan < WPAD_MAX_CONTROLLERS; chan++)
513 {
514
515 AXRmtGetSamples(chan, AudioBuffer[chan], SAMPLES_PER_AUDIO_PACKET);
516
517 if (info[chan].Speakers.active)
518 {
519 intr = OSDisableInterrupts();
520
521 if (WPADCanSendStreamData(chan))
522 {
523 flag = (info[chan].Speakers.first) ? (u32)WENC_FLAG_FIRST : (u32)WENC_FLAG_CONT;
524 if (info[chan].Speakers.first)
525 {
526 info[chan].Speakers.first = FALSE;
527 }
528 WENCGetEncodeData(&info[chan].Speakers.encInfo, flag, (const s16*)AudioBuffer[chan], SAMPLES_PER_AUDIO_PACKET, data);
529
530 WPADSendStreamData(chan, data, AUDIO_PACKET_MAX_LEN);
531 }
532
533 OSRestoreInterrupts(intr);
534 }
535 }
536
537 AXRmtAdvancePtr(SAMPLES_PER_AUDIO_PACKET);
538 }
539 }
540
541 /*---------------------------------------------------------------------------*
542 * Name : SpeakerCallback
543 * Description :
544 * Arguments :
545 * Returns : None.
546 *---------------------------------------------------------------------------*/
SpeakerCallback(s32 chan,s32 result)547 static void SpeakerCallback( s32 chan, s32 result )
548 {
549 int i;
550
551 if (result == WPAD_ERR_NONE)
552 {
553 info[chan].Speakers.active = 1;
554 info[chan].Speakers.first = TRUE;
555 info[chan].Speakers.last = FALSE;
556 memset(&info[chan].Speakers.encInfo, 0, sizeof(WENCInfo));
557
558 if (info[chan].Speakers.muteReq)
559 {
560 WPADControlSpeaker(chan, WPAD_SPEAKER_MUTE, MuteOnCallback);
561 }
562 else
563 {
564 // Use Button map to start sounds
565 for (i = 0; i < NUM_BUTTON_MAPS; i++)
566 {
567 if (info[chan].keepBtnMap & ButtonMap[i][0])
568 {
569 PlaySample(chan, &Samples[ButtonMap[i][1]]);
570 }
571 }
572 }
573
574 // clear btn map.
575 info[chan].keepBtnMap = 0;
576 OSReport("Chan[%d] is ready\n", chan);
577
578 }
579 }
580
581 /*---------------------------------------------------------------------------*
582 * Name : SpeakerOnCallback
583 * Description :
584 * Arguments :
585 * Returns : None.
586 *---------------------------------------------------------------------------*/
SpeakerOnCallback(s32 chan,s32 result)587 static void SpeakerOnCallback( s32 chan, s32 result )
588 {
589 if (result == WPAD_ERR_NONE)
590 {
591 WPADControlSpeaker(chan, WPAD_SPEAKER_PLAY, SpeakerCallback);
592 }
593 }
594
595 /*---------------------------------------------------------------------------*
596 * Name : SpeakerOffCallback
597 * Description :
598 * Arguments :
599 * Returns : None.
600 *---------------------------------------------------------------------------*/
SpeakerOffCallback(s32 chan,s32 result)601 static void SpeakerOffCallback( s32 chan, s32 result )
602 {
603 #pragma unused(result)
604
605 info[chan].Speakers.active = 0;
606
607 OSReport("Chan[%d] is stopped\n", chan);
608 }
609
610 /*---------------------------------------------------------------------------*
611 * Name : MuteOnCallback
612 * Description :
613 * Arguments :
614 * Returns : None.
615 *---------------------------------------------------------------------------*/
MuteOnCallback(s32 chan,s32 result)616 static void MuteOnCallback( s32 chan, s32 result )
617 {
618 if (result == WPAD_ERR_NONE)
619 {
620 info[chan].Speakers.active = 2;
621 }
622 }
623
624 /*---------------------------------------------------------------------------*
625 * Name : MuteOffCallback
626 * Description :
627 * Arguments :
628 * Returns : None.
629 *---------------------------------------------------------------------------*/
MuteOffCallback(s32 chan,s32 result)630 static void MuteOffCallback( s32 chan, s32 result )
631 {
632 if (result == WPAD_ERR_NONE)
633 {
634 info[chan].Speakers.active = 1;
635 }
636 }
637
638 /*---------------------------------------------------------------------------*
639 * Name : ExtensionCallback
640 * Description :
641 * Arguments :
642 * Returns : None.
643 *---------------------------------------------------------------------------*/
ExtensionCallback(s32 chan,s32 result)644 static void ExtensionCallback( s32 chan, s32 result )
645 {
646 switch(result)
647 {
648 case WPAD_DEV_CORE:
649 case WPAD_DEV_FUTURE:
650 case WPAD_DEV_NOT_SUPPORTED: WPADSetDataFormat(chan, WPAD_FMT_CORE); break;
651 case WPAD_DEV_FREESTYLE: WPADSetDataFormat(chan, WPAD_FMT_FREESTYLE); break;
652 case WPAD_DEV_CLASSIC: WPADSetDataFormat(chan, WPAD_FMT_CLASSIC); break;
653 }
654 }
655
656 /*---------------------------------------------------------------------------*
657 * Name : ConnectCallback
658 * Description :
659 * Arguments :
660 * Returns : None.
661 *---------------------------------------------------------------------------*/
ConnectCallback(s32 chan,s32 reason)662 static void ConnectCallback( s32 chan, s32 reason )
663 {
664 OSReport("ConnectCallback(%d) : %s\n", chan, (reason < 0) ? "disconnect" : "connect");
665
666 info[chan].Speakers.active = 0;
667 if (reason >= 0)
668 {
669 WPADSetDataFormat(chan, WPAD_FMT_CORE);
670 }
671 }
672
673
674 /*---------------------------------------------------------------------------*
675 * Name :
676 * Description :
677 * Arguments : None.
678 * Returns : None.
679 *---------------------------------------------------------------------------*/
myAlloc(u32 size)680 void *myAlloc( u32 size )
681 {
682 void *ptr;
683
684 ptr = MEMAllocFromExpHeap(hExpHeap, size);
685 ASSERTMSG(ptr, "Memory allocation failed\n");
686
687 return(ptr);
688 }
689
690 /*---------------------------------------------------------------------------*
691 * Name :
692 * Description :
693 * Arguments : None.
694 * Returns : None.
695 *---------------------------------------------------------------------------*/
myFree(void * ptr)696 u8 myFree( void *ptr )
697 {
698 MEMFreeToExpHeap(hExpHeap, ptr);
699 return(1);
700 }
701
702
703 /*---------------------------------------------------------------------------*
704 Name: LoadSamples
705
706 Description: Loads ADPCM files into Main Memory (Header + Data) and
707 ARAM (Data)
708
709 Arguments: none
710
711 Returns: none
712 *---------------------------------------------------------------------------*/
LoadSamples(void)713 static void LoadSamples( void )
714 {
715 u32 i;
716
717 // Load samples
718 for (i = 0; i < NUM_SAMPLES; i++)
719 {
720 // Load ADPCM file into MRAM (96 byte header included)
721 Samples[i].mramAddr = LoadFileIntoRam(SampleFiles[i]);
722
723 // Sanity Check
724 if (Samples[i].mramAddr == NULL)
725 {
726 OSReport("WARNING! Sample %d not loaded\n", i);
727 continue;
728 }
729 }
730 }
731
732 /*---------------------------------------------------------------------------*
733 Name: PlaySample
734
735 Description: Utility function that will play a sample
736
737 Arguments: sample Pointer to the sample information
738
739 Returns: pointer to the allocated voice
740 *---------------------------------------------------------------------------*/
PlaySample(s32 chan,SampleInfo * sample)741 static void PlaySample( s32 chan, SampleInfo *sample )
742 {
743 AXVPB *voice;
744
745 if (sample->mramAddr == NULL)
746 {
747 OSReport("WARNING! Sample not loaded!\n");
748 return;
749 }
750
751 // Aquire Voice and start
752 voice = AquireVoiceADPCM(chan, sample->mramAddr);
753 if (voice == NULL)
754 {
755 OSReport("WARNING: Ran out of voices!\n");
756 return;
757 }
758 Voices[voice->index].voice = voice;
759 Voices[voice->index].state = VOICE_STATE_START;
760 }
761
762 /*---------------------------------------------------------------------------*
763 Name: AudioFrameCallback
764
765 Description: Callback that process audio data per 5ms audio frame.
766 In this case, it stops, resets, and starts a voice.
767
768 Arguments: none
769
770 Returns: none
771 *---------------------------------------------------------------------------*/
AudioFrameCallback(void)772 static void AudioFrameCallback( void )
773 {
774 u32 i;
775 BOOL bStopFlag = FALSE;
776
777 // Monitor each voice and process states. This must be done in the
778 // callback
779 for (i = 0; i < AX_MAX_VOICES; i++)
780 {
781
782 // Skip NULL entries
783 if (Voices[i].voice == NULL) continue;
784
785 switch (Voices[i].state)
786 {
787 case VOICE_STATE_STOPPED:
788 break;
789 case VOICE_STATE_START:
790 // Start the voice
791 AXSetVoiceState(Voices[i].voice, AX_PB_STATE_RUN);
792 Voices[i].state = VOICE_STATE_STARTED;
793 break;
794 case VOICE_STATE_STARTED:
795 // Skip a frame
796 Voices[i].state = VOICE_STATE_PLAYING;
797 break;
798 case VOICE_STATE_PLAYING:
799 // Check to see if the voice is finished, if so, stop it
800 if (Voices[i].voice->pb.state == AX_PB_STATE_STOP)
801 bStopFlag = TRUE;
802 break;
803 case VOICE_STATE_STOP:
804 // Force a voice to stop
805 bStopFlag = TRUE;
806 break;
807 }
808
809 // A voice must be stopped
810 if (bStopFlag)
811 {
812 AXSetVoiceState(Voices[i].voice, AX_PB_STATE_STOP);
813 AXFreeVoice(Voices[i].voice);
814 Voices[i].voice = NULL;
815 Voices[i].state = VOICE_STATE_STOPPED;
816 bStopFlag = FALSE;
817 }
818 }
819
820 // run the sequencer
821 SEQRunAudioFrame();
822
823 // run the synth
824 SYNRunAudioFrame();
825
826 // tell the mixer to update settings
827 MIXUpdateSettings();
828 }
829
830
831 /*---------------------------------------------------------------------------*
832 Name: VoiceCallback
833
834 Description: Callback for when a voice is dropped.
835
836 Arguments: unused
837
838 Returns: none
839 *---------------------------------------------------------------------------*/
VoiceCallback(void * voiceIn)840 static void VoiceCallback( void * voiceIn )
841 {
842 AXVPB *voice = (AXVPB*)voiceIn;
843
844 // Note: Voice is auto-magically stopped by AX layer when dropped
845
846 // Application clean-up
847 Voices[voice->index].voice = NULL;
848 Voices[voice->index].state = VOICE_STATE_STOPPED;
849 }
850
851 /*---------------------------------------------------------------------------*
852 Name: AquireVoiceADPCM
853
854 Description: Parses the ADPCM header, sets voice parameters
855
856 Arguments: pDSPADPCMData Pointer to the ADPCM data in MRAM
857 pARAMStart ARAM Start address
858
859 Returns: pointer to the allocated voice or NULL
860 *---------------------------------------------------------------------------*/
AquireVoiceADPCM(s32 chan,void * pDSPADPCMData)861 static AXVPB* AquireVoiceADPCM( s32 chan, void *pDSPADPCMData )
862 {
863 DSPADPCM *ps = (DSPADPCM*)pDSPADPCMData;
864 AXPBADDR addr;
865 AXPBADPCM adpcm;
866 AXPBSRC src;
867 AXPBADPCMLOOP adpcmLoop;
868 AXVPB* voice;
869 u32 srcBits;
870 u32 mramAddress;
871 u32 pMRAMStart;
872
873 // Allocate a voice for use
874 voice = AXAcquireVoice(VOICE_PRIO_MED, VoiceCallback, 0);
875
876 if (voice == NULL)
877 {
878 OSReport("WARNING! Voice Acquisition failed!\n");
879 return NULL;
880 }
881
882 // Fill AXPBADDR structure
883 // All the folowing addresses are in nibbles
884
885 addr.loopFlag = ps->loop_flag;
886 addr.format = ps->format;
887
888 pMRAMStart = OSCachedToPhysical(GetDSPADPCMDataAddress(pDSPADPCMData));
889
890 // Support for looping
891 if (addr.loopFlag)
892 {
893 adpcmLoop.loop_pred_scale = ps->lps;
894 adpcmLoop.loop_yn1 = ps->lyn1;
895 adpcmLoop.loop_yn2 = ps->lyn2;
896 }
897
898 mramAddress = (ps->sa + Bytes2Nibbles(pMRAMStart));
899 addr.loopAddressHi = (u16)(mramAddress >> 16);
900 addr.loopAddressLo = (u16)(mramAddress & 0xFFFF);
901
902 mramAddress = (ps->ea + Bytes2Nibbles(pMRAMStart));
903 addr.endAddressHi = (u16)(mramAddress >> 16);
904 addr.endAddressLo = (u16)(mramAddress & 0xFFFF);
905
906 mramAddress = (ps->ca + Bytes2Nibbles(pMRAMStart));
907 addr.currentAddressHi = (u16)(mramAddress >> 16);
908 addr.currentAddressLo = (u16)(mramAddress & 0xFFFF);
909
910 // Fill AXPBADPCM structure
911 adpcm.a[0][0] = ps->coef[0];
912 adpcm.a[0][1] = ps->coef[1];
913 adpcm.a[1][0] = ps->coef[2];
914 adpcm.a[1][1] = ps->coef[3];
915 adpcm.a[2][0] = ps->coef[4];
916 adpcm.a[2][1] = ps->coef[5];
917 adpcm.a[3][0] = ps->coef[6];
918 adpcm.a[3][1] = ps->coef[7];
919 adpcm.a[4][0] = ps->coef[8];
920 adpcm.a[4][1] = ps->coef[9];
921 adpcm.a[5][0] = ps->coef[10];
922 adpcm.a[5][1] = ps->coef[11];
923 adpcm.a[6][0] = ps->coef[12];
924 adpcm.a[6][1] = ps->coef[13];
925 adpcm.a[7][0] = ps->coef[14];
926 adpcm.a[7][1] = ps->coef[15];
927 adpcm.gain = ps->gain;
928 adpcm.pred_scale = ps->ps;
929 adpcm.yn1 = ps->yn1;
930 adpcm.yn2 = ps->yn2;
931
932 // Fill AXPBSRC structure for proper sample rates
933 srcBits = (u32)(0x00010000 * ((f32)ps->sample_rate / AX_IN_SAMPLES_PER_SEC));
934
935 src.ratioHi = (u16)(srcBits >> 16);
936 src.ratioLo = (u16)(srcBits & 0xFFFF);
937 src.currentAddressFrac = 0;
938 src.last_samples[0] = 0;
939 src.last_samples[1] = 0;
940 src.last_samples[2] = 0;
941 src.last_samples[3] = 0;
942
943 // Set voice type
944 AXSetVoiceType(voice, AX_PB_TYPE_NORMAL);
945
946 // Set Address and ADPCM information from header
947 AXSetVoiceAddr(voice, &addr);
948 AXSetVoiceAdpcm(voice, &adpcm);
949 AXSetVoiceAdpcmLoop(voice, &adpcmLoop);
950
951 // Set simple volumes
952 AXSetVoiceMix(voice, &g_mix);
953 AXSetVoiceVe(voice, &g_ve);
954
955 // Set controller speaker
956 AXSetVoiceRmtOn(voice, AX_PB_REMOTE_ON);
957 memset(&g_rmix, 0, sizeof(AXPBRMTMIX));
958 switch(chan)
959 {
960 case WPAD_CHAN0:
961 g_rmix.vMain0 = 0x8000; // chan0 main
962 g_rmix.vAux0 = 0x8000; // chan0 aux
963 break;
964 case WPAD_CHAN1:
965 g_rmix.vMain1 = 0x8000; // chan1 main
966 g_rmix.vAux1 = 0x8000; // chan1 aux
967 break;
968 case WPAD_CHAN2:
969 g_rmix.vMain2 = 0x8000; // chan2 main
970 g_rmix.vAux2 = 0x8000; // chan2 aux
971 break;
972 case WPAD_CHAN3:
973 g_rmix.vMain3 = 0x8000; // chan3 main
974 g_rmix.vAux3 = 0x8000; // chan3 aux
975 break;
976 }
977 AXSetVoiceRmtMix(voice, &g_rmix);
978
979 // Set sample rate
980 AXSetVoiceSrcType(voice, AX_SRC_TYPE_LINEAR);
981 AXSetVoiceSrc(voice, &src);
982
983 return voice;
984 }
985
986 /*---------------------------------------------------------------------------*
987 Name: LoadFileIntoRam
988
989 Description: Loads a file into memory. Memory is allocated.
990
991 Arguments: path File to load into main memory
992
993 Returns: pointer to file in main memory or NULL if not opened
994 *---------------------------------------------------------------------------*/
LoadFileIntoRam(char * path)995 static void * LoadFileIntoRam( char *path )
996 {
997 DVDFileInfo handle;
998 u32 round_length;
999 s32 read_length;
1000 void *buffer;
1001
1002 // Open File
1003 if (!DVDOpen(path, &handle))
1004 {
1005 OSReport("WARNING! Failed to open %s\n", path);
1006 return NULL;
1007 }
1008
1009 // Make sure file length is not 0
1010 if (DVDGetLength(&handle) == 0)
1011 {
1012 OSReport("WARNING! File length is 0\n");
1013 return NULL;
1014 }
1015
1016 round_length = OSRoundUp32B(DVDGetLength(&handle));
1017 buffer = MEMAllocFromExpHeapEx(hExpHeap, round_length, 32);
1018
1019 // Make sure we got a buffer
1020 if (buffer == NULL)
1021 {
1022 OSReport("WARNING! Unable to allocate buffer\n");
1023 return NULL;
1024 }
1025
1026 // Read Files
1027 read_length = DVDRead(&handle, buffer, (s32)(round_length), 0);
1028
1029 // Make sure we read the file correctly
1030 if (read_length <= 0)
1031 {
1032 OSReport("WARNING! File lenght is wrong\n");
1033 return NULL;
1034 }
1035
1036 return buffer;
1037 }
1038
1039 static const s16 HEIGHT = 10;
1040
1041 /*---------------------------------------------------------------------------*
1042 * Name : RenderOperation
1043 * Description :
1044 * Arguments :
1045 * Returns : None.
1046 *---------------------------------------------------------------------------*/
RenderOperation(void)1047 static void RenderOperation( void )
1048 {
1049 s16 y = 96;
1050 char button[256];
1051 u32 i;
1052
1053 for (i = 0; i < NUM_BUTTON_MAPS; i++)
1054 {
1055 switch (ButtonMap[i][0])
1056 {
1057 case WPAD_BUTTON_A: sprintf(button, "A "); break;
1058 case WPAD_BUTTON_B: sprintf(button, "B "); break;
1059 case WPAD_BUTTON_1: sprintf(button, "1 "); break;
1060 case WPAD_BUTTON_2: sprintf(button, "2 "); break;
1061 case WPAD_BUTTON_DOWN: sprintf(button, "Down "); break;
1062 case WPAD_BUTTON_UP: sprintf(button, "Up "); break;
1063 case WPAD_BUTTON_LEFT: sprintf(button, "Left "); break;
1064 case WPAD_BUTTON_RIGHT: sprintf(button, "Right"); break;
1065 }
1066 DEMOPrintf( 16, y+=HEIGHT, 0, "%s : %s", button, SampleFiles[ButtonMap[i][1]]);
1067 }
1068
1069 DEMOPrintf( 16, y+=HEIGHT, 0, "Home : Stop all sounds");
1070 DEMOPrintf( 16, y+=HEIGHT, 0, "Plus : Mute On");
1071 DEMOPrintf( 16, y+=HEIGHT, 0, "Minus : Mute Off");
1072 }
1073
1074 /*---------------------------------------------------------------------------*
1075 * Name : RenderControllerStatus
1076 * Description :
1077 * Arguments :
1078 * Returns : None.
1079 *---------------------------------------------------------------------------*/
RenderControllerStatus(void)1080 static void RenderControllerStatus( void )
1081 {
1082 s16 y = 32;
1083 int chan;
1084
1085 DEMOPrintf( 150, y, 0, "speaker");
1086 DEMOPrintf( 250, y, 0, "volume");
1087 for(chan=0; chan<WPAD_MAX_CONTROLLERS; chan++)
1088 {
1089 y += HEIGHT;
1090 DEMOPrintf( 16, y, 0, "CHAN_%d [%s]",
1091 chan,
1092 (info[chan].status == WPAD_ERR_NO_CONTROLLER) ? "--" :
1093 (info[chan].type == 0) ? "CORE" :
1094 (info[chan].type == 1) ? "NUNCHAKU" :
1095 (info[chan].type == 2) ? "CLASSIC" :
1096 "UNKNOWN"
1097 );
1098 DEMOPrintf( 150, y, 0, "%s", (info[chan].Speakers.active == 0) ? "OFF" :
1099 (info[chan].Speakers.active == 2) ? "MUTE" :
1100 "ON");
1101 DEMOPrintf( 250, y, 0, "%d", WPADGetSpeakerVolume());
1102 }
1103 }
1104
1105 /*---------------------------------------------------------------------------*
1106 * Name : initialize
1107 * Description :
1108 * Arguments :
1109 * Returns : None.
1110 *---------------------------------------------------------------------------*/
initialize(void)1111 static void initialize( void )
1112 {
1113 const GXColor DARKBLUE = { 0, 0, 64, 255 };
1114 const int SCREEN_WIDTH = 320;
1115 const int SCREEN_HEIGHT = 240;
1116
1117 DEMOInit( &GXNtsc480IntDf );
1118 GXSetCopyClear( DARKBLUE, GX_MAX_Z24 );
1119 GXCopyDisp( DEMOGetCurrentBuffer(), GX_TRUE );
1120 DEMOInitCaption( DM_FT_XLU, SCREEN_WIDTH, SCREEN_HEIGHT );
1121 GXSetZMode( GX_ENABLE, GX_ALWAYS, GX_ENABLE ); // Set pixel processing mode
1122 GXSetBlendMode( GX_BM_BLEND, GX_BL_ONE, GX_BL_ONE, GX_LO_CLEAR ); // Translucent mode
1123
1124 DEMOPadInit();
1125
1126 } /* end initialize() */
1127