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