1 /*---------------------------------------------------------------------------*
2   Project:  TwlSDK - demos.TWL - snd - ignoreHWVolumeDemo
3   File:     main.c
4 
5   Copyright 2009 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:: 2009-09-24#$
14   $Rev: 11063 $
15   $Author: okubata_ryoma $
16  *---------------------------------------------------------------------------*/
17 
18 /*---------------------------------------------------------------------------*
19     A sample that makes a sound during setup.
20 
21     USAGE:
22         UP, DOWN: Switch item to control.
23         LEFT, RIGHT: Set configuration related to selected item.
24         A: Set SND Alarm.
25 
26  *---------------------------------------------------------------------------*/
27 
28 #include    <twl.h>
29 #include    "font.h"
30 #include    "snd_data.h"
31 #include    "DEMO.h"
32 
33 /*---------------------------------------------------------------------------*
34     Constant definitions
35  *---------------------------------------------------------------------------*/
36 #define     INDEX_MAX   5
37 
38 #define     TIMER_MIN   1
39 #define     TIMER_MAX  10
40 
41 /*---------------------------------------------------------------------------*
42     Internal Variable Definitions
43  *---------------------------------------------------------------------------*/
44 static u16      screen[32 * 32] ATTRIBUTE_ALIGN(HW_CACHE_LINE_SIZE);
45 static s32      index   =   0;
46 
47 static SNDEXMute        mute     =   SNDEX_MUTE_OFF;
48 static u8               rate     =   0;
49 static u8               volume   =   0;
50 static u8               alarmVol =   0;
51 static u8               timer    =   5;
52 
53 static BOOL             isSetAlarm           = FALSE;
54 static BOOL             isCalledAlarmHandler = FALSE;
55 static BOOL             isPlayingSound       = FALSE;
56 
57 /*---------------------------------------------------------------------------*
58     Internal Function Definitions
59  *---------------------------------------------------------------------------*/
60 static BOOL     CheckResult     (SNDEXResult result);
61 static void     UpdateScreen    (void);
62 static void     VBlankIntr      (void);
63 static void     PrintString     (s16 x, s16 y, u8 palette, char *text, ...);
64 static void     VolumeSwitchCallback(SNDEXResult result, void* arg);
65 static void     AlarmHandler    (void* arg);
66 
67 /*---------------------------------------------------------------------------*
68   Name:         TwlMain
69 
70   Description:  Initialization and main loop.
71 
72   Arguments:    None.
73 
74   Returns:      None.
75  *---------------------------------------------------------------------------*/
76 void
TwlMain(void)77 TwlMain (void)
78 {
79     /* Initialization */
80     OS_Init();
81     GX_Init();
82     GX_DispOff();
83     GXS_DispOff();
84 
85     // When in NITRO mode, stopped by Panic
86     DEMOCheckRunOnTWL();
87 
88     /* Initializes display settings */
89     GX_SetBankForLCDC(GX_VRAM_LCDC_ALL);
90     MI_CpuClearFast((void*)HW_LCDC_VRAM, HW_LCDC_VRAM_SIZE);
91     (void)GX_DisableBankForLCDC();
92     MI_CpuFillFast((void*)HW_OAM, GX_LCD_SIZE_Y, HW_OAM_SIZE);
93     MI_CpuClearFast((void*)HW_PLTT, HW_PLTT_SIZE);
94     MI_CpuFillFast((void*)HW_DB_OAM, GX_LCD_SIZE_Y, HW_DB_OAM_SIZE);
95     MI_CpuClearFast((void*)HW_DB_PLTT, HW_DB_PLTT_SIZE);
96     GX_SetBankForBG(GX_VRAM_BG_128_A);
97     G2_SetBG0Control(GX_BG_SCRSIZE_TEXT_256x256,
98             GX_BG_COLORMODE_16,
99             GX_BG_SCRBASE_0xf800,      // SCR base block 31
100             GX_BG_CHARBASE_0x00000,    // CHR base block 0
101             GX_BG_EXTPLTT_01);
102     G2_SetBG0Priority(0);
103     GX_SetGraphicsMode(GX_DISPMODE_GRAPHICS, GX_BGMODE_0, GX_BG0_AS_2D);
104     GX_SetVisiblePlane(GX_PLANEMASK_BG0);
105     GX_LoadBG0Char(d_CharData, 0, sizeof(d_CharData));
106     GX_LoadBGPltt(d_PaletteData, 0, sizeof(d_PaletteData));
107     MI_CpuClearFast((void*)screen, sizeof(screen));
108     DC_FlushRange(screen, sizeof(screen));
109     GX_LoadBG0Scr(screen, 0, sizeof(screen));
110 
111     /* Interrupt settings */
112     OS_SetIrqFunction(OS_IE_V_BLANK, VBlankIntr);
113     (void)OS_EnableIrqMask(OS_IE_V_BLANK);
114     (void)GX_VBlankIntr(TRUE);
115     (void)OS_EnableIrq();
116     (void)OS_EnableInterrupts();
117 
118     /* Initialize sound */
119     SND_Init();
120     SND_AssignWaveArc((SNDBankData*)sound_bank_data, 0, (SNDWaveArc*)sound_wave_data);
121 
122     /* Initialize extended sound features */
123     SNDEX_Init();
124     (void)CheckResult(SNDEX_GetMute(&mute));
125     (void)CheckResult(SNDEX_GetDSPMixRate(&rate));
126     (void)CheckResult(SNDEX_GetVolume(&volume));
127     UpdateScreen();
128 
129     /* Set the callback function for when the volume button is pressed */
130     SNDEX_SetVolumeSwitchCallback(VolumeSwitchCallback, NULL);
131 
132     /* LCD display start */
133     GX_DispOn();
134     GXS_DispOn();
135 
136     {
137         u16     keyOld  =   PAD_Read();
138         u16     keyTrg;
139         u16     keyNow;
140 
141         /* Main loop */
142         while (TRUE)
143         {
144             /* Get key input data */
145             keyNow  =   PAD_Read();
146             keyTrg  =   (u16)((keyOld ^ keyNow) & keyNow);
147             keyOld  =   keyNow;
148 
149             /* Perform various operations in accordance with input */
150             if (keyTrg & PAD_KEY_UP)
151             {
152                 index   =   (index + INDEX_MAX - 1) % INDEX_MAX;
153             }
154             if (keyTrg & PAD_KEY_DOWN)
155             {
156                 index   =   (index + 1) % INDEX_MAX;
157             }
158             if (keyTrg & PAD_KEY_RIGHT)
159             {
160                 switch (index)
161                 {
162                 case 0: // Mute control
163                     if (mute == SNDEX_MUTE_OFF)
164                     {
165                         if (CheckResult(SNDEX_SetMute(SNDEX_MUTE_ON)) == TRUE)
166                         {
167                             mute    =   SNDEX_MUTE_ON;
168                         }
169                     }
170                     break;
171                 case 1: // DSP mix rate control
172                     if (rate < SNDEX_DSP_MIX_RATE_MAX)
173                     {
174                         if (CheckResult(SNDEX_SetDSPMixRate((u8)(rate + 1))) == TRUE)
175                         {
176                             rate ++;
177                         }
178                     }
179                     break;
180                 case 2: // Volume control
181                     if (volume < SNDEX_VOLUME_MAX)
182                     {
183                         if (CheckResult(SNDEX_SetVolume((u8)(volume + 1))) == TRUE)
184                         {
185                             volume ++;
186                         }
187                     }
188                     break;
189                 case 3: // Alarm Volume control
190                     if (alarmVol < SNDEX_VOLUME_MAX)
191                     {
192                         alarmVol++;
193                     }
194                     break;
195                 case 4: // Timer for Alarm
196                     if (timer < TIMER_MAX)
197                     {
198                         timer++;
199                     }
200                     break;
201                 }
202             }
203             if (keyTrg & PAD_KEY_LEFT)
204             {
205                 switch (index)
206                 {
207                 case 0: // Mute control
208                     if (mute == SNDEX_MUTE_ON)
209                     {
210                         if (CheckResult(SNDEX_SetMute(SNDEX_MUTE_OFF)) == TRUE)
211                         {
212                             mute    =   SNDEX_MUTE_OFF;
213                         }
214                     }
215                     break;
216                 case 1: // DSP mix rate control
217                     if (rate > SNDEX_DSP_MIX_RATE_MIN)
218                     {
219                         if (CheckResult(SNDEX_SetDSPMixRate((u8)(rate - 1))) == TRUE)
220                         {
221                             rate --;
222                         }
223                     }
224                     break;
225                 case 2: // Volume control
226                     if (volume > SNDEX_VOLUME_MIN)
227                     {
228                         if (CheckResult(SNDEX_SetVolume((u8)(volume - 1))) == TRUE)
229                         {
230                             volume --;
231                         }
232                     }
233                     break;
234                 case 3: // Alarm Volume control
235                     if (alarmVol > SNDEX_VOLUME_MIN)
236                     {
237                         alarmVol--;
238                     }
239                     break;
240                 case 4: // Timer for Alarm
241                     if (timer > TIMER_MIN)
242                     {
243                         timer--;
244                     }
245                     break;
246                 }
247             }
248             if (keyTrg & PAD_BUTTON_A)
249             {
250                 /* Either configure a sound to play after a fixed time or stop a sound while it is being played */
251                 if (isPlayingSound)
252                 {
253                     u32 tag;
254                     // Stop the sound (wait for processing to finish)
255                     SND_StopSeq(0);
256                     tag = SND_GetCurrentCommandTag();
257                     (void)SND_FlushCommand(SND_COMMAND_BLOCK | SND_COMMAND_IMMEDIATE);
258                     SND_WaitForCommandProc(tag);
259 
260                     // Wait until sound playback has completely stopped
261                     // The wait time indicated below is not necessarily optimal because it is affected by changes to the volume when SNDEX_ResetIgnoreHWVolume() is run.
262                     //
263                     OS_SpinWait(67 * 1000 * 700); //Approximately 700 ms
264 
265                     // Retry until the volume is successfully reset
266                     while (SNDEX_ResetIgnoreHWVolume() != SNDEX_RESULT_SUCCESS) {}
267 
268                     OS_TPrintf("stop sequence.\n");
269                     isPlayingSound = FALSE;
270                 }
271                 else if (!isSetAlarm)
272                 {
273                     SND_SetupAlarm(0, (u32)OS_SecondsToTicks(timer), 0, AlarmHandler, NULL);
274                     SND_StartTimer(0, 0, 1, 0);
275                     isSetAlarm = TRUE;
276                     OS_TPrintf("set alarm. (%d sec)\n", timer);
277                 }
278             }
279 
280             if (isCalledAlarmHandler)
281             {
282                 // Retry until the volume is successfully changed
283                 while (SNDEX_SetIgnoreHWVolume(alarmVol) != SNDEX_RESULT_SUCCESS) {}
284 
285                 // After the volume is set, it is changed gradually to mitigate popping sounds caused by a sudden shift in volume. This will result in a delay of up to 32 ms, so a wait is inserted here.
286                 //
287                 OS_SpinWait(67 * 1000 * 32);  // Approximately 32 ms
288 
289                 SND_StartSeq(0, sound_seq_data, 0, (SNDBankData*)sound_bank_data);
290                 OS_TPrintf("start sequence.\n");
291                 isCalledAlarmHandler = FALSE;
292                 isPlayingSound = TRUE;
293             }
294 
295             /* Main sound processing */
296             while (SND_RecvCommandReply(SND_COMMAND_NOBLOCK) != NULL)
297             {
298             }
299             (void)SND_FlushCommand(SND_COMMAND_NOBLOCK);
300 
301             /* Make the 'volume' value change in response to changes in audio volume */
302             if (!isCalledAlarmHandler && !isPlayingSound)
303             {
304                 (void)CheckResult(SNDEX_GetVolume(&volume));
305             }
306 
307             /* Screen update */
308             UpdateScreen();
309 
310             /* Wait for V-Blank */
311             OS_WaitVBlankIntr();
312         }
313     }
314 }
315 
316 /*---------------------------------------------------------------------------*
317   Name:         CheckResult
318 
319   Description:  Confirms the results of SNDEX library function calls.
320 
321   Arguments:    result: Value returned from the function
322 
323   Returns:      BOOL: Returns TRUE when the function call was successful.
324                             Returns FALSE if there was a failure for any reason.
325  *---------------------------------------------------------------------------*/
326 static BOOL
CheckResult(SNDEXResult result)327 CheckResult (SNDEXResult result)
328 {
329     switch (result)
330     {
331     case SNDEX_RESULT_SUCCESS:
332         return TRUE;
333     case SNDEX_RESULT_BEFORE_INIT:
334         /* Before library initialization.
335             The SNDEX_Init function must be called before any other functions. */
336         OS_TWarning("Not initialized.\n");
337         break;
338     case SNDEX_RESULT_INVALID_PARAM:
339         /* Unknown parameter.
340             Review the argument(s) being passed to the function. */
341         OS_TWarning("Invalid parameter.\n");
342         break;
343     case SNDEX_RESULT_EXCLUSIVE:
344         /* Mutex is in effect.
345             Do not make consecutive calls of asynchronous functions or use the SNDEX library asynchronously from multiple threads.
346              */
347         OS_TWarning("Overlapped requests.\n");
348         break;
349     case SNDEX_RESULT_ILLEGAL_STATE:
350         /* Abnormal state.
351             The SNDEX library can only be used with TWL hardware.
352             Synchronous functions can't be called from within exception handlers.
353             When the CODEC is in DS Mode, you cannot switch to forced audio output.
354             When the CODEC is in DS Mode, when audio output is being forced, or when the microphone is auto-sampling, the I2S frequency cannot be changed.
355              */
356         OS_TWarning("In unavailable state.\n");
357         break;
358     case SNDEX_RESULT_PXI_SEND_ERROR:
359         /* PXI send error.
360             The data sending queue from ARM9 to ARM7 is full. Please wait a little before retrying so that ARM7 can delete data within the queue.
361              */
362         OS_TWarning("PXI queue full.\n");
363         break;
364     case SNDEX_RESULT_DEVICE_ERROR:
365         /* Failed to operate device.
366             An attempt was made to access an external CPU device (the microprocessor) to get or change volume, but that access failed.
367             If conditions don't improve even after multiple retries, consider it a success and move on, since the problem is likely caused by a runaway microprocessor, malfunctioning hardware, or some other thing not recoverable by software.
368 
369              */
370         OS_TWarning("Micro controller unusual.\n");
371         return TRUE;
372     case SNDEX_RESULT_FATAL_ERROR:
373     default:
374         /* Fatal error.
375             By the library's logic, this cannot occur.
376             It's possible this occurred (1) when a PXI command was directly issued to ARM7 (ignoring the library's internal status management), (2) when memory damage resulted in inconsistencies in internal status management, or (3) when this was combined with an unexpected ARM7 component, or for some similar reason.
377 
378              */
379         OS_TWarning("Fatal error.\n");
380         break;
381     }
382     return FALSE;
383 }
384 
385 /*---------------------------------------------------------------------------*
386   Name:         UpdateScreen
387 
388   Description:  Updates the displayed screen content.
389 
390   Arguments:    None.
391 
392   Returns:      None.
393  *---------------------------------------------------------------------------*/
394 static void
UpdateScreen(void)395 UpdateScreen (void)
396 {
397     /* Clear the virtual screen */
398     MI_CpuClearFast((void*)screen, sizeof(screen));
399 
400     /* Edit a string on the virtual screen */
401     PrintString(2,  1, 0xf, "Mute         : %d/1 [ %s ]", mute, ((mute == SNDEX_MUTE_OFF) ? "OFF" : "ON"));
402     PrintString(2,  3, 0xf, "DSP mix      : %d/8", rate);
403     PrintString(2,  5, 0xf, "Volume       : %d/%d", volume, (u8)SNDEX_VOLUME_MAX);
404 
405     PrintString(2,  7, 0xf, "Alarm Volume : %d/%d", alarmVol, (u8)SNDEX_VOLUME_MAX);
406     PrintString(2,  9, 0xf, "Timer        : %d/%d sec", timer, (u8)TIMER_MAX);
407 
408     PrintString(0, 13, 0xe, "- [A]        : set / stop Alarm");
409     PrintString(0, 15, 0xe, "- [Volume Button] : print log");
410 
411     if (isSetAlarm)
412     {
413         PrintString(2, 19, 0x1, "set Alarm...");
414     }
415     else if (isPlayingSound)
416     {
417         PrintString(2, 19, 0x1,  "playing sound...");
418         PrintString(10, 20, 0x1, "press A to stop.");
419     }
420 
421     /* Change the selected item's display color */
422     PrintString(0, (s16)(index * 2 + 1), 0xf, ">");
423     {
424         s32     i;
425         s32     j;
426 
427         for (i = 0; i < 32; i ++)
428         {
429             j   =   ((index * 2 + 1) * 32) + i;
430             screen[j]   &=  (u16)(~(0xf << 12));
431             screen[j]   |=  (u16)(0x4 << 12);
432         }
433     }
434 }
435 
436 /*---------------------------------------------------------------------------*
437   Name:         VBlankIntr
438 
439   Description:  V-Blank interrupt vector.
440 
441   Arguments:    None.
442 
443   Returns:      None.
444  *---------------------------------------------------------------------------*/
445 static void
VBlankIntr(void)446 VBlankIntr (void)
447 {
448     /* Reflect virtual screen in VRAM */
449     DC_FlushRange(screen, sizeof(screen));
450     GX_LoadBG0Scr(screen, 0, sizeof(screen));
451 
452     /* Sets the IRQ check flag */
453     OS_SetIrqCheckFlag(OS_IE_V_BLANK);
454 }
455 
456 /*---------------------------------------------------------------------------*
457   Name:         PrintString
458 
459   Description:  Positions the text string on the virtual screen. The string can be up to 32 chars.
460 
461   Arguments:    x: X-coordinate where character string starts (x 8 pixels).
462                 y: Y-coordinate where character string starts (x 8 pixels).
463                 palette: Specify text color by palette number.
464                 text: Text string to position. Null-terminated.
465                 ...: Virtual argument.
466 
467   Returns:      None.
468  *---------------------------------------------------------------------------*/
469 static void
PrintString(s16 x,s16 y,u8 palette,char * text,...)470 PrintString (s16 x, s16 y, u8 palette, char *text, ...)
471 {
472     va_list vlist;
473     char    temp[32 + 2];
474     s32     i;
475 
476     va_start(vlist, text);
477     (void)vsnprintf(temp, 33, text, vlist);
478     va_end(vlist);
479 
480     *((u16*)(&temp[32]))    =   0x0000;
481     for (i = 0; ; i++)
482     {
483         if (temp[i] == 0x00)
484         {
485             break;
486         }
487         screen[((y * 32) + x + i) % (32 * 32)] = (u16)((palette << 12) | temp[i]);
488     }
489 }
490 
491 /*---------------------------------------------------------------------------*
492   Name:         VolumeSwitchCallback
493 
494   Description:  Callback function executed when the volume button on the TWL is pressed.
495 
496   Arguments:    result: Result of pressing the volume button
497                 arg: Argument passed when this function is called as a callback function
498 
499 
500   Returns:      None.
501  *---------------------------------------------------------------------------*/
502 static void
VolumeSwitchCallback(SNDEXResult result,void * arg)503 VolumeSwitchCallback (SNDEXResult result, void* arg)
504 {
505 #pragma unused( arg )
506 #pragma unused( result )
507 
508     /* SNDEX library functions cannot be used during callback processing.*
509      * SNDEX_RESULT_EXCLUSIVE is returned.                         */
510     OS_TPrintf("volume switch pressed.\n");
511 }
512 
513 static void
AlarmHandler(void * arg)514 AlarmHandler (void* arg)
515 {
516 #pragma unused( arg )
517 
518     OS_TPrintf("alarm handler called.\n");
519     isSetAlarm  = FALSE;
520     isCalledAlarmHandler = TRUE;
521 }
522