/*---------------------------------------------------------------------------* Project: Revolution WPAD AX demo File: wpad_spdemo.c Copyright (C)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: wpad_spdemo.c,v $ Revision 1.2 08/03/2006 13:32:55 tojo Removed old APIs. Revision 1.1 07/27/2006 02:33:26 aka Initial check-in. $NoKeywords: $ *---------------------------------------------------------------------------*/ #include #include #include #include #include #include #include /*---------------------------------------------------------------------------* SP *---------------------------------------------------------------------------*/ // zero buffer #define ZEROBUFFER_BYTES 512 // data #define SPT_FILE "/spdemo/spdemo.spt" #define SPD_FILE "/spdemo/spdemo.spd" #define SFX_MENU 0 // not used #define SFX_DRUM 1 // chan 0 #define SFX_GUNSHOT 2 // chan 1 #define VOICE_NGC_MAN 3 // chan 2 #define VOICE_NGC_WOMAN 4 // chan 3 #define SFX_HONK_LOOPED 5 // not used static SPSoundTable *SpTable; // application-layer voice abstraction #define MAX_DEMO_VOICES 64 typedef struct { AXVPB *voice; SPSoundEntry *entry; s32 chan; } VoiceInfo; static VoiceInfo vInfo[MAX_DEMO_VOICES]; // function prototypes static void AudioFrameCallback ( void ); static void *LoadFileIntoRam ( char *path ); static void InitVoice ( void ); static void PlaySfx ( s32 chan ); static void StopSfx ( s32 chan ); static VoiceInfo *GetVoice ( void ); static void DropVoiceCallback ( void *p ); /*---------------------------------------------------------------------------* WPAD *---------------------------------------------------------------------------*/ // allocator functions for WPAD static void *myAlloc ( u32 size ); static u8 myFree ( void *ptr ); // function prototypes static void UpdateSpeaker ( OSAlarm *alarm, OSContext *context ); static void SpeakerCallback ( s32 chan, s32 result ); static void SpeakerOnCallback ( s32 chan, s32 result ); static void ConnectCallback ( s32 chan, s32 reason ); // Speaker Status typedef struct SpeakerInfo { u8 active; WENCInfo encInfo; BOOL first; BOOL last; } SpeakerInfo; // Controller Status typedef struct ContInfo { u32 type; s32 status; WPADStatus currStat; WPADStatus prevStat; SpeakerInfo Speakers; u8 play; } ContInfo; static ContInfo info[WPAD_MAX_CONTROLLERS]; // Periodic Alarms for audio streaming static OSAlarm SpeakerAlarm; // Audio buffers #define SAMPLES_PER_AUDIO_PACKET 40 #define AUDIO_PACKET_LEN 20 // SAMPLES_PER_AUDIO_PACKET / 2 /*---------------------------------------------------------------------------* etc. *---------------------------------------------------------------------------*/ // Exp Heap static MEMHeapHandle hExpHeap; // function prototypes static void initialize(); static void RenderOperation(); static void RenderControllerStatus(); /*---------------------------------------------------------------------------* * Name : main * Description : main * Arguments : None. * Returns : None. *---------------------------------------------------------------------------*/ void main (void) { s32 i; s32 chan; u16 button; void *arenaMem2Lo; void *arenaMem2Hi; u8 *SpData; u8 *ZeroBuffer; // Initialize DEMO initialize(); // initialize Exp Heap on MEM2 arenaMem2Lo = OSGetMEM2ArenaLo(); arenaMem2Hi = OSGetMEM2ArenaHi(); hExpHeap = MEMCreateExpHeap(arenaMem2Lo, (u32)arenaMem2Hi - (u32) arenaMem2Lo); ASSERT(hExpHeap != NULL); // // Initialize Audio // AIInit(NULL); AXInit(); MIXInit(); // Load Audio Data SpTable = LoadFileIntoRam(SPT_FILE); SpData = LoadFileIntoRam(SPD_FILE); // Alloc Zero Buffer ZeroBuffer = MEMAllocFromExpHeapEx(hExpHeap, ZEROBUFFER_BYTES, 8); memset(ZeroBuffer, 0, ZEROBUFFER_BYTES); DCFlushRange(ZeroBuffer, ZEROBUFFER_BYTES); // Register Callback with AX for audio processing AXRegisterCallback(&AudioFrameCallback); // Initialize Sound Table SPInitSoundTable(SpTable, SpData, ZeroBuffer); // Application-layer Voice Abstraction InitVoice(); // // Initialize WPAD // WPADRegisterAllocator(myAlloc, myFree); WPADInit(); while (WPADGetStatus() != WPAD_STATE_SETUP) { ; } for (i = 0; i < WPAD_MAX_CONTROLLERS; i++) { WPADSetConnectCallback((s32)i, ConnectCallback); } OSCreateAlarm(&SpeakerAlarm); OSSetPeriodicAlarm(&SpeakerAlarm, OSGetTime(), WPAD_STRM_INTERVAL, UpdateSpeaker); // Spin while (1) { for (chan = 0; chan < WPAD_MAX_CONTROLLERS; chan++) { info[chan].status = WPADProbe(chan, &info[chan].type); if (info[chan].status == WPAD_ERR_NONE) { WPADRead(chan, &info[chan].currStat); button = WPADButtonDown(info[chan].prevStat.button, info[chan].currStat.button); info[chan].prevStat = info[chan].currStat; if (button & WPAD_BUTTON_A) { PlaySfx(chan); } } } DEMOBeforeRender(); RenderOperation(); RenderControllerStatus(); DEMODoneRender(); } OSCancelAlarm(&SpeakerAlarm); } /*---------------------------------------------------------------------------* * Name : UpdateSpeaker * Description : * Arguments : * Returns : None. *---------------------------------------------------------------------------*/ static void UpdateSpeaker(OSAlarm *alarm, OSContext *context) { #pragma unused(alarm, context) s16 pcmData[SAMPLES_PER_AUDIO_PACKET]; u8 encData[AUDIO_PACKET_LEN]; u32 flag; s32 chan; BOOL adv = FALSE; for (chan = 0; chan < WPAD_MAX_CONTROLLERS; chan++) { if (SAMPLES_PER_AUDIO_PACKET == AXRmtGetSamples(chan, pcmData, SAMPLES_PER_AUDIO_PACKET)) { adv = TRUE; if (info[chan].Speakers.active) { flag = (info[chan].Speakers.first) ? (u32)WENC_FLAG_FIRST : (u32)WENC_FLAG_CONT; if (info[chan].Speakers.first) { info[chan].Speakers.first = FALSE; } WENCGetEncodeData(&info[chan].Speakers.encInfo, flag, pcmData, SAMPLES_PER_AUDIO_PACKET, encData); WPADSendStreamData(chan, encData, AUDIO_PACKET_LEN); } } } if (adv) { AXRmtAdvancePtr(SAMPLES_PER_AUDIO_PACKET); } } /*---------------------------------------------------------------------------* * Name : SpeakerCallback * Description : * Arguments : * Returns : None. *---------------------------------------------------------------------------*/ static void SpeakerCallback( s32 chan, s32 result ) { if (result == WPAD_ERR_NONE) { info[chan].Speakers.active = 1; info[chan].Speakers.first = TRUE; info[chan].Speakers.last = FALSE; memset(&info[chan].Speakers.encInfo, 0, sizeof(WENCInfo)); OSReport("Chan[%d] is ready\n", chan); } } /*---------------------------------------------------------------------------* * Name : SpeakerOnCallback * Description : * Arguments : * Returns : None. *---------------------------------------------------------------------------*/ static void SpeakerOnCallback( s32 chan, s32 result ) { if (result == WPAD_ERR_NONE) { WPADControlSpeaker(chan, WPAD_SPEAKER_PLAY, SpeakerCallback); } } /*---------------------------------------------------------------------------* * Name : ConnectCallback * Description : * Arguments : * Returns : None. *---------------------------------------------------------------------------*/ static void ConnectCallback( s32 chan, s32 reason ) { OSReport("ConnectCallback(%d) : %s\n", chan, (reason < 0) ? "disconnect" : "connect"); info[chan].Speakers.active = 0; if (reason >= 0) { WPADSetDataFormat(chan, WPAD_FMT_CORE); WPADControlSpeaker(chan, WPAD_SPEAKER_ON, SpeakerOnCallback); } else { StopSfx(chan); } } /*---------------------------------------------------------------------------* * Name : myAlloc * Description : * Arguments : None. * Returns : None. *---------------------------------------------------------------------------*/ static void *myAlloc(u32 size) { void *ptr; ptr = MEMAllocFromExpHeap(hExpHeap, size); ASSERTMSG(ptr, "Memory allocation failed\n"); return(ptr); } /*---------------------------------------------------------------------------* * Name : myFree * Description : * Arguments : None. * Returns : None. *---------------------------------------------------------------------------*/ static u8 myFree(void *ptr) { MEMFreeToExpHeap(hExpHeap, ptr); return(1); } /*---------------------------------------------------------------------------* * Name : AudioFrameCallback * Description : Callback that process audio data per 3ms audio frame. * Arguments : None. * Returns : : None. *---------------------------------------------------------------------------*/ static void AudioFrameCallback(void) { u32 i; info[0].play = info[1].play = info[2].play = info[3].play = 0; for (i = 0; i < MAX_DEMO_VOICES; i++) { if (vInfo[i].voice) { if ( AX_PB_STATE_STOP == ((vInfo[i].voice)->pb.state)) { MIXReleaseChannel(vInfo[i].voice); AXFreeVoice(vInfo[i].voice); vInfo[i].voice = NULL; vInfo[i].entry = NULL; vInfo[i].chan = -1; } else { info[vInfo[i].chan].play = 1; } } } // tell the mixer to update settings MIXUpdateSettings(); } /*---------------------------------------------------------------------------* * Name : LoadFileIntoRam * Description : Loads a file into memory. Memory is allocated. * Arguments : path File to load into main memory * Returns : : pointer to file in main memory or NULL if not opened *---------------------------------------------------------------------------*/ 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; } /*---------------------------------------------------------------------------* * Name : InitVoice * Description : * Arguments : None. * Returns : : None. *---------------------------------------------------------------------------*/ static void InitVoice(void) { s32 i; for (i = 0; i < MAX_DEMO_VOICES; i++) { vInfo[i].voice = NULL; vInfo[i].entry = NULL; vInfo[i].chan = -1; } } /*---------------------------------------------------------------------------* * Name : PlaySfx * Description : * Arguments : chan * Returns : : None. *---------------------------------------------------------------------------*/ static void PlaySfx(s32 chan) { VoiceInfo *v; BOOL old; old = OSDisableInterrupts(); v = GetVoice(); if (v) { v->voice = AXAcquireVoice(15, DropVoiceCallback, 0); if (v->voice) { v->entry = SPGetSoundEntry(SpTable, (u32)(chan + 1)); v->chan = chan; SPPrepareSound(v->entry, v->voice, (v->entry)->sampleRate); MIXInitChannel(v->voice, 0, 0, -960, -960, -960, 64, 127, -960); switch(chan) { case 0: MIXRmtSetVolumes(v->voice, 0, 0, -960, -960, -960, -960, -960, -960, -960); break; case 1: MIXRmtSetVolumes(v->voice, 0, -960, 0, -960, -960, -960, -960, -960, -960); break; case 2: MIXRmtSetVolumes(v->voice, 0, -960, -960, 0, -960, -960, -960, -960, -960); break; default: MIXRmtSetVolumes(v->voice, 0, -960, -960, -960, 0, -960, -960, -960, -960); break; } AXSetVoiceRmtOn(v->voice, AX_PB_REMOTE_ON); AXSetVoiceState(v->voice, AX_PB_STATE_RUN); info[chan].play = 1; } } OSRestoreInterrupts(old); } /*---------------------------------------------------------------------------* * Name : StopSfx * Description : * Arguments : chan * Returns : : None. *---------------------------------------------------------------------------*/ static void StopSfx(s32 chan) { s32 i; BOOL old; old = OSDisableInterrupts(); for (i = 0; i < MAX_DEMO_VOICES; i++) { if (chan == vInfo[i].chan) { AXSetVoiceState(vInfo[i].voice, AX_PB_STATE_STOP); } } OSRestoreInterrupts(old); } /*---------------------------------------------------------------------------* * Name : GetVoice * Description : * Arguments : None. * Returns : : pointer to VoiceInfo. *---------------------------------------------------------------------------*/ static VoiceInfo *GetVoice(void) { s32 i; for (i = 0; i < MAX_DEMO_VOICES; i++) { if (NULL == vInfo[i].voice) { return(&vInfo[i]); } } return(NULL); } /*---------------------------------------------------------------------------* * Name : DropVoiceCallback * Description : * Arguments : * Returns : : None. *---------------------------------------------------------------------------*/ static void DropVoiceCallback(void *p) { s32 i; for (i = 0; i < MAX_DEMO_VOICES; i++) { if ( (AXVPB *)(p) == vInfo[i].voice) { MIXReleaseChannel(vInfo[i].voice); vInfo[i].voice = NULL; vInfo[i].entry = NULL; vInfo[i].chan = -1; break; } } } /*---------------------------------------------------------------------------* * Name : RenderOperation * Description : * Arguments : None. * Returns : : None. *---------------------------------------------------------------------------*/ static const s16 HEIGHT = 10; static void RenderOperation(void) { s16 y = 64; DEMOPrintf( 16, y += HEIGHT, 0, "button A : Start/Stop Sfx"); } /*---------------------------------------------------------------------------* * Name : RenderControllerStatus * Description : * Arguments : None. * Returns : : None. *---------------------------------------------------------------------------*/ static void RenderControllerStatus(void) { s16 y = 16; int chan; DEMOPrintf( 150, y, 0, "speaker"); DEMOPrintf( 220, y, 0, "sfx"); for( chan = 0; chan < WPAD_MAX_CONTROLLERS; chan++) { y += HEIGHT; DEMOPrintf( 16, y, 0, "CHAN_%d [%s]", chan, (info[chan].status == WPAD_ERR_NO_CONTROLLER) ? "--" : (info[chan].type == 0) ? "CORE" : (info[chan].type == 1) ? "NUNCHAKU" : (info[chan].type == 2) ? "CLASSIC" : "UNKNOWN" ); DEMOPrintf( 150, y, 0, "%s", (info[chan].Speakers.active == 1) ? "ON" : (info[chan].Speakers.active == 2) ? "MUTE" : "OFF"); DEMOPrintf( 220, y, 0, "%s", (info[chan].play == 0) ? "STOP" : "PLAY"); } } /*---------------------------------------------------------------------------* * Name : initialize * Description : Initialize GX. * Arguments : None. * Returns : : None. *---------------------------------------------------------------------------*/ static void initialize(void) { const GXColor DARKBLUE = { 0, 0, 64, 255 }; const int SCREEN_WIDTH = 320; const int SCREEN_HEIGHT = 240; DEMOInit( &GXNtsc480IntDf ); GXSetCopyClear( DARKBLUE, GX_MAX_Z24 ); GXCopyDisp( DEMOGetCurrentBuffer(), GX_TRUE ); DEMOInitCaption( DM_FT_XLU, SCREEN_WIDTH, SCREEN_HEIGHT ); GXSetZMode( GX_ENABLE, GX_ALWAYS, GX_ENABLE ); // Set pixel processing mode GXSetBlendMode( GX_BM_BLEND, GX_BL_ONE, GX_BL_ONE, GX_LO_CLEAR ); // Translucent mode DEMOPadInit(); }