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