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