/*---------------------------------------------------------------------------* Project: AX low-pass filter demo application File: axfilter.c Copyright (C)1998-2006 Nintendo All Rights Reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo of America Inc. and/or Nintendo Company Ltd., and are protected by Federal copyright law. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. $Log: axfilter.c,v $ Revision 1.16 2006/11/21 08:14:48 aka Removed the zero buffer. Revision 1.15 2006/11/07 12:07:37 aka Revised AXSetVoiceBiquadCoefs(). Revision 1.14 2006/11/07 05:58:43 aka Added Biquad IIR filter. Revision 1.13 2006/10/23 02:05:52 aka Changed from AXInit() to AXInitSpecifyMem(). Changed from MIXInit() to MIXInitSpecifyMem(). Changed from SYNInit() to SYNInitSpecifyMem(). Revision 1.12 2006/10/10 08:30:06 aka Revised AXInit(), MIXInit() and SYNInit(). Revision 1.11 2006/09/18 04:28:22 aka Modified using AX_MAX_VOICES instead of MAX_DEMO_VOICES. Revision 1.10 2006/06/08 06:06:07 aka Removed setting of tempDisableFX for new AXFX. Revision 1.9 2006/02/21 01:04:15 mitu modified am.h path. Revision 1.8 2006/02/20 04:13:07 mitu changed include path from dolphin/ to revolution/. Revision 1.7 2006/02/02 08:33:48 aka Added AXFXSetHooks() to alloc from MEM2 in AXFX. Revision 1.6 2006/02/02 07:35:27 aka Modified using MEM functions instead of OSAlloc()/OSFree(). Revision 1.5 2006/02/01 08:22:51 aka Added #ifndef(#ifdef) HOLLYWOOD_REV - #else - #endif. Revision 1.4 2006/01/31 08:07:05 aka Added cast from u32 to u8* in relation to changing API around ARAM. Revision 1.3 2006/01/27 04:54:10 ekwon Corrected "\%" escape sequence warning (replaced with "%%"). Revision 1.2 2005/11/08 02:55:02 aka Changed suiting to Revolution's audio spec. Revision 1.1 2005/11/04 05:01:39 aka Imported from dolphin tree. 3 03/04/24 8:42 Dante Removed reverbDPL2.crosstalk 2 03/02/02 5:05p Akagi Added 3 casts to remove warning messages. 1 03/01/22 12:52 Ntd1 Added axfilter.c $NoKeywords: $ *---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------* * Includes *---------------------------------------------------------------------------*/ #include #include #include #include #include #include #include #include #include #include #include "lpfdemo.h" /*---------------------------------------------------------------------------* * BIQUAD IIR FILTER *---------------------------------------------------------------------------*/ #define USE_BIQUAD //#undef USE_BIQUAD static __BIQUAD_COEF* __biquad_coefs = __biquad_lpf_coefs; //static __BIQUAD_COEF* __biquad_coefs = __biquad_hpf_coefs; //static __BIQUAD_COEF* __biquad_coefs = __biquad_bpf_coefs; /*---------------------------------------------------------------------------* * SP data *---------------------------------------------------------------------------*/ #define SPT_FILE "/axdemo/filter/lpfdemo.spt" #define SPD_FILE "/axdemo/filter/lpfdemo.spd" // use only a single SP sound table static SPSoundTable *sp_table; static u8 *sp_data; /*---------------------------------------------------------------------------* * Exp Heap *---------------------------------------------------------------------------*/ static MEMHeapHandle hExpHeap; /*---------------------------------------------------------------------------* * AX data *---------------------------------------------------------------------------*/ // Constructs for Aux-bus effects static AXFX_REVERBSTD reverbStd; static AXFX_REVERBHI reverbHi; static AXFX_CHORUS chorus; static AXFX_DELAY delay; static AXFX_REVERBHI_DPL2 reverbDPL2; // AX profiling structures // store up to 8 frames, just to be safe #define NUM_AX_PROFILE_FRAMES 8 static AXPROFILE ax_profile[NUM_AX_PROFILE_FRAMES]; /*---------------------------------------------------------------------------* * Voice and sound layer abstractions *---------------------------------------------------------------------------*/ // -------------------------------------------------------------------------- // Sound layer abstraction (for cheesy support of multi-channel sounds) // -------------------------------------------------------------------------- // For the purposes of this demo, a sound can be composed of multiple samples // mapped into multiple channels. In this way, we can hack out some quick-and- // dirty stereo or multi-channel sound effects. // // Note that samples are referenced by their SoundPipeline entry index. // #define MAX_NUM_CHANNELS_PER_SOUND 4 // multichannel sound support for up to four channels #define NUM_DEMO_SOUNDS 10 typedef struct { char *name; // handy string for debugging. No sane application would use strings of any sort in a non-debug runtime. u32 index; // abstraction layer index. u32 num_channels; // number of channels associated with this sound. u32 sfx [MAX_NUM_CHANNELS_PER_SOUND]; // SP sound effect IDs. u32 pan [MAX_NUM_CHANNELS_PER_SOUND]; // initial pan values for each channel. u32 span [MAX_NUM_CHANNELS_PER_SOUND]; // initial pan values for each channel. } DEMO_SOUND; static DEMO_SOUND demo_sound[] = { // Name index NumChans SP sample indices Initial Pan Initial SPan // ------------- ----- -------- --------------------------------------- -------------- ----------------- { "GuitarScape", 0, 2, {MSX_GS_LEFT, MSX_GS_RIGHT, 0, 0 }, {0, 127, 0, 0}, {127, 127, 0, 0} }, { "Splash", 1, 2, {MSX_SPL_LEFT, MSX_SPL_RIGHT, 0, 0 }, {0, 127, 0, 0}, {127, 127, 0, 0} }, { "CarDemo", 2, 2, {MSX_CAR_LEFT, MSX_CAR_RIGHT, 0, 0 }, {0, 127, 0, 0}, {127, 127, 0, 0} }, { "Gunshot", 3, 1, {SFX_GUNSHOT, 0, 0, 0 }, {63, 0, 0, 0}, {127, 0, 0, 0} }, { "PinkNoise", 4, 1, {SFX_PINK, 0, 0, 0 }, {63, 0, 0, 0}, {127, 0, 0, 0} }, { "WhiteNoise", 5, 1, {SFX_WHITE, 0, 0, 0 }, {63, 0, 0, 0}, {127, 0, 0, 0} }, { "MachineGun", 6, 1, {SFX_MACHINE_GUN, 0, 0, 0 }, {63, 0, 0, 0}, {127, 0, 0, 0} }, { "Explosion", 7, 1, {SFX_EXPLOSION, 0, 0, 0 }, {63, 0, 0, 0}, {127, 0, 0, 0} }, { "Footsteps", 8, 1, {SFX_FOOTSTEPS, 0, 0, 0 }, {63, 0, 0, 0}, {127, 0, 0, 0} }, { "Voice", 9, 1, {SFX_NGC_MAN, 0, 0, 0 }, {63, 0, 0, 0}, {127, 0, 0, 0} } }; // -------------------------------------------------------------------------- // Voice layer abstraction // -------------------------------------------------------------------------- // #define mISLOOPED(x) ((x->type)&0x1) // Checks SP entry 'type' to see if the voice is looped or not typedef struct _DEMOVOICE { AXVPB *ax_voice; SPSoundEntry *sp_entry; struct _DEMOVOICE *link; } DEMO_VOICE; static DEMO_VOICE demo_voices[AX_MAX_VOICES]; /*---------------------------------------------------------------------------* * Prototypes *---------------------------------------------------------------------------*/ // for AX and voice abstraction layer static void ax_demo_callback (void); static void ax_drop_voice_callback (void *p); static void stop_all_voices (void); static void init_effects (void); static void play_sound (u32 sound, u32 loop_flag); // for UI menus static void MNU_sound (DEMOWinMenuInfo *menu, u32 item, u32 *result); static void MNU_play (DEMOWinMenuInfo *menu, u32 item, u32 *result); static void MNU_play_looped (DEMOWinMenuInfo *menu, u32 item, u32 *result); static void MNU_stop (DEMOWinMenuInfo *menu, u32 item, u32 *result); static void MNU_auxa (DEMOWinMenuInfo *menu, u32 item, u32 *result); static void MNU_auxa_settings (DEMOWinMenuInfo *menu, u32 item, u32 *result); static void MNU_compressor (DEMOWinMenuInfo *menu, u32 item, u32 *result); static void MNU_filter (DEMOWinMenuInfo *menu, u32 item, u32 *result); /*---------------------------------------------------------------------------* * User Interface stuff *---------------------------------------------------------------------------*/ volatile static u32 soundIndex = 0; // current sound effect to play volatile static u32 auxaIndex = 0; // current effect to apply to AuxA bus volatile static u32 compressFlag = 0; // current compressor state volatile static u32 filterFlag = 1; // current state of filter volatile static u32 old_filterFlag = 1; volatile static u32 filterCutoff = 0; // current LPF cut-off frequency DEMOWinInfo *DebugWin; DEMOWinInfo *StatusWin; DEMOWinMenuItem MenuItem[] = { { "Sound : Guitarscape", DEMOWIN_ITM_NONE, MNU_sound, NULL }, { " ", DEMOWIN_ITM_SEPARATOR, NULL, NULL }, { "Play ", DEMOWIN_ITM_NONE, MNU_play, NULL }, { "Play Looped", DEMOWIN_ITM_NONE, MNU_play_looped, NULL }, { "Stop", DEMOWIN_ITM_NONE, MNU_stop, NULL }, { " ", DEMOWIN_ITM_SEPARATOR, NULL, NULL }, { "Filter : ON ", DEMOWIN_ITM_NONE, MNU_filter, NULL }, { "Compressor: OFF", DEMOWIN_ITM_NONE, MNU_compressor, NULL }, { "AuxA : (none)", DEMOWIN_ITM_NONE, MNU_auxa, NULL }, { " ", DEMOWIN_ITM_SEPARATOR, NULL, NULL }, { " ", DEMOWIN_ITM_SEPARATOR, NULL, NULL }, { "", DEMOWIN_ITM_TERMINATOR, NULL, NULL } }; DEMOWinMenuInfo Menu = { "AX Low-pass filter test", // title NULL, // window handle MenuItem, // list of menu items 14, // max num of items to display at a time DEMOWIN_MNU_NONE, // attribute flags // user callbacks NULL, // callback for menu open event NULL, // callback for cursor move event NULL, // callback for item select event NULL, // callback for cancel event // private members 0, 0, 0, 0, 0 }; DEMOWinMenuInfo *MenuPtr; /*===========================================================================* * F U N C T I O N D E F I N I T I O N S *===========================================================================*/ /*---------------------------------------------------------------------------* * VOICE ABSTRACTION LAYER STUFF /*---------------------------------------------------------------------------* /*---------------------------------------------------------------------------* * Name : * Description : * Arguments : * Returns : *---------------------------------------------------------------------------*/ static void stop_all_voices(void) { u32 i; BOOL old; old = OSDisableInterrupts(); for (i=0; iindex]; if (0==i) { first_demo_v = demo_v; // save reference to first voice } demo_v->ax_voice = ax_v; demo_v->sp_entry = SPGetSoundEntry(sp_table, demo_sound[sound].sfx[i]); demo_v->link = prev_demo_v; prev_demo_v = demo_v; MIXInitChannel(ax_v, 0, 0, 0, -960, -960, (s32)demo_sound[sound].pan[i], (s32)demo_sound[sound].span[i], -120); SPPrepareSound(demo_v->sp_entry, ax_v, (demo_v->sp_entry)->sampleRate); // initialize filter! #ifndef USE_BIQUAD lpf.on = (u16)filterFlag; // filter state is determined by filterFlag lpf.yn1 = 0; // when activated, the history sample must be reset lpf.a0 = __coefs[filterCutoff].a0; // set coefficients to current, user-selected cutoff lpf.b0 = __coefs[filterCutoff].b0; AXSetVoiceLpf(ax_v, &lpf); // Tell AX to update the voice's LPF parameters! #else biquad.on = (u16)filterFlag; biquad.xn1 = 0; biquad.xn2 = 0; biquad.yn1 = 0; biquad.yn2 = 0; biquad.b0 = __biquad_coefs[filterCutoff].b0; biquad.b1 = __biquad_coefs[filterCutoff].b1; biquad.b2 = __biquad_coefs[filterCutoff].b2; biquad.a1 = __biquad_coefs[filterCutoff].a1; biquad.a2 = __biquad_coefs[filterCutoff].a2; AXSetVoiceBiquad(ax_v, &biquad); #endif AXSetVoiceState(ax_v, AX_PB_STATE_RUN); // activate the voice! if (FALSE == loop_flag) { // user has requested one-shot playback of the sound. ax_v->pb.addr.loopFlag = AXPBADDR_LOOP_OFF; ax_v->pb.addr.endAddressHi = (u16)(demo_v->sp_entry->endAddr >> 16); ax_v->pb.addr.endAddressLo = (u16)(demo_v->sp_entry->endAddr & 0xFFFF); } } else { // AX voice allocation failed. So, we must not FREE any AX voices we may have // acquired for other channels. DEMOWinLogPrintf(DebugWin, "ERROR: Voice Allocation failed!\n"); curr_demo_v = prev_demo_v; while(NULL == curr_demo_v) { AXFreeVoice(curr_demo_v->ax_voice); // release any AX voices currently allocated for this sound curr_demo_v->ax_voice = NULL; // clear AX voice pointer from DEMO_VOICE curr_demo_v->sp_entry = NULL; // clear SP entry pointer from DEMO_VOICE tmp_demo_v = curr_demo_v; // save current DEMO_VOICE curr_demo_v=curr_demo_v->link; // advance to next DEMO_VOICE tmp_demo_v->link = NULL; // clear link of old DEMO_VOICE } OSRestoreInterrupts(old); return; } } first_demo_v->link = demo_v; // first link references last link - it's a circle! OSRestoreInterrupts(old); } // end play_sound() /*---------------------------------------------------------------------------* * Name : * Description : * Arguments : None. * Returns : None. *---------------------------------------------------------------------------*/ static void ax_demo_callback(void) { #ifndef USE_BIQUAD AXPBLPF lpf; #else AXPBBIQUAD biquad; #endif u32 i; // This is the user callback invoked by AX every audio frame. // This callback is responsible for culling any stopped voices // from our DEMO_VOICE abstraction layer, and for updating the // voice parameters of any active voices. // // For the purposes of this demo, we are only updated the // voice parameters associated with the low-pass fitler feature // of the DSP. // // check for voices which have stopped for (i=0; ipb.state)) { // If the voice has stopped, clear it from the abstraction layer MIXReleaseChannel(demo_voices[i].ax_voice); AXFreeVoice(demo_voices[i].ax_voice); demo_voices[i].ax_voice = NULL; demo_voices[i].sp_entry = NULL; demo_voices[i].link = NULL; } else { // Otherwise, update any AX voice parameters. For this demo, we only care about // the low-pass filter parameters. // has filter ON/OFF state changed? if (old_filterFlag != filterFlag) { if (filterFlag) { // Yes. The filter has been turned on. So initialize the parameters! #ifndef USE_BIQUAD lpf.on = 1; lpf.yn1 = 0; lpf.a0 = __coefs[filterCutoff].a0; lpf.b0 = __coefs[filterCutoff].b0; AXSetVoiceLpf(demo_voices[i].ax_voice, &lpf); #else biquad.on = 1; biquad.xn1 = 0; biquad.xn2 = 0; biquad.yn1 = 0; biquad.yn2 = 0; biquad.b0 = __biquad_coefs[filterCutoff].b0; biquad.b1 = __biquad_coefs[filterCutoff].b1; biquad.b2 = __biquad_coefs[filterCutoff].b2; biquad.a1 = __biquad_coefs[filterCutoff].a1; biquad.a2 = __biquad_coefs[filterCutoff].a2; AXSetVoiceBiquad(demo_voices[i].ax_voice, &biquad); #endif } else { // Yes. The filter has been turned off. So clear the filter "ON" bit. #ifndef USE_BIQUAD lpf.on = 0; AXSetVoiceLpf(demo_voices[i].ax_voice, &lpf); #else biquad.on = 0; AXSetVoiceBiquad(demo_voices[i].ax_voice, &biquad); #endif } } else { // ON/OFF state has not changed. // However, if the filter is on, then the coefs may have changed, so update those. if (filterFlag) { #ifndef USE_BIQUAD AXSetVoiceLpfCoefs (demo_voices[i].ax_voice, __coefs[filterCutoff].a0, __coefs[filterCutoff].b0); #else AXSetVoiceBiquadCoefs (demo_voices[i].ax_voice, __biquad_coefs[filterCutoff].b0, __biquad_coefs[filterCutoff].b1, __biquad_coefs[filterCutoff].b2, __biquad_coefs[filterCutoff].a1, __biquad_coefs[filterCutoff].a2); #endif } } } } } old_filterFlag = filterFlag; } // end ax_demo_callback() /*---------------------------------------------------------------------------* * Name : ax_drop_voice_callback() * Description : Invoked by AX when a voice has been forciby dropped. * Must delete references to the voice from our abstraction layer * and release the associated MIXer channel. * Arguments : None. * Returns : None. *---------------------------------------------------------------------------*/ static void ax_drop_voice_callback(void *p) { AXVPB *ax_v; DEMO_VOICE *d_v; DEMO_VOICE *tmp_d_v; ax_v = (AXVPB *)p; // AX gives us a pointer to the AXVPB of the dropped voice d_v = &demo_voices[ax_v->index]; // Its index corresponds uniquely to a DEMO_VOICE in our abstraction layer. MIXReleaseChannel(ax_v); // Release the mixer channel for this AX voice. d_v->ax_voice = NULL; // Delete the reference to the AX voice from the DEMO_VOICE. d_v->sp_entry = NULL; // Delete the reference to the SP entry from the DEMO_VOICE. // Now, if a multi-channel sound drops a voice, then all other voices (each of which are associated // with a specific channel) must be STOPPED as well. So, traverse the links and STOP every voice. while(d_v->link) { if (d_v->ax_voice) { // The associated AX voice exists. So stop it. Note that we only need to stop // the voice; the user callback (ax_demo_callback) will reset the pointers and // perform a cleanup AXSetVoiceState(d_v->ax_voice, AX_PB_STATE_STOP); } tmp_d_v = d_v; // save current DEMO_VOICE reference d_v = d_v->link; // advance through the links tmp_d_v->link = NULL; // clear saved DEMO_VOICE link. } } // end ax_demo_callback() /*---------------------------------------------------------------------------* * MENU FUNCTIONS /*---------------------------------------------------------------------------* /*---------------------------------------------------------------------------* * Name : * Description : * Arguments : * Returns : *---------------------------------------------------------------------------*/ static void MNU_play(DEMOWinMenuInfo *menu, u32 item, u32 *result) { #pragma unused(menu, item, result) play_sound(soundIndex, FALSE); } // end MNU_play() /*---------------------------------------------------------------------------* * Name : * Description : * Arguments : * Returns : *---------------------------------------------------------------------------*/ static void MNU_play_looped(DEMOWinMenuInfo *menu, u32 item, u32 *result) { #pragma unused(menu, item, result) play_sound(soundIndex, TRUE); } // end MNU_play_looped() /*---------------------------------------------------------------------------* * Name : * Description : * Arguments : * Returns : *---------------------------------------------------------------------------*/ static void MNU_stop(DEMOWinMenuInfo *menu, u32 item, u32 *result) { #pragma unused(menu, item, result) stop_all_voices(); } // end MNU_stop() /*---------------------------------------------------------------------------* * Name : * Description : * Arguments : * Returns : *---------------------------------------------------------------------------*/ static void MNU_compressor(DEMOWinMenuInfo *menu, u32 item, u32 *result) { #pragma unused(menu, item, result) BOOL old; old = OSDisableInterrupts(); compressFlag ^= 1; AXSetCompressor(compressFlag); OSRestoreInterrupts(old); if (compressFlag) { menu->items[item].name = "Compressor: ON "; } else { menu->items[item].name = "Compressor: OFF"; } } // end MNU_compressor() /*---------------------------------------------------------------------------* * Name : * Description : * Arguments : * Returns : *---------------------------------------------------------------------------*/ static void MNU_filter(DEMOWinMenuInfo *menu, u32 item, u32 *result) { #pragma unused(menu, item, result) BOOL old; old = OSDisableInterrupts(); filterFlag ^= 1; OSRestoreInterrupts(old); if (filterFlag) { menu->items[item].name = "Filter : ON "; } else { menu->items[item].name = "Filter : OFF"; } } // end MNU_filter() /*---------------------------------------------------------------------------* * Name : * Description : * Arguments : * Returns : *---------------------------------------------------------------------------*/ static void MNU_auxa(DEMOWinMenuInfo *menu, u32 item, u32 *result) { #pragma unused(menu, item, result) auxaIndex++; auxaIndex %= 6; switch (auxaIndex) { case 0: AXRegisterAuxACallback(NULL, NULL); menu->items[item].name = "AuxA : (none)"; break; case 1: AXRegisterAuxACallback((void*)&AXFXReverbStdCallback, (void*)&reverbStd); menu->items[item].name = "AuxA : ReverbStd"; break; case 2: AXRegisterAuxACallback((void*)&AXFXReverbHiCallback, (void*)&reverbHi); menu->items[item].name = "AuxA : ReverbHi"; break; case 3: AXRegisterAuxACallback((void*)&AXFXReverbHiCallbackDpl2, (void*)&reverbDPL2); menu->items[item].name = "AuxA : ReverbDPL2"; break; case 4: AXRegisterAuxACallback((void*)&AXFXChorusCallback, (void*)&chorus); menu->items[item].name = "AuxA : Chorus"; break; case 5: AXRegisterAuxACallback((void*)&AXFXDelayCallback, (void*)&delay); menu->items[item].name = "AuxA : Delay"; break; } } // end MNU_auxa() /*---------------------------------------------------------------------------* * Name : * Description : * Arguments : * Returns : *---------------------------------------------------------------------------*/ static void MNU_auxa_settings(DEMOWinMenuInfo *menu, u32 item, u32 *result) { #pragma unused(menu, item, result) } // end MNU_auxa_settings() /*---------------------------------------------------------------------------* * Name : * Description : * Arguments : * Returns : *---------------------------------------------------------------------------*/ static void MNU_sound(DEMOWinMenuInfo *menu, u32 item, u32 *result) { #pragma unused(result) static char dummy[80]; soundIndex++; soundIndex %= NUM_DEMO_SOUNDS; sprintf(dummy, "Sound : %s", demo_sound[soundIndex].name); menu->items[item].name = dummy; } // end MNU_sound /*---------------------------------------------------------------------------* * Name : status_win_update() * Description : refresh callback for status window * Arguments : * Returns : *---------------------------------------------------------------------------*/ #define MAX_TICK_RANGE 100 static void status_win_update(DEMOWinInfo *window) { int substickY; static int tick; BOOL old; u32 i; u32 cpuCycles; u32 userCycles; u32 axCycles; u32 voices; u32 maxCpuCycles =0; u32 maxUserCycles=0; u32 maxAxCycles =0; u32 maxVoices =0; // retrieve stick position as sampled by the DEMOWin windowing system. substickY = (MenuPtr->handle)->pad.pads[0].substickY; // If filtering is active, then allow substick (y-axis) to change filter cut-off frequency if (filterFlag) { // if filtering is active, then the Y-axis of the substick controls the // cut-off frequency. if (substickY > 63) { tick--; if (tick < 0) { tick = 0; } } else if (substickY < -63) { tick++; if (tick > MAX_TICK_RANGE) { tick = MAX_TICK_RANGE; } } filterCutoff = (u32)( ((f32)(tick) / (f32)(MAX_TICK_RANGE)) * (f32)(NUM_FREQ_CUTOFF - 1)); } #ifndef USE_BIQUAD DEMOWinPrintfXY(window, 0, 1, "Cutoff Freq : %s ", __coefs[filterCutoff].text); #else DEMOWinPrintfXY(window, 0, 1, "Cutoff Freq : %s ", __biquad_coefs[filterCutoff].text); #endif old = OSDisableInterrupts(); i = AXGetProfile(); if (i) { // up to 4 audio frames can complete within a 60Hz video frame // so spin thru the accumulated audio frame profiles and find the peak values while (i) { i--; cpuCycles = (u32)(ax_profile[i].axFrameEnd - ax_profile[i].axFrameStart); userCycles = (u32)(ax_profile[i].userCallbackEnd - ax_profile[i].userCallbackStart); axCycles = cpuCycles - userCycles; voices = ax_profile[i].axNumVoices; // find peak values over the last i audio frames if (cpuCycles > maxCpuCycles) maxCpuCycles = cpuCycles; if (userCycles > maxUserCycles) maxUserCycles = userCycles; if (axCycles > maxAxCycles) maxAxCycles = axCycles; if (voices > maxVoices) maxVoices = voices; } OSRestoreInterrupts(old); DEMOWinPrintfXY(window, 0, 4, "Total CPU : %5.2f%%", (f32)OSTicksToNanoseconds(maxCpuCycles) / 50000); DEMOWinPrintfXY(window, 0, 6, "User : %5.2f%%", (f32)OSTicksToNanoseconds(maxUserCycles) / 50000); DEMOWinPrintfXY(window, 0, 7, "AX : %5.2f%%", (f32)OSTicksToNanoseconds(maxAxCycles) / 50000); DEMOWinPrintfXY(window, 0, 9, "Voices : %5d", maxVoices); } OSRestoreInterrupts(old); } /*---------------------------------------------------------------------------* * Name : * Description : * Arguments : * Returns : *---------------------------------------------------------------------------*/ void init_effects(void) { reverbStd.time = 3.0f; reverbStd.preDelay = 0.1f; reverbStd.damping = 0.5f; reverbStd.coloration = 0.5f; reverbStd.mix = 0.5f; reverbHi.time = 3.0f; reverbHi.preDelay = 0.1f; reverbHi.damping = 0.5f; reverbHi.coloration = 0.5f; reverbHi.crosstalk = 0.3f; reverbHi.mix = 0.5f; reverbDPL2.time = 3.0f; reverbDPL2.preDelay = 0.1f; reverbDPL2.damping = 0.5f; reverbDPL2.coloration = 0.5f; reverbDPL2.mix = 0.5f; chorus.baseDelay = 15; chorus.variation = 0; chorus.period = 500; delay.delay[0] = 500; delay.delay[1] = 500; delay.delay[2] = 500; delay.feedback[0] = 50; delay.feedback[1] = 50; delay.feedback[2] = 50; delay.output[0] = 100; delay.output[1] = 100; delay.output[2] = 100; AXFXReverbStdInit(&reverbStd); // initialize reverb AXFXReverbHiInit(&reverbHi); // initialize reverb AXFXReverbHiInitDpl2(&reverbDPL2); // initialize DPL2-compatible reverb AXFXChorusInit(&chorus); // initialize chorus AXFXDelayInit(&delay); // initialize delay } // end init_effects() /*---------------------------------------------------------------------------* *---------------------------------------------------------------------------*/ static void* LoadFileIntoRam(char *path) { DVDFileInfo handle; u32 round_length; s32 read_length; void *buffer; // Open File if (!DVDOpen(path, &handle)) { OSReport("WARNING! Failed to open %s\n", path); return NULL; } // Make sure file length is not 0 if (DVDGetLength(&handle) == 0) { OSReport("WARNING! File length is 0\n"); return NULL; } round_length = OSRoundUp32B(DVDGetLength(&handle)); buffer = MEMAllocFromExpHeapEx(hExpHeap, round_length, 32); // Make sure we got a buffer if (buffer == NULL) { OSReport("WARNING! Unable to allocate buffer\n"); return NULL; } // Read Files read_length = DVDRead(&handle, buffer, (s32)(round_length), 0); // Make sure we read the file correctly if (read_length <= 0) { OSReport("WARNING! File lenght is wrong\n"); return NULL; } return buffer; } /*---------------------------------------------------------------------------* *---------------------------------------------------------------------------*/ static void* PrivateAlloc(u32 size) { return MEMAllocFromExpHeapEx(hExpHeap, size, 32); } /*---------------------------------------------------------------------------* *---------------------------------------------------------------------------*/ static void PrivateFree(void* addr) { MEMFreeToExpHeap(hExpHeap, addr); } /*---------------------------------------------------------------------------* * Name : main() * Description : Hold on to your seatbelts! * Arguments : None. * Returns : None. *---------------------------------------------------------------------------*/ void main(void) { void *arenaMem2Lo; void *arenaMem2Hi; void *axBuffer; void *mixBuffer; // initialize system DEMOInit(NULL); DEMOWinInit(); SISetSamplingRate(5); // initialize Exp Heap on MEM2 arenaMem2Lo = OSGetMEM2ArenaLo(); arenaMem2Hi = OSGetMEM2ArenaHi(); hExpHeap = MEMCreateExpHeap(arenaMem2Lo, (u32)arenaMem2Hi - (u32) arenaMem2Lo); // initialize AI subsystem AIInit(NULL); // initialize AX audio system and MIXer application axBuffer = MEMAllocFromExpHeapEx(hExpHeap, AXGetMemorySize(AX_MAX_VOICES), 32); mixBuffer = MEMAllocFromExpHeap(hExpHeap, MIXGetMemorySize(AX_MAX_VOICES)); AXInitSpecifyMem(AX_MAX_VOICES, axBuffer); MIXInitSpecifyMem(mixBuffer); AXSetMode(AX_MODE_DPL2); MIXSetSoundMode(MIX_SOUND_MODE_DPL2); // ----------------------------------------------------------- // Load SP data! // ----------------------------------------------------------- // Load sound table sp_table = LoadFileIntoRam(SPT_FILE); // Load sound effects sp_data = LoadFileIntoRam(SPD_FILE); // ----------------------------------------------------------- // initialize sound table! // ----------------------------------------------------------- SPInitSoundTable(sp_table, sp_data, NULL); // ----------------------------------------------------------- // Initialize demo voice abstraction layer // ----------------------------------------------------------- AXRegisterCallback(ax_demo_callback); // ----------------------------------------------------------- // Initialize AUX-bus effects // ----------------------------------------------------------- AXFXSetHooks((AXFXAlloc)PrivateAlloc, (AXFXFree)PrivateFree); init_effects(); // ----------------------------------------------------------- // initialize profiling for AX // ----------------------------------------------------------- AXInitProfile(ax_profile, NUM_AX_PROFILE_FRAMES); // ----------------------------------------------------------- // Invoke menu system! // ----------------------------------------------------------- MenuPtr = DEMOWinCreateMenuWindow( &Menu, 20, 100 ); DebugWin = DEMOWinCreateWindow( (u16)(MenuPtr->handle->x2+10), 20, 620, 440, "Debug", 1024, NULL ); StatusWin = DEMOWinCreateWindow( (u16)(MenuPtr->handle->x1), (u16)(MenuPtr->handle->y2+10), (u16)(MenuPtr->handle->x2), (u16)(MenuPtr->handle->y2+120), "Status", 0, status_win_update ); DEMOWinOpenWindow(DebugWin); DEMOWinOpenWindow(StatusWin); DEMOWinLogPrintf(DebugWin, "-------------------------------\n"); DEMOWinLogPrintf(DebugWin, "AX Low-pass filter test\n"); DEMOWinLogPrintf(DebugWin, "-------------------------------\n"); DEMOWinLogPrintf(DebugWin, "\n"); DEMOWinLogPrintf(DebugWin, "Mode is AX_MODE_DPL2.\n\n"); DEMOWinLogPrintf(DebugWin, "- Use sub-stick up/down to change\n"); DEMOWinLogPrintf(DebugWin, " filter cut-off frequency.\n"); while (1) { DEMOWinMenu(MenuPtr); } } // end main()