/*---------------------------------------------------------------------------* Project: Revolution WPAD AX demo File: wpad_seqdemo.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_seqdemo.c,v $ Revision 1.2 08/03/2006 13:32:55 tojo Removed old APIs. Revision 1.1 07/26/2006 07:47:32 aka Initial check-in. $NoKeywords: $ *---------------------------------------------------------------------------*/ #include #include #include #include #include #include #include #include /*---------------------------------------------------------------------------* SEQ *---------------------------------------------------------------------------*/ // zero buffer #define ZEROBUFFER_BYTES 512 // data #define GM_WT "/axdemo/synth/gm16adpcm.wt" #define GM_PCM "/axdemo/synth/gm16adpcm.pcm" #define MIDI_FILE_MAIN "/axdemo/midi/2nd_time.mid" #define MIDI_FILE_REMOTE "/axdemo/midi/autumnal.mid" // function prototypes static void *LoadFileIntoRam ( char *path ); static void AudioFrameCallback ( void ); static void VoiceInitCallback ( AXVPB *axvpb, SYNSYNTH *synth, u8 midiChannel ); static void VoiceUpdateCallback ( AXVPB *axvpb, SYNSYNTH *synth, u8 midiChannel ); /*---------------------------------------------------------------------------* 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; SEQSEQUENCE Sequence; 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; SEQSEQUENCE SequenceMain; u8 *MidiFileMain; u8 *MidiFileRemote; u8 *Wavetable; u8 *Pcm; 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(); SYNInit(); SEQInit(); // Load Audio Data Wavetable = LoadFileIntoRam(GM_WT); Pcm = LoadFileIntoRam(GM_PCM); MidiFileMain = LoadFileIntoRam(MIDI_FILE_MAIN); MidiFileRemote = LoadFileIntoRam(MIDI_FILE_REMOTE); // 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); // Prepare Sequence for Main SEQAddSequence(&SequenceMain, MidiFileMain, Wavetable, Pcm, ZeroBuffer, 16, 15, 1 ); // Play Sequence for Main SEQSetState(&SequenceMain, SEQ_STATE_RUNLOOPED); // Prepare Sequence for Remotes for (i = 0; i < WPAD_MAX_CONTROLLERS; i++) { SEQAddSequence(&info[i].Sequence, MidiFileRemote, Wavetable, Pcm, ZeroBuffer, 16, 15, 1 ); SYNSetMasterVolume( &info[i].Sequence.synth, -960); SYNSetInitCallback( &info[i].Sequence.synth, VoiceInitCallback); SYNSetUpdateCallback(&info[i].Sequence.synth, VoiceUpdateCallback); } // // 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) { if (!info[chan].play) { SEQSetState(&info[chan].Sequence, SEQ_STATE_RUN); } else { SEQSetState(&info[chan].Sequence, SEQ_STATE_STOP); } } if (SEQGetState(&info[chan].Sequence) == SEQ_STATE_RUN) { info[chan].play = 1; } else { info[chan].play = 0; } } } 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 { SEQSetState(&info[chan].Sequence, SEQ_STATE_STOP); info[chan].play = 0; } } /*---------------------------------------------------------------------------* * 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) { // run the sequencer SEQRunAudioFrame(); // run the synth SYNRunAudioFrame(); // 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 : 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 Sequence"); } /*---------------------------------------------------------------------------* * 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, "sequence"); 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(); } /*---------------------------------------------------------------------------* * Name : VoiceInitCallback * Description : * Arguments : * Returns : : None. *---------------------------------------------------------------------------*/ static void VoiceInitCallback(AXVPB *axvpb, SYNSYNTH *synth, u8 midiChannel) { #pragma unused(midiChannel) s32 i; for (i = 0; i < WPAD_MAX_CONTROLLERS; i++) { if (synth == &info[i].Sequence.synth) { break; } } switch(i) { case 0: MIXRmtSetVolumes(axvpb, 0, 0, -960, -960, -960, -960, -960, -960, -960); break; case 1: MIXRmtSetVolumes(axvpb, 0, -960, 0, -960, -960, -960, -960, -960, -960); break; case 2: MIXRmtSetVolumes(axvpb, 0, -960, -960, 0, -960, -960, -960, -960, -960); break; default: MIXRmtSetVolumes(axvpb, 0, -960, -960, -960, 0, -960, -960, -960, -960); break; } AXSetVoiceRmtOn(axvpb, AX_PB_REMOTE_ON); } /*---------------------------------------------------------------------------* * Name : VoiceUpdateCallback * Description : * Arguments : * Returns : : None. *---------------------------------------------------------------------------*/ static void VoiceUpdateCallback(AXVPB *axvpb, SYNSYNTH *synth, u8 midiChannel) { #pragma unused(axvpb) #pragma unused(synth) #pragma unused(midiChannel) }