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