1 /*---------------------------------------------------------------------------*
2   Project:  TwlSDK - library - dsp
3   File:     dsp_util.c
4 
5   Copyright 2007-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-08-24#$
14   $Rev: 11010 $
15   $Author: yosizaki $
16  *---------------------------------------------------------------------------*/
17 #include <twl.h>
18 #include <twl/dsp.h>
19 #include <twl/dsp/common/pipe.h>
20 #include <twl/dsp/common/audio.h>
21 
22 #include "dsp_process.h"
23 
24 /*---------------------------------------------------------------------------*/
25 /* Constants */
26 
27 // Sound playback priority
28 #define DSP_SOUND_PRIORITY_SHUTTER          0
29 #define DSP_SOUND_PRIORITY_NORMAL           16
30 #define DSP_SOUND_PRIORITY_NONE             32
31 
32 // For WAVE file parsing
33 #define MAKE_FOURCC(cc1, cc2, cc3, cc4) (u32)((cc1) | (cc2 << 8) | (cc3 << 16) | (cc4 << 24))
34 #define MAKE_TAG_DATA(ca) (u32)((*(ca)) | (*(ca+1) << 8) | (*(ca+2) << 16) | (*(ca+3) << 24))
35 #define FOURCC_RIFF MAKE_FOURCC('R', 'I', 'F', 'F')
36 #define FOURCC_WAVE MAKE_FOURCC('W', 'A', 'V', 'E')
37 #define FOURCC_fmt  MAKE_FOURCC('f', 'm', 't', ' ')
38 #define FOURCC_data MAKE_FOURCC('d', 'a', 't', 'a')
39 
40 // Wait (in ms) inserted before playback of shutter sound
41 #define DSP_WAIT_SHUTTER                    60
42 
43 /*---------------------------------------------------------------------------*/
44 /* Variables */
45 
46 // DSP sound playback flag
47 static BOOL DSPiSoundPlaying = FALSE;
48 static OSAlarm DSPiSoundAlarm;
49 
50 // Current DSP sound playback priority
51 static int DSPiSoundPriority = DSP_SOUND_PRIORITY_NONE;
52 
53 // DSP sampling control information
54 typedef struct DSPAudioCaptureInfo
55 {
56     DSPAddr bufferAddress;
57     DSPWord bufferLength;
58     DSPWord currentPosition;
59 }
60 DSPAudioCaptureInfo;
61 static DSPAudioCaptureInfo  DSPiAudioCapture;
62 extern DSPAddr              DSPiAudioCaptureAddress;
63 static DSPAddr              DSPiReadPosition = 0;
64 
65 // Local buffer on the ARM9 side
66 static u32  DSPiLocalRingLength = 0;
67 static u8  *DSPiLocalRingBuffer = NULL;
68 static int  DSPiLocalRingOffset = 0;
69 static DSPAddr DSPiAudioCaptureAddress = 0;
70 
71 static BOOL DSPiPlayingShutter = FALSE;
72 
73 // Function that returns the sound setting when shutter sound playback is completed
DSPiShutterPostProcessCallback(SNDEXResult result,void * arg)74 static void DSPiShutterPostProcessCallback( SNDEXResult result, void* arg )
75 {
76 #pragma unused(arg)
77     if(result == SNDEX_RESULT_EXCLUSIVE)
78     {
79         // Function that returns the shutter sound setting always succeeds
80         OS_TPanic("SNDEXi_PostProcessForShutterSound SNDEX_RESULT_EXCLUSIVE ... DSP_PlayShutterSound\n");
81     }
82     if(result != SNDEX_RESULT_SUCCESS)
83     {
84         // Function that returns the shutter sound setting always succeeds
85         OS_TPanic("SNDEXi_PostProcessForShutterSound Error ... DSP_PlayShutterSound\n");
86     }
87     DSPiPlayingShutter = FALSE;
88 }
89 
90 /*---------------------------------------------------------------------------*/
91 /* Functions */
92 
93 // Function generated after DSP plays a sound and ring buffer waits for empty playback
sound_handler(void * arg)94 static void sound_handler(void* arg)
95 {
96 #pragma unused(arg)
97     DSPiSoundPlaying = FALSE;
98 }
99 
100 /*---------------------------------------------------------------------------*
101   Name:         DSPi_PipeCallbackForSound
102 
103   Description:  Sound playback completed callback.
104 
105   Arguments:    userdata: Optional user-defined argument
106                 port: Port number
107                 peer: Transmission direction
108 
109   Returns:      None.
110  *---------------------------------------------------------------------------*/
DSPi_PipeCallbackForSound(void * userdata,int port,int peer)111 static void DSPi_PipeCallbackForSound(void *userdata, int port, int peer)
112 {
113     (void)userdata;
114     if (peer == DSP_PIPE_INPUT)
115     {
116         // Receive command response from DSP
117         DSPAudioDriverResponse  response;
118         DSPPipe                 pipe[1];
119         (void)DSP_LoadPipe(pipe, port, peer);
120         if (DSP_GetPipeReadableSize(pipe) >= sizeof(response))
121         {
122             DSP_ReadPipe(pipe, &response, sizeof(response));
123             response.ctrl = DSP_32BIT_TO_DSP(response.ctrl);
124             response.result = DSP_32BIT_TO_DSP(response.result);
125             // Audio output command
126             if ((response.ctrl & DSP_AUDIO_DRIVER_TARGET_MASK) == DSP_AUDIO_DRIVER_TARGET_OUTPUT)
127             {
128                 // Stop command
129                 if ((response.ctrl & DSP_AUDIO_DRIVER_CONTROL_MASK) == DSP_AUDIO_DRIVER_CONTROL_STOP)
130                 {
131                     //DSPiSoundPlaying = FALSE;
132                     OS_CreateAlarm(&DSPiSoundAlarm);
133                     OS_SetAlarm(&DSPiSoundAlarm, OS_MilliSecondsToTicks(30), sound_handler, NULL);
134                     if(DSPiSoundPriority == DSP_SOUND_PRIORITY_SHUTTER)
135                     {
136                           // Function that returns the shutter sound setting always succeeds
137                           (void)SNDEXi_PostProcessForShutterSound(DSPiShutterPostProcessCallback, 0);
138                     }
139                     DSPiSoundPriority = DSP_SOUND_PRIORITY_NONE;
140                 }
141             }
142             // Audio input command
143             if ((response.ctrl & DSP_AUDIO_DRIVER_TARGET_MASK) == DSP_AUDIO_DRIVER_TARGET_INPUT)
144             {
145                 // Start command
146                 if ((response.ctrl & DSP_AUDIO_DRIVER_CONTROL_MASK) == DSP_AUDIO_DRIVER_CONTROL_START)
147                 {
148                     DSPiAudioCaptureAddress = (DSPAddr)response.result;
149                 }
150             }
151         }
152     }
153 }
154 
155 /*---------------------------------------------------------------------------*
156   Name:         DSPi_PlaySoundEx
157 
158   Description:  Plays sound directly from DSP sound output.
159 
160   Arguments:    src: Sampling data that is the source data
161                            It is necessary to use a 4-byte boundary alignment with PCM16bit32768kHz
162                 len: Sampling data byte size
163                 ctrl: Combination with DSP_AUDIO_DRIVER_MODE_*
164                 priority: Playback priority
165 
166   Returns:      None.
167  *---------------------------------------------------------------------------*/
DSPi_PlaySoundEx(const void * src,u32 len,u32 ctrl,int priority)168 static void DSPi_PlaySoundEx(const void *src, u32 len, u32 ctrl, int priority)
169 {
170     // Ignore if the component has not started anything up
171     DSPProcessContext  *context = DSP_FindProcess(NULL);
172     if (context)
173     {
174         // If priority is lower than the sound that is currently in playback, do not playback
175         if (DSPiSoundPriority < priority)
176         {
177             OS_TWarning("still now playing high-priority sound.\n");
178         }
179         else
180         {
181             DSPiSoundPriority = priority;
182             ctrl |= DSP_AUDIO_DRIVER_TARGET_OUTPUT;
183             ctrl |= DSP_AUDIO_DRIVER_CONTROL_START;
184             // Change from byte size to half-word size
185             len >>= 1;
186             // Set the callback for completion notification
187             DSP_SetPipeCallback(DSP_PIPE_AUDIO, DSPi_PipeCallbackForSound, NULL);
188             DSPiSoundPlaying = TRUE;
189             {
190                 DSPAudioDriverCommand   command;
191                 command.ctrl = DSP_32BIT_TO_DSP(ctrl);
192                 command.buf = DSP_32BIT_TO_DSP(src);
193                 command.len = DSP_32BIT_TO_DSP(len);
194                 command.opt = DSP_32BIT_TO_DSP(0);
195                 DSP_WriteProcessPipe(context, DSP_PIPE_AUDIO, &command, sizeof(command));
196             }
197         }
198     }
199 }
200 
201 /*---------------------------------------------------------------------------*
202   Name:         DSP_PlaySound
203 
204   Description:  Plays sound directly from DSP sound output.
205 
206   Arguments:    src: Sampling data that is the source data
207                          It is necessary to use a 4-byte boundary alignment with PCM16bit32768kHz
208                 len: Sampling data byte size
209                 stereo: If stereo, TRUE. If monaural, FALSE
210 
211 
212   Returns:      None.
213  *---------------------------------------------------------------------------*/
DSPi_PlaySoundCore(const void * src,u32 len,BOOL stereo)214 void DSPi_PlaySoundCore(const void *src, u32 len, BOOL stereo)
215 {
216     u32     ctrl = (stereo != FALSE) ? DSP_AUDIO_DRIVER_MODE_STEREO : DSP_AUDIO_DRIVER_MODE_MONAURAL;
217     DSPi_PlaySoundEx(src, len, ctrl, DSP_SOUND_PRIORITY_NORMAL);
218 }
219 
220 /*---------------------------------------------------------------------------*
221   Name:         DSP_PlayShutterSound
222 
223   Description:  Plays sound directly from DSP sound output.
224 
225   Arguments:    src: Sampling data that is the source data
226                          It is necessary to use a 4-byte boundary alignment with PCM16bit32768kHz
227                 len: Sampling data byte size
228 
229 
230   Returns:      If SNDEX function is successful, returns TRUE
231  *---------------------------------------------------------------------------*/
232 #if 0 // Do not apply a thorough WAVE check up to here
233 BOOL DSPi_PlayShutterSoundCore(const void *src, u32 len)
234 {
235     u8* wave_data = (u8*)src;
236     u32 cur = 0;
237     u32 tag;
238     u32 wave_len;
239     u32 raw_len;
240     BOOL    fFmt = FALSE, fData = FALSE;
241 
242     static SNDEXFrequency freq;
243     u32 sampling;
244     u32 chunkSize;
245 
246     // Check whether the given data is a WAVE file
247     if(len < cur+12)
248         return FALSE;
249     tag = MAKE_TAG_DATA(wave_data+cur);
250     if(tag != FOURCC_RIFF)
251         return FALSE;
252     cur+=4;
253 
254     wave_len = MAKE_TAG_DATA(wave_data+cur);
255     cur+=4;
256 
257     tag = MAKE_TAG_DATA(wave_data+cur);
258     if(tag != FOURCC_WAVE)
259         return FALSE;
260     cur+=4;
261 
262     while (wave_len > 0)
263     {
264         if(len < cur+8)
265             return FALSE;
266         tag = MAKE_TAG_DATA(wave_data+cur);
267         cur+=4;
268         chunkSize = MAKE_TAG_DATA(wave_data+cur);
269         cur+=4;
270 
271         if(len < cur+chunkSize)
272             return FALSE;
273 
274         switch (tag)
275         {
276         case FOURCC_fmt:
277             // Check the sampling rate from the format information
278             // If not playing back the shutter sound, get the frequency
279             if(!DSPi_IsShutterSoundPlayingCore)
280             {
281                 if(SNDEX_GetI2SFrequency(&freq) != SNDEX_RESULT_SUCCESS)
282                     return FALSE;
283             }
284             sampling = MAKE_TAG_DATA(wave_data+cur+4);
285             cur+=chunkSize;
286             if( ((freq == SNDEX_FREQUENCY_32730)&&(sampling != 32730))||((freq == SNDEX_FREQUENCY_47610)&&(sampling != 47610)) )
287                 return FALSE;
288             fFmt = TRUE;
289             break;
290         case FOURCC_data:
291             raw_len = chunkSize;
292             fData = TRUE;
293             break;
294         default:
295             cur+=chunkSize;
296             break;
297         }
298         if(fFmt && fData)
299             break;
300         wave_len -= chunkSize;
301     }
302     if(!(fFmt && fData))
303         return FALSE;
304 
305     // When headphones are connected, turn off the system speaker
306     // Insert an approximately 60-msec wait before playback of the shutter sound to stabilize output
307     // This wait should be inserted in any situation to unify the sense of operation
308     OS_SpinWait(67 * DSP_WAIT_SHUTTER * 1000);	// Approximately 60 msec
309 
310     // Change the sound output mode in the function
311     while(SNDEXi_PreProcessForShutterSound() != SNDEX_RESULT_SUCCESS)
312     {
313         OS_Sleep(1); // Retry until successful
314     }
315 
316     {
317         u32     ctrl = DSP_AUDIO_DRIVER_MODE_MONAURAL;
318         // Reduce the shutter sound on the left and right 50%
319         ctrl |= DSP_AUDIO_DRIVER_MODE_HALFVOL;
320         DSPi_PlaySoundEx((wave_data+cur), raw_len, ctrl, DSP_SOUND_PRIORITY_SHUTTER);
321         DSPiPlayingShutter = TRUE;
322     }
323 
324     return TRUE;
325 }
326 #else // The purpose of the check is only for not making the shutter sound with a different frequency
DSPi_PlayShutterSoundCore(const void * src,u32 len)327 BOOL DSPi_PlayShutterSoundCore(const void *src, u32 len)
328 {
329 #pragma unused(len)
330     u32 cur;
331     u32 sampling;
332     u32 raw_len;
333     u8* wave_data = (u8*)src;
334     static SNDEXFrequency freq;
335 
336     if(len < 44)
337         return FALSE;
338 
339     if(MAKE_TAG_DATA(wave_data) != FOURCC_RIFF)
340         return FALSE;
341 
342     if(MAKE_TAG_DATA(wave_data+8) != FOURCC_WAVE)
343         return FALSE;
344 
345     cur = 24;
346     sampling = MAKE_TAG_DATA(wave_data+cur);
347 
348     // Check the sampling rate from the format information
349     // If not playing back the shutter sound, get the frequency
350     if(!DSPi_IsShutterSoundPlayingCore)
351     {
352         if(SNDEX_GetI2SFrequency(&freq) != SNDEX_RESULT_SUCCESS)
353             return FALSE;
354     }
355     if( ((freq == SNDEX_FREQUENCY_32730)&&(sampling != 32730))||((freq == SNDEX_FREQUENCY_47610)&&(sampling != 47610)) )
356         return FALSE;
357 
358     cur += 16;
359     raw_len = MAKE_TAG_DATA(wave_data+cur);
360     cur += 4;
361 
362     if(len < cur+raw_len)
363         return FALSE;
364 
365     // When headphones are connected, turn off the system speaker
366     // Insert an approximately 60-msec wait before playback of the shutter sound to stabilize output
367     // This wait should be inserted in any situation to unify the sense of operation
368     OS_SpinWait(67 * DSP_WAIT_SHUTTER * 1000);	// Approximately 60 msec
369 
370     // Change the sound output mode in the function
371     while(SNDEXi_PreProcessForShutterSound() != SNDEX_RESULT_SUCCESS)
372     {
373         OS_Sleep(1); // Retry until successful
374     }
375 
376     {
377         u32     ctrl = DSP_AUDIO_DRIVER_MODE_MONAURAL;
378         // Reduce the shutter sound on the left and right 50%
379         ctrl |= DSP_AUDIO_DRIVER_MODE_HALFVOL;
380         DSPi_PlaySoundEx((wave_data+cur), raw_len, ctrl, DSP_SOUND_PRIORITY_SHUTTER);
381         DSPiPlayingShutter = TRUE;
382     }
383 
384     return TRUE;
385 }
386 #endif
387 
388 /*---------------------------------------------------------------------------*
389   Name:         DSP_StopSound
390 
391   Description:  Stops playback from DSP sound output.
392 
393   Arguments:    None.
394 
395   Returns:      None.
396  *---------------------------------------------------------------------------*/
DSPi_StopSoundCore(void)397 void DSPi_StopSoundCore(void)
398 {
399     // Ignore if the component has not started anything up
400     DSPProcessContext  *context = DSP_FindProcess(NULL);
401     if (context && DSPiSoundPlaying)
402     {
403         int     ctrl = 0;
404         ctrl |= DSP_AUDIO_DRIVER_TARGET_OUTPUT;
405         ctrl |= DSP_AUDIO_DRIVER_CONTROL_STOP;
406         {
407             DSPAudioDriverCommand   command;
408             command.ctrl = DSP_32BIT_TO_DSP(ctrl);
409             command.buf = DSP_32BIT_TO_DSP(0);
410             command.len = DSP_32BIT_TO_DSP(0);
411             command.opt = DSP_32BIT_TO_DSP(0);
412             DSP_WriteProcessPipe(context, DSP_PIPE_AUDIO, &command, sizeof(command));
413         }
414     }
415 }
416 
417 /*---------------------------------------------------------------------------*
418   Name:         DSP_IsSoundPlaying
419 
420   Description:  Determines whether DSP sound output is currently in playback.
421 
422   Arguments:    None.
423 
424   Returns:      TRUE if DSP sound output is currently in playback.
425  *---------------------------------------------------------------------------*/
DSPi_IsSoundPlayingCore(void)426 BOOL DSPi_IsSoundPlayingCore(void)
427 {
428     return DSPiSoundPlaying;
429 }
430 
431 /*---------------------------------------------------------------------------*
432   Name:         DSP_IsShutterSoundPlaying
433 
434   Description:  Determines whether DSP sound output is playing back the current shutter sound.
435 
436   Arguments:    None.
437 
438   Returns:      TRUE if DSP sound output is playing back the current shutter sound.
439  *---------------------------------------------------------------------------*/
DSPi_IsShutterSoundPlayingCore(void)440 BOOL DSPi_IsShutterSoundPlayingCore(void)
441 {
442     return (DSPiSoundPlaying | DSPiPlayingShutter);
443 }
444 
445 /*---------------------------------------------------------------------------*
446   Name:         DSP_StartSampling
447 
448   Description:  Samples microphone sound directly from DSP sound input.
449 
450   Arguments:    buffer: Ring buffer used in sampling
451                          It is necessary to align the boundary to 16-bit integer multiples
452                 length: Ring buffer size
453                          It is necessary to align the boundary to 16-bit integer multiples
454 
455   Returns:      None.
456  *---------------------------------------------------------------------------*/
DSPi_StartSamplingCore(void * buffer,u32 length)457 void DSPi_StartSamplingCore(void *buffer, u32 length)
458 {
459     SDK_ALIGN2_ASSERT(buffer);
460     SDK_ALIGN2_ASSERT(length);
461     {
462         // Ignore if the component has not started anything up
463         DSPProcessContext  *context = DSP_FindProcess(NULL);
464         if (context)
465         {
466             int     ctrl = 0;
467             ctrl |= DSP_AUDIO_DRIVER_TARGET_INPUT;
468             ctrl |= DSP_AUDIO_DRIVER_CONTROL_START;
469             DSPiLocalRingLength = length;
470             DSPiLocalRingBuffer = (u8 *)buffer;
471             DSPiLocalRingOffset = 0;
472             DSPiAudioCaptureAddress = 0;
473             DSPiReadPosition = 0;
474             // Set the callback for completion notification
475             DSP_SetPipeCallback(DSP_PIPE_AUDIO, DSPi_PipeCallbackForSound, NULL);
476             {
477                 DSPAudioDriverCommand   command;
478                 command.ctrl = DSP_32BIT_TO_DSP(ctrl);
479                 command.buf = DSP_32BIT_TO_DSP(0);
480                 command.len = DSP_32BIT_TO_DSP(0);
481                 command.opt = DSP_32BIT_TO_DSP(0);
482                 DSP_WriteProcessPipe(context, DSP_PIPE_AUDIO, &command, sizeof(command));
483             }
484         }
485     }
486 }
487 
488 /*---------------------------------------------------------------------------*
489   Name:         DSP_StopSampling
490 
491   Description:  Stops sampling from DSP sound input.
492 
493   Arguments:    None.
494 
495 
496   Returns:      None.
497  *---------------------------------------------------------------------------*/
DSPi_StopSamplingCore(void)498 void DSPi_StopSamplingCore(void)
499 {
500     // Ignore if the component has not started anything up
501     DSPProcessContext  *context = DSP_FindProcess(NULL);
502     if (context)
503     {
504         int     ctrl = 0;
505         ctrl |= DSP_AUDIO_DRIVER_TARGET_INPUT;
506         ctrl |= DSP_AUDIO_DRIVER_CONTROL_STOP;
507         // Set the callback for completion notification
508         DSP_SetPipeCallback(DSP_PIPE_AUDIO, DSPi_PipeCallbackForSound, NULL);
509         {
510             DSPAudioDriverCommand   command;
511             command.ctrl = DSP_32BIT_TO_DSP(ctrl);
512             command.buf = DSP_32BIT_TO_DSP(0);
513             command.len = DSP_32BIT_TO_DSP(0);
514             command.opt = DSP_32BIT_TO_DSP(0);
515             DSP_WriteProcessPipe(context, DSP_PIPE_AUDIO, &command, sizeof(command));
516         }
517     }
518 }
519 
520 /*---------------------------------------------------------------------------*
521   Name:         DSP_SyncSamplingBuffer
522 
523   Description:  Reads only the updated portions from the ring buffer in DSP to the ARM9 processor.
524 
525   Arguments:    None.
526 
527   Returns:      None.
528  *---------------------------------------------------------------------------*/
DSPi_SyncSamplingBufferCore(void)529 void DSPi_SyncSamplingBufferCore(void)
530 {
531     // If not receiving the monitoring address of DSP, do nothing
532     if (DSPiAudioCaptureAddress != 0)
533     {
534         // Get the capture information in DSP and read if not read
535         DSP_LoadData(DSP_ADDR_TO_ARM(DSPiAudioCaptureAddress), &DSPiAudioCapture, sizeof(DSPiAudioCapture));
536         if (DSPiAudioCapture.currentPosition != DSPiReadPosition)
537         {
538             // Because the sizes of the ring buffers are different on the DSP and ARM9 processor, be careful of the end as you copy them
539             //
540             int     cur = DSPiAudioCapture.currentPosition;
541             int     end = (DSPiReadPosition > cur) ? DSPiAudioCapture.bufferLength : cur;
542             int     len = end - DSPiReadPosition;
543             while (len > 0)
544             {
545                 int     segment = (int)MATH_MIN(len, DSP_WORD_TO_DSP32(DSPiLocalRingLength - DSPiLocalRingOffset));
546                 DSP_LoadData(DSP_ADDR_TO_ARM(DSPiAudioCapture.bufferAddress + DSPiReadPosition),
547                              &DSPiLocalRingBuffer[DSPiLocalRingOffset], DSP_WORD_TO_ARM(segment));
548                 len -= segment;
549                 DSPiReadPosition += segment;
550                 if (DSPiReadPosition >= DSPiAudioCapture.bufferLength)
551                 {
552                     DSPiReadPosition -= DSPiAudioCapture.bufferLength;
553                 }
554                 DSPiLocalRingOffset += (int)DSP_WORD_TO_ARM32(segment);
555                 if (DSPiLocalRingOffset >= DSPiLocalRingLength)
556                 {
557                     DSPiLocalRingOffset -= DSPiLocalRingLength;
558                 }
559             }
560         }
561     }
562 }
563 
564 /*---------------------------------------------------------------------------*
565   Name:         DSP_GetLastSamplingAddress
566 
567   Description:  Gets the latest sampling position of the local ring buffer loaded to ARM9.
568 
569 
570   Arguments:    None.
571 
572   Returns:      Latest sampling position.
573  *---------------------------------------------------------------------------*/
DSPi_GetLastSamplingAddressCore(void)574 const u8* DSPi_GetLastSamplingAddressCore(void)
575 {
576     int     offset = DSPiLocalRingOffset - (int)sizeof(u16);
577     if (offset < 0)
578     {
579         offset += DSPiLocalRingLength;
580     }
581     return &DSPiLocalRingBuffer[offset];
582 }
583 
584