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