1 /*---------------------------------------------------------------------------*
2   Project:  Revolution AX simple demo
3   File:     axsimple.c
4 
5   Copyright (C)1998-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: axsimple.c,v $
14   Revision 1.8  2009/04/02 01:28:39  aka
15   Copied from SDK_3_2_DEV_BRANCH.
16 
17   Revision 1.7.22.1  2009/04/02 01:26:38  aka
18   Revised miss spelling (aquire->acquire).
19 
20   Revision 1.7  2006/11/21 08:29:31  aka
21   Removed the zero buffer.
22 
23   Revision 1.6  2006/10/23 02:05:52  aka
24   Changed from AXInit() to AXInitSpecifyMem().
25   Changed from MIXInit() to MIXInitSpecifyMem().
26   Changed from SYNInit() to SYNInitSpecifyMem().
27 
28   Revision 1.5  2006/10/10 08:30:06  aka
29   Revised AXInit(), MIXInit() and SYNInit().
30 
31   Revision 1.4  2006/02/03 12:12:08  aka
32   Revised comment.
33 
34   Revision 1.3  2006/02/02 07:56:31  aka
35   Modified using MEM functions instead of OSAlloc()/OSFree().
36 
37   Revision 1.2  2006/02/01 05:42:37  aka
38   Added #ifndef(#ifdef) HOLLYWOOD_REV - #else - #endif.
39 
40   Revision 1.1  2005/11/04 05:01:39  aka
41   Imported from dolphin tree.
42 
43     11    03/04/04 13:25 Suzuki
44     remove the definition of DSPADPCM
45 
46     10    12/04/02 6:23p Dante
47     Fixed pred_scale, yn1, & yn2 refrences for AXPBADPCM structure.
48 
49     9     02/11/20 7:39 Dante
50     Added looped sample support, and fixed dropped sample callback support
51 
52     8     02/11/13 11:06 Dante
53     Bug fix: GetDSPADPCMDataSize32B's conversion from nibbles to bytes must
54     occur after round up. Clean up (removed unused variables).
55 
56     7     02/11/13 4:03 Dante
57     Fixed GetDSPADPCMDataSize32B to return bytes not nibbles. Changed
58     OSReports to reflect size.
59 
60     6     02/11/12 9:52 Dante
61     Removed redundant SRC commands
62 
63     5     02/11/09 12:37 Dante
64     Additions from SDK 2002-Dec-05 Patch1
65 
66     4     02/11/09 12:34 Dante
67     Added support for non-native sample rate ADPCM files and a printed
68     intro.
69 
70     3     02/10/30 6:31 Dante
71     Set the loop address for one shot samples to the zero buffer
72 
73   $NoKeywords: $
74  *---------------------------------------------------------------------------*/
75 
76 /*---------------------------------------------------------------------------*
77     This program loads and initializes ADPCM samples. The samples were created
78     using DSPADPCM.exe. The files are expected to have a 96 byte header.
79  *---------------------------------------------------------------------------*/
80 
81 #include <demo.h>
82 #include <string.h>
83 #include <revolution/mem.h>
84 
85 // User defines
86 #define NUM_SAMPLES     10
87 #define NUM_BUTTON_MAPS 11
88 // Filenames for samples to be played
89 char SampleFiles [NUM_SAMPLES][256] =
90 {
91     "axdemo/simple/snare.dsp",
92     "axdemo/simple/tom.dsp",
93     "axdemo/simple/cymbal.dsp",
94     "axdemo/simple/ride.dsp",
95     "axdemo/simple/cowbell.dsp",
96     "axdemo/simple/kick.dsp",
97     "axdemo/simple/bongo1.dsp",
98     "axdemo/simple/bongo2.dsp",
99     "axdemo/simple/bongo3.dsp",
100     "axdemo/simple/bongo4.dsp",
101 };
102 // Button mappings for samples {Button, index in file table}
103 u32 ButtonMap [NUM_BUTTON_MAPS][2] =
104 {
105     {PAD_BUTTON_A,      0},
106     {PAD_BUTTON_B,      1},
107     {PAD_BUTTON_X,      2},
108     {PAD_BUTTON_Y,      3},
109     {PAD_TRIGGER_Z,     4},
110     {PAD_TRIGGER_L,     5},
111     {PAD_TRIGGER_R,     5},
112     {PAD_BUTTON_DOWN,   6},
113     {PAD_BUTTON_UP,     7},
114     {PAD_BUTTON_LEFT,   8},
115     {PAD_BUTTON_RIGHT,  9}
116 };
117 
118 // This demo uses a very simple mixing paradigm. All sounds are played at
119 // a volume of 1.0 (0x8000). Please see the MIX library for a more
120 // comprehensive mixing library
121 AXPBMIX g_mix = {
122     0x8000,       // volume left
123     0x0000,       // volume ramp left
124     0x8000,       // volume right
125     0x0000,       // volume ramp right
126 
127     0x0000,       // volume AUX A left
128     0x0000,       // volume ramp AUX A left
129     0x0000,       // volume AUX A right
130     0x0000,       // volume ramp AUX A right
131 
132     0x0000,       // volume AUX B left
133     0x0000,       // volume ramp AUX B left
134     0x0000,       // volume AUX B right
135     0x0000,       // volume ramp AUX B right
136 
137     0x0000,       // volume AUX C left
138     0x0000,       // volume ramp AUX C left
139     0x0000,       // volume AUX C right
140     0x0000,       // volume ramp AUX C right
141 
142     0x0000,       // volume surround
143     0x0000,       // volume ramp surround
144     0x0000,       // volume AUX A surround
145     0x0000,       // volume ramp AUX A surround
146     0x0000,       // volume AUX B surround
147     0x0000,       // volume ramp AUX B surround
148     0x0000,       // volume AUX C surround
149     0x0000,       // volume ramp AUX C surround
150 };
151 
152 AXPBVE g_ve = {
153     0x8000,     // volume at start of frame, 0x8000 = 1.0
154     0           // signed per sample delta (160 samples per frame)
155 };
156 
157 // User Structures to keep track of data
158 typedef struct
159 {
160     void        *mramAddr;
161 
162 } SampleInfo;
163 
164 typedef struct
165 {
166     AXVPB           *voice;
167     u32             state;
168 } VoiceInfo;
169 
170 // Voice  Defines
171 #define VOICE_PRIO_HIGH 31
172 #define VOICE_PRIO_MED  15
173 #define VOICE_PRIO_LOW  1
174 
175 #define VOICE_STATE_STOPPED        0
176 #define VOICE_STATE_START          1
177 #define VOICE_STATE_STARTED        2
178 #define VOICE_STATE_PLAYING        3
179 #define VOICE_STATE_STOP           4
180 
181 // Exp Heap
182 static MEMHeapHandle hExpHeap;
183 
184 // Utility Macro Functions
185 #define RoundUp64(x) (((u32)(x) + 64 - 1) & ~(64 - 1))
186 #define Bytes2Nibbles(n) (n << 1)
187 #define Nibbles2Bytes(n) (n >> 1)
188 #define GetDSPADPCMDataAddress(a) ((void*)((u32)a + sizeof(DSPADPCM)))
189 #define GetDSPADPCMDataSize32B(a) (RoundUp64(((DSPADPCM*)a)->num_adpcm_nibbles) >> 1)
190 #define GetVoiceCurrentAddr32(v) (*(u32 *)(&((v)->pb.addr.currentAddressHi)))
191 #define GetVoiceLoopAddr32(v) (*(u32 *)(&((v)->pb.addr.loopAddressHi)))
192 #define GetVoiceEndAddr32(v) (*(u32 *)(&((v)->pb.addr.endAddressHi)))
193 
194 // Sample Info
195 static SampleInfo       Samples[NUM_SAMPLES];
196 // AX Voice info
197 static VoiceInfo        Voices[AX_MAX_VOICES];
198 
199 // function Prototypes
200 static void * LoadFileIntoRam(char *path);
201 static void AudioFrameCallback(void);
202 static AXVPB* AcquireVoiceADPCM(void *pDSPADPCMData);
203 static void LoadSamples(void);
204 static void PlaySample(SampleInfo *sample);
205 static void VoiceCallback(void * voiceIn);
206 static void PrintIntro(void);
207 
208 /*---------------------------------------------------------------------------*
209  *---------------------------------------------------------------------------*/
main()210 void main ()
211 {
212     u32        i;
213     u32        button;
214     void       *arenaMem2Lo;
215     void       *arenaMem2Hi;
216     void       *axBuffer;
217 
218     // Initialize OS & Audio
219     DEMOInit(NULL);
220     DEMOPadInit();
221 
222     // initialize Exp Heap on MEM2
223     arenaMem2Lo = OSGetMEM2ArenaLo();
224     arenaMem2Hi = OSGetMEM2ArenaHi();
225     hExpHeap    = MEMCreateExpHeap(arenaMem2Lo, (u32)arenaMem2Hi - (u32) arenaMem2Lo);
226 
227     // initialize AI & AX
228     axBuffer = MEMAllocFromExpHeapEx(hExpHeap, AXGetMemorySize(AX_MAX_VOICES), 32);
229 
230     AIInit(NULL);
231     AXInitSpecifyMem(AX_MAX_VOICES, axBuffer);
232 
233     // Load Voice data into MRAM
234     LoadSamples();
235 
236     // Register Callback with AX for audio processing
237     AXRegisterCallback(&AudioFrameCallback);
238 
239     // Print Intro
240     PrintIntro();
241 
242     // Spin
243     while (1)
244     {
245         VIWaitForRetrace();
246 
247 
248         // User Input
249         DEMOPadRead();
250         button = DEMOPadGetButtonDown(0);
251         // Stop all sounds when START/PAUSE is pressed
252         if (button & PAD_BUTTON_START)
253         {
254             // Stop all voices
255             for (i = 0; i < AX_MAX_VOICES; i++)
256             {
257                 Voices[i].state = VOICE_STATE_STOP;
258             }
259             continue;
260         }
261         // Use Button map to start sounds
262         for (i = 0; i < NUM_BUTTON_MAPS; i++)
263         {
264             if (button & ButtonMap[i][0])
265             {
266                 PlaySample(&Samples[ButtonMap[i][1]]);
267             }
268         }
269     }
270 }
271 
272 /*---------------------------------------------------------------------------*
273     Name:           LoadSamples
274 
275     Description:    Loads ADPCM files into Main Memory (Header + Data) and
276                     ARAM (Data)
277 
278     Arguments:      none
279 
280     Returns:        none
281  *---------------------------------------------------------------------------*/
LoadSamples(void)282 static void LoadSamples(void)
283 {
284     u32 i;
285 
286     // Load samples
287     for (i = 0; i < NUM_SAMPLES; i++)
288     {
289         // Load ADPCM file into MRAM (96 byte header included)
290         Samples[i].mramAddr = LoadFileIntoRam(SampleFiles[i]);
291 
292         // Sanity Check
293         if (Samples[i].mramAddr == NULL)
294         {
295             OSReport("WARNING! Sample %d not loaded\n", i);
296             continue;
297         }
298     }
299 }
300 
301 /*---------------------------------------------------------------------------*
302     Name:           PlaySample
303 
304     Description:    Utility function that will play a sample
305 
306     Arguments:      sample    Pointer to the sample information
307 
308     Returns:        pointer to the allocated voice
309  *---------------------------------------------------------------------------*/
PlaySample(SampleInfo * sample)310 static void PlaySample(SampleInfo *sample)
311 {
312     AXVPB    *voice;
313 
314     if (sample->mramAddr == NULL)
315     {
316         OSReport("WARNING! Sample not loaded!\n");
317         return;
318     }
319 
320     // Acquire Voice and start
321     voice = AcquireVoiceADPCM(sample->mramAddr);
322     if (voice == NULL)
323     {
324         OSReport("WARNING: Ran out of voices!\n");
325         return;
326     }
327     Voices[voice->index].voice = voice;
328     Voices[voice->index].state = VOICE_STATE_START;
329 }
330 
331 /*---------------------------------------------------------------------------*
332     Name:            AudioFrameCallback
333 
334     Description:    Callback that process audio data per 5ms audio frame.
335                     In this case, it stops, resets, and starts a voice.
336 
337     Arguments:      none
338 
339     Returns:        none
340  *---------------------------------------------------------------------------*/
AudioFrameCallback(void)341 static void AudioFrameCallback(void)
342 {
343     u32 i;
344     BOOL bStopFlag = FALSE;
345 
346     // Monitor each voice and process states. This must be done in the
347     // callback
348     for (i = 0; i < AX_MAX_VOICES; i++)
349     {
350 
351         // Skip NULL entries
352         if (Voices[i].voice == NULL) continue;
353 
354         switch (Voices[i].state)
355         {
356             case VOICE_STATE_STOPPED:
357                 break;
358             case VOICE_STATE_START:
359                 // Start the voice
360                 AXSetVoiceState(Voices[i].voice, AX_PB_STATE_RUN);
361                 Voices[i].state = VOICE_STATE_STARTED;
362                 break;
363             case VOICE_STATE_STARTED:
364                 // Skip a frame
365                 Voices[i].state = VOICE_STATE_PLAYING;
366                 break;
367             case VOICE_STATE_PLAYING:
368                 // Check to see if the voice is finished, if so, stop it
369                 if (Voices[i].voice->pb.state == AX_PB_STATE_STOP)
370                     bStopFlag = TRUE;
371                 break;
372             case VOICE_STATE_STOP:
373                 // Force a voice to stop
374                 bStopFlag = TRUE;
375                 break;
376         }
377 
378         // A voice must be stopped
379         if (bStopFlag)
380         {
381             AXSetVoiceState(Voices[i].voice, AX_PB_STATE_STOP);
382             AXFreeVoice(Voices[i].voice);
383             Voices[i].voice = NULL;
384             Voices[i].state = VOICE_STATE_STOPPED;
385             bStopFlag = FALSE;
386         }
387     }
388 }
389 
390 /*---------------------------------------------------------------------------*
391     Name:           VoiceCallback
392 
393     Description:    Callback for when a voice is dropped.
394 
395     Arguments:      unused
396 
397     Returns:        none
398  *---------------------------------------------------------------------------*/
VoiceCallback(void * voiceIn)399 static void VoiceCallback(void * voiceIn)
400 {
401     AXVPB *voice = (AXVPB*)voiceIn;
402 
403     // Note: Voice is auto-magically stopped by AX layer when dropped
404 
405     // Application clean-up
406     Voices[voice->index].voice = NULL;
407     Voices[voice->index].state = VOICE_STATE_STOPPED;
408 }
409 
410 /*---------------------------------------------------------------------------*
411     Name:           AcquireVoiceADPCM
412 
413     Description:    Parses the ADPCM header, sets voice parameters
414 
415     Arguments:      pDSPADPCMData    Pointer to the ADPCM data in MRAM
416                     pARAMStart        ARAM Start address
417 
418     Returns:        pointer to the allocated voice or NULL
419  *---------------------------------------------------------------------------*/
AcquireVoiceADPCM(void * pDSPADPCMData)420 static AXVPB* AcquireVoiceADPCM(void *pDSPADPCMData)
421 {
422     DSPADPCM            *ps = (DSPADPCM*)pDSPADPCMData;
423     AXPBADDR            addr;
424     AXPBADPCM           adpcm;
425     AXPBSRC             src;
426     AXPBADPCMLOOP       adpcmLoop;
427     AXVPB*              voice;
428     u32                 srcBits;
429     u32                 mramAddress;
430     u32                 pMRAMStart;
431 
432     // Allocate a voice for use
433     voice = AXAcquireVoice(VOICE_PRIO_MED, VoiceCallback, 0);
434 
435     if (voice == NULL)
436     {
437         OSReport("WARNING! Voice Acquisition failed!\n");
438         return NULL;
439     }
440 
441     // Fill AXPBADDR structure
442     // All the folowing addresses are in nibbles
443 
444     addr.loopFlag           = ps->loop_flag;
445     addr.format             = ps->format;
446 
447     pMRAMStart = OSCachedToPhysical(GetDSPADPCMDataAddress(pDSPADPCMData));
448 
449     // Support for looping
450     if (addr.loopFlag)
451     {
452         adpcmLoop.loop_pred_scale = ps->lps;
453         adpcmLoop.loop_yn1        = ps->lyn1;
454         adpcmLoop.loop_yn2        = ps->lyn2;
455     }
456 
457     mramAddress             = (ps->sa + Bytes2Nibbles(pMRAMStart));
458     addr.loopAddressHi      = (u16)(mramAddress >> 16);
459     addr.loopAddressLo      = (u16)(mramAddress & 0xFFFF);
460 
461     mramAddress             = (ps->ea + Bytes2Nibbles(pMRAMStart));
462     addr.endAddressHi       = (u16)(mramAddress >> 16);
463     addr.endAddressLo       = (u16)(mramAddress & 0xFFFF);
464 
465     mramAddress             = (ps->ca + Bytes2Nibbles(pMRAMStart));
466     addr.currentAddressHi   = (u16)(mramAddress >> 16);
467     addr.currentAddressLo   = (u16)(mramAddress & 0xFFFF);
468 
469     // Fill AXPBADPCM structure
470     adpcm.a[0][0]           = ps->coef[0];
471     adpcm.a[0][1]           = ps->coef[1];
472     adpcm.a[1][0]           = ps->coef[2];
473     adpcm.a[1][1]           = ps->coef[3];
474     adpcm.a[2][0]           = ps->coef[4];
475     adpcm.a[2][1]           = ps->coef[5];
476     adpcm.a[3][0]           = ps->coef[6];
477     adpcm.a[3][1]           = ps->coef[7];
478     adpcm.a[4][0]           = ps->coef[8];
479     adpcm.a[4][1]           = ps->coef[9];
480     adpcm.a[5][0]           = ps->coef[10];
481     adpcm.a[5][1]           = ps->coef[11];
482     adpcm.a[6][0]           = ps->coef[12];
483     adpcm.a[6][1]           = ps->coef[13];
484     adpcm.a[7][0]           = ps->coef[14];
485     adpcm.a[7][1]           = ps->coef[15];
486     adpcm.gain              = ps->gain;
487     adpcm.pred_scale        = ps->ps;
488     adpcm.yn1               = ps->yn1;
489     adpcm.yn2               = ps->yn2;
490 
491     // Fill AXPBSRC structure for proper sample rates
492     srcBits = (u32)(0x00010000 * ((f32)ps->sample_rate / AX_IN_SAMPLES_PER_SEC));
493     src.ratioHi = (u16)(srcBits >> 16);
494     src.ratioLo = (u16)(srcBits & 0xFFFF);
495     src.currentAddressFrac = 0;
496     src.last_samples[0] = 0;
497     src.last_samples[1] = 0;
498     src.last_samples[2] = 0;
499     src.last_samples[3] = 0;
500 
501     // Set voice type
502     AXSetVoiceType(voice, AX_PB_TYPE_NORMAL);
503 
504     // Set Address and ADPCM information from header
505     AXSetVoiceAddr(voice, &addr);
506     AXSetVoiceAdpcm(voice, &adpcm);
507     AXSetVoiceAdpcmLoop(voice, &adpcmLoop);
508 
509     // Set simple volumes
510     AXSetVoiceMix(voice, &g_mix);
511     AXSetVoiceVe(voice, &g_ve);
512 
513     // Set sample rate
514     AXSetVoiceSrcType(voice, AX_SRC_TYPE_LINEAR);
515     AXSetVoiceSrc(voice, &src);
516 
517     return voice;
518 }
519 
520 /*---------------------------------------------------------------------------*
521     Name:            LoadFileIntoRam
522 
523     Description:    Loads a file into memory. Memory is allocated.
524 
525     Arguments:      path    File to load into main memory
526 
527     Returns:        pointer to file in main memory or NULL if not opened
528  *---------------------------------------------------------------------------*/
LoadFileIntoRam(char * path)529 static void * LoadFileIntoRam(char *path)
530 {
531     DVDFileInfo handle;
532     u32         round_length;
533     s32         read_length;
534     void        *buffer;
535 
536     // Open File
537     if (!DVDOpen(path, &handle))
538     {
539         OSReport("WARNING! Failed to open %s\n", path);
540         return NULL;
541     }
542 
543     // Make sure file length is not 0
544     if (DVDGetLength(&handle) == 0)
545     {
546         OSReport("WARNING! File length is 0\n");
547         return NULL;
548     }
549 
550     round_length = OSRoundUp32B(DVDGetLength(&handle));
551     buffer       = MEMAllocFromExpHeapEx(hExpHeap, round_length,  32);
552 
553     // Make sure we got a buffer
554     if (buffer == NULL)
555     {
556         OSReport("WARNING! Unable to allocate buffer\n");
557         return NULL;
558     }
559 
560     // Read Files
561     read_length  = DVDRead(&handle, buffer, (s32)(round_length), 0);
562 
563     // Make sure we read the file correctly
564     if (read_length <= 0)
565     {
566         OSReport("WARNING! File lenght is wrong\n");
567         return NULL;
568     }
569 
570     return buffer;
571 }
572 
573 /*---------------------------------------------------------------------------*
574     Name:           PrintIntro
575 
576     Description:    Prints Intro to debug output
577 
578     Arguments:      none
579 
580     Returns:        none
581  *---------------------------------------------------------------------------*/
PrintIntro(void)582 static void PrintIntro(void)
583 {
584     char button[256];
585     u32 i;
586 
587     OSReport("\n\n****************************************************\n");
588     OSReport(" AXSimple - Plays DSPADPCM.exe files\n");
589     OSReport("****************************************************\n");
590 
591     for (i = 0; i < NUM_BUTTON_MAPS; i++)
592     {
593         switch (ButtonMap[i][0])
594         {
595             case PAD_BUTTON_A:
596                 sprintf(button, "A Button");
597                 break;
598             case PAD_BUTTON_B:
599                 sprintf(button, "B Button");
600                 break;
601             case PAD_BUTTON_X:
602                 sprintf(button, "X Button");
603                 break;
604             case PAD_BUTTON_Y:
605                 sprintf(button, "Y Button");
606                 break;
607             case PAD_TRIGGER_Z:
608                 sprintf(button, "Z Button");
609                 break;
610             case PAD_TRIGGER_L:
611                 sprintf(button, "L Button");
612                 break;
613             case PAD_TRIGGER_R:
614                 sprintf(button, "R Button");
615                 break;
616             case PAD_BUTTON_DOWN:
617                 sprintf(button, "+Control Pad Down");
618                 break;
619             case PAD_BUTTON_UP:
620                 sprintf(button, "+Control Pad Up");
621                 break;
622             case PAD_BUTTON_LEFT:
623                 sprintf(button, "+Control Pad Left");
624                 break;
625             case PAD_BUTTON_RIGHT:
626                 sprintf(button, "+Control Pad Right");
627                 break;
628         }
629         OSReport("%s => %s\n", button, SampleFiles[ButtonMap[i][1]]);
630     }
631 
632     OSReport("Start/Pause => Stop all sounds\n");
633     OSReport("****************************************************\n");
634 }
635