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:: 2010-04-16#$
14 $Rev: 11320 $
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 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