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