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