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