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