/*---------------------------------------------------------------------------* 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.13.2.1 2007/12/11 01:17:50 tojo Modified to handle the error code of WPADStatus. Revision 1.13 2007/07/10 12:14:46 tojo (none) Revision 1.12 2007/05/10 04:17:34 aka Revised to use AXRmtGetSamplesLeft(). Revision 1.11 2007/04/23 09:16:08 tojo (none) Revision 1.10 2007/02/09 06:45:13 tojo Modifed to follow the guideline. Revision 1.9 2006/11/21 08:38:15 aka Removed the zero buffer. Revision 1.8 2006/10/23 02:16:23 aka Changed from AXInit() to AXInitSpecifyMem(). Changed from MIXInit() to MIXInitSpecifyMem(). Changed from SYNInit() to SYNInitSpecifyMem(). Revision 1.7 2006/10/23 01:18:00 tojo Called WPADSetConnectCallback before initialization complete. Revision 1.6 2006/10/11 03:03:50 aka Revised AXInit(), MIXInit() and SYNInit(). Revision 1.5 2006/09/19 06:35:07 tojo (none) Revision 1.4 2006/09/18 11:59:38 tojo (none) Revision 1.3 2006/09/15 05:49:02 tojo Fixed to corrupt audio streaming Revision 1.2 2006/08/03 13:32:55 tojo Removed old apis. Revision 1.1 2006/07/26 07:47:32 aka Initial check-in. $NoKeywords: $ *---------------------------------------------------------------------------*/ #include #include #include #include #include #include #include #include /*---------------------------------------------------------------------------* SEQ *---------------------------------------------------------------------------*/ // 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 *---------------------------------------------------------------------------*/ // Audio buffers #define SAMPLES_PER_AUDIO_PACKET 40 #define AUDIO_PACKET_LEN 20 // SAMPLES_PER_AUDIO_PACKET / 2 // 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 SpeakerOffCallback( s32 chan, s32 result ); static void ConnectCallback ( s32 chan, s32 reason ); static void ExtensionCallback ( s32 chan, s32 result ); // Speaker Status typedef struct SpeakerInfo { u8 active; WENCInfo encInfo; BOOL first; BOOL last; s32 status; s16 pcmData[SAMPLES_PER_AUDIO_PACKET]; u8 encData[AUDIO_PACKET_LEN]; OSTime pending; } SpeakerInfo; typedef union Status { WPADStatus cr; WPADFSStatus fs; WPADCLStatus cl; } Status; // Controller Status typedef struct ContInfo { u32 type; s32 status; Status currStat; Status prevStat; SpeakerInfo Speakers; SEQSEQUENCE Sequence; u8 play; } ContInfo; static ContInfo info[WPAD_MAX_CONTROLLERS]; // Periodic Alarms for audio streaming static OSAlarm SpeakerAlarm; /*---------------------------------------------------------------------------* 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; void *axBuffer; void *mixBuffer; void *synBuffer; // Initialize DEMO initialize(); // initialize Exp Heap on MEM2 arenaMem2Lo = OSGetMEM2ArenaLo(); arenaMem2Hi = OSGetMEM2ArenaHi(); hExpHeap = MEMCreateExpHeap(arenaMem2Lo, (u32)arenaMem2Hi - (u32) arenaMem2Lo); ASSERT(hExpHeap != NULL); // // Initialize Audio // axBuffer = OSAlloc(AXGetMemorySize(AX_MAX_VOICES)); mixBuffer = MEMAllocFromExpHeap(hExpHeap, MIXGetMemorySize(AX_MAX_VOICES)); synBuffer = MEMAllocFromExpHeap(hExpHeap, SYNGetMemorySize(AX_MAX_VOICES)); AIInit(NULL); AXInitSpecifyMem(AX_MAX_VOICES, axBuffer); MIXInitSpecifyMem(mixBuffer); SYNInitSpecifyMem(synBuffer); SEQInit(); // Load Audio Data Wavetable = LoadFileIntoRam(GM_WT); Pcm = LoadFileIntoRam(GM_PCM); MidiFileMain = LoadFileIntoRam(MIDI_FILE_MAIN); MidiFileRemote = LoadFileIntoRam(MIDI_FILE_REMOTE); // Register Callback with AX for audio processing AXRegisterCallback(&AudioFrameCallback); // Prepare Sequence for Main SEQAddSequence(&SequenceMain, MidiFileMain, Wavetable, Pcm, NULL, 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, NULL, 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(); for (i = 0; i < WPAD_MAX_CONTROLLERS; i++) { WPADSetConnectCallback((s32)i, ConnectCallback); } while (WPADGetStatus() != WPAD_STATE_SETUP) { ; } 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_NO_CONTROLLER) { WPADRead(chan, &info[chan].currStat.cr); if (info[chan].currStat.cr.err != WPAD_ERR_INVALID) { button = WPADButtonDown(info[chan].prevStat.cr.button, info[chan].currStat.cr.button); info[chan].prevStat.cr = info[chan].currStat.cr; } if (button & WPAD_BUTTON_A) { switch (info[chan].Speakers.active) { case 0: WPADControlSpeaker(chan, WPAD_SPEAKER_ON, SpeakerOnCallback); break; case 1: WPADControlSpeaker(chan, WPAD_SPEAKER_OFF, SpeakerOffCallback); break; default: break; } } if (SEQGetState(&info[chan].Sequence) == SEQ_STATE_RUN) { info[chan].play = 1; } else { info[chan].play = 0; if (info[chan].Speakers.active == 1) { info[chan].Speakers.active = 2; WPADControlSpeaker(chan, WPAD_SPEAKER_OFF, SpeakerOffCallback); } } } } DEMOBeforeRender(); DEMOPrintf( 16, 16, 0, "WPAD Demo -- WPAD Seqdemo"); RenderOperation(); RenderControllerStatus(); DEMODoneRender(); } OSCancelAlarm(&SpeakerAlarm); } /*---------------------------------------------------------------------------* * Name : UpdateSpeaker * Description : * Arguments : * Returns : None. *---------------------------------------------------------------------------*/ static void UpdateSpeaker( OSAlarm *alarm, OSContext *context ) { #pragma unused(alarm, context) u32 flag; s32 chan; BOOL intr; if (SAMPLES_PER_AUDIO_PACKET <= AXRmtGetSamplesLeft()) { for(chan = 0; chan < WPAD_MAX_CONTROLLERS; chan++) { AXRmtGetSamples(chan, info[chan].Speakers.pcmData, SAMPLES_PER_AUDIO_PACKET); if (info[chan].Speakers.active) { intr = OSDisableInterrupts(); if (WPADCanSendStreamData(chan)) { 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, info[chan].Speakers.pcmData, SAMPLES_PER_AUDIO_PACKET, info[chan].Speakers.encData); info[chan].Speakers.status = WPADSendStreamData(chan, info[chan].Speakers.encData, AUDIO_PACKET_LEN); } OSRestoreInterrupts(intr); } } 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)); info[chan].Speakers.status = WPAD_ERR_NONE; memset(info[chan].Speakers.pcmData, 0, SAMPLES_PER_AUDIO_PACKET); memset(info[chan].Speakers.encData, 0, AUDIO_PACKET_LEN); SEQSetState(&info[chan].Sequence, SEQ_STATE_RUN); OSReport("Chan[%d] is ready\n", chan); } } /*---------------------------------------------------------------------------* * Name : SpeakerOffCallback * Description : * Arguments : * Returns : None. *---------------------------------------------------------------------------*/ static void SpeakerOffCallback( s32 chan, s32 result ) { #pragma unused(result) SEQSetState(&info[chan].Sequence, SEQ_STATE_STOP); info[chan].Speakers.active = 0; OSReport("Chan[%d] is stopped.\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 : ExtensionCallback * Description : * Arguments : * Returns : None. *---------------------------------------------------------------------------*/ static void ExtensionCallback( s32 chan, s32 result ) { switch(result) { case WPAD_DEV_CORE: case WPAD_DEV_FUTURE: case WPAD_DEV_NOT_SUPPORTED: WPADSetDataFormat(chan, WPAD_FMT_CORE); break; case WPAD_DEV_FREESTYLE: WPADSetDataFormat(chan, WPAD_FMT_FREESTYLE); break; case WPAD_DEV_CLASSIC: WPADSetDataFormat(chan, WPAD_FMT_CLASSIC); break; } } /*---------------------------------------------------------------------------* * 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); WPADSetExtensionCallback(chan, ExtensionCallback); } 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 = 80; DEMOPrintf( 16, y += HEIGHT, 0, "button A : Start/Stop Sequence"); } /*---------------------------------------------------------------------------* * Name : RenderControllerStatus * Description : * Arguments : None. * Returns: : None. *---------------------------------------------------------------------------*/ static void RenderControllerStatus( void ) { s16 y = 32; 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) }