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