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