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.18.2.2 2007/12/11 01:36:56 tojo
15 (none)
16
17 Revision 1.18.2.1 2007/12/11 01:17:50 tojo
18 Modified to handle the error code of WPADStatus.
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_INVALID)
393 {
394 info[chan].button = WPADButtonDown(info[chan].prevStat.cr.button, info[chan].currStat.cr.button);
395 info[chan].prevStat.cr = info[chan].currStat.cr;
396 }
397 if (WPADIsSpeakerEnabled(chan))
398 {
399 BOOL active = FALSE;
400
401 // Stop all sounds when START/PAUSE is pressed
402 if (info[chan].currStat.cr.button & WPAD_BUTTON_HOME)
403 {
404 // Stop all voices
405 for (i = 0; i < AX_MAX_VOICES; i++)
406 {
407 Voices[i].state = VOICE_STATE_STOP;
408 }
409 continue;
410 }
411 if (info[chan].button & WPAD_BUTTON_PLUS)
412 {
413 if (info[chan].Speakers.active == 1)
414 {
415 WPADControlSpeaker(chan, WPAD_SPEAKER_MUTE, MuteOnCallback);
416 }
417 info[chan].Speakers.muteReq = TRUE;
418 continue;
419 }
420 if (info[chan].button & WPAD_BUTTON_MINUS)
421 {
422 if (info[chan].Speakers.active == 2)
423 {
424 WPADControlSpeaker(chan, WPAD_SPEAKER_MUTE_OFF, MuteOffCallback);
425 }
426 info[chan].Speakers.muteReq = FALSE;
427 continue;
428 }
429
430 // Use Button map to start sounds
431 for (i = 0; i < NUM_BUTTON_MAPS; i++)
432 {
433 if (info[chan].button & ButtonMap[i][0])
434 {
435 PlaySample(chan, &Samples[ButtonMap[i][1]]);
436 }
437 }
438
439 // Check whether one voice at least is active
440 for (i = 0; i < AX_MAX_VOICES; i++)
441 {
442 if (Voices[i].state != VOICE_STATE_STOPPED && Voices[i].state != VOICE_STATE_STOP)
443 {
444 active = TRUE;
445 }
446 }
447
448 if (!active && info[chan].Speakers.active != 3)
449 {
450 // start shutdown speaker.
451 info[chan].Speakers.active = 3;
452 WPADControlSpeaker(chan, WPAD_SPEAKER_OFF, SpeakerOffCallback);
453 }
454 }
455 else
456 {
457 if (info[chan].button & WPAD_BUTTON_PLUS)
458 {
459 info[chan].Speakers.muteReq = TRUE;
460 }
461 else if (info[chan].button & WPAD_BUTTON_MINUS)
462 {
463 info[chan].Speakers.muteReq = FALSE;
464 }
465 else if (!info[chan].Speakers.muteReq && !(info[chan].button & WPAD_BUTTON_HOME) && info[chan].button)
466 {
467 WPADControlSpeaker(chan, WPAD_SPEAKER_ON, SpeakerOnCallback);
468 info[chan].keepBtnMap = info[chan].button;
469 }
470 }
471 }
472 }
473
474 DEMOBeforeRender();
475
476 DEMOPrintf( 16, 16, 0, "WPAD Demo -- WPAD Axdemo");
477
478 RenderOperation();
479 RenderControllerStatus();
480
481 DEMODoneRender();
482
483 }
484
485 OSCancelAlarm(&SpeakerAlarm);
486
487 }
488
489 /*---------------------------------------------------------------------------*
490 * Name : UpdateSpeaker
491 * Description :
492 * Arguments :
493 * Returns : None.
494 *---------------------------------------------------------------------------*/
UpdateSpeaker(OSAlarm * alarm,OSContext * context)495 static void UpdateSpeaker( OSAlarm *alarm, OSContext *context )
496 {
497 #pragma unused(alarm, context)
498
499 u8 data[24];
500 u32 flag;
501 s32 chan;
502 BOOL intr;
503
504 if (SAMPLES_PER_AUDIO_PACKET <= AXRmtGetSamplesLeft())
505 {
506
507 for(chan = 0; chan < WPAD_MAX_CONTROLLERS; chan++)
508 {
509
510 AXRmtGetSamples(chan, AudioBuffer[chan], SAMPLES_PER_AUDIO_PACKET);
511
512 if (info[chan].Speakers.active)
513 {
514 intr = OSDisableInterrupts();
515
516 if (WPADCanSendStreamData(chan))
517 {
518 flag = (info[chan].Speakers.first) ? (u32)WENC_FLAG_FIRST : (u32)WENC_FLAG_CONT;
519 if (info[chan].Speakers.first)
520 {
521 info[chan].Speakers.first = FALSE;
522 }
523 WENCGetEncodeData(&info[chan].Speakers.encInfo, flag, (const s16*)AudioBuffer[chan], SAMPLES_PER_AUDIO_PACKET, data);
524
525 WPADSendStreamData(chan, data, AUDIO_PACKET_MAX_LEN);
526 }
527
528 OSRestoreInterrupts(intr);
529 }
530 }
531
532 AXRmtAdvancePtr(SAMPLES_PER_AUDIO_PACKET);
533 }
534 }
535
536 /*---------------------------------------------------------------------------*
537 * Name : SpeakerCallback
538 * Description :
539 * Arguments :
540 * Returns : None.
541 *---------------------------------------------------------------------------*/
SpeakerCallback(s32 chan,s32 result)542 static void SpeakerCallback( s32 chan, s32 result )
543 {
544 int i;
545
546 if (result == WPAD_ERR_NONE)
547 {
548 info[chan].Speakers.active = 1;
549 info[chan].Speakers.first = TRUE;
550 info[chan].Speakers.last = FALSE;
551 memset(&info[chan].Speakers.encInfo, 0, sizeof(WENCInfo));
552
553 if (info[chan].Speakers.muteReq)
554 {
555 WPADControlSpeaker(chan, WPAD_SPEAKER_MUTE, MuteOnCallback);
556 }
557 else
558 {
559 // Use Button map to start sounds
560 for (i = 0; i < NUM_BUTTON_MAPS; i++)
561 {
562 if (info[chan].keepBtnMap & ButtonMap[i][0])
563 {
564 PlaySample(chan, &Samples[ButtonMap[i][1]]);
565 }
566 }
567 }
568
569 // clear btn map.
570 info[chan].keepBtnMap = 0;
571 OSReport("Chan[%d] is ready\n", chan);
572
573 }
574 }
575
576 /*---------------------------------------------------------------------------*
577 * Name : SpeakerOnCallback
578 * Description :
579 * Arguments :
580 * Returns : None.
581 *---------------------------------------------------------------------------*/
SpeakerOnCallback(s32 chan,s32 result)582 static void SpeakerOnCallback( s32 chan, s32 result )
583 {
584 if (result == WPAD_ERR_NONE)
585 {
586 WPADControlSpeaker(chan, WPAD_SPEAKER_PLAY, SpeakerCallback);
587 }
588 }
589
590 /*---------------------------------------------------------------------------*
591 * Name : SpeakerOffCallback
592 * Description :
593 * Arguments :
594 * Returns : None.
595 *---------------------------------------------------------------------------*/
SpeakerOffCallback(s32 chan,s32 result)596 static void SpeakerOffCallback( s32 chan, s32 result )
597 {
598 #pragma unused(result)
599
600 info[chan].Speakers.active = 0;
601
602 OSReport("Chan[%d] is stopped\n", chan);
603 }
604
605 /*---------------------------------------------------------------------------*
606 * Name : MuteOnCallback
607 * Description :
608 * Arguments :
609 * Returns : None.
610 *---------------------------------------------------------------------------*/
MuteOnCallback(s32 chan,s32 result)611 static void MuteOnCallback( s32 chan, s32 result )
612 {
613 if (result == WPAD_ERR_NONE)
614 {
615 info[chan].Speakers.active = 2;
616 }
617 }
618
619 /*---------------------------------------------------------------------------*
620 * Name : MuteOffCallback
621 * Description :
622 * Arguments :
623 * Returns : None.
624 *---------------------------------------------------------------------------*/
MuteOffCallback(s32 chan,s32 result)625 static void MuteOffCallback( s32 chan, s32 result )
626 {
627 if (result == WPAD_ERR_NONE)
628 {
629 info[chan].Speakers.active = 1;
630 }
631 }
632
633 /*---------------------------------------------------------------------------*
634 * Name : ExtensionCallback
635 * Description :
636 * Arguments :
637 * Returns : None.
638 *---------------------------------------------------------------------------*/
ExtensionCallback(s32 chan,s32 result)639 static void ExtensionCallback( s32 chan, s32 result )
640 {
641 switch(result)
642 {
643 case WPAD_DEV_CORE:
644 case WPAD_DEV_FUTURE:
645 case WPAD_DEV_NOT_SUPPORTED: WPADSetDataFormat(chan, WPAD_FMT_CORE); break;
646 case WPAD_DEV_FREESTYLE: WPADSetDataFormat(chan, WPAD_FMT_FREESTYLE); break;
647 case WPAD_DEV_CLASSIC: WPADSetDataFormat(chan, WPAD_FMT_CLASSIC); break;
648 }
649 }
650
651 /*---------------------------------------------------------------------------*
652 * Name : ConnectCallback
653 * Description :
654 * Arguments :
655 * Returns : None.
656 *---------------------------------------------------------------------------*/
ConnectCallback(s32 chan,s32 reason)657 static void ConnectCallback( s32 chan, s32 reason )
658 {
659 OSReport("ConnectCallback(%d) : %s\n", chan, (reason < 0) ? "disconnect" : "connect");
660
661 info[chan].Speakers.active = 0;
662 if (reason >= 0)
663 {
664 WPADSetDataFormat(chan, WPAD_FMT_CORE);
665 }
666 }
667
668
669 /*---------------------------------------------------------------------------*
670 * Name :
671 * Description :
672 * Arguments : None.
673 * Returns : None.
674 *---------------------------------------------------------------------------*/
myAlloc(u32 size)675 void *myAlloc( u32 size )
676 {
677 void *ptr;
678
679 ptr = MEMAllocFromExpHeap(hExpHeap, size);
680 ASSERTMSG(ptr, "Memory allocation failed\n");
681
682 return(ptr);
683 }
684
685 /*---------------------------------------------------------------------------*
686 * Name :
687 * Description :
688 * Arguments : None.
689 * Returns : None.
690 *---------------------------------------------------------------------------*/
myFree(void * ptr)691 u8 myFree( void *ptr )
692 {
693 MEMFreeToExpHeap(hExpHeap, ptr);
694 return(1);
695 }
696
697
698 /*---------------------------------------------------------------------------*
699 Name: LoadSamples
700
701 Description: Loads ADPCM files into Main Memory (Header + Data) and
702 ARAM (Data)
703
704 Arguments: none
705
706 Returns: none
707 *---------------------------------------------------------------------------*/
LoadSamples(void)708 static void LoadSamples( void )
709 {
710 u32 i;
711
712 // Load samples
713 for (i = 0; i < NUM_SAMPLES; i++)
714 {
715 // Load ADPCM file into MRAM (96 byte header included)
716 Samples[i].mramAddr = LoadFileIntoRam(SampleFiles[i]);
717
718 // Sanity Check
719 if (Samples[i].mramAddr == NULL)
720 {
721 OSReport("WARNING! Sample %d not loaded\n", i);
722 continue;
723 }
724 }
725 }
726
727 /*---------------------------------------------------------------------------*
728 Name: PlaySample
729
730 Description: Utility function that will play a sample
731
732 Arguments: sample Pointer to the sample information
733
734 Returns: pointer to the allocated voice
735 *---------------------------------------------------------------------------*/
PlaySample(s32 chan,SampleInfo * sample)736 static void PlaySample( s32 chan, SampleInfo *sample )
737 {
738 AXVPB *voice;
739
740 if (sample->mramAddr == NULL)
741 {
742 OSReport("WARNING! Sample not loaded!\n");
743 return;
744 }
745
746 // Aquire Voice and start
747 voice = AquireVoiceADPCM(chan, sample->mramAddr);
748 if (voice == NULL)
749 {
750 OSReport("WARNING: Ran out of voices!\n");
751 return;
752 }
753 Voices[voice->index].voice = voice;
754 Voices[voice->index].state = VOICE_STATE_START;
755 }
756
757 /*---------------------------------------------------------------------------*
758 Name: AudioFrameCallback
759
760 Description: Callback that process audio data per 5ms audio frame.
761 In this case, it stops, resets, and starts a voice.
762
763 Arguments: none
764
765 Returns: none
766 *---------------------------------------------------------------------------*/
AudioFrameCallback(void)767 static void AudioFrameCallback( void )
768 {
769 u32 i;
770 BOOL bStopFlag = FALSE;
771
772 // Monitor each voice and process states. This must be done in the
773 // callback
774 for (i = 0; i < AX_MAX_VOICES; i++)
775 {
776
777 // Skip NULL entries
778 if (Voices[i].voice == NULL) continue;
779
780 switch (Voices[i].state)
781 {
782 case VOICE_STATE_STOPPED:
783 break;
784 case VOICE_STATE_START:
785 // Start the voice
786 AXSetVoiceState(Voices[i].voice, AX_PB_STATE_RUN);
787 Voices[i].state = VOICE_STATE_STARTED;
788 break;
789 case VOICE_STATE_STARTED:
790 // Skip a frame
791 Voices[i].state = VOICE_STATE_PLAYING;
792 break;
793 case VOICE_STATE_PLAYING:
794 // Check to see if the voice is finished, if so, stop it
795 if (Voices[i].voice->pb.state == AX_PB_STATE_STOP)
796 bStopFlag = TRUE;
797 break;
798 case VOICE_STATE_STOP:
799 // Force a voice to stop
800 bStopFlag = TRUE;
801 break;
802 }
803
804 // A voice must be stopped
805 if (bStopFlag)
806 {
807 AXSetVoiceState(Voices[i].voice, AX_PB_STATE_STOP);
808 AXFreeVoice(Voices[i].voice);
809 Voices[i].voice = NULL;
810 Voices[i].state = VOICE_STATE_STOPPED;
811 bStopFlag = FALSE;
812 }
813 }
814
815 // run the sequencer
816 SEQRunAudioFrame();
817
818 // run the synth
819 SYNRunAudioFrame();
820
821 // tell the mixer to update settings
822 MIXUpdateSettings();
823 }
824
825
826 /*---------------------------------------------------------------------------*
827 Name: VoiceCallback
828
829 Description: Callback for when a voice is dropped.
830
831 Arguments: unused
832
833 Returns: none
834 *---------------------------------------------------------------------------*/
VoiceCallback(void * voiceIn)835 static void VoiceCallback( void * voiceIn )
836 {
837 AXVPB *voice = (AXVPB*)voiceIn;
838
839 // Note: Voice is auto-magically stopped by AX layer when dropped
840
841 // Application clean-up
842 Voices[voice->index].voice = NULL;
843 Voices[voice->index].state = VOICE_STATE_STOPPED;
844 }
845
846 /*---------------------------------------------------------------------------*
847 Name: AquireVoiceADPCM
848
849 Description: Parses the ADPCM header, sets voice parameters
850
851 Arguments: pDSPADPCMData Pointer to the ADPCM data in MRAM
852 pARAMStart ARAM Start address
853
854 Returns: pointer to the allocated voice or NULL
855 *---------------------------------------------------------------------------*/
AquireVoiceADPCM(s32 chan,void * pDSPADPCMData)856 static AXVPB* AquireVoiceADPCM( s32 chan, void *pDSPADPCMData )
857 {
858 DSPADPCM *ps = (DSPADPCM*)pDSPADPCMData;
859 AXPBADDR addr;
860 AXPBADPCM adpcm;
861 AXPBSRC src;
862 AXPBADPCMLOOP adpcmLoop;
863 AXVPB* voice;
864 u32 srcBits;
865 u32 mramAddress;
866 u32 pMRAMStart;
867
868 // Allocate a voice for use
869 voice = AXAcquireVoice(VOICE_PRIO_MED, VoiceCallback, 0);
870
871 if (voice == NULL)
872 {
873 OSReport("WARNING! Voice Acquisition failed!\n");
874 return NULL;
875 }
876
877 // Fill AXPBADDR structure
878 // All the folowing addresses are in nibbles
879
880 addr.loopFlag = ps->loop_flag;
881 addr.format = ps->format;
882
883 pMRAMStart = OSCachedToPhysical(GetDSPADPCMDataAddress(pDSPADPCMData));
884
885 // Support for looping
886 if (addr.loopFlag)
887 {
888 adpcmLoop.loop_pred_scale = ps->lps;
889 adpcmLoop.loop_yn1 = ps->lyn1;
890 adpcmLoop.loop_yn2 = ps->lyn2;
891 }
892
893 mramAddress = (ps->sa + Bytes2Nibbles(pMRAMStart));
894 addr.loopAddressHi = (u16)(mramAddress >> 16);
895 addr.loopAddressLo = (u16)(mramAddress & 0xFFFF);
896
897 mramAddress = (ps->ea + Bytes2Nibbles(pMRAMStart));
898 addr.endAddressHi = (u16)(mramAddress >> 16);
899 addr.endAddressLo = (u16)(mramAddress & 0xFFFF);
900
901 mramAddress = (ps->ca + Bytes2Nibbles(pMRAMStart));
902 addr.currentAddressHi = (u16)(mramAddress >> 16);
903 addr.currentAddressLo = (u16)(mramAddress & 0xFFFF);
904
905 // Fill AXPBADPCM structure
906 adpcm.a[0][0] = ps->coef[0];
907 adpcm.a[0][1] = ps->coef[1];
908 adpcm.a[1][0] = ps->coef[2];
909 adpcm.a[1][1] = ps->coef[3];
910 adpcm.a[2][0] = ps->coef[4];
911 adpcm.a[2][1] = ps->coef[5];
912 adpcm.a[3][0] = ps->coef[6];
913 adpcm.a[3][1] = ps->coef[7];
914 adpcm.a[4][0] = ps->coef[8];
915 adpcm.a[4][1] = ps->coef[9];
916 adpcm.a[5][0] = ps->coef[10];
917 adpcm.a[5][1] = ps->coef[11];
918 adpcm.a[6][0] = ps->coef[12];
919 adpcm.a[6][1] = ps->coef[13];
920 adpcm.a[7][0] = ps->coef[14];
921 adpcm.a[7][1] = ps->coef[15];
922 adpcm.gain = ps->gain;
923 adpcm.pred_scale = ps->ps;
924 adpcm.yn1 = ps->yn1;
925 adpcm.yn2 = ps->yn2;
926
927 // Fill AXPBSRC structure for proper sample rates
928 srcBits = (u32)(0x00010000 * ((f32)ps->sample_rate / AX_IN_SAMPLES_PER_SEC));
929
930 src.ratioHi = (u16)(srcBits >> 16);
931 src.ratioLo = (u16)(srcBits & 0xFFFF);
932 src.currentAddressFrac = 0;
933 src.last_samples[0] = 0;
934 src.last_samples[1] = 0;
935 src.last_samples[2] = 0;
936 src.last_samples[3] = 0;
937
938 // Set voice type
939 AXSetVoiceType(voice, AX_PB_TYPE_NORMAL);
940
941 // Set Address and ADPCM information from header
942 AXSetVoiceAddr(voice, &addr);
943 AXSetVoiceAdpcm(voice, &adpcm);
944 AXSetVoiceAdpcmLoop(voice, &adpcmLoop);
945
946 // Set simple volumes
947 AXSetVoiceMix(voice, &g_mix);
948 AXSetVoiceVe(voice, &g_ve);
949
950 // Set controller speaker
951 AXSetVoiceRmtOn(voice, AX_PB_REMOTE_ON);
952 memset(&g_rmix, 0, sizeof(AXPBRMTMIX));
953 switch(chan)
954 {
955 case WPAD_CHAN0:
956 g_rmix.vMain0 = 0x8000; // chan0 main
957 g_rmix.vAux0 = 0x8000; // chan0 aux
958 break;
959 case WPAD_CHAN1:
960 g_rmix.vMain1 = 0x8000; // chan1 main
961 g_rmix.vAux1 = 0x8000; // chan1 aux
962 break;
963 case WPAD_CHAN2:
964 g_rmix.vMain2 = 0x8000; // chan2 main
965 g_rmix.vAux2 = 0x8000; // chan2 aux
966 break;
967 case WPAD_CHAN3:
968 g_rmix.vMain3 = 0x8000; // chan3 main
969 g_rmix.vAux3 = 0x8000; // chan3 aux
970 break;
971 }
972 AXSetVoiceRmtMix(voice, &g_rmix);
973
974 // Set sample rate
975 AXSetVoiceSrcType(voice, AX_SRC_TYPE_LINEAR);
976 AXSetVoiceSrc(voice, &src);
977
978 return voice;
979 }
980
981 /*---------------------------------------------------------------------------*
982 Name: LoadFileIntoRam
983
984 Description: Loads a file into memory. Memory is allocated.
985
986 Arguments: path File to load into main memory
987
988 Returns: pointer to file in main memory or NULL if not opened
989 *---------------------------------------------------------------------------*/
LoadFileIntoRam(char * path)990 static void * LoadFileIntoRam( char *path )
991 {
992 DVDFileInfo handle;
993 u32 round_length;
994 s32 read_length;
995 void *buffer;
996
997 // Open File
998 if (!DVDOpen(path, &handle))
999 {
1000 OSReport("WARNING! Failed to open %s\n", path);
1001 return NULL;
1002 }
1003
1004 // Make sure file length is not 0
1005 if (DVDGetLength(&handle) == 0)
1006 {
1007 OSReport("WARNING! File length is 0\n");
1008 return NULL;
1009 }
1010
1011 round_length = OSRoundUp32B(DVDGetLength(&handle));
1012 buffer = MEMAllocFromExpHeapEx(hExpHeap, round_length, 32);
1013
1014 // Make sure we got a buffer
1015 if (buffer == NULL)
1016 {
1017 OSReport("WARNING! Unable to allocate buffer\n");
1018 return NULL;
1019 }
1020
1021 // Read Files
1022 read_length = DVDRead(&handle, buffer, (s32)(round_length), 0);
1023
1024 // Make sure we read the file correctly
1025 if (read_length <= 0)
1026 {
1027 OSReport("WARNING! File lenght is wrong\n");
1028 return NULL;
1029 }
1030
1031 return buffer;
1032 }
1033
1034 static const s16 HEIGHT = 10;
1035
1036 /*---------------------------------------------------------------------------*
1037 * Name : RenderOperation
1038 * Description :
1039 * Arguments :
1040 * Returns : None.
1041 *---------------------------------------------------------------------------*/
RenderOperation(void)1042 static void RenderOperation( void )
1043 {
1044 s16 y = 96;
1045 char button[256];
1046 u32 i;
1047
1048 for (i = 0; i < NUM_BUTTON_MAPS; i++)
1049 {
1050 switch (ButtonMap[i][0])
1051 {
1052 case WPAD_BUTTON_A: sprintf(button, "A "); break;
1053 case WPAD_BUTTON_B: sprintf(button, "B "); break;
1054 case WPAD_BUTTON_1: sprintf(button, "1 "); break;
1055 case WPAD_BUTTON_2: sprintf(button, "2 "); break;
1056 case WPAD_BUTTON_DOWN: sprintf(button, "Down "); break;
1057 case WPAD_BUTTON_UP: sprintf(button, "Up "); break;
1058 case WPAD_BUTTON_LEFT: sprintf(button, "Left "); break;
1059 case WPAD_BUTTON_RIGHT: sprintf(button, "Right"); break;
1060 }
1061 DEMOPrintf( 16, y+=HEIGHT, 0, "%s : %s", button, SampleFiles[ButtonMap[i][1]]);
1062 }
1063
1064 DEMOPrintf( 16, y+=HEIGHT, 0, "Home : Stop all sounds");
1065 DEMOPrintf( 16, y+=HEIGHT, 0, "Plus : Mute On");
1066 DEMOPrintf( 16, y+=HEIGHT, 0, "Minus : Mute Off");
1067 }
1068
1069 /*---------------------------------------------------------------------------*
1070 * Name : RenderControllerStatus
1071 * Description :
1072 * Arguments :
1073 * Returns : None.
1074 *---------------------------------------------------------------------------*/
RenderControllerStatus(void)1075 static void RenderControllerStatus( void )
1076 {
1077 s16 y = 32;
1078 int chan;
1079
1080 DEMOPrintf( 150, y, 0, "speaker");
1081 DEMOPrintf( 250, y, 0, "volume");
1082 for(chan=0; chan<WPAD_MAX_CONTROLLERS; chan++)
1083 {
1084 y += HEIGHT;
1085 DEMOPrintf( 16, y, 0, "CHAN_%d [%s]",
1086 chan,
1087 (info[chan].status == WPAD_ERR_NO_CONTROLLER) ? "--" :
1088 (info[chan].type == 0) ? "CORE" :
1089 (info[chan].type == 1) ? "NUNCHAKU" :
1090 (info[chan].type == 2) ? "CLASSIC" :
1091 "UNKNOWN"
1092 );
1093 DEMOPrintf( 150, y, 0, "%s", (info[chan].Speakers.active == 0) ? "OFF" :
1094 (info[chan].Speakers.active == 2) ? "MUTE" :
1095 "ON");
1096 DEMOPrintf( 250, y, 0, "%d", WPADGetSpeakerVolume());
1097 }
1098 }
1099
1100 /*---------------------------------------------------------------------------*
1101 * Name : initialize
1102 * Description :
1103 * Arguments :
1104 * Returns : None.
1105 *---------------------------------------------------------------------------*/
initialize(void)1106 static void initialize( void )
1107 {
1108 const GXColor DARKBLUE = { 0, 0, 64, 255 };
1109 const int SCREEN_WIDTH = 320;
1110 const int SCREEN_HEIGHT = 240;
1111
1112 DEMOInit( &GXNtsc480IntDf );
1113 GXSetCopyClear( DARKBLUE, GX_MAX_Z24 );
1114 GXCopyDisp( DEMOGetCurrentBuffer(), GX_TRUE );
1115 DEMOInitCaption( DM_FT_XLU, SCREEN_WIDTH, SCREEN_HEIGHT );
1116 GXSetZMode( GX_ENABLE, GX_ALWAYS, GX_ENABLE ); // Set pixel processing mode
1117 GXSetBlendMode( GX_BM_BLEND, GX_BL_ONE, GX_BL_ONE, GX_LO_CLEAR ); // Translucent mode
1118
1119 DEMOPadInit();
1120
1121 } /* end initialize() */
1122