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