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-07-16#$
14 $Rev: 10913 $
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 // Set the callback for completion notification
474 DSP_SetPipeCallback(DSP_PIPE_AUDIO, DSPi_PipeCallbackForSound, NULL);
475 {
476 DSPAudioDriverCommand command;
477 command.ctrl = DSP_32BIT_TO_DSP(ctrl);
478 command.buf = DSP_32BIT_TO_DSP(0);
479 command.len = DSP_32BIT_TO_DSP(0);
480 command.opt = DSP_32BIT_TO_DSP(0);
481 DSP_WriteProcessPipe(context, DSP_PIPE_AUDIO, &command, sizeof(command));
482 }
483 }
484 }
485 }
486
487 /*---------------------------------------------------------------------------*
488 Name: DSP_StopSampling
489
490 Description: Stops sampling from DSP sound input.
491
492 Arguments: None.
493
494
495 Returns: None.
496 *---------------------------------------------------------------------------*/
DSPi_StopSamplingCore(void)497 void DSPi_StopSamplingCore(void)
498 {
499 // Ignore if the component has not started anything up
500 DSPProcessContext *context = DSP_FindProcess(NULL);
501 if (context)
502 {
503 int ctrl = 0;
504 ctrl |= DSP_AUDIO_DRIVER_TARGET_INPUT;
505 ctrl |= DSP_AUDIO_DRIVER_CONTROL_STOP;
506 // Set the callback for completion notification
507 DSP_SetPipeCallback(DSP_PIPE_AUDIO, DSPi_PipeCallbackForSound, NULL);
508 {
509 DSPAudioDriverCommand command;
510 command.ctrl = DSP_32BIT_TO_DSP(ctrl);
511 command.buf = DSP_32BIT_TO_DSP(0);
512 command.len = DSP_32BIT_TO_DSP(0);
513 command.opt = DSP_32BIT_TO_DSP(0);
514 DSP_WriteProcessPipe(context, DSP_PIPE_AUDIO, &command, sizeof(command));
515 }
516 }
517 }
518
519 /*---------------------------------------------------------------------------*
520 Name: DSP_SyncSamplingBuffer
521
522 Description: Reads only the updated portions from the ring buffer in DSP to the ARM9 processor.
523
524 Arguments: None.
525
526 Returns: None.
527 *---------------------------------------------------------------------------*/
DSPi_SyncSamplingBufferCore(void)528 void DSPi_SyncSamplingBufferCore(void)
529 {
530 // If not receiving the monitoring address of DSP, do nothing
531 if (DSPiAudioCaptureAddress != 0)
532 {
533 // Get the capture information in DSP and read if not read
534 DSP_LoadData(DSP_ADDR_TO_ARM(DSPiAudioCaptureAddress), &DSPiAudioCapture, sizeof(DSPiAudioCapture));
535 if (DSPiAudioCapture.currentPosition != DSPiReadPosition)
536 {
537 // Because the sizes of the ring buffers are different on the DSP and ARM9 processor, be careful of the end as you copy them
538 //
539 int cur = DSPiAudioCapture.currentPosition;
540 int end = (DSPiReadPosition > cur) ? DSPiAudioCapture.bufferLength : cur;
541 int len = end - DSPiReadPosition;
542 while (len > 0)
543 {
544 int segment = (int)MATH_MIN(len, DSP_WORD_TO_DSP32(DSPiLocalRingLength - DSPiLocalRingOffset));
545 DSP_LoadData(DSP_ADDR_TO_ARM(DSPiAudioCapture.bufferAddress + DSPiReadPosition),
546 &DSPiLocalRingBuffer[DSPiLocalRingOffset], DSP_WORD_TO_ARM(segment));
547 len -= segment;
548 DSPiReadPosition += segment;
549 if (DSPiReadPosition >= DSPiAudioCapture.bufferLength)
550 {
551 DSPiReadPosition -= DSPiAudioCapture.bufferLength;
552 }
553 DSPiLocalRingOffset += (int)DSP_WORD_TO_ARM32(segment);
554 if (DSPiLocalRingOffset >= DSPiLocalRingLength)
555 {
556 DSPiLocalRingOffset -= DSPiLocalRingLength;
557 }
558 }
559 }
560 }
561 }
562
563 /*---------------------------------------------------------------------------*
564 Name: DSP_GetLastSamplingAddress
565
566 Description: Gets the latest sampling position of the local ring buffer loaded to ARM9.
567
568
569 Arguments: None.
570
571 Returns: Latest sampling position.
572 *---------------------------------------------------------------------------*/
DSPi_GetLastSamplingAddressCore(void)573 const u8* DSPi_GetLastSamplingAddressCore(void)
574 {
575 int offset = DSPiLocalRingOffset - (int)sizeof(u16);
576 if (offset < 0)
577 {
578 offset += DSPiLocalRingLength;
579 }
580 return &DSPiLocalRingBuffer[offset];
581 }
582
583