1 /*---------------------------------------------------------------------------*
2 Project: Revolution PMIC graphic demo
3 File: pmic_graphic.c
4
5 Copyright (C)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 $Log: pmic_graphic.c,v $
14 Revision 1.4.2.3 2009/12/14 00:21:52 aka
15 Fixed misspelling.
16
17 Revision 1.4.2.2 2009/11/19 07:30:49 aka
18 Copied from HEAD.
19
20 Revision 1.7 2009/11/19 07:28:06 aka
21 Removed WPADShutdown() & WPADDisconnect().
22
23 Revision 1.6 2009/10/15 07:48:10 aka
24 Changed from tab to spaces.
25
26 Revision 1.5 2009/10/15 07:45:29 aka
27 Revised error handling of PMICQuit().
28
29 Revision 1.4 2009/04/17 06:34:21 ozeki_kohei
30 Correct behavior for the case that device is detached while PMICQuit().
31
32 Revision 1.3 2009/04/02 01:49:07 dante.treglia
33 * Added shutdown code.
34
35 Revision 1.2 2008/09/10 21:10:36 carlmu
36 Fixed read code to better deal with end-of-buffer wrap-around.
37
38 Revision 1.1 2008/08/06 01:39:14 carlmu
39 Added graphic demo.
40
41 $NoKeywords: $
42 *---------------------------------------------------------------------------*/
43
44 #include <string.h>
45 #include <demo.h>
46 #include <revolution/mem.h>
47 #include <revolution/pmic.h>
48 #include <revolution/wpad.h>
49
50 #include "audio.h"
51
52 BOOL PMICIsUp( void );
53
54 static MEMHeapHandle mem2Heap;
55 static u16 button;
56 static u8* work;
57
58
59 static void* myAlloc ( u32 size );
60 static u8 myFree ( void* ptr );
61 static BOOL usePMIC ( void );
62 static void *procMic ( void* p );
63 static void DrawStuff ( void );
64 static void shutdown ( void );
65
66 /*---------------------------------------------------------------------------*
67 Name: main
68
69 Description: main func.
70
71 Arguments: none.
72
73 Returns: none.
74 *---------------------------------------------------------------------------*/
75
main(void)76 void main(void)
77 {
78 void* arenaMem2Lo;
79 void* arenaMem2Hi;
80 s32 status;
81 u32 type;
82 WPADStatus currWpad;
83 WPADStatus prevWpad;
84 BOOL running = TRUE;
85
86 // init DEMO lib.
87 DEMOInit(NULL);
88 DEMOPadInit();
89
90 OSReport ("\n\n");
91 OSReport ("************************************************\n");
92 OSReport ("pmic_graphic: Graphic demo for Party Mic device\n");
93 OSReport ("************************************************\n");
94 OSReport ("This demo requires the Party Mic and a Wii Remote.\n");
95 OSReport ("The audio captured by the Mic is display onscreen.\n");
96 OSReport ("\n");
97
98 // init MEM lib.
99 arenaMem2Lo = OSGetMEM2ArenaLo();
100 arenaMem2Hi = OSGetMEM2ArenaHi();
101 mem2Heap = MEMCreateExpHeap(arenaMem2Lo, (u32)arenaMem2Hi - (u32) arenaMem2Lo);
102
103 // init Audio libs.
104 AUDIOInit(&mem2Heap, procMic); // xxx
105
106 // init WPAD.
107 WPADRegisterAllocator(myAlloc, myFree);
108 WPADInit();
109
110 // init PMIC work area.
111 work = MEMAllocFromExpHeapEx(mem2Heap, PMIC_MEM2_WORK, 32);
112
113 do
114 {
115 status = WPADGetStatus();
116
117 } while (WPAD_STATE_SETUP != status);
118
119 memset(&currWpad, 0, sizeof(WPADStatus));
120 memset(&prevWpad, 0, sizeof(WPADStatus));
121
122 // spin...
123 while (running)
124 {
125 // read Remote.
126 status = WPADProbe(WPAD_CHAN0, &type);
127
128 if (WPAD_ERR_NONE == status)
129 {
130 WPADRead(WPAD_CHAN0, &currWpad);
131 }
132
133 button = WPADButtonDown(prevWpad.button, currWpad.button);
134 prevWpad = currWpad;
135
136 // operate P-Mic.
137 running = usePMIC();
138
139 DEMOBeforeRender();
140 DrawStuff();
141 DEMODoneRender();
142 }
143
144 shutdown();
145
146 OSHalt("Demo finished!\n");
147 }
148
149 /*---------------------------------------------------------------------------*
150 Name: myAlloc
151
152 Description: allocate memory.
153
154 Arguments: size bytes to allocate.
155
156 Returns: pointer.
157 *---------------------------------------------------------------------------*/
158
myAlloc(u32 size)159 static void* myAlloc(u32 size)
160 {
161 void *ptr;
162
163 ptr = MEMAllocFromExpHeap(mem2Heap, size);
164
165 return ptr;
166 }
167
168 /*---------------------------------------------------------------------------*
169 Name: myFree
170
171 Description: free memory.
172
173 Arguments: ptr pointer to free.
174
175 Returns: always 1.
176 *---------------------------------------------------------------------------*/
177
myFree(void * ptr)178 static u8 myFree(void* ptr)
179 {
180 MEMFreeToExpHeap(mem2Heap, ptr);
181
182 return 1;
183 }
184
185 /*---------------------------------------------------------------------------*
186 *---------------------------------------------------------------------------*
187 * very simple P-Mic operation... *
188 *---------------------------------------------------------------------------*
189 *---------------------------------------------------------------------------*/
190
191 enum
192 {
193 STATE_NOT_INITIALIZED=0,
194 STATE_NOT_READY,
195 STATE_PROBE,
196 STATE_OPEN,
197 STATE_WAIT_FOR_OPEN,
198 STATE_STOPPED,
199 STATE_START,
200 STATE_WAIT_FOR_START,
201 STATE_RUN,
202 STATE_DO_STOP,
203 STATE_WAIT_FOR_STOP,
204
205 STATE_CLOSE,
206 STATE_WAIT_FOR_CLOSE,
207 STATE_WAIT_REOPEN,
208 STATE_QUIT,
209 STATE_WAIT_RESTART,
210 STATE_DIE
211 };
212
213 static u32 state = STATE_NOT_INITIALIZED;
214
215 static volatile PMIC_ERR errCode;
216
217 static void funcCb ( PMIC_ERR result, void* arg );
218
219 /*---------------------------------------------------------------------------*
220 Name: PMICIsUp
221
222 Description: Indicates if P-Mic library has been initialized
223
224 Arguments: none.
225
226 Returns: BOOL indicating whether P-Mic library has been initialized
227 *---------------------------------------------------------------------------*/
228
PMICIsUp(void)229 BOOL PMICIsUp(void)
230 {
231 if ( state == STATE_NOT_INITIALIZED ||
232 state == STATE_QUIT ||
233 state == STATE_WAIT_RESTART )
234 {
235 return FALSE;
236 }
237
238 return TRUE;
239 }
240
241 /*---------------------------------------------------------------------------*
242 Name: usePMIC
243
244 Description: operate P-Mic.
245
246 Arguments: none.
247
248 Returns: BOOL indicating whether to continue running or not.
249 *---------------------------------------------------------------------------*/
250
usePMIC(void)251 static BOOL usePMIC(void)
252 {
253 static s32 count = 0;
254
255 PMIC_ERR retval;
256
257 switch (state)
258 {
259
260 case STATE_NOT_INITIALIZED:
261
262 OSReport("Starting init lib.\n");
263
264 retval = PMICInit(work);
265
266 if (retval == PMIC_ERR_OK)
267 {
268 OSReport("(%ld) ok to call PMICInit() -> %ld\n", count++, retval);
269 state = STATE_NOT_READY;
270 }
271 else
272 {
273 OSReport("(%ld) fail to call PMICInit() -> %ld\n", count++, retval);
274 state = STATE_DIE; // fatal
275 }
276 break;
277
278 case STATE_NOT_READY:
279
280 OSReport("Insert P-Mic, or Push B to quit lib.\n");
281 state = STATE_PROBE;
282 break;
283
284 case STATE_PROBE:
285
286 retval = PMICProbe();
287
288 if (retval == PMIC_ERR_OK)
289 {
290 OSReport("(%ld) P-Mic is inserted -> %ld\n", count++, retval);
291 state = STATE_OPEN;
292 }
293 else if (retval != PMIC_ERR_NO_DEVICE)
294 {
295 OSReport("(%ld) fail to call PMICProbe() -> %ld\n", count++, retval);
296 state = STATE_DIE;
297 }
298 else if (button & WPAD_BUTTON_B)
299 {
300 state = STATE_QUIT;
301 }
302 break;
303
304 case STATE_OPEN:
305
306 OSReport("Got P-Mic device. Opening.\n");
307
308 errCode = (PMIC_ERR)(PMIC_ERR_OK + 1);
309 retval = PMICOpenAsync(funcCb, NULL);
310
311 if (retval == PMIC_ERR_OK)
312 {
313 OSReport("(%ld) ok to call PMICOpenAsync() -> %ld\n", count++, retval);
314 state = STATE_WAIT_FOR_OPEN;
315 }
316 else
317 {
318 OSReport("(%ld) fail to call PMICOpenAsync() -> %ld\n", count++, retval);
319 errCode = PMIC_ERR_OK;
320 state = STATE_NOT_READY; // probe again
321 }
322 break;
323
324 case STATE_WAIT_FOR_OPEN:
325
326 // errCode comes from the PMICOpenAsync callback
327 if (errCode > PMIC_ERR_OK) // still waiting?
328 {
329 break;
330 }
331 else if (errCode == PMIC_ERR_OK)
332 {
333 OSReport("(%ld) ok to open P-Mic -> %ld.\n", count++, errCode);
334 state = STATE_STOPPED;
335 OSReport("Push A to start P-Mic, or B to close P-Mic, or Remove P-Mic.\n");
336 }
337 else
338 {
339 OSReport("(%ld) fail to open P-Mic -> %ld.\n", count++, errCode);
340 state = STATE_NOT_READY; // probe again
341 }
342 break;
343
344 case STATE_STOPPED:
345
346 retval = PMICProbe();
347
348 if (retval == PMIC_ERR_NO_DEVICE)
349 {
350 OSReport("(%ld) P-Mic is removed -> %ld\n", count++, retval);
351 state = STATE_NOT_READY;
352 }
353 else if (retval != PMIC_ERR_OK)
354 {
355 OSReport("(%ld) fail to call PMICProbe() -> %ld\n", count++, retval);
356 state = STATE_DIE;
357 }
358 else
359 {
360 state = STATE_START;
361 }
362 break;
363
364 case STATE_START:
365
366 errCode = (PMIC_ERR)(PMIC_ERR_OK + 1);
367 retval = PMICStartAsync(funcCb, NULL);
368
369 if (retval == PMIC_ERR_OK)
370 {
371 OSReport("(%ld) ok to call PMICStartAsync() -> %ld\n", count++, retval);
372 state = STATE_WAIT_FOR_START;
373 }
374 else
375 {
376 OSReport("(%ld) fail to call PMICStartAsync() -> %ld\n", count++, retval);
377 errCode = PMIC_ERR_OK;
378 state = STATE_NOT_READY;
379 }
380 break;
381
382 case STATE_WAIT_FOR_START:
383
384 // errCode is returned by PMICStartAsync callback
385 if (errCode > PMIC_ERR_OK) // still waiting?
386 {
387 break;
388 }
389 else if (errCode == PMIC_ERR_OK)
390 {
391 OSReport("(%ld) ok to start P-Mic -> %ld.\n", count++, errCode);
392 state = STATE_RUN;
393 OSReport("Push A to stop P-Mic, or B to close P-Mic, or Remove P-Mic.\n");
394 }
395 else
396 {
397 OSReport("(%ld) fail to start P-Mic -> %ld.\n", count++, errCode);
398 state = STATE_NOT_READY;
399 }
400 break;
401
402 case STATE_RUN:
403
404 retval = PMICProbe();
405
406 if (retval == PMIC_ERR_NO_DEVICE)
407 {
408 OSReport("(%ld) P-Mic is removed -> %ld\n", count++, retval);
409 state = STATE_NOT_READY;
410 }
411 else if (retval != PMIC_ERR_OK)
412 {
413 OSReport("(%ld) fail to call PMICProbe() -> %ld\n", count++, retval);
414 state = STATE_DIE;
415 }
416 else if (button & WPAD_BUTTON_A)
417 {
418 // AUDIOStartPlay(); // start playing p-mic data
419 state = STATE_DO_STOP;
420 }
421 else if (button & WPAD_BUTTON_B)
422 {
423 state = STATE_CLOSE;
424 }
425 break;
426
427 case STATE_DO_STOP:
428
429 errCode = (PMIC_ERR)(PMIC_ERR_OK + 1);
430 retval = PMICStopAsync(funcCb, NULL);
431
432 if (retval == PMIC_ERR_OK)
433 {
434 OSReport("(%ld) ok to call PMICStopAsync() -> %ld\n", count++, retval);
435 state = STATE_WAIT_FOR_STOP;
436 }
437 else
438 {
439 OSReport("(%ld) fail to call PMICStopAsync() -> %ld\n", count++, retval);
440 errCode = PMIC_ERR_OK;
441 state = STATE_NOT_READY;
442 }
443 break;
444
445 case STATE_WAIT_FOR_STOP:
446
447 // errCode comes from the PMICStopAsync callback
448 if (errCode > PMIC_ERR_OK) // still waiting?
449 {
450 break;
451 }
452 else if (errCode == PMIC_ERR_OK)
453 {
454 OSReport("(%ld) ok to stop P-Mic -> %ld.\n", count++, errCode);
455 state = STATE_STOPPED;
456 }
457 else
458 {
459 OSReport("(%ld) fail to stop P-Mic -> %ld.\n", count++, errCode);
460 state = STATE_NOT_READY;
461 }
462 break;
463
464 case STATE_CLOSE:
465
466 errCode = (PMIC_ERR)(PMIC_ERR_OK + 1);
467 retval = PMICCloseAsync(funcCb, NULL);
468
469 if (retval == PMIC_ERR_OK)
470 {
471 OSReport("(%ld) ok to call PMICCloseAsync() -> %ld\n", count++, retval);
472 state = STATE_WAIT_FOR_CLOSE;
473 }
474 else
475 {
476 OSReport("(%ld) fail to call PMICCloseAsync() -> %ld\n", count++, retval);
477 errCode = PMIC_ERR_OK;
478 state = STATE_NOT_READY;
479 }
480 break;
481
482 case STATE_WAIT_FOR_CLOSE:
483
484 // errCode comes from the PMICCloseAsync callback
485 if (errCode > PMIC_ERR_OK) // still waiting?
486 {
487 break;
488 }
489 else if (errCode == PMIC_ERR_OK)
490 {
491 OSReport("(%ld) ok to close P-Mic -> %ld.\n", count++, errCode);
492 state = STATE_WAIT_REOPEN;
493 OSReport("Push A to open P-Mic, or B to call PMICQuit.\n");
494 }
495 else
496 {
497 OSReport("(%ld) fail to close P-Mic -> %ld.\n", count++, errCode);
498 state = STATE_NOT_READY; // or DIE?
499 }
500 break;
501
502 case STATE_WAIT_REOPEN:
503
504 if (button & WPAD_BUTTON_A)
505 {
506 state = STATE_OPEN;
507 }
508 else if (button & WPAD_BUTTON_B)
509 {
510 state = STATE_QUIT;
511 }
512 break;
513
514 case STATE_QUIT:
515
516 retval = PMICQuit();
517
518 if (retval == PMIC_ERR_OK)
519 {
520 OSReport("(%ld) ok to call PMICQuit() -> %ld\n", count++, retval);
521 state = STATE_WAIT_RESTART;
522 OSReport("Push A to init P-Mic, or B to quit application.\n");
523 }
524 else
525 {
526 OSReport("(%ld) fail to call PMICQuit() -> %ld\n", count++, retval);
527 state = STATE_DIE;
528 }
529 break;
530
531 case STATE_WAIT_RESTART:
532
533 if (button & WPAD_BUTTON_A)
534 {
535 state = STATE_NOT_INITIALIZED;
536 }
537 else if (button & WPAD_BUTTON_B)
538 {
539 state = STATE_DIE;
540 }
541 break;
542
543 case STATE_DIE:
544
545 OSReport("Quitting application\n");
546
547 return FALSE;
548 }
549
550 return TRUE;
551 }
552
553 /*---------------------------------------------------------------------------*
554 Name: funcCb
555
556 Description: callback for PMICOpen/Close/Start/StopAsync
557
558 Arguments: result error code.
559 arg (not used.)
560
561 Returns: none.
562 *---------------------------------------------------------------------------*/
563
funcCb(PMIC_ERR result,void * arg)564 static void funcCb(PMIC_ERR result, void* arg)
565 {
566 #pragma unused(arg)
567
568 errCode = result;
569 }
570
571 /*---------------------------------------------------------------------------*
572 Graphics-related functions
573 *---------------------------------------------------------------------------*/
574
575 #define FRAMES_TO_DISPLAY 333
576 #define AUDIO_SAMPLES_PER_FRAME (16 * 3) // 16KHz (mono) x 3msec
577 #define AUDIO_SAMPLES_TOTAL (AUDIO_SAMPLES_PER_FRAME * FRAMES_TO_DISPLAY)
578 #define AUDIO_BYTES_PER_FRAME (AUDIO_SAMPLES_PER_FRAME * 2) // 16-bit
579 #define AUDIO_BYTES_TOTAL (AUDIO_BYTES_PER_FRAME * FRAMES_TO_DISPLAY)
580
581 #define SCREEN_WIDTH 640
582 #define SCREEN_HEIGHT 480
583
584 // Don't use parentheses here:
585 #define AUDIO_SCALE 1/4
586
587 const GXColor LIME = { 0, 255, 0, 255 };
588 const GXColor AQUA = { 0, 255, 255, 255 };
589 const GXColor MAGENTA = { 255, 0, 255, 255 };
590 const GXColor GRAY = { 128, 128, 128, 255 };
591 const GXColor WHITE = { 255, 255, 255, 255 };
592 const GXColor TEAL = { 0, 128, 128, 255 };
593 const GXColor ORANGE = { 255, 165, 0, 255 };
594 const GXColor PINK = { 255, 192, 203, 255 };
595 const GXColor GREEN = { 0, 128, 0, 255 };
596
597 static s16* srcBuff = 0;
598 static u32 currPos = 0;
599
600 /*---------------------------------------------------------------------------*
601 Name: procMic
602
603 Description: Thread process that runs to process microphone input.
604 It is started by AUDIOInit. It should initialize itself
605 and then sleep. It will be woken periodically by the audio
606 callback. It just gathers the samples and stores them in a
607 local buffer.
608
609 Arguments: p (not used.)
610
611 Returns: none.
612 *---------------------------------------------------------------------------*/
613
procMic(void * p)614 static void *procMic(void* p)
615 {
616 #pragma unused(p)
617
618 s32 reqSamples;
619 s32 gotSamples;
620 s32 space;
621
622 // Initialize
623
624 srcBuff = myAlloc(AUDIO_BYTES_TOTAL);
625 memset(srcBuff, 0, AUDIO_BYTES_TOTAL);
626
627 currPos = 0;
628
629 while (1)
630 {
631 // Wait for data to arrive
632 AUDIOSleepThread();
633
634 // get P-Mic's data (16KHz).
635 space = AUDIO_SAMPLES_TOTAL - (s32) currPos;
636 reqSamples = AUDIO_SAMPLES_PER_FRAME < space ? AUDIO_SAMPLES_PER_FRAME : space;
637 gotSamples = PMICRead(&srcBuff[currPos], reqSamples);
638
639 if (gotSamples > 0)
640 {
641 currPos += gotSamples;
642
643 if (currPos == AUDIO_SAMPLES_TOTAL)
644 {
645 currPos = 0;
646 reqSamples = AUDIO_SAMPLES_PER_FRAME - space;
647
648 if (reqSamples > 0)
649 {
650 gotSamples = PMICRead(&srcBuff[currPos], reqSamples);
651
652 if (gotSamples > 0)
653 {
654 currPos += gotSamples;
655 }
656 }
657 }
658 }
659 }
660 }
661
662 /*---------------------------------------------------------------------------*
663 Name: drawMic
664
665 Description: This is called by the main routine. It draws a graphic
666 representation of the audio samples using GX.
667
668 Arguments: none.
669
670 Returns: none.
671 *---------------------------------------------------------------------------*/
672
DrawStuff(void)673 static void DrawStuff(void)
674 {
675 s32 i;
676 Mtx mv;
677
678 // Make sure we don't try to draw before buffer is initialized.
679 // This shouldn't happen, but we check to be safe.
680 if (srcBuff == 0)
681 {
682 return;
683 }
684
685 // Initialize graphics state
686
687 DEMOInitCaption( DM_FT_XLU, SCREEN_WIDTH, SCREEN_HEIGHT );
688
689 MTXIdentity(mv);
690 GXLoadPosMtxImm(mv, GX_PNMTX0);
691 GXSetCurrentMtx(GX_PNMTX0);
692
693 GXSetNumChans(1);
694 GXSetNumTevStages(1);
695 GXSetNumTexGens(0);
696 GXSetTevOp(GX_TEVSTAGE0, GX_PASSCLR);
697 GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR0A0);
698
699 GXClearVtxDesc();
700 GXSetVtxDesc(GX_VA_POS, GX_DIRECT);
701 GXSetVtxDesc(GX_VA_CLR0, GX_DIRECT);
702 GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_S16, 0);
703 GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0);
704
705 // Draw the base line
706
707 #define LINE_CLR WHITE
708
709 GXBegin(GX_LINES, GX_VTXFMT0, 2);
710 GXPosition2s16(0, SCREEN_HEIGHT/2);
711 GXColor4u8(LINE_CLR.r, LINE_CLR.g, LINE_CLR.b, LINE_CLR.a);
712 GXPosition2s16(SCREEN_WIDTH, SCREEN_HEIGHT/2);
713 GXColor4u8(LINE_CLR.r, LINE_CLR.g, LINE_CLR.b, LINE_CLR.a);
714 GXEnd();
715
716 // Draw the graph
717
718 #define DATA_CLR LIME
719
720 GXBegin(GX_LINES, GX_VTXFMT0, AUDIO_SAMPLES_TOTAL);
721 for (i = 0; i < AUDIO_SAMPLES_TOTAL; i++)
722 {
723 s16 sample;
724
725 sample = srcBuff[(i+currPos)%AUDIO_SAMPLES_TOTAL];
726 // If you want the display to wrap instead of scroll, use this:
727 // sample = srcBuff[i];
728
729 GXPosition2s16((s16)(i*SCREEN_WIDTH/AUDIO_SAMPLES_TOTAL),
730 (s16)(sample*AUDIO_SCALE+SCREEN_HEIGHT/2));
731
732 GXColor4u8(DATA_CLR.r, DATA_CLR.g, DATA_CLR.b, DATA_CLR.a);
733 }
734
735 GXEnd();
736 }
737
738 /*---------------------------------------------------------------------------*
739 Name: shutdown
740
741 Description: close libraries.
742
743 Arguments: none.
744
745 Returns: none.
746 *---------------------------------------------------------------------------*/
747
shutdown(void)748 static void shutdown(void)
749 {
750 PMIC_ERR retval;
751
752 // Wait for any pending microphone transaction
753 while (errCode > PMIC_ERR_OK)
754 {
755 OSYieldThread();
756 }
757
758 // Make sure the microphone is stopped
759 errCode = (PMIC_ERR)(PMIC_ERR_OK + 1);
760 retval = PMICStopAsync(funcCb, NULL);
761 if (retval == PMIC_ERR_OK)
762 {
763 while (errCode > PMIC_ERR_OK)
764 {
765 OSYieldThread();
766 }
767 }
768
769 // Close the microphone
770 errCode = (PMIC_ERR)(PMIC_ERR_OK + 1);
771 retval = PMICCloseAsync(funcCb, NULL);
772 if (retval == PMIC_ERR_OK)
773 {
774 while (errCode > PMIC_ERR_OK)
775 {
776 OSYieldThread();
777 }
778 }
779
780 // Kill audio
781 AUDIOQuit();
782
783 // Close PMIC library
784 PMICQuit();
785
786 // Free the PMIC work area
787 MEMFreeToExpHeap(mem2Heap, work);
788 }
789