1 /*---------------------------------------------------------------------------*
2 Project: TwlSDK - SND - demos - stream
3 File: main.c
4
5 Copyright 2007-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 $Date:: 2008-10-02#$
14 $Rev: 8827 $
15 $Author: yosizaki $
16 *---------------------------------------------------------------------------*/
17
18 //---------------------------------------------------------------------------
19 // USAGE:
20 // A: start Bgm1
21 // X : start Bgm2
22 // B: stop Bgm
23 //---------------------------------------------------------------------------
24
25 #ifdef SDK_TWL
26 #include <twl.h>
27 #else
28 #include <nitro.h>
29 #endif
30
31 #define MAKE_FOURCC(cc1, cc2, cc3, cc4) (u32)((cc1) | (cc2 << 8) | (cc3 << 16) | (cc4 << 24))
32
33 #define FOURCC_RIFF MAKE_FOURCC('R', 'I', 'F', 'F')
34 #define FOURCC_WAVE MAKE_FOURCC('W', 'A', 'V', 'E')
35 #define FOURCC_fmt MAKE_FOURCC('f', 'm', 't', ' ')
36 #define FOURCC_data MAKE_FOURCC('d', 'a', 't', 'a')
37
38 #define L_CHANNEL 4
39 #define R_CHANNEL 5
40 #define ALARM_NUM 0
41 #define STREAM_THREAD_PRIO 12
42 #define THREAD_STACK_SIZE 1024
43 #define STRM_BUF_PAGESIZE 1024*32
44 #define STRM_BUF_SIZE STRM_BUF_PAGESIZE*2
45
46 // WAV format header
47 typedef struct WaveFormat
48 {
49 u16 format;
50 u16 channels;
51 s32 sampleRate;
52 u32 dataRate;
53 u16 blockAlign;
54 u16 bitPerSample;
55 }
56 WaveFormat;
57
58 // Stream object
59 typedef struct StreamInfo
60 {
61 FSFile file;
62 WaveFormat format;
63 u32 beginPos;
64 s32 dataSize;
65 u32 bufPage;
66 BOOL isPlay;
67 }
68 StreamInfo;
69
70 static BOOL ReadWaveFormat(StreamInfo * strm);
71 static void ReadStrmData(StreamInfo * strm);
72 static void SoundAlarmHandler(void *arg);
73 static void StrmThread(void *arg);
74 static void VBlankIntr(void);
75 static void PlayStream(StreamInfo * strm, const char *filename);
76 static void StopStream(StreamInfo * strm);
77
78 static u16 Cont;
79 static u16 Trg;
80 static u64 strmThreadStack[THREAD_STACK_SIZE / sizeof(u64)];
81 static OSThread strmThread;
82 static OSMessageQueue msgQ;
83 static OSMessage msgBuf[1];
84
85 static u8 strm_lbuf[STRM_BUF_SIZE] ATTRIBUTE_ALIGN(32);
86 static u8 strm_rbuf[STRM_BUF_SIZE] ATTRIBUTE_ALIGN(32);
87 static u8 strm_tmp[STRM_BUF_PAGESIZE * 2] ATTRIBUTE_ALIGN(32);
88
89 // Filename
90 const char filename1[] = "kart_title.32.wav";
91 const char filename2[] = "fanfare.32.wav";
92
93 static StreamInfo strm;
94
95 /*---------------------------------------------------------------------------*
96 Name: NitroMain
97
98 Description: Main.
99
100 Arguments: None.
101
102 Returns: None.
103 *---------------------------------------------------------------------------*/
NitroMain()104 void NitroMain()
105 {
106 // Initialization
107 OS_Init();
108 GX_Init();
109 (void)OS_EnableIrq();
110 (void)OS_EnableInterrupts();
111 FS_Init(FS_DMA_NOT_USE);
112 SND_Init();
113
114 FS_InitFile(&strm.file);
115 strm.isPlay = FALSE;
116
117 // V-Blank interrupt settings
118 OS_SetIrqFunction(OS_IE_V_BLANK, VBlankIntr);
119 (void)OS_EnableIrqMask(OS_IE_V_BLANK);
120 (void)OS_EnableIrq();
121 (void)GX_VBlankIntr(TRUE);
122
123 // Print usage
124 OS_Printf("=================================\n");
125 OS_Printf("USAGE:\n");
126 OS_Printf(" A : start bgm1\n");
127 OS_Printf(" X : start bgm2\n");
128 OS_Printf(" B : stop bgm\n");
129 OS_Printf("=================================\n");
130
131 // Lock the channel
132 SND_LockChannel((1 << L_CHANNEL) | (1 << R_CHANNEL), 0);
133
134 /* Startup stream thread */
135 OS_CreateThread(&strmThread,
136 StrmThread,
137 NULL,
138 strmThreadStack + THREAD_STACK_SIZE / sizeof(u64),
139 THREAD_STACK_SIZE, STREAM_THREAD_PRIO);
140 OS_WakeupThreadDirect(&strmThread);
141
142 //================ Main loop
143 while (1)
144 {
145 u16 ReadData;
146
147 OS_WaitVBlankIntr();
148
149 // Receive ARM7 command reply
150 while (SND_RecvCommandReply(SND_COMMAND_NOBLOCK) != NULL)
151 {
152 }
153
154 ReadData = PAD_Read();
155 Trg = (u16)(ReadData & (ReadData ^ Cont));
156 Cont = ReadData;
157
158 // Plays the PSG square wave
159 if (Trg & PAD_BUTTON_A)
160 {
161 PlayStream(&strm, filename1);
162 }
163
164 if (Trg & PAD_BUTTON_X)
165 {
166 PlayStream(&strm, filename2);
167 }
168
169 if (Trg & PAD_BUTTON_B)
170 {
171 StopStream(&strm);
172 }
173
174 // Command flush
175 (void)SND_FlushCommand(SND_COMMAND_NOBLOCK);
176 }
177 }
178
179 /*---------------------------------------------------------------------------*
180 Name: PlayStream
181
182 Description: Plays streaming playback.
183
184 Arguments: strm: Stream object
185 filename: Name of the streaming playback file
186
187 Returns: None.
188 *---------------------------------------------------------------------------*/
PlayStream(StreamInfo * strm,const char * filename)189 static void PlayStream(StreamInfo * strm, const char *filename)
190 {
191 int timerValue;
192 u32 alarmPeriod;
193
194 // Stops if during playback
195 if (strm->isPlay)
196 {
197 u32 tag;
198 StopStream(strm);
199 tag = SND_GetCurrentCommandTag();
200 (void)SND_FlushCommand(SND_COMMAND_NOBLOCK | SND_COMMAND_IMMEDIATE);
201 SND_WaitForCommandProc(tag); // Waits for a stop
202 }
203
204 // File scanning
205 if (FS_IsFile(&strm->file))
206 (void)FS_CloseFile(&strm->file);
207 if (!FS_OpenFileEx(&strm->file, filename, FS_FILEMODE_R))
208 {
209 OS_Panic("Error: failed to open file %s\n", filename);
210 }
211 if (!ReadWaveFormat(strm))
212 {
213 OS_Panic("Error: failed to read wavefile\n");
214 }
215
216 strm->isPlay = TRUE;
217
218 /* Set parameters */
219 timerValue = SND_TIMER_CLOCK / strm->format.sampleRate;
220 alarmPeriod = timerValue * STRM_BUF_PAGESIZE / 32U;
221 alarmPeriod /= (strm->format.bitPerSample == 16) ? sizeof(s16) : sizeof(s8);
222
223 // Read initial stream data
224 (void)FS_SeekFile(&strm->file, (s32)strm->beginPos, FS_SEEK_SET);
225 strm->bufPage = 0;
226 ReadStrmData(strm);
227 ReadStrmData(strm);
228
229 // Set up the channel and alarm
230 SND_SetupChannelPcm(L_CHANNEL,
231 (strm->format.bitPerSample ==
232 16) ? SND_WAVE_FORMAT_PCM16 : SND_WAVE_FORMAT_PCM8, strm_lbuf,
233 SND_CHANNEL_LOOP_REPEAT, 0, STRM_BUF_SIZE / sizeof(u32), 127,
234 SND_CHANNEL_DATASHIFT_NONE, timerValue, 0);
235 SND_SetupChannelPcm(R_CHANNEL,
236 (strm->format.bitPerSample ==
237 16) ? SND_WAVE_FORMAT_PCM16 : SND_WAVE_FORMAT_PCM8,
238 (strm->format.channels == 1) ? strm_lbuf : strm_rbuf,
239 SND_CHANNEL_LOOP_REPEAT, 0, STRM_BUF_SIZE / sizeof(u32), 127,
240 SND_CHANNEL_DATASHIFT_NONE, timerValue, 127);
241 SND_SetupAlarm(ALARM_NUM, alarmPeriod, alarmPeriod, SoundAlarmHandler, strm);
242 SND_StartTimer((1 << L_CHANNEL) | (1 << R_CHANNEL), 0, 1 << ALARM_NUM, 0);
243 }
244
245 /*---------------------------------------------------------------------------*
246 Name: StopStream
247
248 Description: Stops streaming playback.
249
250 Arguments: strm: Stream object
251
252 Returns: None.
253 *---------------------------------------------------------------------------*/
StopStream(StreamInfo * strm)254 static void StopStream(StreamInfo * strm)
255 {
256 SND_StopTimer((1 << L_CHANNEL) | (1 << R_CHANNEL), 0, 1 << ALARM_NUM, 0);
257 if (FS_IsFile(&strm->file))
258 (void)FS_CloseFile(&strm->file);
259 strm->isPlay = FALSE;
260 }
261
262 /*---------------------------------------------------------------------------*
263 Name: StrmThread
264
265 Description: The stream thread.
266
267 Arguments: arg: User data (unused)
268
269 Returns: None.
270 *---------------------------------------------------------------------------*/
StrmThread(void *)271 static void StrmThread(void * /*arg */ )
272 {
273 OSMessage message;
274
275 OS_InitMessageQueue(&msgQ, msgBuf, 1);
276
277 while (1)
278 {
279 (void)OS_ReceiveMessage(&msgQ, &message, OS_MESSAGE_BLOCK);
280 (void)ReadStrmData((StreamInfo *) message);
281 }
282 }
283
284 /*---------------------------------------------------------------------------*
285 Name: SoundAlarmHandler
286
287 Description: Alarm callback function.
288
289 Arguments: arg: stream object
290
291 Returns: None.
292 *---------------------------------------------------------------------------*/
SoundAlarmHandler(void * arg)293 static void SoundAlarmHandler(void *arg)
294 {
295 (void)OS_SendMessage(&msgQ, (OSMessage)arg, OS_MESSAGE_NOBLOCK);
296 }
297
298 /*---------------------------------------------------------------------------*
299 Name: ReadStrmData
300
301 Description: Function to read streaming data.
302 Reads one page's worth of streaming data in the buffer from the file.
303
304 Arguments: strm: Stream object
305
306 Returns: None.
307 *---------------------------------------------------------------------------*/
ReadStrmData(StreamInfo * strm)308 static void ReadStrmData(StreamInfo * strm)
309 {
310 int i;
311 s32 readSize;
312 u8 *lbuf, *rbuf;
313
314 // Stream has reached its terminus
315 if (strm->dataSize <= 0)
316 {
317 StopStream(strm);
318 return;
319 }
320
321 // Buffer page settings
322 if (strm->bufPage == 0)
323 {
324 lbuf = strm_lbuf;
325 rbuf = strm_rbuf;
326 strm->bufPage = 1;
327 }
328 else
329 {
330 lbuf = strm_lbuf + STRM_BUF_PAGESIZE;
331 rbuf = strm_rbuf + STRM_BUF_PAGESIZE;
332 strm->bufPage = 0;
333 }
334
335 // Read data
336 if (strm->format.channels == 1)
337 {
338 // Monaural
339 readSize = FS_ReadFile(&strm->file,
340 strm_tmp,
341 (strm->dataSize <
342 STRM_BUF_PAGESIZE) ? strm->dataSize : STRM_BUF_PAGESIZE);
343 if (readSize == -1)
344 OS_Panic("read file end\n");
345
346 if (strm->format.bitPerSample == 16)
347 {
348 // 16-bit data
349 for (i = 0; i < readSize / sizeof(s16); i++)
350 {
351 ((s16 *)lbuf)[i] = ((s16 *)strm_tmp)[i];
352 }
353 for (; i < STRM_BUF_PAGESIZE / sizeof(s16); i++)
354 {
355 ((s16 *)lbuf)[i] = 0; // If the stream has reached its terminus, the remainder is filled in with zeroes
356 }
357 }
358 else
359 {
360 // 8-bit data
361 for (i = 0; i < readSize / sizeof(s8); i++)
362 {
363 ((s8 *)lbuf)[i] = (s8)((s16)strm_tmp[i] - 128);
364 }
365 for (; i < STRM_BUF_PAGESIZE / sizeof(s8); i++)
366 {
367 ((s8 *)lbuf)[i] = 0;
368 }
369 }
370 }
371 else
372 {
373 // Stereo
374 readSize = FS_ReadFile(&strm->file,
375 strm_tmp,
376 (strm->dataSize <
377 STRM_BUF_PAGESIZE * 2) ? strm->dataSize : STRM_BUF_PAGESIZE * 2);
378 if (readSize == -1)
379 OS_Panic("read file end\n");
380
381 if (strm->format.bitPerSample == 16)
382 {
383 // 16-bit data
384 for (i = 0; i < (readSize / 2) / sizeof(s16); i++)
385 {
386 ((s16 *)lbuf)[i] = ((s16 *)strm_tmp)[2 * i];
387 ((s16 *)rbuf)[i] = ((s16 *)strm_tmp)[2 * i + 1];
388 }
389 for (; i < STRM_BUF_PAGESIZE / sizeof(s16); i++)
390 {
391 ((s16 *)lbuf)[i] = 0;
392 ((s16 *)rbuf)[i] = 0;
393 }
394 }
395 else
396 {
397 // 8-bit data
398 for (i = 0; i < (readSize / 2) / sizeof(s8); i++)
399 {
400 ((s8 *)lbuf)[i] = (s8)((s16)strm_tmp[2 * i] - 128);
401 ((s8 *)rbuf)[i] = (s8)((s16)strm_tmp[2 * i + 1] - 128);
402 }
403 for (; i < STRM_BUF_PAGESIZE / sizeof(s8); i++)
404 {
405 ((s8 *)lbuf)[i] = 0;
406 ((s8 *)rbuf)[i] = 0;
407 }
408 }
409 }
410
411 strm->dataSize -= readSize;
412
413 return;
414 }
415
416
417 /*---------------------------------------------------------------------------*
418 Name: ReadWaveFormat
419
420 Description: Gets the data size and first position of a data string, and the WAVE format data header.
421
422 Arguments: strm: Stream object
423
424 Returns: If readout is successful: TRUE; if failed: FALSE
425 *---------------------------------------------------------------------------*/
ReadWaveFormat(StreamInfo * strm)426 static BOOL ReadWaveFormat(StreamInfo * strm)
427 {
428 u32 tag;
429 s32 size;
430 BOOL fFmt = FALSE, fData = FALSE;
431
432 (void)FS_SeekFileToBegin(&strm->file);
433
434 (void)FS_ReadFile(&strm->file, &tag, 4);
435 if (tag != FOURCC_RIFF)
436 return FALSE;
437
438 (void)FS_ReadFile(&strm->file, &size, 4);
439
440 (void)FS_ReadFile(&strm->file, &tag, 4);
441 if (tag != FOURCC_WAVE)
442 return FALSE;
443
444 while (size > 0)
445 {
446 s32 chunkSize;
447 if (FS_ReadFile(&strm->file, &tag, 4) == -1)
448 {
449 return FALSE;
450 }
451 if (FS_ReadFile(&strm->file, &chunkSize, 4) == -1)
452 {
453 return FALSE;
454 }
455
456 switch (tag)
457 {
458 case FOURCC_fmt:
459 if (FS_ReadFile(&strm->file, (u8 *)&strm->format, chunkSize) == -1)
460 {
461 return FALSE;
462 }
463 fFmt = TRUE;
464 break;
465 case FOURCC_data:
466 strm->beginPos = FS_GetFilePosition(&strm->file);
467 strm->dataSize = chunkSize;
468 (void)FS_SeekFile(&strm->file, chunkSize, FS_SEEK_CUR);
469 fData = TRUE;
470 break;
471 default:
472 (void)FS_SeekFile(&strm->file, chunkSize, FS_SEEK_CUR);
473 break;
474 }
475 if (fFmt && fData)
476 {
477 return TRUE; // Forces end if fmt and data have finished being read
478 }
479
480 size -= chunkSize;
481 }
482
483 if (size != 0)
484 return FALSE;
485 return TRUE;
486 }
487
488 //--------------------------------------------------------------------------------
489 // V-Blank interrupt process
490 //
VBlankIntr(void)491 static void VBlankIntr(void)
492 {
493 // Interrupt check flag
494 OS_SetIrqCheckFlag(OS_IE_V_BLANK);
495 }
496