/*---------------------------------------------------------------------------* Project: Revolution PMIC graphic demo File: pmic_graphic.c Copyright (C)2008 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: pmic_graphic.c,v $ Revision 1.4.2.3 2009/12/14 00:21:52 aka Fixed misspelling. Revision 1.4.2.2 2009/11/19 07:30:49 aka Copied from HEAD. Revision 1.7 2009/11/19 07:28:06 aka Removed WPADShutdown() & WPADDisconnect(). Revision 1.6 2009/10/15 07:48:10 aka Changed from tab to spaces. Revision 1.5 2009/10/15 07:45:29 aka Revised error handling of PMICQuit(). Revision 1.4 2009/04/17 06:34:21 ozeki_kohei Correct behavior for the case that device is detached while PMICQuit(). Revision 1.3 2009/04/02 01:49:07 dante.treglia * Added shutdown code. Revision 1.2 2008/09/10 21:10:36 carlmu Fixed read code to better deal with end-of-buffer wrap-around. Revision 1.1 2008/08/06 01:39:14 carlmu Added graphic demo. $NoKeywords: $ *---------------------------------------------------------------------------*/ #include #include #include #include #include #include "audio.h" BOOL PMICIsUp( void ); static MEMHeapHandle mem2Heap; static u16 button; static u8* work; static void* myAlloc ( u32 size ); static u8 myFree ( void* ptr ); static BOOL usePMIC ( void ); static void *procMic ( void* p ); static void DrawStuff ( void ); static void shutdown ( void ); /*---------------------------------------------------------------------------* Name: main Description: main func. Arguments: none. Returns: none. *---------------------------------------------------------------------------*/ void main(void) { void* arenaMem2Lo; void* arenaMem2Hi; s32 status; u32 type; WPADStatus currWpad; WPADStatus prevWpad; BOOL running = TRUE; // init DEMO lib. DEMOInit(NULL); DEMOPadInit(); OSReport ("\n\n"); OSReport ("************************************************\n"); OSReport ("pmic_graphic: Graphic demo for Party Mic device\n"); OSReport ("************************************************\n"); OSReport ("This demo requires the Party Mic and a Wii Remote.\n"); OSReport ("The audio captured by the Mic is display onscreen.\n"); OSReport ("\n"); // init MEM lib. arenaMem2Lo = OSGetMEM2ArenaLo(); arenaMem2Hi = OSGetMEM2ArenaHi(); mem2Heap = MEMCreateExpHeap(arenaMem2Lo, (u32)arenaMem2Hi - (u32) arenaMem2Lo); // init Audio libs. AUDIOInit(&mem2Heap, procMic); // xxx // init WPAD. WPADRegisterAllocator(myAlloc, myFree); WPADInit(); // init PMIC work area. work = MEMAllocFromExpHeapEx(mem2Heap, PMIC_MEM2_WORK, 32); do { status = WPADGetStatus(); } while (WPAD_STATE_SETUP != status); memset(&currWpad, 0, sizeof(WPADStatus)); memset(&prevWpad, 0, sizeof(WPADStatus)); // spin... while (running) { // read Remote. status = WPADProbe(WPAD_CHAN0, &type); if (WPAD_ERR_NONE == status) { WPADRead(WPAD_CHAN0, &currWpad); } button = WPADButtonDown(prevWpad.button, currWpad.button); prevWpad = currWpad; // operate P-Mic. running = usePMIC(); DEMOBeforeRender(); DrawStuff(); DEMODoneRender(); } shutdown(); OSHalt("Demo finished!\n"); } /*---------------------------------------------------------------------------* Name: myAlloc Description: allocate memory. Arguments: size bytes to allocate. Returns: pointer. *---------------------------------------------------------------------------*/ static void* myAlloc(u32 size) { void *ptr; ptr = MEMAllocFromExpHeap(mem2Heap, size); return ptr; } /*---------------------------------------------------------------------------* Name: myFree Description: free memory. Arguments: ptr pointer to free. Returns: always 1. *---------------------------------------------------------------------------*/ static u8 myFree(void* ptr) { MEMFreeToExpHeap(mem2Heap, ptr); return 1; } /*---------------------------------------------------------------------------* *---------------------------------------------------------------------------* * very simple P-Mic operation... * *---------------------------------------------------------------------------* *---------------------------------------------------------------------------*/ enum { STATE_NOT_INITIALIZED=0, STATE_NOT_READY, STATE_PROBE, STATE_OPEN, STATE_WAIT_FOR_OPEN, STATE_STOPPED, STATE_START, STATE_WAIT_FOR_START, STATE_RUN, STATE_DO_STOP, STATE_WAIT_FOR_STOP, STATE_CLOSE, STATE_WAIT_FOR_CLOSE, STATE_WAIT_REOPEN, STATE_QUIT, STATE_WAIT_RESTART, STATE_DIE }; static u32 state = STATE_NOT_INITIALIZED; static volatile PMIC_ERR errCode; static void funcCb ( PMIC_ERR result, void* arg ); /*---------------------------------------------------------------------------* Name: PMICIsUp Description: Indicates if P-Mic library has been initialized Arguments: none. Returns: BOOL indicating whether P-Mic library has been initialized *---------------------------------------------------------------------------*/ BOOL PMICIsUp(void) { if ( state == STATE_NOT_INITIALIZED || state == STATE_QUIT || state == STATE_WAIT_RESTART ) { return FALSE; } return TRUE; } /*---------------------------------------------------------------------------* Name: usePMIC Description: operate P-Mic. Arguments: none. Returns: BOOL indicating whether to continue running or not. *---------------------------------------------------------------------------*/ static BOOL usePMIC(void) { static s32 count = 0; PMIC_ERR retval; switch (state) { case STATE_NOT_INITIALIZED: OSReport("Starting init lib.\n"); retval = PMICInit(work); if (retval == PMIC_ERR_OK) { OSReport("(%ld) ok to call PMICInit() -> %ld\n", count++, retval); state = STATE_NOT_READY; } else { OSReport("(%ld) fail to call PMICInit() -> %ld\n", count++, retval); state = STATE_DIE; // fatal } break; case STATE_NOT_READY: OSReport("Insert P-Mic, or Push B to quit lib.\n"); state = STATE_PROBE; break; case STATE_PROBE: retval = PMICProbe(); if (retval == PMIC_ERR_OK) { OSReport("(%ld) P-Mic is inserted -> %ld\n", count++, retval); state = STATE_OPEN; } else if (retval != PMIC_ERR_NO_DEVICE) { OSReport("(%ld) fail to call PMICProbe() -> %ld\n", count++, retval); state = STATE_DIE; } else if (button & WPAD_BUTTON_B) { state = STATE_QUIT; } break; case STATE_OPEN: OSReport("Got P-Mic device. Opening.\n"); errCode = (PMIC_ERR)(PMIC_ERR_OK + 1); retval = PMICOpenAsync(funcCb, NULL); if (retval == PMIC_ERR_OK) { OSReport("(%ld) ok to call PMICOpenAsync() -> %ld\n", count++, retval); state = STATE_WAIT_FOR_OPEN; } else { OSReport("(%ld) fail to call PMICOpenAsync() -> %ld\n", count++, retval); errCode = PMIC_ERR_OK; state = STATE_NOT_READY; // probe again } break; case STATE_WAIT_FOR_OPEN: // errCode comes from the PMICOpenAsync callback if (errCode > PMIC_ERR_OK) // still waiting? { break; } else if (errCode == PMIC_ERR_OK) { OSReport("(%ld) ok to open P-Mic -> %ld.\n", count++, errCode); state = STATE_STOPPED; OSReport("Push A to start P-Mic, or B to close P-Mic, or Remove P-Mic.\n"); } else { OSReport("(%ld) fail to open P-Mic -> %ld.\n", count++, errCode); state = STATE_NOT_READY; // probe again } break; case STATE_STOPPED: retval = PMICProbe(); if (retval == PMIC_ERR_NO_DEVICE) { OSReport("(%ld) P-Mic is removed -> %ld\n", count++, retval); state = STATE_NOT_READY; } else if (retval != PMIC_ERR_OK) { OSReport("(%ld) fail to call PMICProbe() -> %ld\n", count++, retval); state = STATE_DIE; } else { state = STATE_START; } break; case STATE_START: errCode = (PMIC_ERR)(PMIC_ERR_OK + 1); retval = PMICStartAsync(funcCb, NULL); if (retval == PMIC_ERR_OK) { OSReport("(%ld) ok to call PMICStartAsync() -> %ld\n", count++, retval); state = STATE_WAIT_FOR_START; } else { OSReport("(%ld) fail to call PMICStartAsync() -> %ld\n", count++, retval); errCode = PMIC_ERR_OK; state = STATE_NOT_READY; } break; case STATE_WAIT_FOR_START: // errCode is returned by PMICStartAsync callback if (errCode > PMIC_ERR_OK) // still waiting? { break; } else if (errCode == PMIC_ERR_OK) { OSReport("(%ld) ok to start P-Mic -> %ld.\n", count++, errCode); state = STATE_RUN; OSReport("Push A to stop P-Mic, or B to close P-Mic, or Remove P-Mic.\n"); } else { OSReport("(%ld) fail to start P-Mic -> %ld.\n", count++, errCode); state = STATE_NOT_READY; } break; case STATE_RUN: retval = PMICProbe(); if (retval == PMIC_ERR_NO_DEVICE) { OSReport("(%ld) P-Mic is removed -> %ld\n", count++, retval); state = STATE_NOT_READY; } else if (retval != PMIC_ERR_OK) { OSReport("(%ld) fail to call PMICProbe() -> %ld\n", count++, retval); state = STATE_DIE; } else if (button & WPAD_BUTTON_A) { // AUDIOStartPlay(); // start playing p-mic data state = STATE_DO_STOP; } else if (button & WPAD_BUTTON_B) { state = STATE_CLOSE; } break; case STATE_DO_STOP: errCode = (PMIC_ERR)(PMIC_ERR_OK + 1); retval = PMICStopAsync(funcCb, NULL); if (retval == PMIC_ERR_OK) { OSReport("(%ld) ok to call PMICStopAsync() -> %ld\n", count++, retval); state = STATE_WAIT_FOR_STOP; } else { OSReport("(%ld) fail to call PMICStopAsync() -> %ld\n", count++, retval); errCode = PMIC_ERR_OK; state = STATE_NOT_READY; } break; case STATE_WAIT_FOR_STOP: // errCode comes from the PMICStopAsync callback if (errCode > PMIC_ERR_OK) // still waiting? { break; } else if (errCode == PMIC_ERR_OK) { OSReport("(%ld) ok to stop P-Mic -> %ld.\n", count++, errCode); state = STATE_STOPPED; } else { OSReport("(%ld) fail to stop P-Mic -> %ld.\n", count++, errCode); state = STATE_NOT_READY; } break; case STATE_CLOSE: errCode = (PMIC_ERR)(PMIC_ERR_OK + 1); retval = PMICCloseAsync(funcCb, NULL); if (retval == PMIC_ERR_OK) { OSReport("(%ld) ok to call PMICCloseAsync() -> %ld\n", count++, retval); state = STATE_WAIT_FOR_CLOSE; } else { OSReport("(%ld) fail to call PMICCloseAsync() -> %ld\n", count++, retval); errCode = PMIC_ERR_OK; state = STATE_NOT_READY; } break; case STATE_WAIT_FOR_CLOSE: // errCode comes from the PMICCloseAsync callback if (errCode > PMIC_ERR_OK) // still waiting? { break; } else if (errCode == PMIC_ERR_OK) { OSReport("(%ld) ok to close P-Mic -> %ld.\n", count++, errCode); state = STATE_WAIT_REOPEN; OSReport("Push A to open P-Mic, or B to call PMICQuit.\n"); } else { OSReport("(%ld) fail to close P-Mic -> %ld.\n", count++, errCode); state = STATE_NOT_READY; // or DIE? } break; case STATE_WAIT_REOPEN: if (button & WPAD_BUTTON_A) { state = STATE_OPEN; } else if (button & WPAD_BUTTON_B) { state = STATE_QUIT; } break; case STATE_QUIT: retval = PMICQuit(); if (retval == PMIC_ERR_OK) { OSReport("(%ld) ok to call PMICQuit() -> %ld\n", count++, retval); state = STATE_WAIT_RESTART; OSReport("Push A to init P-Mic, or B to quit application.\n"); } else { OSReport("(%ld) fail to call PMICQuit() -> %ld\n", count++, retval); state = STATE_DIE; } break; case STATE_WAIT_RESTART: if (button & WPAD_BUTTON_A) { state = STATE_NOT_INITIALIZED; } else if (button & WPAD_BUTTON_B) { state = STATE_DIE; } break; case STATE_DIE: OSReport("Quitting application\n"); return FALSE; } return TRUE; } /*---------------------------------------------------------------------------* Name: funcCb Description: callback for PMICOpen/Close/Start/StopAsync Arguments: result error code. arg (not used.) Returns: none. *---------------------------------------------------------------------------*/ static void funcCb(PMIC_ERR result, void* arg) { #pragma unused(arg) errCode = result; } /*---------------------------------------------------------------------------* Graphics-related functions *---------------------------------------------------------------------------*/ #define FRAMES_TO_DISPLAY 333 #define AUDIO_SAMPLES_PER_FRAME (16 * 3) // 16KHz (mono) x 3msec #define AUDIO_SAMPLES_TOTAL (AUDIO_SAMPLES_PER_FRAME * FRAMES_TO_DISPLAY) #define AUDIO_BYTES_PER_FRAME (AUDIO_SAMPLES_PER_FRAME * 2) // 16-bit #define AUDIO_BYTES_TOTAL (AUDIO_BYTES_PER_FRAME * FRAMES_TO_DISPLAY) #define SCREEN_WIDTH 640 #define SCREEN_HEIGHT 480 // Don't use parentheses here: #define AUDIO_SCALE 1/4 const GXColor LIME = { 0, 255, 0, 255 }; const GXColor AQUA = { 0, 255, 255, 255 }; const GXColor MAGENTA = { 255, 0, 255, 255 }; const GXColor GRAY = { 128, 128, 128, 255 }; const GXColor WHITE = { 255, 255, 255, 255 }; const GXColor TEAL = { 0, 128, 128, 255 }; const GXColor ORANGE = { 255, 165, 0, 255 }; const GXColor PINK = { 255, 192, 203, 255 }; const GXColor GREEN = { 0, 128, 0, 255 }; static s16* srcBuff = 0; static u32 currPos = 0; /*---------------------------------------------------------------------------* Name: procMic Description: Thread process that runs to process microphone input. It is started by AUDIOInit. It should initialize itself and then sleep. It will be woken periodically by the audio callback. It just gathers the samples and stores them in a local buffer. Arguments: p (not used.) Returns: none. *---------------------------------------------------------------------------*/ static void *procMic(void* p) { #pragma unused(p) s32 reqSamples; s32 gotSamples; s32 space; // Initialize srcBuff = myAlloc(AUDIO_BYTES_TOTAL); memset(srcBuff, 0, AUDIO_BYTES_TOTAL); currPos = 0; while (1) { // Wait for data to arrive AUDIOSleepThread(); // get P-Mic's data (16KHz). space = AUDIO_SAMPLES_TOTAL - (s32) currPos; reqSamples = AUDIO_SAMPLES_PER_FRAME < space ? AUDIO_SAMPLES_PER_FRAME : space; gotSamples = PMICRead(&srcBuff[currPos], reqSamples); if (gotSamples > 0) { currPos += gotSamples; if (currPos == AUDIO_SAMPLES_TOTAL) { currPos = 0; reqSamples = AUDIO_SAMPLES_PER_FRAME - space; if (reqSamples > 0) { gotSamples = PMICRead(&srcBuff[currPos], reqSamples); if (gotSamples > 0) { currPos += gotSamples; } } } } } } /*---------------------------------------------------------------------------* Name: drawMic Description: This is called by the main routine. It draws a graphic representation of the audio samples using GX. Arguments: none. Returns: none. *---------------------------------------------------------------------------*/ static void DrawStuff(void) { s32 i; Mtx mv; // Make sure we don't try to draw before buffer is initialized. // This shouldn't happen, but we check to be safe. if (srcBuff == 0) { return; } // Initialize graphics state DEMOInitCaption( DM_FT_XLU, SCREEN_WIDTH, SCREEN_HEIGHT ); MTXIdentity(mv); GXLoadPosMtxImm(mv, GX_PNMTX0); GXSetCurrentMtx(GX_PNMTX0); GXSetNumChans(1); GXSetNumTevStages(1); GXSetNumTexGens(0); GXSetTevOp(GX_TEVSTAGE0, GX_PASSCLR); GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR0A0); GXClearVtxDesc(); GXSetVtxDesc(GX_VA_POS, GX_DIRECT); GXSetVtxDesc(GX_VA_CLR0, GX_DIRECT); GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_S16, 0); GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0); // Draw the base line #define LINE_CLR WHITE GXBegin(GX_LINES, GX_VTXFMT0, 2); GXPosition2s16(0, SCREEN_HEIGHT/2); GXColor4u8(LINE_CLR.r, LINE_CLR.g, LINE_CLR.b, LINE_CLR.a); GXPosition2s16(SCREEN_WIDTH, SCREEN_HEIGHT/2); GXColor4u8(LINE_CLR.r, LINE_CLR.g, LINE_CLR.b, LINE_CLR.a); GXEnd(); // Draw the graph #define DATA_CLR LIME GXBegin(GX_LINES, GX_VTXFMT0, AUDIO_SAMPLES_TOTAL); for (i = 0; i < AUDIO_SAMPLES_TOTAL; i++) { s16 sample; sample = srcBuff[(i+currPos)%AUDIO_SAMPLES_TOTAL]; // If you want the display to wrap instead of scroll, use this: // sample = srcBuff[i]; GXPosition2s16((s16)(i*SCREEN_WIDTH/AUDIO_SAMPLES_TOTAL), (s16)(sample*AUDIO_SCALE+SCREEN_HEIGHT/2)); GXColor4u8(DATA_CLR.r, DATA_CLR.g, DATA_CLR.b, DATA_CLR.a); } GXEnd(); } /*---------------------------------------------------------------------------* Name: shutdown Description: close libraries. Arguments: none. Returns: none. *---------------------------------------------------------------------------*/ static void shutdown(void) { PMIC_ERR retval; // Wait for any pending microphone transaction while (errCode > PMIC_ERR_OK) { OSYieldThread(); } // Make sure the microphone is stopped errCode = (PMIC_ERR)(PMIC_ERR_OK + 1); retval = PMICStopAsync(funcCb, NULL); if (retval == PMIC_ERR_OK) { while (errCode > PMIC_ERR_OK) { OSYieldThread(); } } // Close the microphone errCode = (PMIC_ERR)(PMIC_ERR_OK + 1); retval = PMICCloseAsync(funcCb, NULL); if (retval == PMIC_ERR_OK) { while (errCode > PMIC_ERR_OK) { OSYieldThread(); } } // Kill audio AUDIOQuit(); // Close PMIC library PMICQuit(); // Free the PMIC work area MEMFreeToExpHeap(mem2Heap, work); }