1 /*---------------------------------------------------------------------------*
2   Project:  TwlSDK - WM - demos - wmPadRead-child
3   File:     main.c
4 
5   Copyright 2007-2008 Nintendo. All rights reserved.
6 
7   These coded instructions, statements, and computer programs contain
8   proprietary information of Nintendo of America Inc. and/or Nintendo
9   Company Ltd., and are protected by Federal copyright law. They may
10   not be disclosed to third parties or copied or duplicated in any form,
11   in whole or in part, without the prior written consent of Nintendo.
12 
13   $Date:: 2008-09-17#$
14   $Rev: 8556 $
15   $Author: okubata_ryoma $
16  *---------------------------------------------------------------------------*/
17 
18 //
19 // This is the child program distributed by the mpdldemo/mpdlntr2rvl demo included with RevoEX, a development kit for the Wii's wireless features.
20 //
21 // It communicates with the Wii and sends button input.
22 //
23 
24 #ifdef SDK_TWL
25 #include <twl.h>
26 #else
27 #include <nitro.h>
28 #endif
29 
30 #include <nitro/wm.h>
31 #include "data.h"
32 #include "wh.h"
33 
34 #include "tpdata.h"
35 
36 /*---------------------------------------------------------------------------*
37     Constant Definitions
38  *---------------------------------------------------------------------------*/
39 #define PICTURE_FRAME_PER_GAME_FRAME    1
40 
41 /* The GGID used in this demo */
42 #define MY_GGID SDK_MAKEGGID_SYSTEM(0x20)
43 
44 #define DMA_NO  3
45 
46 #define SAMPLING_BUFFER_SIZE ( 1024 * 1024 ) // 1M
47 
48 #define FFT_NSHIFT      9
49 #define FFT_N           (1 << FFT_NSHIFT)
50 
51 /*---------------------------------------------------------------------------*
52     Variable Definitions
53  *---------------------------------------------------------------------------*/
54 
55 static WMBssDesc mbParentBssDesc ATTRIBUTE_ALIGN(32);
56 static BOOL isMultiBooted;
57 
58 static GXOamAttr oamBak[128];
59 
60 typedef struct MyPadData {
61     u16 keyData;
62     u8 touchPanel_x;
63     u8 touchPanel_y;
64     u8 mic;
65     u8 touch;
66     u8 padding[2];
67 } MyPadData;
68 
69 // Send/receive buffer for display
70 static MyPadData gRecvData[1 + WH_CHILD_MAX]; // Touch information for all players, delivered by the parent
71 static BOOL gRecvFlag[1 + WH_CHILD_MAX];
72 
73 u16     keyData;
74 
75 static GXOamAttr gOam[128];
76 
77 TPData  raw_point;
78 TPData  disp_point;
79 TPCalibrateParam calibrate;
80 
81 static BOOL input_mic;
82 static MICAutoParam gMicAutoParam;
83 static u8 *gMicData;
84 
85 
86 // FFT buffers
87 static fx16 sinTable[FFT_N - FFT_N / 4];
88 #ifdef USE_FFTREAL
89 static fx16 sinTable2[(FFT_N - FFT_N / 4) / 2];
90 static fx32 data[FFT_N];
91 #else
92 static fx32 data[FFT_N * 2];
93 #endif
94 static s32 power[FFT_N/2+1];
95 static s32 smoothedPower[FFT_N/2+1];
96 
97 /*---------------------------------------------------------------------------*
98     Function Declarations
99  *---------------------------------------------------------------------------*/
100 static void InitializeAllocateSystem(void);
101 static void SetPoint16x16(int objNo, u16 pos_x, u16 pos_y, int charNo, int paletteNo);
102 static void Initialize(void);
103 static BOOL DoConnect(void);
104 static void DoPadSharing(void);
105 static BOOL CheckMicData(void *address);
106 static void ObjSet(int objNo, int x, int y, int charNo, int paletteNo);
107 static void VBlankIntr(void);
108 
109 
110 //================================================================================
111 /*---------------------------------------------------------------------------*
112   Name:         NitroMain / TwlMain
113 
114   Description:  Main.
115 
116   Arguments:    None.
117 
118   Returns:      None.
119  *---------------------------------------------------------------------------*/
120 #ifdef SDK_TWL
TwlMain()121 void TwlMain()
122 #else
123 void NitroMain()
124 #endif
125 {
126     Initialize();
127 
128     // Initialize shared data
129     MI_CpuClear8( gRecvData, sizeof(gRecvData) );
130 
131     // This child checks to see if it is a child started from multiboot
132     isMultiBooted = MB_IsMultiBootChild();
133 
134     if (isMultiBooted)
135     {
136         //--------------------------------------------------------------
137         // If it was started from multi-boot, it will be reset once, and communication will be disconnected.
138         // The child maintains the BssDesc of the parent that booted it. Use this information to reconnect to the parent.
139         //
140         // Here, there is no particular problem with extracting the MAC address from BssDesc, then specifying that MAC address and scanning for and connecting to that parent. However, to connect to the parent directly using the stored BssDesc, it is necessary to set the parent's and child's communications size and transfer mode to match ahead of time.
141         //
142         //
143         //
144         //--------------------------------------------------------------
145 
146         /*
147          * Gets parent data for reconnecting to the parent.
148          * The WMBssDesc used for the connection must be 32-byte aligned.
149          * When reconnecting without using the parent's MAC address to rescan, make the KS/CS flags and the maximum send size match in advance for the parent and the child.
150          * All of these values may be 0 if you rescan before connecting.
151          *
152          */
153         MB_ReadMultiBootParentBssDesc(&mbParentBssDesc,
154                                       WH_PARENT_MAX_SIZE, // Maximum parent send size
155                                       WH_CHILD_MAX_SIZE,  // Maximum child send size
156                                       0,   // Reserved region (always 0)
157                                       0);  // Reserved region (always 0)
158     }
159 
160     while (TRUE)
161     {
162         int connectedFlag = FALSE;
163 
164         // Initialize wireless
165         if (!WH_Initialize())
166         {
167             OS_Panic("WH_Initialize failed.");
168         }
169 
170         connectedFlag = DoConnect();
171         // At this point the transition to the child state is complete
172 
173         if (connectedFlag)
174         {
175             DoPadSharing();
176             // Control does not return here until exiting from communication mode
177         }
178 
179         WH_Finalize();
180     }
181 }
182 
Initialize(void)183 void Initialize(void)
184 {
185     //================ Initialization
186     //---- OS Initialization
187     OS_Init();
188 
189     //---- TP Initialization
190     TP_Init();
191 
192     // Get CalibrateParameter from FlashMemory
193     if (!TP_GetUserInfo(&calibrate))
194     {
195         OS_Panic("FATAL ERROR: can't read UserOwnerInfo\n");
196     }
197     else
198     {
199         OS_Printf("Get Calibration Parameter from NVRAM\n");
200     }
201 
202     TP_SetCalibrateParam(&calibrate);
203 
204     //---- GX Initialization
205     GX_Init();
206 
207     //================ Initial settings
208     //---- All Power ON
209     GX_SetPower(GX_POWER_ALL);
210 
211     //----  Enable V-Blank interrupt
212     (void)OS_SetIrqFunction(OS_IE_V_BLANK, VBlankIntr);
213     (void)OS_EnableIrqMask(OS_IE_V_BLANK);
214     (void)OS_EnableIrq();
215 
216     //---- V-Blank occurence settings
217     (void)GX_VBlankIntr(TRUE);
218 
219     //---- Clear VRAM
220     GX_SetBankForLCDC(GX_VRAM_LCDC_ALL);
221     MI_CpuClearFast((void *)HW_LCDC_VRAM, HW_LCDC_VRAM_SIZE);
222     (void)GX_DisableBankForLCDC();
223 
224     //---- OAM and palette clear
225     MI_CpuFillFast((void *)HW_OAM, 192, HW_OAM_SIZE);
226     MI_CpuClearFast((void *)HW_PLTT, HW_PLTT_SIZE);
227 
228     MI_CpuFillFast((void *)HW_DB_OAM, 192, HW_DB_OAM_SIZE);
229     MI_CpuClearFast((void *)HW_DB_PLTT, HW_DB_PLTT_SIZE);
230 
231     //---- Set bank A for OBJ
232     GX_SetBankForOBJ(GX_VRAM_OBJ_128_A);
233 
234     GX_SetBankForSubOBJ(GX_VRAM_SUB_OBJ_128_D);
235 
236     //---- Set to graphics display mode
237     GX_SetGraphicsMode(GX_DISPMODE_GRAPHICS, GX_BGMODE_0, GX_BG0_AS_2D);
238 
239     //---- Set only OBJ display ON
240     GX_SetVisiblePlane(GX_PLANEMASK_OBJ);
241 
242     //---- Used with 32-KB OBJ in 2D mapping mode
243     GX_SetOBJVRamModeChar(GX_OBJVRAMMODE_CHAR_2D);
244 
245     //---- Data load
246     GX_LoadOBJ( sampleCharData, 0, sizeof(sampleCharData) );
247     GX_LoadOBJPltt( samplePlttData, 0, sizeof(samplePlttData) );
248 
249     GXS_SetGraphicsMode(GX_BGMODE_0);    // BGMODE 0
250 
251     GXS_SetVisiblePlane(GX_PLANEMASK_OBJ);       // Make OBJs visible
252     GXS_SetOBJVRamModeBmp(GX_OBJVRAMMODE_BMP_1D_128K);   // 2D mapping OBJ
253 
254     /* Load character bitmap data */
255     GXS_LoadOBJ( sampleCharData, 0, sizeof(sampleCharData) );
256     GXS_LoadOBJPltt( samplePlttData, 0, sizeof(samplePlttData) );
257 
258     //---- Move hidden OBJ off screen
259     MI_DmaFill32(DMA_NO, oamBak, 0xc0, sizeof(oamBak));
260 
261     //================ Miscellaneous initialization
262 
263     /////////////////////
264     // Memory allocation
265     InitializeAllocateSystem();
266 
267     /////////////////////
268     // Microphone-related initialization
269 
270     //---- MIC Initialization
271     MIC_Init();
272 
273     //---- PMIC Initialization
274     PM_Init();
275     // AMP on
276     (void)PM_SetAmp(PM_AMP_ON);
277     // Adjust AMP gain
278     (void)PM_SetAmpGain(PM_AMPGAIN_80);
279 
280     // Auto sampling settings
281     // Because the memory allocated with OS_Alloc is 32-byte aligned, other memory is not destroyed even if the cache is manipulated.
282     //
283     gMicData = (u8 *)OS_Alloc(SAMPLING_BUFFER_SIZE);
284     // Perform 12-bit sampling to increase the accuracy of Fourier transforms
285     gMicAutoParam.type = MIC_SAMPLING_TYPE_12BIT;
286     gMicAutoParam.buffer = (void *)gMicData;
287     gMicAutoParam.size = SAMPLING_BUFFER_SIZE;
288     gMicAutoParam.loop_enable = TRUE;
289     gMicAutoParam.full_callback = NULL;
290 #ifdef SDK_TWL
291     gMicAutoParam.rate = MIC_SAMPLING_RATE_8180;
292     (void)MIC_StartLimitedSampling(&gMicAutoParam);
293 #else   // ifdef SDK_TWL
294     gMicAutoParam.rate = MIC_SAMPLING_RATE_8K;
295     (void)MIC_StartAutoSampling(&gMicAutoParam);
296 #endif  // ifdef SDK_TWL else
297 
298     /////////////////////
299     // FFT-related
300 
301     MATH_MakeFFTSinTable(sinTable, FFT_NSHIFT);
302 #ifdef USE_FFTREAL
303     MATH_MakeFFTSinTable(sinTable2, FFT_NSHIFT - 1);
304 #endif
305 
306     //Start display
307     OS_WaitVBlankIntr();
308     GX_DispOn();
309 
310     GXS_DispOn();
311 }
312 
DoConnect(void)313 BOOL DoConnect(void)
314 {
315     s32 retry = 0;
316     enum
317     {
318         MAX_RETRY = 5
319     };
320 
321     ObjSet(0, 100, 80, 'C', 4);
322     ObjSet(1, 110, 80, 'O', 4);
323     ObjSet(2, 120, 80, 'N', 4);
324     ObjSet(3, 130, 80, 'N', 4);
325     ObjSet(4, 140, 80, 'E', 4);
326     ObjSet(5, 150, 80, 'C', 4);
327     ObjSet(6, 160, 80, 'T', 4);
328     MI_CpuClear8( (GXOamAttr *)&oamBak[7], sizeof(GXOamAttr) * 12);
329 
330     //---- Wait for V-Blank interrupt completion
331     OS_WaitVBlankIntr();
332 
333     // If this is mulitboot, use the GGID declared by the parent device.
334     //   Normally, this should match MY_GGID.
335     WH_SetGgid(isMultiBooted ? mbParentBssDesc.gameInfo.ggid : MY_GGID);
336     retry = 0;
337 
338     while (TRUE)
339     {
340         // Distributes processes based on communication status
341         switch (WH_GetSystemState())
342         {
343         case WH_SYSSTATE_CONNECT_FAIL:
344             {
345                 // If WM_StartConnect() fails, the WM internal state is invalid. Use WM_Reset to reset the state to the IDLE state.
346                 //
347                 WH_Reset();
348             }
349             break;
350         case WH_SYSSTATE_IDLE:
351             {
352                 if (retry < MAX_RETRY)
353                 {
354                     // Scan for a parent device and connect.
355                     //   When the local device was multibooted, use the parent device BSSID it remembers and connect only to the parent device that was the multiboot source.
356                     //
357                     (void)WH_ChildConnectAuto(WH_CONNECTMODE_DS_CHILD, isMultiBooted ? mbParentBssDesc.bssid : NULL, 0);
358                     retry++;
359                     break;
360                 }
361                 // If connection to parent is not possible after MAX_RETRY, display ERROR
362             }
363         case WH_SYSSTATE_ERROR:
364             return FALSE;
365 
366         case WH_SYSSTATE_DATASHARING:
367             return TRUE;
368 
369         case WH_SYSSTATE_BUSY:
370         case WH_SYSSTATE_SCANNING:
371         case WH_SYSSTATE_CONNECTED:
372         default:
373             break;
374         }
375 
376         OS_WaitVBlankIntr();
377     }
378 
379     // Can't reach here
380     return FALSE;
381 }
382 
DoPadSharing(void)383 void DoPadSharing(void)
384 {
385     int myAid;
386     myAid = WM_GetAID();
387 
388     //================ Main loop
389     while (TRUE)
390     {
391         // Draw Marker by calibrated point
392         while (TP_RequestRawSampling(&raw_point) != 0)
393         {
394         };
395         TP_GetCalibratedPoint(&disp_point, &raw_point);
396 
397         if (disp_point.touch)
398         {
399             SetPoint16x16(0, disp_point.x, disp_point.y, myAid + '0', 1);
400 
401             switch (disp_point.validity)
402             {
403             case TP_VALIDITY_VALID:
404                 OS_TPrintf("( %d, %d ) -> ( %d, %d )\n", raw_point.x, raw_point.y, disp_point.x,
405                            disp_point.y);
406                 break;
407             case TP_VALIDITY_INVALID_X:
408                 OS_TPrintf("( *%d, %d ) -> ( *%d, %d )\n", raw_point.x, raw_point.y, disp_point.x,
409                            disp_point.y);
410                 break;
411             case TP_VALIDITY_INVALID_Y:
412                 OS_TPrintf("( %d, *%d ) -> ( %d, *%d )\n", raw_point.x, raw_point.y, disp_point.x,
413                            disp_point.y);
414                 break;
415             case TP_VALIDITY_INVALID_XY:
416                 OS_TPrintf("( *%d, *%d ) -> ( *%d, *%d )\n", raw_point.x, raw_point.y, disp_point.x,
417                            disp_point.y);
418                 break;
419             }
420         }
421         //---- Wait for V-Blank interrupt completion
422         OS_WaitVBlankIntr();
423 
424         //---- Load pad data
425         keyData = PAD_Read();
426 
427         {
428             static MyPadData sendData ATTRIBUTE_ALIGN(32);
429             int i;
430 
431             // Prepare data to send
432             MI_CpuClear8(&sendData, sizeof(sendData));
433             sendData.keyData = (u16)(keyData | (PAD_DetectFold() ? PAD_DETECT_FOLD_MASK : 0));
434             sendData.touch        = (u8)disp_point.touch;
435             sendData.touchPanel_x = (u8)disp_point.x;
436             sendData.touchPanel_y = (u8)disp_point.y;
437             sendData.mic = (u8)input_mic;
438 
439             if ( WH_StepDS((void*)&sendData) )
440             {
441                 for (i = 0; i < (1 + WH_CHILD_MAX); i++)
442                 {
443                     u8* data;
444 
445                     data = (u8 *)WH_GetSharedDataAdr((u16)i);
446 
447                     if (data != NULL)
448                     {
449                         gRecvFlag[i] = TRUE;
450                         MI_CpuCopy8(data, &gRecvData[i], sizeof(gRecvData[0]));
451                     }
452                     else
453                     {
454                         gRecvFlag[i] = FALSE;
455                     }
456                 }
457             }
458         }
459 
460         //---- Display pad information as OBJ
461         ObjSet(0, 200, 90, 'A', (keyData & PAD_BUTTON_A) ? 1 : 2);
462         ObjSet(1, 180, 95, 'B', (keyData & PAD_BUTTON_B) ? 1 : 2);
463 
464         ObjSet(2, 60, 50, 'L', (keyData & PAD_BUTTON_L) ? 1 : 2);
465         ObjSet(3, 180, 50, 'R', (keyData & PAD_BUTTON_R) ? 1 : 2);
466 
467         ObjSet(4, 60, 80, 'U', (keyData & PAD_KEY_UP) ? 1 : 2);
468         ObjSet(5, 60, 100, 'D', (keyData & PAD_KEY_DOWN) ? 1 : 2);
469         ObjSet(6, 50, 90, 'L', (keyData & PAD_KEY_LEFT) ? 1 : 2);
470         ObjSet(7, 70, 90, 'R', (keyData & PAD_KEY_RIGHT) ? 1 : 2);
471 
472         ObjSet(8, 130, 95, 'S', (keyData & PAD_BUTTON_START) ? 1 : 2);
473         ObjSet(9, 110, 95, 'S', (keyData & PAD_BUTTON_SELECT) ? 1 : 2);
474 
475         ObjSet(10, 200, 75, 'X', (keyData & PAD_BUTTON_X) ? 1 : 2);
476         ObjSet(11, 180, 80, 'Y', (keyData & PAD_BUTTON_Y) ? 1 : 2);
477 
478         //---- Display Folding Detect Status with OBJ
479         ObjSet(12, 120, 30, 'F', (PAD_DetectFold())? 1 : 2);
480 
481         ObjSet(13, 100, 5, 'A', 4);
482         ObjSet(14, 110, 5, 'I', 4);
483         ObjSet(15, 120, 5, 'D', 4);
484         ObjSet(16, 130, 5, (myAid / 10) ? (myAid / 10) + '0' : ' ', 4);
485         ObjSet(17, 140, 5, (myAid % 10) + '0', 4);
486 
487         // Microphone input check
488         input_mic = CheckMicData(MIC_GetLastSamplingAddress());
489         ObjSet(18, 120, 120, 'M', input_mic ? 1 : 2);
490 
491         if( WH_GetSystemState() != WH_SYSSTATE_DATASHARING )
492         {
493             break;
494         }
495 
496         // Render all player's touch information from the received information
497         {
498             int i;
499 
500             for(i = 0; i < WH_CHILD_MAX + 1; i++)
501             {
502                 if( i != myAid && gRecvData[i].touch )
503                 {
504                     SetPoint16x16(i+1, gRecvData[i].touchPanel_x, gRecvData[i].touchPanel_y, '0'+i, 2);
505                 }
506             }
507         }
508     }
509 }
510 
511 //--------------------------------------------------------------------------------
512 //  Set OBJ
513 //
ObjSet(int objNo,int x,int y,int charNo,int paletteNo)514 void ObjSet(int objNo, int x, int y, int charNo, int paletteNo)
515 {
516     G2_SetOBJAttr((GXOamAttr *)&oamBak[objNo],
517                   x,
518                   y,
519                   0,
520                   GX_OAM_MODE_NORMAL,
521                   FALSE,
522                   GX_OAM_EFFECT_NONE, GX_OAM_SHAPE_8x8, GX_OAM_COLOR_16, charNo, paletteNo, 0);
523 }
524 
525 
526 //--------------------------------------------------------------------------------
527 //    V-Blank interrupt process
528 //
VBlankIntr(void)529 void VBlankIntr(void)
530 {
531     //---- OAM updating
532     DC_FlushRange(oamBak, sizeof(oamBak));
533     /* I/O register is accessed using DMA operation, so cache wait is not needed */
534     // DC_WaitWriteBufferEmpty();
535     MI_DmaCopy32(DMA_NO, oamBak, (void *)HW_OAM, sizeof(oamBak));
536 
537     /* Flush cache of OAM buffers to main memory */
538     DC_FlushRange(gOam, sizeof(gOam));
539     /* I/O register is accessed using DMA operation, so cache wait is not needed */
540     // DC_WaitWriteBufferEmpty();
541     GXS_LoadOAM(gOam, 0, sizeof(gOam));
542     MI_DmaFill32(3, gOam, 192, sizeof(gOam));       // Clear OAM buffer
543 
544     //---- Interrupt check flag
545     OS_SetIrqCheckFlag(OS_IE_V_BLANK);
546 }
547 
548 /*---------------------------------------------------------------------------*
549   Name:         InitializeAllocateSystem
550 
551   Description:  Initializes the memory allocation system within the main memory arena.
552 
553   Arguments:    None.
554 
555   Returns:      None.
556  *---------------------------------------------------------------------------*/
InitializeAllocateSystem(void)557 static void InitializeAllocateSystem(void)
558 {
559     void   *tempLo;
560     OSHeapHandle hh;
561 
562     // Based on the premise that OS_Init has been already called
563     tempLo = OS_InitAlloc(OS_ARENA_MAIN, OS_GetMainArenaLo(), OS_GetMainArenaHi(), 1);
564     OS_SetArenaLo(OS_ARENA_MAIN, tempLo);
565     hh = OS_CreateHeap(OS_ARENA_MAIN, OS_GetMainArenaLo(), OS_GetMainArenaHi());
566     if (hh < 0)
567     {
568         OS_Panic("ARM9: Fail to create heap...\n");
569     }
570     hh = OS_SetCurrentHeap(OS_ARENA_MAIN, hh);
571 }
572 
573 /*---------------------------------------------------------------------------*
574   Name:         SetPoint16x16
575 
576   Description:  Displays a 16x16 OBJ on indicated point.
577 
578   Arguments:    x: X position
579                 y: Y position
580 
581   Returns:      None.
582  *---------------------------------------------------------------------------*/
SetPoint16x16(int objNo,u16 pos_x,u16 pos_y,int charNo,int paletteNo)583 static void SetPoint16x16(int objNo, u16 pos_x, u16 pos_y, int charNo, int paletteNo)
584 {
585     G2_SetOBJAttr((GXOamAttr *)&gOam[objNo],
586                   pos_x - 8,
587                   pos_y - 8,
588                   0,
589                   GX_OAM_MODE_NORMAL,
590                   FALSE,
591                   GX_OAM_EFFECT_NONE, GX_OAM_SHAPE_8x8, GX_OAM_COLOR_16, charNo, paletteNo, 0);
592 }
593 
594 /*---------------------------------------------------------------------------*
595   Name:         CheckMicData
596 
597   Arguments:    address: Mic sampling data
598 
599   Returns:      Whether mic input exists.
600  *---------------------------------------------------------------------------*/
CheckMicData(void * address)601 static BOOL CheckMicData(void *address)
602 {
603     s32     i;
604 
605     u16    *p;
606 
607     // If sampling has never been performed, do nothing and stop.
608     // (Because it would delete memory cache(s) unrelated to the microphone)
609     if ((address < gMicData) || (address >= (gMicData + SAMPLING_BUFFER_SIZE)))
610     {
611         return FALSE;
612     }
613 
614     // Apply a FFT for the FFT_N most recent sampling values
615     // With 12bit sampling, each value is two bytes
616     p = (u16 *)((u32)address - (FFT_N-1)*2);
617     if ((u32)p < (u32)gMicData)
618     {
619         p = (u16 *)((u32)p + SAMPLING_BUFFER_SIZE);
620     }
621     DC_InvalidateRange((void *)((u32)p & 0xffffffe0), 32);
622     for (i = 0; i < FFT_N; i++)
623     {
624 #ifdef USE_FFTREAL
625         data[i] = ((*p) << (FX32_SHIFT - (16 - 12)));
626 #else
627         data[i * 2] = ((*p) << (FX32_SHIFT - (16 - 12)));
628         data[i * 2 + 1] = 0; // Substitute 0 in the imaginary part
629 #endif
630         p++;
631         if ((u32)p >= (u32)(gMicData + SAMPLING_BUFFER_SIZE))
632         {
633             p = (u16 *)((u32)p - SAMPLING_BUFFER_SIZE);
634         }
635         if (((u32)p % 32) == 0)
636         {
637             DC_InvalidateRange(p, 32);
638         }
639     }
640 
641 #ifdef USE_FFTREAL
642     MATH_FFTReal(data, FFT_NSHIFT, sinTable, sinTable2);
643 #else
644     MATH_FFT(data, FFT_NSHIFT, sinTable);
645 #endif
646 
647     // Do not calculate above FFT_N/2 because only conjugated, inverted values of those below FFT_N/2 can be obtained
648     for (i = 0; i <= FFT_N/2; i++)
649     {
650         fx32 real;
651         fx32 imm;
652 
653 #ifdef USE_FFTREAL
654         if (0 < i  && i < FFT_N/2)
655         {
656             real = data[i * 2];
657             imm  = data[i * 2 + 1];
658         }
659         else
660         {
661             if (i == 0)
662             {
663                 real = data[0];
664             }
665             else // i == FFT_N/2
666             {
667                 real = data[1];
668             }
669             imm  = 0;
670         }
671 #else
672         real = data[i * 2];
673         imm  = data[i * 2 + 1];
674 #endif
675 
676         // Calculate the power of each frequency
677         // If only the relative value size is necessary, omitting the sqrt can result in a speed increase
678         power[i] = FX_Whole(FX_Sqrt(FX_MUL(real, real) + FX_MUL(imm, imm)));
679 
680         // Record the accumulated time values for the power of each frequency
681         // Perform damping a little at a time
682         smoothedPower[i] += power[i] - (smoothedPower[i]/8);
683     }
684 
685     // Microphone input check
686     {
687         s32 totalPower = 0;
688 
689 #define FILTER_LOW 12    // ((8000 Hz/2)/(FFT_N/2)) * 12 = 187.5 Hz
690 #define FILTER_HIGH 64   // ((8000 Hz/2)/(FFT_N/2)) * 64 = 1000.5 Hz
691         for (i = FILTER_LOW; i < FILTER_HIGH; i++)
692         {
693             totalPower += smoothedPower[i];
694         }
695         //OS_TPrintf("totalPower = %d, ave = %d\n", totalPower, totalPower/(FILTER_HIGH-FILTER_LOW));
696         // Check whether the input value is above a fixed amount
697         if (totalPower > (FILTER_HIGH-FILTER_LOW)*64)
698         {
699             return TRUE;
700         }
701     }
702     return FALSE;
703 }
704 
705 /*====== End of main.c ======*/
706