1 /*---------------------------------------------------------------------------*
2   Project:  AX low-pass filter demo application
3   File:     axfilter.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: axfilter.c,v $
14   Revision 1.16  2006/11/21 08:14:48  aka
15   Removed the zero buffer.
16 
17   Revision 1.15  2006/11/07 12:07:37  aka
18   Revised AXSetVoiceBiquadCoefs().
19 
20   Revision 1.14  2006/11/07 05:58:43  aka
21   Added Biquad IIR filter.
22 
23   Revision 1.13  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.12  2006/10/10 08:30:06  aka
29   Revised AXInit(), MIXInit() and SYNInit().
30 
31   Revision 1.11  2006/09/18 04:28:22  aka
32   Modified using AX_MAX_VOICES instead of MAX_DEMO_VOICES.
33 
34   Revision 1.10  2006/06/08 06:06:07  aka
35   Removed setting of tempDisableFX for new AXFX.
36 
37   Revision 1.9  2006/02/21 01:04:15  mitu
38   modified am.h path.
39 
40   Revision 1.8  2006/02/20 04:13:07  mitu
41   changed include path from dolphin/ to revolution/.
42 
43   Revision 1.7  2006/02/02 08:33:48  aka
44   Added AXFXSetHooks() to alloc from MEM2 in AXFX.
45 
46   Revision 1.6  2006/02/02 07:35:27  aka
47   Modified using MEM functions instead of OSAlloc()/OSFree().
48 
49   Revision 1.5  2006/02/01 08:22:51  aka
50   Added #ifndef(#ifdef) HOLLYWOOD_REV - #else - #endif.
51 
52   Revision 1.4  2006/01/31 08:07:05  aka
53   Added cast from u32 to u8* in relation to changing API around ARAM.
54 
55   Revision 1.3  2006/01/27 04:54:10  ekwon
56   Corrected "\%" escape sequence warning (replaced with "%%").
57 
58   Revision 1.2  2005/11/08 02:55:02  aka
59   Changed suiting to Revolution's audio spec.
60 
61   Revision 1.1  2005/11/04 05:01:39  aka
62   Imported from dolphin tree.
63 
64     3     03/04/24 8:42 Dante
65     Removed reverbDPL2.crosstalk
66 
67     2     03/02/02 5:05p Akagi
68     Added 3 casts to remove warning messages.
69 
70     1     03/01/22 12:52 Ntd1
71     Added axfilter.c
72 
73   $NoKeywords: $
74  *---------------------------------------------------------------------------*/
75 
76 /*---------------------------------------------------------------------------*
77  * Includes
78  *---------------------------------------------------------------------------*/
79 
80 #include <stdlib.h>
81 #include <string.h>
82 #include <stddef.h>
83 
84 #include <revolution.h>
85 #include <demo.h>
86 #include <demo/DEMOWin.h>
87 #include <revolution/mix.h>
88 #include <revolution/sp.h>
89 #include <revolution/axfx.h>
90 #include <revolution/mem.h>
91 
92 #include "lpfdemo.h"
93 
94 /*---------------------------------------------------------------------------*
95  * BIQUAD IIR FILTER
96  *---------------------------------------------------------------------------*/
97 
98 #define USE_BIQUAD
99 //#undef  USE_BIQUAD
100 
101 static __BIQUAD_COEF* __biquad_coefs = __biquad_lpf_coefs;
102 //static __BIQUAD_COEF* __biquad_coefs = __biquad_hpf_coefs;
103 //static __BIQUAD_COEF* __biquad_coefs = __biquad_bpf_coefs;
104 
105 /*---------------------------------------------------------------------------*
106  * SP data
107  *---------------------------------------------------------------------------*/
108 
109 #define SPT_FILE "/axdemo/filter/lpfdemo.spt"
110 #define SPD_FILE "/axdemo/filter/lpfdemo.spd"
111 
112 // use only a single SP sound table
113 static SPSoundTable *sp_table;
114 static u8           *sp_data;
115 
116 /*---------------------------------------------------------------------------*
117  * Exp Heap
118  *---------------------------------------------------------------------------*/
119 
120 static MEMHeapHandle hExpHeap;
121 
122 /*---------------------------------------------------------------------------*
123  * AX data
124  *---------------------------------------------------------------------------*/
125 
126 // Constructs for Aux-bus effects
127 static AXFX_REVERBSTD     reverbStd;
128 static AXFX_REVERBHI      reverbHi;
129 static AXFX_CHORUS        chorus;
130 static AXFX_DELAY         delay;
131 static AXFX_REVERBHI_DPL2 reverbDPL2;
132 
133 // AX profiling structures
134 
135 // store up to 8 frames, just to be safe
136 #define NUM_AX_PROFILE_FRAMES 8
137 
138 static AXPROFILE ax_profile[NUM_AX_PROFILE_FRAMES];
139 
140 /*---------------------------------------------------------------------------*
141  * Voice and sound layer abstractions
142  *---------------------------------------------------------------------------*/
143 
144 
145 // --------------------------------------------------------------------------
146 // Sound layer abstraction (for cheesy support of multi-channel sounds)
147 // --------------------------------------------------------------------------
148 // For the purposes of this demo, a sound can be composed of multiple samples
149 // mapped into multiple channels. In this way, we can hack out some quick-and-
150 // dirty stereo or multi-channel sound effects.
151 //
152 // Note that samples are referenced by their SoundPipeline entry index.
153 //
154 
155 #define MAX_NUM_CHANNELS_PER_SOUND  4   // multichannel sound support for up to four channels
156 #define NUM_DEMO_SOUNDS            10
157 
158 typedef struct
159 {
160     char *name;                                 // handy string for debugging. No sane application would use strings of any sort in a non-debug runtime.
161     u32   index;                                // abstraction layer index.
162     u32   num_channels;                         // number of channels associated with this sound.
163 
164     u32   sfx  [MAX_NUM_CHANNELS_PER_SOUND];    // SP sound effect IDs.
165     u32   pan  [MAX_NUM_CHANNELS_PER_SOUND];    // initial pan values for each channel.
166     u32   span [MAX_NUM_CHANNELS_PER_SOUND];    // initial pan values for each channel.
167 
168 } DEMO_SOUND;
169 
170 
171 
172 static DEMO_SOUND demo_sound[] =
173 {
174 //    Name          index NumChans SP sample indices                        Initial Pan     Initial SPan
175 //    ------------- ----- -------- ---------------------------------------  --------------  -----------------
176     { "GuitarScape",  0,    2,     {MSX_GS_LEFT,     MSX_GS_RIGHT,  0, 0 }, {0, 127, 0, 0}, {127, 127, 0, 0} },
177     { "Splash",       1,    2,     {MSX_SPL_LEFT,    MSX_SPL_RIGHT, 0, 0 }, {0, 127, 0, 0}, {127, 127, 0, 0} },
178     { "CarDemo",      2,    2,     {MSX_CAR_LEFT,    MSX_CAR_RIGHT, 0, 0 }, {0, 127, 0, 0}, {127, 127, 0, 0} },
179     { "Gunshot",      3,    1,     {SFX_GUNSHOT,     0,             0, 0 }, {63,  0, 0, 0}, {127,   0, 0, 0} },
180     { "PinkNoise",    4,    1,     {SFX_PINK,        0,             0, 0 }, {63,  0, 0, 0}, {127,   0, 0, 0} },
181     { "WhiteNoise",   5,    1,     {SFX_WHITE,       0,             0, 0 }, {63,  0, 0, 0}, {127,   0, 0, 0} },
182     { "MachineGun",   6,    1,     {SFX_MACHINE_GUN, 0,             0, 0 }, {63,  0, 0, 0}, {127,   0, 0, 0} },
183     { "Explosion",    7,    1,     {SFX_EXPLOSION,   0,             0, 0 }, {63,  0, 0, 0}, {127,   0, 0, 0} },
184     { "Footsteps",    8,    1,     {SFX_FOOTSTEPS,   0,             0, 0 }, {63,  0, 0, 0}, {127,   0, 0, 0} },
185     { "Voice",        9,    1,     {SFX_NGC_MAN,     0,             0, 0 }, {63,  0, 0, 0}, {127,   0, 0, 0} }
186 
187 };
188 
189 // --------------------------------------------------------------------------
190 // Voice layer abstraction
191 // --------------------------------------------------------------------------
192 //
193 
194 #define mISLOOPED(x) ((x->type)&0x1)    // Checks SP entry 'type' to see if the voice is looped or not
195 
196 typedef struct _DEMOVOICE
197 {
198     AXVPB        *ax_voice;
199     SPSoundEntry *sp_entry;
200     struct _DEMOVOICE   *link;
201 
202 } DEMO_VOICE;
203 
204 
205 static DEMO_VOICE demo_voices[AX_MAX_VOICES];
206 
207 
208 
209 /*---------------------------------------------------------------------------*
210  * Prototypes
211  *---------------------------------------------------------------------------*/
212 
213 // for AX and voice abstraction layer
214 static void         ax_demo_callback        (void);
215 static void         ax_drop_voice_callback  (void *p);
216 static void         stop_all_voices         (void);
217 static void         init_effects            (void);
218 static void         play_sound              (u32 sound, u32 loop_flag);
219 
220 // for UI menus
221 static void         MNU_sound               (DEMOWinMenuInfo *menu, u32 item, u32 *result);
222 static void         MNU_play                (DEMOWinMenuInfo *menu, u32 item, u32 *result);
223 static void         MNU_play_looped         (DEMOWinMenuInfo *menu, u32 item, u32 *result);
224 static void         MNU_stop                (DEMOWinMenuInfo *menu, u32 item, u32 *result);
225 static void         MNU_auxa                (DEMOWinMenuInfo *menu, u32 item, u32 *result);
226 static void         MNU_auxa_settings       (DEMOWinMenuInfo *menu, u32 item, u32 *result);
227 static void         MNU_compressor          (DEMOWinMenuInfo *menu, u32 item, u32 *result);
228 static void         MNU_filter              (DEMOWinMenuInfo *menu, u32 item, u32 *result);
229 
230 /*---------------------------------------------------------------------------*
231  * User Interface stuff
232  *---------------------------------------------------------------------------*/
233 
234 volatile static u32 soundIndex     = 0;   // current sound effect to play
235 volatile static u32 auxaIndex      = 0;   // current effect to apply to AuxA bus
236 volatile static u32 compressFlag   = 0;   // current compressor state
237 volatile static u32 filterFlag     = 1;   // current state of filter
238 volatile static u32 old_filterFlag = 1;
239 volatile static u32 filterCutoff   = 0;   // current LPF cut-off frequency
240 
241 DEMOWinInfo *DebugWin;
242 DEMOWinInfo *StatusWin;
243 
244 DEMOWinMenuItem MenuItem[] =
245 {
246 
247     { "Sound     : Guitarscape",    DEMOWIN_ITM_NONE,       MNU_sound,          NULL },
248     { " ",                          DEMOWIN_ITM_SEPARATOR,  NULL,               NULL },
249     { "Play          ",             DEMOWIN_ITM_NONE,       MNU_play,           NULL },
250     { "Play Looped",                DEMOWIN_ITM_NONE,       MNU_play_looped,    NULL },
251     { "Stop",                       DEMOWIN_ITM_NONE,       MNU_stop,           NULL },
252     { " ",                          DEMOWIN_ITM_SEPARATOR,  NULL,               NULL },
253     { "Filter    : ON ",            DEMOWIN_ITM_NONE,       MNU_filter,         NULL },
254     { "Compressor: OFF",            DEMOWIN_ITM_NONE,       MNU_compressor,     NULL },
255     { "AuxA      : (none)",         DEMOWIN_ITM_NONE,       MNU_auxa,           NULL },
256     { " ",                          DEMOWIN_ITM_SEPARATOR,  NULL,               NULL },
257     { " ",                          DEMOWIN_ITM_SEPARATOR,  NULL,               NULL },
258     { "",                           DEMOWIN_ITM_TERMINATOR, NULL,               NULL }
259 };
260 
261 
262 DEMOWinMenuInfo Menu =
263 {
264     "AX Low-pass filter test",  // title
265     NULL,                       // window handle
266     MenuItem,                   // list of menu items
267     14,                         // max num of items to display at a time
268     DEMOWIN_MNU_NONE,           // attribute flags
269 
270     // user callbacks
271     NULL,                       // callback for menu open event
272     NULL,                       // callback for cursor move event
273     NULL,                       // callback for item select event
274     NULL,                       // callback for cancel event
275 
276     // private members
277     0, 0, 0, 0, 0
278 };
279 
280 DEMOWinMenuInfo *MenuPtr;
281 
282 /*===========================================================================*
283  *                   F U N C T I O N    D E F I N I T I O N S
284  *===========================================================================*/
285 
286 
287 /*---------------------------------------------------------------------------*
288  * VOICE ABSTRACTION LAYER STUFF
289 /*---------------------------------------------------------------------------*
290 
291 
292 /*---------------------------------------------------------------------------*
293  * Name        :
294  * Description :
295  * Arguments   :
296  * Returns     :
297  *---------------------------------------------------------------------------*/
298 
stop_all_voices(void)299 static void stop_all_voices(void)
300 {
301 
302     u32  i;
303     BOOL old;
304 
305         old = OSDisableInterrupts();
306 
307         for (i=0; i<AX_MAX_VOICES; i++)
308         {
309             if (demo_voices[i].ax_voice)
310             {
311                 // hard stop!
312                 AXSetVoiceState(demo_voices[i].ax_voice, AX_PB_STATE_STOP);
313 
314             }
315 
316         }
317 
318         OSRestoreInterrupts(old);
319 
320 
321 } // end stop_voice()
322 
323 /*---------------------------------------------------------------------------*
324  * Name        :
325  * Description :
326  * Arguments   :
327  * Returns     :
328  *---------------------------------------------------------------------------*/
329 
play_sound(u32 sound,u32 loop_flag)330 static void play_sound(u32 sound, u32 loop_flag)
331 {
332 
333     u32         i;
334 #ifndef USE_BIQUAD
335     AXPBLPF     lpf;
336 #else
337     AXPBBIQUAD  biquad;
338 #endif
339 
340     AXVPB      *ax_v;
341     DEMO_VOICE *demo_v;
342     DEMO_VOICE *prev_demo_v;
343     DEMO_VOICE *first_demo_v;
344     DEMO_VOICE *curr_demo_v;
345     DEMO_VOICE *tmp_demo_v;
346 
347     BOOL        old;
348 
349 
350         ASSERTMSG( (sound < NUM_DEMO_SOUNDS), "ERROR: Sound reference out of range.\n");
351         ASSERTMSG( (demo_sound[sound].num_channels <= MAX_NUM_CHANNELS_PER_SOUND), "ERROR: Channel quantity out of range.\n");
352 
353         old = OSDisableInterrupts();
354 
355         prev_demo_v = NULL;
356 
357         for (i=0; i<demo_sound[sound].num_channels; i++)
358         {
359             ax_v = AXAcquireVoice(15, ax_drop_voice_callback, 0);
360             if (ax_v)
361             {
362                 demo_v = &demo_voices[ax_v->index];
363                 if (0==i)
364                 {
365                     first_demo_v = demo_v; // save reference to first voice
366                 }
367 
368                 demo_v->ax_voice = ax_v;
369                 demo_v->sp_entry = SPGetSoundEntry(sp_table, demo_sound[sound].sfx[i]);
370                 demo_v->link     = prev_demo_v;
371                 prev_demo_v      = demo_v;
372 
373                 MIXInitChannel(ax_v, 0, 0, 0, -960, -960, (s32)demo_sound[sound].pan[i], (s32)demo_sound[sound].span[i], -120);
374                 SPPrepareSound(demo_v->sp_entry, ax_v, (demo_v->sp_entry)->sampleRate);
375 
376                 // initialize filter!
377 #ifndef USE_BIQUAD
378                 lpf.on  = (u16)filterFlag;              // filter state is determined by filterFlag
379                 lpf.yn1 = 0;                            // when activated, the history sample must be reset
380                 lpf.a0  = __coefs[filterCutoff].a0;     // set coefficients to current, user-selected cutoff
381                 lpf.b0  = __coefs[filterCutoff].b0;
382                 AXSetVoiceLpf(ax_v, &lpf);              // Tell AX to update the voice's LPF parameters!
383 #else
384                 biquad.on  = (u16)filterFlag;
385                 biquad.xn1 = 0;
386                 biquad.xn2 = 0;
387                 biquad.yn1 = 0;
388                 biquad.yn2 = 0;
389                 biquad.b0  = __biquad_coefs[filterCutoff].b0;
390                 biquad.b1  = __biquad_coefs[filterCutoff].b1;
391                 biquad.b2  = __biquad_coefs[filterCutoff].b2;
392                 biquad.a1  = __biquad_coefs[filterCutoff].a1;
393                 biquad.a2  = __biquad_coefs[filterCutoff].a2;
394                 AXSetVoiceBiquad(ax_v, &biquad);
395 #endif
396                 AXSetVoiceState(ax_v, AX_PB_STATE_RUN); // activate the voice!
397                 if (FALSE == loop_flag)
398                 {
399                     // user has requested one-shot playback of the sound.
400                     ax_v->pb.addr.loopFlag      = AXPBADDR_LOOP_OFF;
401                     ax_v->pb.addr.endAddressHi  = (u16)(demo_v->sp_entry->endAddr >> 16);
402                     ax_v->pb.addr.endAddressLo  = (u16)(demo_v->sp_entry->endAddr & 0xFFFF);
403                 }
404 
405             }
406             else
407             {
408 
409                 // AX voice allocation failed. So, we must not FREE any AX voices we may have
410                 // acquired for other channels.
411 
412                 DEMOWinLogPrintf(DebugWin, "ERROR: Voice Allocation failed!\n");
413                 curr_demo_v = prev_demo_v;
414                 while(NULL == curr_demo_v)
415                 {
416                     AXFreeVoice(curr_demo_v->ax_voice);    // release any AX voices currently allocated for this sound
417                     curr_demo_v->ax_voice = NULL;          // clear AX voice pointer from DEMO_VOICE
418                     curr_demo_v->sp_entry = NULL;          // clear SP entry pointer from DEMO_VOICE
419                     tmp_demo_v = curr_demo_v;                 // save current DEMO_VOICE
420                     curr_demo_v=curr_demo_v->link;            // advance to next DEMO_VOICE
421                     tmp_demo_v->link = NULL;               // clear link of old DEMO_VOICE
422                 }
423                 OSRestoreInterrupts(old);
424                 return;
425 
426             }
427         }
428         first_demo_v->link = demo_v;   // first link references last link - it's a circle!
429         OSRestoreInterrupts(old);
430 
431 } // end play_sound()
432 
433 /*---------------------------------------------------------------------------*
434  * Name        :
435  * Description :
436  * Arguments   : None.
437  * Returns     : None.
438  *---------------------------------------------------------------------------*/
ax_demo_callback(void)439 static void ax_demo_callback(void)
440 {
441 #ifndef USE_BIQUAD
442     AXPBLPF lpf;
443 #else
444     AXPBBIQUAD biquad;
445 #endif
446     u32 i;
447 
448 
449         // This is the user callback invoked by AX every audio frame.
450         // This callback is responsible for culling any stopped voices
451         // from our DEMO_VOICE abstraction layer, and for updating the
452         // voice parameters of any active voices.
453         //
454         // For the purposes of this demo, we are only updated the
455         // voice parameters associated with the low-pass fitler feature
456         // of the DSP.
457         //
458 
459         // check for voices which have stopped
460         for (i=0; i<AX_MAX_VOICES; i++)
461         {
462             if (demo_voices[i].ax_voice)
463             {
464                 // Is the voice stopped?
465                 if ( AX_PB_STATE_STOP == ((demo_voices[i].ax_voice)->pb.state))
466                 {
467                     // If the voice has stopped, clear it from the abstraction layer
468                     MIXReleaseChannel(demo_voices[i].ax_voice);
469                     AXFreeVoice(demo_voices[i].ax_voice);
470                     demo_voices[i].ax_voice = NULL;
471                     demo_voices[i].sp_entry = NULL;
472                     demo_voices[i].link     = NULL;
473                 }
474                 else
475                 {
476                     // Otherwise, update any AX voice parameters. For this demo, we only care about
477                     // the low-pass filter parameters.
478 
479                     // has filter ON/OFF state changed?
480                     if (old_filterFlag != filterFlag)
481                     {
482                         if (filterFlag)
483                         {
484                             // Yes. The filter has been turned on. So initialize the parameters!
485 #ifndef USE_BIQUAD
486                             lpf.on  = 1;
487                             lpf.yn1 = 0;
488                             lpf.a0  = __coefs[filterCutoff].a0;
489                             lpf.b0  = __coefs[filterCutoff].b0;
490                             AXSetVoiceLpf(demo_voices[i].ax_voice, &lpf);
491 #else
492                             biquad.on  = 1;
493                             biquad.xn1 = 0;
494                             biquad.xn2 = 0;
495                             biquad.yn1 = 0;
496                             biquad.yn2 = 0;
497                             biquad.b0  = __biquad_coefs[filterCutoff].b0;
498                             biquad.b1  = __biquad_coefs[filterCutoff].b1;
499                             biquad.b2  = __biquad_coefs[filterCutoff].b2;
500                             biquad.a1  = __biquad_coefs[filterCutoff].a1;
501                             biquad.a2  = __biquad_coefs[filterCutoff].a2;
502                             AXSetVoiceBiquad(demo_voices[i].ax_voice, &biquad);
503 #endif
504                         }
505                         else
506                         {
507                             // Yes. The filter has been turned off. So clear the filter "ON" bit.
508 #ifndef USE_BIQUAD
509                             lpf.on  = 0;
510                             AXSetVoiceLpf(demo_voices[i].ax_voice, &lpf);
511 #else
512                             biquad.on  = 0;
513                             AXSetVoiceBiquad(demo_voices[i].ax_voice, &biquad);
514 #endif
515                         }
516                     }
517                     else
518                     {
519                         // ON/OFF state has not changed.
520                         // However, if the filter is on, then the coefs may have changed, so update those.
521                         if (filterFlag)
522                         {
523 #ifndef USE_BIQUAD
524                             AXSetVoiceLpfCoefs    (demo_voices[i].ax_voice,
525                                                    __coefs[filterCutoff].a0,
526                                                    __coefs[filterCutoff].b0);
527 #else
528                             AXSetVoiceBiquadCoefs (demo_voices[i].ax_voice,
529                                                    __biquad_coefs[filterCutoff].b0,
530                                                    __biquad_coefs[filterCutoff].b1,
531                                                    __biquad_coefs[filterCutoff].b2,
532                                                    __biquad_coefs[filterCutoff].a1,
533                                                    __biquad_coefs[filterCutoff].a2);
534 #endif
535                         }
536 
537                     }
538                 }
539             }
540         }
541         old_filterFlag = filterFlag;
542 
543 } // end ax_demo_callback()
544 
545 /*---------------------------------------------------------------------------*
546  * Name        : ax_drop_voice_callback()
547  * Description : Invoked by AX when a voice has been forciby dropped.
548  *               Must delete references to the voice from our abstraction layer
549  *               and release the associated MIXer channel.
550  * Arguments   : None.
551  * Returns     : None.
552  *---------------------------------------------------------------------------*/
ax_drop_voice_callback(void * p)553 static void ax_drop_voice_callback(void *p)
554 {
555 
556     AXVPB      *ax_v;
557     DEMO_VOICE *d_v;
558     DEMO_VOICE *tmp_d_v;
559 
560         ax_v = (AXVPB *)p;                  // AX gives us a pointer to the AXVPB of the dropped voice
561         d_v  = &demo_voices[ax_v->index];   // Its index corresponds uniquely to a DEMO_VOICE in our abstraction layer.
562 
563         MIXReleaseChannel(ax_v);            // Release the mixer channel for this AX voice.
564 
565         d_v->ax_voice = NULL;               // Delete the reference to the AX voice from the DEMO_VOICE.
566         d_v->sp_entry = NULL;               // Delete the reference to the SP entry from the DEMO_VOICE.
567 
568         // Now, if a multi-channel sound drops a voice, then all other voices (each of which are associated
569         // with a specific channel) must be STOPPED as well. So, traverse the links and STOP every voice.
570         while(d_v->link)
571         {
572             if (d_v->ax_voice)
573             {
574                 // The associated AX voice exists. So stop it. Note that we only need to stop
575                 // the voice; the user callback (ax_demo_callback) will reset the pointers and
576                 // perform a cleanup
577                 AXSetVoiceState(d_v->ax_voice, AX_PB_STATE_STOP);
578             }
579             tmp_d_v = d_v;          // save current DEMO_VOICE reference
580             d_v = d_v->link;        // advance through the links
581             tmp_d_v->link = NULL;   // clear saved DEMO_VOICE link.
582         }
583 
584 } // end ax_demo_callback()
585 
586 /*---------------------------------------------------------------------------*
587  * MENU FUNCTIONS
588 /*---------------------------------------------------------------------------*
589 
590 
591 /*---------------------------------------------------------------------------*
592  * Name        :
593  * Description :
594  * Arguments   :
595  * Returns     :
596  *---------------------------------------------------------------------------*/
MNU_play(DEMOWinMenuInfo * menu,u32 item,u32 * result)597 static void MNU_play(DEMOWinMenuInfo *menu, u32 item, u32 *result)
598 {
599 #pragma unused(menu, item, result)
600 
601     play_sound(soundIndex, FALSE);
602 
603 } // end MNU_play()
604 
605 
606 /*---------------------------------------------------------------------------*
607  * Name        :
608  * Description :
609  * Arguments   :
610  * Returns     :
611  *---------------------------------------------------------------------------*/
MNU_play_looped(DEMOWinMenuInfo * menu,u32 item,u32 * result)612 static void MNU_play_looped(DEMOWinMenuInfo *menu, u32 item, u32 *result)
613 {
614 #pragma unused(menu, item, result)
615 
616     play_sound(soundIndex, TRUE);
617 
618 } // end MNU_play_looped()
619 
620 
621 /*---------------------------------------------------------------------------*
622  * Name        :
623  * Description :
624  * Arguments   :
625  * Returns     :
626  *---------------------------------------------------------------------------*/
MNU_stop(DEMOWinMenuInfo * menu,u32 item,u32 * result)627 static void MNU_stop(DEMOWinMenuInfo *menu, u32 item, u32 *result)
628 {
629 #pragma unused(menu, item, result)
630 
631     stop_all_voices();
632 
633 } // end MNU_stop()
634 
635 
636 /*---------------------------------------------------------------------------*
637  * Name        :
638  * Description :
639  * Arguments   :
640  * Returns     :
641  *---------------------------------------------------------------------------*/
642 
MNU_compressor(DEMOWinMenuInfo * menu,u32 item,u32 * result)643 static void MNU_compressor(DEMOWinMenuInfo *menu, u32 item, u32 *result)
644 {
645 #pragma unused(menu, item, result)
646 
647     BOOL old;
648 
649         old = OSDisableInterrupts();
650 
651         compressFlag ^= 1;
652         AXSetCompressor(compressFlag);
653 
654         OSRestoreInterrupts(old);
655 
656         if (compressFlag)
657         {
658             menu->items[item].name = "Compressor: ON ";
659         }
660         else
661         {
662             menu->items[item].name = "Compressor: OFF";
663         }
664 
665 
666 } // end MNU_compressor()
667 
668 
669 /*---------------------------------------------------------------------------*
670  * Name        :
671  * Description :
672  * Arguments   :
673  * Returns     :
674  *---------------------------------------------------------------------------*/
675 
MNU_filter(DEMOWinMenuInfo * menu,u32 item,u32 * result)676 static void MNU_filter(DEMOWinMenuInfo *menu, u32 item, u32 *result)
677 {
678 #pragma unused(menu, item, result)
679 
680     BOOL old;
681 
682         old = OSDisableInterrupts();
683 
684         filterFlag ^= 1;
685 
686         OSRestoreInterrupts(old);
687 
688         if (filterFlag)
689         {
690             menu->items[item].name = "Filter    : ON ";
691         }
692         else
693         {
694             menu->items[item].name = "Filter    : OFF";
695         }
696 
697 
698 } // end MNU_filter()
699 
700 
701 /*---------------------------------------------------------------------------*
702  * Name        :
703  * Description :
704  * Arguments   :
705  * Returns     :
706  *---------------------------------------------------------------------------*/
707 
MNU_auxa(DEMOWinMenuInfo * menu,u32 item,u32 * result)708 static void MNU_auxa(DEMOWinMenuInfo *menu, u32 item, u32 *result)
709 {
710 #pragma unused(menu, item, result)
711 
712     auxaIndex++;
713 
714     auxaIndex %= 6;
715 
716     switch (auxaIndex)
717     {
718         case 0:
719             AXRegisterAuxACallback(NULL, NULL);
720             menu->items[item].name = "AuxA      : (none)";
721             break;
722         case 1:
723             AXRegisterAuxACallback((void*)&AXFXReverbStdCallback, (void*)&reverbStd);
724             menu->items[item].name = "AuxA      : ReverbStd";
725             break;
726         case 2:
727             AXRegisterAuxACallback((void*)&AXFXReverbHiCallback, (void*)&reverbHi);
728             menu->items[item].name = "AuxA      : ReverbHi";
729             break;
730 
731         case 3:
732             AXRegisterAuxACallback((void*)&AXFXReverbHiCallbackDpl2, (void*)&reverbDPL2);
733             menu->items[item].name = "AuxA      : ReverbDPL2";
734             break;
735 
736 
737         case 4:
738             AXRegisterAuxACallback((void*)&AXFXChorusCallback, (void*)&chorus);
739             menu->items[item].name = "AuxA      : Chorus";
740             break;
741 
742         case 5:
743             AXRegisterAuxACallback((void*)&AXFXDelayCallback, (void*)&delay);
744             menu->items[item].name = "AuxA      : Delay";
745             break;
746 
747     }
748 
749 
750 } // end MNU_auxa()
751 
752 
753 /*---------------------------------------------------------------------------*
754  * Name        :
755  * Description :
756  * Arguments   :
757  * Returns     :
758  *---------------------------------------------------------------------------*/
759 
MNU_auxa_settings(DEMOWinMenuInfo * menu,u32 item,u32 * result)760 static void MNU_auxa_settings(DEMOWinMenuInfo *menu, u32 item, u32 *result)
761 {
762 #pragma unused(menu, item, result)
763 
764 } // end MNU_auxa_settings()
765 
766 /*---------------------------------------------------------------------------*
767  * Name        :
768  * Description :
769  * Arguments   :
770  * Returns     :
771  *---------------------------------------------------------------------------*/
772 
MNU_sound(DEMOWinMenuInfo * menu,u32 item,u32 * result)773 static void MNU_sound(DEMOWinMenuInfo *menu, u32 item, u32 *result)
774 {
775 #pragma unused(result)
776 
777     static char dummy[80];
778 
779         soundIndex++;
780         soundIndex %= NUM_DEMO_SOUNDS;
781 
782         sprintf(dummy, "Sound     : %s", demo_sound[soundIndex].name);
783         menu->items[item].name = dummy;
784 
785 } // end MNU_sound
786 
787 /*---------------------------------------------------------------------------*
788  * Name        : status_win_update()
789  * Description : refresh callback for status window
790  * Arguments   :
791  * Returns     :
792  *---------------------------------------------------------------------------*/
793 
794 #define MAX_TICK_RANGE 100
795 
status_win_update(DEMOWinInfo * window)796 static void status_win_update(DEMOWinInfo *window)
797 {
798 
799     int substickY;
800 
801     static int tick;
802 
803     BOOL old;
804 
805     u32 i;
806 
807     u32 cpuCycles;
808     u32 userCycles;
809     u32 axCycles;
810     u32 voices;
811 
812     u32 maxCpuCycles =0;
813     u32 maxUserCycles=0;
814     u32 maxAxCycles  =0;
815     u32 maxVoices    =0;
816 
817         // retrieve stick position as sampled by the DEMOWin windowing system.
818         substickY = (MenuPtr->handle)->pad.pads[0].substickY;
819 
820         // If filtering is active, then allow substick (y-axis) to change filter cut-off frequency
821         if (filterFlag)
822         {
823             // if filtering is active, then the Y-axis of the substick controls the
824             // cut-off frequency.
825 
826             if (substickY > 63)
827             {
828                 tick--;
829                 if (tick < 0)
830                 {
831                     tick = 0;
832                 }
833             }
834             else if (substickY < -63)
835             {
836                 tick++;
837                 if (tick > MAX_TICK_RANGE)
838                 {
839                     tick = MAX_TICK_RANGE;
840                 }
841             }
842 
843             filterCutoff = (u32)( ((f32)(tick) / (f32)(MAX_TICK_RANGE)) * (f32)(NUM_FREQ_CUTOFF - 1));
844         }
845 #ifndef USE_BIQUAD
846         DEMOWinPrintfXY(window, 0, 1, "Cutoff Freq  : %s ", __coefs[filterCutoff].text);
847 #else
848         DEMOWinPrintfXY(window, 0, 1, "Cutoff Freq  : %s ", __biquad_coefs[filterCutoff].text);
849 #endif
850 
851         old = OSDisableInterrupts();
852 
853         i = AXGetProfile();
854 
855         if (i)
856         {
857             // up to 4 audio frames can complete within a 60Hz video frame
858             // so spin thru the accumulated audio frame profiles and find the peak values
859             while (i)
860             {
861                 i--;
862 
863                 cpuCycles   = (u32)(ax_profile[i].axFrameEnd      - ax_profile[i].axFrameStart);
864                 userCycles  = (u32)(ax_profile[i].userCallbackEnd - ax_profile[i].userCallbackStart);
865                 axCycles    = cpuCycles - userCycles;
866                 voices      = ax_profile[i].axNumVoices;
867 
868                 // find peak values over the last i audio frames
869                 if (cpuCycles > maxCpuCycles)     maxCpuCycles    = cpuCycles;
870                 if (userCycles > maxUserCycles)   maxUserCycles   = userCycles;
871                 if (axCycles > maxAxCycles)       maxAxCycles     = axCycles;
872                 if (voices > maxVoices)           maxVoices       = voices;
873 
874             }
875             OSRestoreInterrupts(old);
876 
877             DEMOWinPrintfXY(window, 0, 4, "Total CPU    : %5.2f%%", (f32)OSTicksToNanoseconds(maxCpuCycles) / 50000);
878             DEMOWinPrintfXY(window, 0, 6, "User         : %5.2f%%", (f32)OSTicksToNanoseconds(maxUserCycles) / 50000);
879             DEMOWinPrintfXY(window, 0, 7, "AX           : %5.2f%%", (f32)OSTicksToNanoseconds(maxAxCycles) / 50000);
880             DEMOWinPrintfXY(window, 0, 9, "Voices       : %5d",    maxVoices);
881 
882         }
883 
884         OSRestoreInterrupts(old);
885 
886 }
887 
888 /*---------------------------------------------------------------------------*
889  * Name        :
890  * Description :
891  * Arguments   :
892  * Returns     :
893  *---------------------------------------------------------------------------*/
894 
init_effects(void)895 void init_effects(void)
896 {
897 
898     reverbStd.time              = 3.0f;
899     reverbStd.preDelay          = 0.1f;
900     reverbStd.damping           = 0.5f;
901     reverbStd.coloration        = 0.5f;
902     reverbStd.mix               = 0.5f;
903 
904     reverbHi.time               = 3.0f;
905     reverbHi.preDelay           = 0.1f;
906     reverbHi.damping            = 0.5f;
907     reverbHi.coloration         = 0.5f;
908     reverbHi.crosstalk          = 0.3f;
909     reverbHi.mix                = 0.5f;
910 
911     reverbDPL2.time             = 3.0f;
912     reverbDPL2.preDelay         = 0.1f;
913     reverbDPL2.damping          = 0.5f;
914     reverbDPL2.coloration       = 0.5f;
915     reverbDPL2.mix              = 0.5f;
916 
917     chorus.baseDelay            = 15;
918     chorus.variation            = 0;
919     chorus.period               = 500;
920 
921     delay.delay[0]              = 500;
922     delay.delay[1]              = 500;
923     delay.delay[2]              = 500;
924     delay.feedback[0]           = 50;
925     delay.feedback[1]           = 50;
926     delay.feedback[2]           = 50;
927     delay.output[0]             = 100;
928     delay.output[1]             = 100;
929     delay.output[2]             = 100;
930 
931     AXFXReverbStdInit(&reverbStd);      // initialize reverb
932     AXFXReverbHiInit(&reverbHi);        // initialize reverb
933     AXFXReverbHiInitDpl2(&reverbDPL2);  // initialize DPL2-compatible reverb
934     AXFXChorusInit(&chorus);            // initialize chorus
935     AXFXDelayInit(&delay);              // initialize delay
936 
937 
938 } // end init_effects()
939 
940 /*---------------------------------------------------------------------------*
941  *---------------------------------------------------------------------------*/
LoadFileIntoRam(char * path)942 static void* LoadFileIntoRam(char *path)
943 {
944     DVDFileInfo handle;
945     u32         round_length;
946     s32         read_length;
947     void        *buffer;
948 
949     // Open File
950     if (!DVDOpen(path, &handle))
951     {
952         OSReport("WARNING! Failed to open %s\n", path);
953         return NULL;
954     }
955 
956     // Make sure file length is not 0
957     if (DVDGetLength(&handle) == 0)
958     {
959         OSReport("WARNING! File length is 0\n");
960         return NULL;
961     }
962 
963     round_length = OSRoundUp32B(DVDGetLength(&handle));
964     buffer       = MEMAllocFromExpHeapEx(hExpHeap, round_length,  32);
965 
966     // Make sure we got a buffer
967     if (buffer == NULL)
968     {
969         OSReport("WARNING! Unable to allocate buffer\n");
970         return NULL;
971     }
972 
973     // Read Files
974     read_length = DVDRead(&handle, buffer, (s32)(round_length), 0);
975 
976     // Make sure we read the file correctly
977     if (read_length <= 0)
978     {
979         OSReport("WARNING! File lenght is wrong\n");
980         return NULL;
981     }
982 
983     return buffer;
984 }
985 
986 /*---------------------------------------------------------------------------*
987  *---------------------------------------------------------------------------*/
PrivateAlloc(u32 size)988 static void* PrivateAlloc(u32 size)
989 {
990     return MEMAllocFromExpHeapEx(hExpHeap, size, 32);
991 }
992 
993 /*---------------------------------------------------------------------------*
994  *---------------------------------------------------------------------------*/
PrivateFree(void * addr)995 static void PrivateFree(void* addr)
996 {
997     MEMFreeToExpHeap(hExpHeap, addr);
998 }
999 
1000 /*---------------------------------------------------------------------------*
1001  * Name        : main()
1002  * Description : Hold on to your seatbelts!
1003  * Arguments   : None.
1004  * Returns     : None.
1005  *---------------------------------------------------------------------------*/
main(void)1006 void main(void)
1007 {
1008     void       *arenaMem2Lo;
1009     void       *arenaMem2Hi;
1010     void       *axBuffer;
1011     void       *mixBuffer;
1012 
1013     // initialize system
1014     DEMOInit(NULL);
1015     DEMOWinInit();
1016 
1017     SISetSamplingRate(5);
1018 
1019     // initialize Exp Heap on MEM2
1020     arenaMem2Lo = OSGetMEM2ArenaLo();
1021     arenaMem2Hi = OSGetMEM2ArenaHi();
1022     hExpHeap    = MEMCreateExpHeap(arenaMem2Lo, (u32)arenaMem2Hi - (u32) arenaMem2Lo);
1023 
1024     // initialize AI subsystem
1025     AIInit(NULL);
1026 
1027     // initialize AX audio system and MIXer application
1028     axBuffer  = MEMAllocFromExpHeapEx(hExpHeap, AXGetMemorySize(AX_MAX_VOICES), 32);
1029     mixBuffer = MEMAllocFromExpHeap(hExpHeap, MIXGetMemorySize(AX_MAX_VOICES));
1030 
1031     AXInitSpecifyMem(AX_MAX_VOICES, axBuffer);
1032     MIXInitSpecifyMem(mixBuffer);
1033 
1034     AXSetMode(AX_MODE_DPL2);
1035     MIXSetSoundMode(MIX_SOUND_MODE_DPL2);
1036 
1037     // -----------------------------------------------------------
1038     // Load SP data!
1039     // -----------------------------------------------------------
1040 
1041     // Load sound table
1042     sp_table = LoadFileIntoRam(SPT_FILE);
1043 
1044     // Load sound effects
1045     sp_data  = LoadFileIntoRam(SPD_FILE);
1046 
1047     // -----------------------------------------------------------
1048     // initialize sound table!
1049     // -----------------------------------------------------------
1050     SPInitSoundTable(sp_table, sp_data, NULL);
1051 
1052     // -----------------------------------------------------------
1053     // Initialize demo voice abstraction layer
1054     // -----------------------------------------------------------
1055     AXRegisterCallback(ax_demo_callback);
1056 
1057     // -----------------------------------------------------------
1058     // Initialize AUX-bus effects
1059     // -----------------------------------------------------------
1060     AXFXSetHooks((AXFXAlloc)PrivateAlloc, (AXFXFree)PrivateFree);
1061     init_effects();
1062 
1063     // -----------------------------------------------------------
1064     // initialize profiling for AX
1065     // -----------------------------------------------------------
1066     AXInitProfile(ax_profile, NUM_AX_PROFILE_FRAMES);
1067 
1068     // -----------------------------------------------------------
1069     // Invoke menu system!
1070     // -----------------------------------------------------------
1071     MenuPtr     = DEMOWinCreateMenuWindow(
1072         &Menu,
1073         20,
1074         100
1075         );
1076 
1077     DebugWin    = DEMOWinCreateWindow(
1078         (u16)(MenuPtr->handle->x2+10),
1079         20,
1080         620,
1081         440,
1082         "Debug",
1083         1024,
1084         NULL
1085         );
1086 
1087     StatusWin   = DEMOWinCreateWindow(
1088         (u16)(MenuPtr->handle->x1),
1089         (u16)(MenuPtr->handle->y2+10),
1090         (u16)(MenuPtr->handle->x2),
1091         (u16)(MenuPtr->handle->y2+120),
1092         "Status",
1093         0,
1094         status_win_update
1095         );
1096 
1097     DEMOWinOpenWindow(DebugWin);
1098     DEMOWinOpenWindow(StatusWin);
1099 
1100     DEMOWinLogPrintf(DebugWin, "-------------------------------\n");
1101     DEMOWinLogPrintf(DebugWin, "AX Low-pass filter test\n");
1102     DEMOWinLogPrintf(DebugWin, "-------------------------------\n");
1103 
1104     DEMOWinLogPrintf(DebugWin, "\n");
1105 
1106     DEMOWinLogPrintf(DebugWin, "Mode is AX_MODE_DPL2.\n\n");
1107     DEMOWinLogPrintf(DebugWin, "- Use sub-stick up/down to change\n");
1108     DEMOWinLogPrintf(DebugWin, "  filter cut-off frequency.\n");
1109 
1110     while (1)
1111     {
1112 
1113         DEMOWinMenu(MenuPtr);
1114 
1115     }
1116 
1117 } // end main()
1118