1 /*---------------------------------------------------------------------------*
2 Project: SP Demo application
3 File: spdemo.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: spdemo.c,v $
14 Revision 1.8 2006/03/06 09:59:03 kawaset
15 Eliminated warnings.
16
17 Revision 1.7 02/21/2006 01:04:31 mitu
18 modified am.h path.
19
20 Revision 1.6 02/20/2006 04:13:12 mitu
21 changed include path from dolphin/ to revolution/.
22
23 Revision 1.5 02/02/2006 08:17:04 aka
24 Modified using MEM functions instead of OSAlloc()/OSFree().
25
26 Revision 1.4 02/01/2006 08:30:07 aka
27 Added #ifndef(#ifdef) HOLLYWOOD_REV - #else - #endif.
28
29 Revision 1.3 2006/01/27 04:55:47 ekwon
30 Corrected "\%" escape sequence warning (replaced with "%%").
31
32 Revision 1.2 11/08/2005 03:01:25 aka
33 Changed suiting to Revolution's audio spec.
34
35 Revision 1.1 11/04/2005 05:02:22 aka
36 Copyright 2001 Nintendo. All rights reserved.
37
38 1 9/05/01 8:09p Eugene
39 Demonstration of SP and AM libraries. Uses AX!
40 created
41
42 $NoKeywords: $
43 *---------------------------------------------------------------------------*/
44
45 /*---------------------------------------------------------------------------*
46 * Includes
47 *---------------------------------------------------------------------------*/
48
49 #include <demo.h>
50 #include <demo/DEMOWin.h>
51 #include <revolution.h>
52 #include <revolution/mix.h>
53 #include <revolution/sp.h>
54 #ifndef HOLLYWOOD_REV
55 #include <dolphin/am.h>
56 #else
57 #include <revolution/mem.h>
58 #include <string.h>
59 #endif
60
61 #include "spdemo.h"
62
63 /*---------------------------------------------------------------------------*
64 * SP data
65 *---------------------------------------------------------------------------*/
66
67 #define SPT_FILE "/SPDEMO/spdemo.spt"
68 #define SPD_FILE "/SPDEMO/spdemo.spd"
69
70 static SPSoundTable *sp_table;
71 #ifdef HOLLYWOOD_REV
72 static u8 *sp_data;
73 #endif
74
75 #ifndef HOLLYWOOD_REV
76 /*---------------------------------------------------------------------------*
77 * ARAM initialization
78 *---------------------------------------------------------------------------*/
79
80 // Use AR allocator to divide ARAM into 3 blocks
81 #define MAX_ARAM_BLOCKS 3
82
83 // Give a whopping 8MB of ARAM to audio!
84 #define AUDIO_BLOCK_SIZE_BYTES (8*1024*1024)
85
86
87 static u32 aramZeroBase;
88 static u32 aramUserBase;
89 static u32 aramMemArray[MAX_ARAM_BLOCKS];
90
91
92 // transfer buffer for ARAM audio manager (AM)
93 #define XFER_BUFFER_SIZE_BYTES (16*1024)
94
95 u8 xfer_buffer[XFER_BUFFER_SIZE_BYTES] ATTRIBUTE_ALIGN(32);
96
97 #else
98 /*---------------------------------------------------------------------------*
99 * Exp Heap
100 *---------------------------------------------------------------------------*/
101
102 static MEMHeapHandle hExpHeap;
103
104 /*---------------------------------------------------------------------------*
105 * Zero Buffer
106 *---------------------------------------------------------------------------*/
107
108 #define ZEROBUFFER_BYTES 256
109 #endif
110
111 /*---------------------------------------------------------------------------*
112 * AX Profiling
113 *---------------------------------------------------------------------------*/
114
115 // store up to 8 frames, just to be safe
116 #define NUM_AX_PROFILE_FRAMES 8
117
118 static AXPROFILE ax_profile[NUM_AX_PROFILE_FRAMES];
119
120 /*---------------------------------------------------------------------------*
121 * Application-layer voice abstraction
122 *---------------------------------------------------------------------------*/
123
124 #define MAX_DEMO_VOICES 64
125
126 typedef struct
127 {
128 AXVPB *ax_voice;
129 SPSoundEntry *sp_entry;
130
131 } DEMO_VOICE;
132
133 DEMO_VOICE demo_voice[MAX_DEMO_VOICES];
134
135 // Checks SP entry 'type' to see if the voice is looped or not
136 #define mISLOOPED(x) ((x->type)&0x1)
137
138
139 /*---------------------------------------------------------------------------*
140 * Prototypes
141 *---------------------------------------------------------------------------*/
142
143 static DEMO_VOICE *get_demo_voice (void);
144 static void init_demo_voices (void);
145 static void ax_demo_callback (void);
146 static void ax_drop_voice_callback (void *p);
147 static void play_sfx (u32 sfx);
148
149 // for UI menus
150 static void MNU_play_click (DEMOWinMenuInfo *menu, u32 item);
151 static void MNU_play_sfx (DEMOWinMenuInfo *menu, u32 item, u32 *result);
152 static void MNU_stop_sfx (DEMOWinMenuInfo *menu, u32 item, u32 *result);
153 static void MNU_stop_looping (DEMOWinMenuInfo *menu, u32 item, u32 *result);
154 static void ax_profile_update (DEMOWinInfo *window);
155
156
157 /*---------------------------------------------------------------------------*
158 * UI Stuff
159 *---------------------------------------------------------------------------*/
160
161 DEMOWinInfo *DebugWin;
162 DEMOWinInfo *ProfileWin;
163
164 DEMOWinMenuItem MenuItem[] =
165 {
166 { "Sound Effect", DEMOWIN_ITM_SEPARATOR, NULL, NULL },
167 { " Noisy Drum", DEMOWIN_ITM_NONE, MNU_play_sfx, NULL },
168 { " Gunshot", DEMOWIN_ITM_NONE, MNU_play_sfx, NULL },
169 { " Voice-Man", DEMOWIN_ITM_NONE, MNU_play_sfx, NULL },
170 { " Voice-Woman", DEMOWIN_ITM_NONE, MNU_play_sfx, NULL },
171 { " Looping Strings", DEMOWIN_ITM_NONE, MNU_play_sfx, NULL },
172 { " ", DEMOWIN_ITM_SEPARATOR, NULL, NULL },
173 { "Voice Control", DEMOWIN_ITM_SEPARATOR, NULL, NULL },
174 { " Stop All", DEMOWIN_ITM_NONE, MNU_stop_sfx, NULL },
175 { " Stop All Looping", DEMOWIN_ITM_NONE, MNU_stop_looping, NULL },
176 { " ", DEMOWIN_ITM_SEPARATOR, NULL, NULL },
177 { "", DEMOWIN_ITM_TERMINATOR, NULL, NULL }
178 };
179
180 DEMOWinMenuInfo Menu =
181 {
182 "AX Sound Pipeline Demo", // title
183 NULL, // window handle
184 MenuItem, // list of menu items
185 10, // max num of items to display at a time
186 DEMOWIN_MNU_NONE, // attribute flags
187
188 // user callbacks
189 NULL, // callback for menu open event
190 MNU_play_click, // callback for cursor move event
191 NULL, // callback for item select event
192 NULL, // callback for cancel event
193
194 // private members
195 0, 0, 0, 0, 0
196 };
197
198 DEMOWinMenuInfo *MenuPtr;
199
200 /*===========================================================================*
201 * F U N C T I O N D E F I N I T I O N S
202 *===========================================================================*/
203
204 /*---------------------------------------------------------------------------*
205 * Name : ax_profile_updatek()
206 * Description : refresh callback for AX profile window
207 * Arguments :
208 * Returns :
209 *---------------------------------------------------------------------------*/
210
ax_profile_update(DEMOWinInfo * window)211 static void ax_profile_update(DEMOWinInfo *window)
212 {
213
214 BOOL old;
215
216 u32 i;
217
218 u32 cpuCycles;
219 u32 userCycles;
220 u32 axCycles;
221 u32 voices;
222
223 u32 maxCpuCycles =0;
224 u32 maxUserCycles=0;
225 u32 maxAxCycles =0;
226 u32 maxVoices =0;
227
228 old = OSDisableInterrupts();
229
230 i = AXGetProfile();
231
232 if (i)
233 {
234 // up to 4 audio frames can complete within a 60Hz video frame
235 // so spin thru the accumulated audio frame profiles and find the peak values
236 while (i)
237 {
238 i--;
239
240 cpuCycles = (u32)(ax_profile[i].axFrameEnd - ax_profile[i].axFrameStart);
241 userCycles = (u32)(ax_profile[i].userCallbackEnd - ax_profile[i].userCallbackStart);
242 axCycles = cpuCycles - userCycles;
243 voices = ax_profile[i].axNumVoices;
244
245 // find peak values over the last i audio frames
246 if (cpuCycles > maxCpuCycles) maxCpuCycles = cpuCycles;
247 if (userCycles > maxUserCycles) maxUserCycles = userCycles;
248 if (axCycles > maxAxCycles) maxAxCycles = axCycles;
249 if (voices > maxVoices) maxVoices = voices;
250
251 }
252 OSRestoreInterrupts(old);
253
254 DEMOWinPrintfXY(window, 0, 2, "Total CPU : %5.2f%%", (f32)OSTicksToNanoseconds(maxCpuCycles) / 50000);
255 DEMOWinPrintfXY(window, 0, 4, "User : %5.2f%%", (f32)OSTicksToNanoseconds(maxUserCycles) / 50000);
256 DEMOWinPrintfXY(window, 0, 5, "AX : %5.2f%%", (f32)OSTicksToNanoseconds(maxAxCycles) / 50000);
257 DEMOWinPrintfXY(window, 0, 7, "Voices : %5d", maxVoices);
258
259 }
260
261 OSRestoreInterrupts(old);
262
263 } // end profile_update()
264
265 /*---------------------------------------------------------------------------*
266 * Name : MNU_play_click()
267 * Description : Callback for menu system, plays 'click' for cursor movement
268 * Arguments :
269 * Returns :
270 *---------------------------------------------------------------------------*/
271
MNU_play_click(DEMOWinMenuInfo * menu,u32 item)272 static void MNU_play_click(DEMOWinMenuInfo *menu, u32 item)
273 {
274
275 #pragma unused(menu)
276 #pragma unused(item)
277
278 play_sfx(SFX_MENU);
279
280
281 } // end MNU_play_click()
282
283
284 /*---------------------------------------------------------------------------*
285 * Name : MNU_play_sfx()
286 * Description : Play sound effect selected from menu.
287 * Arguments :
288 * Returns :
289 *---------------------------------------------------------------------------*/
290
MNU_play_sfx(DEMOWinMenuInfo * menu,u32 item,u32 * result)291 static void MNU_play_sfx(DEMOWinMenuInfo *menu, u32 item, u32 *result)
292 {
293
294 #pragma unused(menu)
295 #pragma unused(result)
296
297 play_sfx(item);
298
299 } // end MNU_play_sfx()
300
301
302
303 /*---------------------------------------------------------------------------*
304 * Name : MNU_stop_sfx()
305 * Description : Stops all voices. Note that voices are freed by the AX user
306 * callback on the next frame.
307 * Arguments :
308 * Returns :
309 *---------------------------------------------------------------------------*/
310
MNU_stop_sfx(DEMOWinMenuInfo * menu,u32 item,u32 * result)311 static void MNU_stop_sfx(DEMOWinMenuInfo *menu, u32 item, u32 *result)
312 {
313 #pragma unused(menu)
314 #pragma unused(item)
315 #pragma unused(result)
316
317 u32 i;
318 BOOL old;
319
320 old = OSDisableInterrupts();
321
322 for (i=0; i<MAX_DEMO_VOICES; i++)
323 {
324 if (demo_voice[i].ax_voice)
325 {
326 AXSetVoiceState(demo_voice[i].ax_voice, AX_PB_STATE_STOP);
327 }
328 }
329
330 OSRestoreInterrupts(old);
331
332 } // end MNU_stop_sfx()
333
334 /*---------------------------------------------------------------------------*
335 * Name : MNU_stop_looping()
336 * Description : Stops looped sfx only. Note that voices are freed by the
337 * AX user callback. Note also that SPPrepareEnd() is used,
338 * so the sound effect will play to the end (beyond the loop)
339 * and THEN get stopped.
340 * Arguments :
341 * Returns :
342 *---------------------------------------------------------------------------*/
343
MNU_stop_looping(DEMOWinMenuInfo * menu,u32 item,u32 * result)344 static void MNU_stop_looping(DEMOWinMenuInfo *menu, u32 item, u32 *result)
345 {
346 #pragma unused(menu)
347 #pragma unused(item)
348 #pragma unused(result)
349
350 u32 i;
351 BOOL old;
352
353 old = OSDisableInterrupts();
354
355 for (i=0; i<MAX_DEMO_VOICES; i++)
356 {
357 if ( (demo_voice[i].ax_voice) && mISLOOPED(demo_voice[i].sp_entry) )
358 {
359 SPPrepareEnd(demo_voice[i].sp_entry, demo_voice[i].ax_voice);
360 }
361 }
362
363 OSRestoreInterrupts(old);
364
365 } // end MNU_stop_looping()
366
367
368
369 /*---------------------------------------------------------------------------*
370 * Name :
371 * Description :
372 * Arguments : None.
373 * Returns : None.
374 *---------------------------------------------------------------------------*/
init_demo_voices()375 static void init_demo_voices()
376 {
377
378 u32 i;
379
380 for (i=0; i<MAX_DEMO_VOICES; i++)
381 {
382 demo_voice[i].ax_voice = NULL;
383 demo_voice[i].sp_entry = NULL;
384 }
385
386 } // end init_demo_voices()
387
388
389 /*---------------------------------------------------------------------------*
390 * Name :
391 * Description :
392 * Arguments : None.
393 * Returns : None.
394 *---------------------------------------------------------------------------*/
get_demo_voice()395 static DEMO_VOICE *get_demo_voice()
396 {
397
398 u32 i;
399
400 i=0;
401 while (i < MAX_DEMO_VOICES)
402 {
403
404 if (NULL == demo_voice[i].ax_voice)
405 {
406 return(&demo_voice[i]);
407 }
408 i++;
409 }
410
411 return(NULL);
412
413 } // end get_demo_voice()
414
415 /*---------------------------------------------------------------------------*
416 * Name :
417 * Description :
418 * Arguments : None.
419 * Returns : None.
420 *---------------------------------------------------------------------------*/
421
ax_demo_callback(void)422 static void ax_demo_callback(void)
423 {
424
425 u32 i;
426
427 for (i=0; i<MAX_DEMO_VOICES; i++)
428 {
429 if (demo_voice[i].ax_voice)
430 {
431 if ( AX_PB_STATE_STOP == ((demo_voice[i].ax_voice)->pb.state))
432 {
433 MIXReleaseChannel(demo_voice[i].ax_voice);
434 AXFreeVoice(demo_voice[i].ax_voice);
435 demo_voice[i].ax_voice = NULL;
436 }
437 }
438 }
439
440 } // end ax_demo_callback()
441
442 /*---------------------------------------------------------------------------*
443 * Name : ax_drop_voice_callback()
444 * Description : Invoked by AX when a voice has been forciby dropped.
445 * Must delete references to the voice from our abstraction layer
446 * and release the associated MIXer channel.
447 * Arguments : None.
448 * Returns : None.
449 *---------------------------------------------------------------------------*/
450
ax_drop_voice_callback(void * p)451 static void ax_drop_voice_callback(void *p)
452 {
453
454 u32 i;
455
456 // search for abstracted voice associated with low-level AX voice.
457 for (i=0; i<MAX_DEMO_VOICES; i++)
458 {
459 // found it!
460 if ( (AXVPB *)(p) == demo_voice[i].ax_voice)
461 {
462 // release mixer channel, delete reference to AX voice (and SP entry, just for neatness)
463 MIXReleaseChannel(demo_voice[i].ax_voice);
464 demo_voice[i].ax_voice = NULL;
465 demo_voice[i].sp_entry = NULL;
466 break;
467 }
468 }
469
470 // freak out if the voice doesn't exist in our voice abstraction list
471 ASSERTMSG(i != MAX_DEMO_VOICES, "AXVoiceCallback: unknown voice reference!\n");
472
473 } // end ax_demo_callback()
474
475 /*---------------------------------------------------------------------------*
476 * Name : play_sfx()
477 * Description :
478 * Arguments : None.
479 * Returns : None.
480 *---------------------------------------------------------------------------*/
481
play_sfx(u32 sfx)482 static void play_sfx(u32 sfx)
483 {
484
485 DEMO_VOICE *v;
486 BOOL old;
487
488
489 old = OSDisableInterrupts();
490
491 v = get_demo_voice();
492 if (v)
493 {
494
495 v->ax_voice = AXAcquireVoice(15, ax_drop_voice_callback, 0);
496 if (v->ax_voice)
497 {
498
499 v->sp_entry = SPGetSoundEntry(sp_table, sfx);
500
501 SPPrepareSound(v->sp_entry, v->ax_voice, (v->sp_entry)->sampleRate);
502
503 MIXInitChannel(v->ax_voice, 0, 0, -960, -960, -960, 64, 127, 0);
504 AXSetVoiceState(v->ax_voice, AX_PB_STATE_RUN);
505
506 OSRestoreInterrupts(old);
507
508 }
509 else
510 {
511 OSRestoreInterrupts(old);
512 DEMOWinLogPrintf(DebugWin, "SFX: AX Voice allocation failed.\n");
513 }
514
515 }
516 else
517 {
518 OSRestoreInterrupts(old);
519 DEMOWinLogPrintf(DebugWin, "(No free voices in abstraction layer)\n");
520 }
521
522 } // end play_sfx()
523
524 #ifdef HOLLYWOOD_REV
525 /*---------------------------------------------------------------------------*
526 *---------------------------------------------------------------------------*/
LoadFileIntoRam(char * path)527 static void* LoadFileIntoRam(char *path)
528 {
529 DVDFileInfo handle;
530 u32 round_length;
531 s32 read_length;
532 void *buffer;
533
534 // Open File
535 if (!DVDOpen(path, &handle))
536 {
537 OSReport("WARNING! Failed to open %s\n", path);
538 return NULL;
539 }
540
541 // Make sure file length is not 0
542 if (DVDGetLength(&handle) == 0)
543 {
544 OSReport("WARNING! File length is 0\n");
545 return NULL;
546 }
547
548 round_length = OSRoundUp32B(DVDGetLength(&handle));
549 buffer = MEMAllocFromExpHeapEx(hExpHeap, round_length, 32);
550
551 // Make sure we got a buffer
552 if (buffer == NULL)
553 {
554 OSReport("WARNING! Unable to allocate buffer\n");
555 return NULL;
556 }
557
558 // Read Files
559 read_length = DVDRead(&handle, buffer, (s32)(round_length), 0);
560
561 // Make sure we read the file correctly
562 if (read_length <= 0)
563 {
564 OSReport("WARNING! File lenght is wrong\n");
565 return NULL;
566 }
567
568 return buffer;
569 }
570 #endif
571
572 /*---------------------------------------------------------------------------*
573 * Name : main()
574 * Description : Hold on to your seatbelts!
575 * Arguments : None.
576 * Returns : None.
577 *---------------------------------------------------------------------------*/
main(void)578 void main(void)
579 {
580 #ifdef HOLLYWOOD_REV
581 void *arenaMem2Lo;
582 void *arenaMem2Hi;
583 u8 *zeroBuffer;
584 #endif
585
586 // initialize system
587 DEMOInit(NULL);
588 DEMOWinInit();
589
590 #ifndef HOLLYWOOD_REV
591 // initialize ARAM w/ stack allocator
592 ARInit(aramMemArray, MAX_ARAM_BLOCKS);
593 ARQInit();
594 #else
595 // initialize Exp Heap on MEM2
596 arenaMem2Lo = OSGetMEM2ArenaLo();
597 arenaMem2Hi = OSGetMEM2ArenaHi();
598 hExpHeap = MEMCreateExpHeap(arenaMem2Lo, (u32)arenaMem2Hi - (u32) arenaMem2Lo);
599 #endif
600
601 // initialize AI subsystem
602 AIInit(NULL);
603
604 // initialize AX audio system and MIXer application
605 AXInit();
606 MIXInit();
607
608 #ifndef HOLLYWOOD_REV
609 // -----------------------------------------------------------
610 // Initialize ARAM audio manager (AM)
611 // -----------------------------------------------------------
612
613 // get a block from the AR ARAM allocator
614 aramUserBase = ARAlloc(AUDIO_BLOCK_SIZE_BYTES);
615
616 // initialize AM with the block
617 AMInit(aramUserBase, AUDIO_BLOCK_SIZE_BYTES);
618
619 // retrieve start of zero buffer, as created by AM
620 aramZeroBase = AMGetZeroBuffer();
621 #endif
622
623 // -----------------------------------------------------------
624 // Load SP data!
625 // -----------------------------------------------------------
626 #ifndef HOLLYWOOD_REV
627 // Retrieve sound table
628 sp_table = (SPSoundTable *)AMLoadFile(SPT_FILE, NULL);
629
630 // Load sound effects into ARAM
631 aramUserBase = AMPushBuffered(SPD_FILE, (void *)xfer_buffer, XFER_BUFFER_SIZE_BYTES);
632 #else
633 // Load sound table
634 sp_table = LoadFileIntoRam(SPT_FILE);
635
636 // Load sound effects
637 sp_data = LoadFileIntoRam(SPD_FILE);
638 #endif
639
640 #ifdef HOLLYWOOD_REV
641 // -----------------------------------------------------------
642 // Prepare Zero Buffer
643 // -----------------------------------------------------------
644 zeroBuffer = MEMAllocFromExpHeapEx(hExpHeap, ZEROBUFFER_BYTES, 8);
645 memset(zeroBuffer, 0, ZEROBUFFER_BYTES);
646 DCFlushRange(zeroBuffer, ZEROBUFFER_BYTES);
647 #endif
648
649 // -----------------------------------------------------------
650 // initialize sound table!
651 // -----------------------------------------------------------
652 #ifndef HOLLYWOOD_REV
653 SPInitSoundTable(sp_table, (u8*)aramUserBase, (u8*)aramZeroBase);
654 #else
655 SPInitSoundTable(sp_table, sp_data, zeroBuffer);
656 #endif
657
658 // -----------------------------------------------------------
659 // Initialize demo voice abstraction layer
660 // -----------------------------------------------------------
661 init_demo_voices();
662 AXRegisterCallback(ax_demo_callback);
663
664 // initialize profiling for AX
665 AXInitProfile(ax_profile, NUM_AX_PROFILE_FRAMES);
666
667 // -----------------------------------------------------------
668 // Invoke menu system!
669 // -----------------------------------------------------------
670
671 MenuPtr = DEMOWinCreateMenuWindow(&Menu, 20, 100);
672 DebugWin = DEMOWinCreateWindow((u16)(MenuPtr->handle->x2+10), 20, 620, 440, "Debug", 1024, NULL);
673 ProfileWin = DEMOWinCreateWindow((u16)(MenuPtr->handle->x1), (u16)(MenuPtr->handle->y2+10), (u16)(MenuPtr->handle->x2), (u16)(MenuPtr->handle->y2+160), "AX Profile", 0, ax_profile_update);
674
675 DEMOWinOpenWindow(DebugWin);
676 DEMOWinOpenWindow(ProfileWin);
677
678 DEMOWinLogPrintf(DebugWin, "-------------------------------\n");
679 DEMOWinLogPrintf(DebugWin, "AX Sound Pipeline Demo!\n");
680 DEMOWinLogPrintf(DebugWin, "-------------------------------\n");
681
682 while (1)
683 {
684
685 DEMOWinMenu(MenuPtr);
686
687 }
688
689 } // end main()
690