1 /*----------------------------------------------------------------------------
2 *
3 * File:
4 * eas_wavefile.c
5 *
6 * Contents and purpose:
7 * This file implements the wave file parser.
8 *
9 * Copyright Sonic Network Inc. 2005
10
11 * Licensed under the Apache License, Version 2.0 (the "License");
12 * you may not use this file except in compliance with the License.
13 * You may obtain a copy of the License at
14 *
15 * http://www.apache.org/licenses/LICENSE-2.0
16 *
17 * Unless required by applicable law or agreed to in writing, software
18 * distributed under the License is distributed on an "AS IS" BASIS,
19 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 * See the License for the specific language governing permissions and
21 * limitations under the License.
22 *
23 *----------------------------------------------------------------------------
24 * Revision Control:
25 * $Revision: 852 $
26 * $Date: 2007-09-04 11:43:49 -0700 (Tue, 04 Sep 2007) $
27 *----------------------------------------------------------------------------
28 */
29
30 #include "eas_data.h"
31 #include "eas_report.h"
32 #include "eas_host.h"
33 #include "eas_config.h"
34 #include "eas_parser.h"
35 #include "eas_pcm.h"
36 #include "eas_wavefile.h"
37
38 /* lint is choking on the ARM math.h file, so we declare the log10 function here */
39 extern double log10(double x);
40
41 /* increase gain to compensate for loss in mixer */
42 #define WAVE_GAIN_OFFSET 6
43
44 /* constant for 1200 / log10(2.0) */
45 #define PITCH_CENTS_CONVERSION 3986.313714
46
47 /*----------------------------------------------------------------------------
48 * WAVE file defines
49 *----------------------------------------------------------------------------
50 */
51 /* RIFF chunks */
52 #define CHUNK_TYPE(a,b,c,d) ( \
53 ( ((EAS_U32)(a) & 0xFF) << 24 ) \
54 + ( ((EAS_U32)(b) & 0xFF) << 16 ) \
55 + ( ((EAS_U32)(c) & 0xFF) << 8 ) \
56 + ( ((EAS_U32)(d) & 0xFF) ) )
57
58 #define CHUNK_RIFF CHUNK_TYPE('R','I','F','F')
59 #define CHUNK_WAVE CHUNK_TYPE('W','A','V','E')
60 #define CHUNK_FMT CHUNK_TYPE('f','m','t',' ')
61 #define CHUNK_DATA CHUNK_TYPE('d','a','t','a')
62 #define CHUNK_LIST CHUNK_TYPE('L','I','S','T')
63 #define CHUNK_INFO CHUNK_TYPE('I','N','F','O')
64 #define CHUNK_INAM CHUNK_TYPE('I','N','A','M')
65 #define CHUNK_ICOP CHUNK_TYPE('I','C','O','P')
66 #define CHUNK_IART CHUNK_TYPE('I','A','R','T')
67
68 /* wave file format identifiers */
69 #define WAVE_FORMAT_PCM 0x0001
70 #define WAVE_FORMAT_IMA_ADPCM 0x0011
71
72 /* file size for streamed file */
73 #define FILE_SIZE_STREAMING 0x80000000
74
75 /*----------------------------------------------------------------------------
76 * prototypes
77 *----------------------------------------------------------------------------
78 */
79 static EAS_RESULT WaveCheckFileType (S_EAS_DATA *pEASData, EAS_FILE_HANDLE fileHandle, EAS_VOID_PTR *pHandle, EAS_I32 offset);
80 static EAS_RESULT WavePrepare (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData);
81 static EAS_RESULT WaveState (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_STATE *pState);
82 static EAS_RESULT WaveClose (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData);
83 static EAS_RESULT WaveReset (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData);
84 static EAS_RESULT WaveLocate (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 time, EAS_BOOL *pParserLocate);
85 static EAS_RESULT WavePause (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData);
86 static EAS_RESULT WaveResume (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData);
87 static EAS_RESULT WaveSetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 value);
88 static EAS_RESULT WaveGetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 *pValue);
89 static EAS_RESULT WaveParseHeader (S_EAS_DATA *pEASData, EAS_FILE_HANDLE fileHandle, S_WAVE_STATE *pWaveData);
90 static EAS_RESULT WaveGetMetaData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 *pMediaLength);
91
92 #ifdef MMAPI_SUPPORT
93 static EAS_RESULT SaveFmtChunk (S_EAS_DATA *pEASData, EAS_FILE_HANDLE fileHandle, S_WAVE_STATE *pWaveData, EAS_I32 size);
94 #endif
95
96 /*----------------------------------------------------------------------------
97 *
98 * EAS_Wave_Parser
99 *
100 * This structure contains the functional interface for the Wave file parser
101 *----------------------------------------------------------------------------
102 */
103 const S_FILE_PARSER_INTERFACE EAS_Wave_Parser =
104 {
105 WaveCheckFileType,
106 WavePrepare,
107 NULL,
108 NULL,
109 WaveState,
110 WaveClose,
111 WaveReset,
112 WavePause,
113 WaveResume,
114 WaveLocate,
115 WaveSetData,
116 WaveGetData,
117 WaveGetMetaData
118 };
119
120 /*----------------------------------------------------------------------------
121 * WaveCheckFileType()
122 *----------------------------------------------------------------------------
123 * Purpose:
124 * Check the file type to see if we can parse it
125 *
126 * Inputs:
127 * pEASData - pointer to overall EAS data structure
128 * handle - pointer to file handle
129 *
130 * Outputs:
131 *
132 *
133 * Side Effects:
134 *
135 *----------------------------------------------------------------------------
136 */
WaveCheckFileType(S_EAS_DATA * pEASData,EAS_FILE_HANDLE fileHandle,EAS_VOID_PTR * pHandle,EAS_I32 offset)137 static EAS_RESULT WaveCheckFileType (S_EAS_DATA *pEASData, EAS_FILE_HANDLE fileHandle, EAS_VOID_PTR *pHandle, EAS_I32 offset)
138 {
139 S_WAVE_STATE *pWaveData;
140
141 /* zero the memory to insure complete initialization */
142 *pHandle = NULL;
143
144 /* read the file header */
145 if (WaveParseHeader(pEASData, fileHandle, NULL) == EAS_SUCCESS)
146 {
147
148 /* check for static memory allocation */
149 if (pEASData->staticMemoryModel)
150 pWaveData = EAS_CMEnumData(EAS_CM_WAVE_DATA);
151 else
152 pWaveData = EAS_HWMalloc(pEASData->hwInstData, sizeof(S_WAVE_STATE));
153 if (!pWaveData)
154 return EAS_ERROR_MALLOC_FAILED;
155 EAS_HWMemSet(pWaveData, 0, sizeof(S_WAVE_STATE));
156
157 /* return a pointer to the instance data */
158 pWaveData->fileHandle = fileHandle;
159 pWaveData->fileOffset = offset;
160 *pHandle = pWaveData;
161 }
162
163 return EAS_SUCCESS;
164 }
165
166 /*----------------------------------------------------------------------------
167 * WavePrepare()
168 *----------------------------------------------------------------------------
169 * Purpose:
170 * Prepare to parse the file.
171 *
172 * Inputs:
173 * pEASData - pointer to overall EAS data structure
174 * handle - pointer to file handle
175 *
176 * Outputs:
177 *
178 *
179 * Side Effects:
180 *
181 *----------------------------------------------------------------------------
182 */
WavePrepare(S_EAS_DATA * pEASData,EAS_VOID_PTR pInstData)183 static EAS_RESULT WavePrepare (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
184 {
185 S_WAVE_STATE *pWaveData;
186 EAS_RESULT result;
187
188 /* validate parser state */
189 pWaveData = (S_WAVE_STATE*) pInstData;
190 if (pWaveData->streamHandle != NULL)
191 return EAS_ERROR_NOT_VALID_IN_THIS_STATE;
192
193 /* back to start of file */
194 pWaveData->time = 0;
195 if ((result = EAS_HWFileSeek(pEASData->hwInstData, pWaveData->fileHandle, pWaveData->fileOffset)) != EAS_SUCCESS)
196 return result;
197
198 /* parse the file header */
199 if ((result = WaveParseHeader(pEASData, pWaveData->fileHandle, pWaveData)) != EAS_SUCCESS)
200 return result;
201
202 return EAS_SUCCESS;
203 }
204
205 /*----------------------------------------------------------------------------
206 * WaveState()
207 *----------------------------------------------------------------------------
208 * Purpose:
209 * Returns the current state of the stream
210 *
211 * Inputs:
212 * pEASData - pointer to overall EAS data structure
213 * handle - pointer to file handle
214 * pState - pointer to variable to store state
215 *
216 * Outputs:
217 *
218 *
219 * Side Effects:
220 *
221 * Notes:
222 * This interface is also exposed in the internal library for use by the other modules.
223 *----------------------------------------------------------------------------
224 */
WaveState(S_EAS_DATA * pEASData,EAS_VOID_PTR pInstData,EAS_STATE * pState)225 static EAS_RESULT WaveState (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_STATE *pState)
226 {
227 S_WAVE_STATE *pWaveData;
228
229 /* return current state */
230 pWaveData = (S_WAVE_STATE*) pInstData;
231 if (pWaveData->streamHandle)
232 return EAS_PEState(pEASData, pWaveData->streamHandle, pState);
233
234 /* if no stream handle, and time is not zero, we are done */
235 if (pWaveData->time > 0)
236 *pState = EAS_STATE_STOPPED;
237 else
238 *pState = EAS_STATE_OPEN;
239 return EAS_SUCCESS;
240 }
241
242 /*----------------------------------------------------------------------------
243 * WaveClose()
244 *----------------------------------------------------------------------------
245 * Purpose:
246 * Close the file and clean up
247 *
248 * Inputs:
249 * pEASData - pointer to overall EAS data structure
250 * handle - pointer to file handle
251 *
252 * Outputs:
253 *
254 *
255 * Side Effects:
256 *
257 *----------------------------------------------------------------------------
258 */
WaveClose(S_EAS_DATA * pEASData,EAS_VOID_PTR pInstData)259 static EAS_RESULT WaveClose (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
260 {
261 S_WAVE_STATE *pWaveData;
262 EAS_RESULT result;
263
264 pWaveData = (S_WAVE_STATE*) pInstData;
265
266 /* close the stream */
267 if (pWaveData->streamHandle)
268 {
269 if ((result = EAS_PEClose(pEASData, pWaveData->streamHandle)) != EAS_SUCCESS)
270 return result;
271 pWaveData->streamHandle = NULL;
272 }
273
274 /* if using dynamic memory, free it */
275 if (!pEASData->staticMemoryModel)
276 {
277
278 #ifdef MMAPI_SUPPORT
279 /* need to free the fmt chunk */
280 if (pWaveData->fmtChunk != NULL)
281 EAS_HWFree(pEASData->hwInstData, pWaveData->fmtChunk);
282 #endif
283
284 /* free the instance data */
285 EAS_HWFree(pEASData->hwInstData, pWaveData);
286
287 }
288 return EAS_SUCCESS;
289 }
290
291 /*----------------------------------------------------------------------------
292 * WaveReset()
293 *----------------------------------------------------------------------------
294 * Purpose:
295 * Reset the sequencer. Used for locating backwards in the file.
296 *
297 * Inputs:
298 * pEASData - pointer to overall EAS data structure
299 * handle - pointer to file handle
300 *
301 * Outputs:
302 *
303 *
304 * Side Effects:
305 *
306 *----------------------------------------------------------------------------
307 */
WaveReset(S_EAS_DATA * pEASData,EAS_VOID_PTR pInstData)308 static EAS_RESULT WaveReset (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
309 {
310 EAS_PCM_HANDLE streamHandle;
311
312 /* reset to first byte of data in the stream */
313 streamHandle = ((S_WAVE_STATE*)pInstData)->streamHandle;
314 if (streamHandle)
315 return EAS_PEReset(pEASData, streamHandle);
316 return EAS_ERROR_NOT_VALID_IN_THIS_STATE;
317 }
318
319 /*----------------------------------------------------------------------------
320 * WaveLocate()
321 *----------------------------------------------------------------------------
322 * Purpose:
323 * Rewind/fast-forward in file.
324 *
325 * Inputs:
326 * pEASData - pointer to overall EAS data structure
327 * handle - pointer to file handle
328 * time - time (in msecs)
329 *
330 * Outputs:
331 *
332 *
333 * Side Effects:
334 *
335 *----------------------------------------------------------------------------
336 */
337 /*lint -esym(715, pParserLocate) reserved for future use */
WaveLocate(S_EAS_DATA * pEASData,EAS_VOID_PTR pInstData,EAS_I32 time,EAS_BOOL * pParserLocate)338 static EAS_RESULT WaveLocate (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 time, EAS_BOOL *pParserLocate)
339 {
340 EAS_PCM_HANDLE streamHandle;
341
342 /* reset to first byte of data in the stream */
343 streamHandle = ((S_WAVE_STATE*)pInstData)->streamHandle;
344 if (streamHandle)
345 return EAS_PELocate(pEASData, streamHandle, time);
346 return EAS_ERROR_NOT_VALID_IN_THIS_STATE;
347 }
348
349 /*----------------------------------------------------------------------------
350 * WavePause()
351 *----------------------------------------------------------------------------
352 * Purpose:
353 * Mute and stop rendering a PCM stream. Sets the gain target to zero and stops the playback
354 * at the end of the next audio frame.
355 *
356 * Inputs:
357 * pEASData - pointer to EAS library instance data
358 * handle - pointer to S_WAVE_STATE for this stream
359 *
360 * Outputs:
361 *
362 *
363 * Side Effects:
364 *
365 *----------------------------------------------------------------------------
366 */
367 /*lint -esym(715, pEASData) reserved for future use */
WavePause(S_EAS_DATA * pEASData,EAS_VOID_PTR pInstData)368 static EAS_RESULT WavePause (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
369 {
370 EAS_PCM_HANDLE streamHandle;
371
372 /* pause the stream */
373 streamHandle = ((S_WAVE_STATE*)pInstData)->streamHandle;
374 if (streamHandle)
375 return EAS_PEPause(pEASData, streamHandle);
376 return EAS_ERROR_NOT_VALID_IN_THIS_STATE;
377 }
378
379 /*----------------------------------------------------------------------------
380 * WaveResume()
381 *----------------------------------------------------------------------------
382 * Purpose:
383 * Resume rendering a PCM stream. Sets the gain target back to its
384 * previous setting and restarts playback at the end of the next audio
385 * frame.
386 *
387 * Inputs:
388 * pEASData - pointer to EAS library instance data
389 * handle - pointer to S_WAVE_STATE for this stream
390 *
391 * Outputs:
392 *
393 *
394 * Side Effects:
395 *
396 *----------------------------------------------------------------------------
397 */
398 /*lint -esym(715, pEASData) reserved for future use */
WaveResume(S_EAS_DATA * pEASData,EAS_VOID_PTR pInstData)399 static EAS_RESULT WaveResume (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
400 {
401 EAS_PCM_HANDLE streamHandle;
402
403 /* resume the stream */
404 streamHandle = ((S_WAVE_STATE*)pInstData)->streamHandle;
405 if (streamHandle)
406 return EAS_PEResume(pEASData, streamHandle);
407 return EAS_ERROR_NOT_VALID_IN_THIS_STATE;
408 }
409
410 /*----------------------------------------------------------------------------
411 * WaveSetData()
412 *----------------------------------------------------------------------------
413 * Purpose:
414 *
415 * Inputs:
416 * pEASData - pointer to EAS library instance data
417 * handle - pointer to S_WAVE_STATE for this stream
418 *
419 * Outputs:
420 *
421 *
422 * Side Effects:
423 *
424 *----------------------------------------------------------------------------
425 */
WaveSetData(S_EAS_DATA * pEASData,EAS_VOID_PTR pInstData,EAS_I32 param,EAS_I32 value)426 static EAS_RESULT WaveSetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 value)
427 {
428 S_WAVE_STATE *pWaveData = (S_WAVE_STATE*) pInstData;
429
430 switch (param)
431 {
432 /* set metadata callback */
433 case PARSER_DATA_METADATA_CB:
434 EAS_HWMemCpy(&pWaveData->metadata, (void*) value, sizeof(S_METADATA_CB));
435 return EAS_SUCCESS;
436
437 case PARSER_DATA_PLAYBACK_RATE:
438 value = (EAS_I32) (PITCH_CENTS_CONVERSION * log10((double) value / (double) (1 << 28)));
439 return EAS_PEUpdatePitch(pEASData, pWaveData->streamHandle, (EAS_I16) value);
440
441 case PARSER_DATA_VOLUME:
442 return EAS_PEUpdateVolume(pEASData, pWaveData->streamHandle, (EAS_I16) value);
443
444 default:
445 return EAS_ERROR_INVALID_PARAMETER;
446 }
447 }
448
449 /*----------------------------------------------------------------------------
450 * WaveGetData()
451 *----------------------------------------------------------------------------
452 * Purpose:
453 *
454 * Inputs:
455 * pEASData - pointer to EAS library instance data
456 * handle - pointer to S_WAVE_STATE for this stream
457 *
458 * Outputs:
459 *
460 *
461 * Side Effects:
462 *
463 *----------------------------------------------------------------------------
464 */
465 /*lint -esym(715, pEASData) reserved for future use */
WaveGetData(S_EAS_DATA * pEASData,EAS_VOID_PTR pInstData,EAS_I32 param,EAS_I32 * pValue)466 static EAS_RESULT WaveGetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 *pValue)
467 {
468 S_WAVE_STATE *pWaveData;
469
470 pWaveData = (S_WAVE_STATE*) pInstData;
471 switch (param)
472 {
473 /* return file type as WAVE */
474 case PARSER_DATA_FILE_TYPE:
475 *pValue = pWaveData->fileType;
476 break;
477
478 #ifdef MMAPI_SUPPORT
479 /* return pointer to 'fmt' chunk */
480 case PARSER_DATA_FORMAT:
481 *pValue = (EAS_I32) pWaveData->fmtChunk;
482 break;
483 #endif
484
485 case PARSER_DATA_GAIN_OFFSET:
486 *pValue = WAVE_GAIN_OFFSET;
487 break;
488
489 default:
490 return EAS_ERROR_INVALID_PARAMETER;
491 }
492
493 return EAS_SUCCESS;
494 }
495
496 /*----------------------------------------------------------------------------
497 * WaveParseHeader()
498 *----------------------------------------------------------------------------
499 * Purpose:
500 * Parse the WAVE file header.
501 *
502 * Inputs:
503 * pEASData - pointer to EAS library instance data
504 * handle - pointer to S_WAVE_STATE for this stream
505 *
506 * Outputs:
507 *
508 *
509 * Side Effects:
510 *
511 *----------------------------------------------------------------------------
512 */
WaveParseHeader(S_EAS_DATA * pEASData,EAS_FILE_HANDLE fileHandle,S_WAVE_STATE * pWaveData)513 static EAS_RESULT WaveParseHeader (S_EAS_DATA *pEASData, EAS_FILE_HANDLE fileHandle, S_WAVE_STATE *pWaveData)
514 {
515 S_PCM_OPEN_PARAMS params;
516 EAS_RESULT result;
517 EAS_U32 tag;
518 EAS_U32 fileSize;
519 EAS_U32 size;
520 EAS_I32 pos;
521 EAS_I32 audioOffset;
522 EAS_U16 usTemp;
523 EAS_BOOL parseDone;
524 EAS_U32 avgBytesPerSec;
525
526 /* init some data (and keep lint happy) */
527 params.sampleRate = 0;
528 params.size = 0;
529 audioOffset = 0;
530 params.decoder = 0;
531 params.blockSize = 0;
532 params.pCallbackFunc = NULL;
533 params.cbInstData = NULL;
534 params.loopSamples = 0;
535 params.fileHandle = fileHandle;
536 params.volume = 0x7fff;
537 params.envData = 0;
538 avgBytesPerSec = 8000;
539
540 /* check for 'RIFF' tag */
541 if ((result = EAS_HWGetDWord(pEASData->hwInstData, fileHandle, &tag, EAS_TRUE)) != EAS_FALSE)
542 return result;
543 if (tag != CHUNK_RIFF)
544 return EAS_ERROR_UNRECOGNIZED_FORMAT;
545
546 /* get size */
547 if ((result = EAS_HWGetDWord(pEASData->hwInstData, fileHandle, &fileSize, EAS_FALSE)) != EAS_FALSE)
548 return result;
549
550 /* check for 'WAVE' tag */
551 if ((result = EAS_HWGetDWord(pEASData->hwInstData, fileHandle, &tag, EAS_TRUE)) != EAS_FALSE)
552 return result;
553 if (tag != CHUNK_WAVE)
554 return EAS_ERROR_UNRECOGNIZED_FORMAT;
555
556 /* this is enough to say we recognize the file */
557 if (pWaveData == NULL)
558 return EAS_SUCCESS;
559
560 /* check for streaming mode */
561 pWaveData->flags = 0;
562 pWaveData->mediaLength = -1;
563 pWaveData->infoChunkPos = -1;
564 pWaveData->infoChunkSize = -1;
565 if (fileSize== FILE_SIZE_STREAMING)
566 {
567 pWaveData->flags |= PCM_FLAGS_STREAMING;
568 fileSize = 0x7fffffff;
569 }
570
571 /* find out where we're at */
572 if ((result = EAS_HWFilePos(pEASData->hwInstData, fileHandle, &pos)) != EAS_SUCCESS)
573 return result;
574 fileSize -= 4;
575
576 parseDone = EAS_FALSE;
577 for (;;)
578 {
579 /* get tag and size for next chunk */
580 if ((result = EAS_HWGetDWord(pEASData->hwInstData, fileHandle, &tag, EAS_TRUE)) != EAS_FALSE)
581 return result;
582 if ((result = EAS_HWGetDWord(pEASData->hwInstData, fileHandle, &size, EAS_FALSE)) != EAS_FALSE)
583 return result;
584
585 /* process chunk */
586 pos += 8;
587 switch (tag)
588 {
589 case CHUNK_FMT:
590
591 #ifdef MMAPI_SUPPORT
592 if ((result = SaveFmtChunk(pEASData, fileHandle, pWaveData, (EAS_I32) size)) != EAS_SUCCESS)
593 return result;
594 #endif
595
596 /* get audio format */
597 if ((result = EAS_HWGetWord(pEASData->hwInstData, fileHandle, &usTemp, EAS_FALSE)) != EAS_FALSE)
598 return result;
599 if (usTemp == WAVE_FORMAT_PCM)
600 {
601 params.decoder = EAS_DECODER_PCM;
602 pWaveData->fileType = EAS_FILE_WAVE_PCM;
603 }
604 else if (usTemp == WAVE_FORMAT_IMA_ADPCM)
605 {
606 params.decoder = EAS_DECODER_IMA_ADPCM;
607 pWaveData->fileType = EAS_FILE_WAVE_IMA_ADPCM;
608 }
609 else
610 return EAS_ERROR_UNRECOGNIZED_FORMAT;
611
612 /* get number of channels */
613 if ((result = EAS_HWGetWord(pEASData->hwInstData, fileHandle, &usTemp, EAS_FALSE)) != EAS_FALSE)
614 return result;
615 if (usTemp == 2)
616 pWaveData->flags |= PCM_FLAGS_STEREO;
617 else if (usTemp != 1)
618 return EAS_ERROR_UNRECOGNIZED_FORMAT;
619
620 /* get sample rate */
621 if ((result = EAS_HWGetDWord(pEASData->hwInstData, fileHandle, ¶ms.sampleRate, EAS_FALSE)) != EAS_FALSE)
622 return result;
623
624 /* get stream rate */
625 if ((result = EAS_HWGetDWord(pEASData->hwInstData, fileHandle, &avgBytesPerSec, EAS_FALSE)) != EAS_FALSE)
626 return result;
627
628 /* get block alignment */
629 if ((result = EAS_HWGetWord(pEASData->hwInstData, fileHandle, &usTemp, EAS_FALSE)) != EAS_FALSE)
630 return result;
631 params.blockSize = usTemp;
632
633 /* get bits per sample */
634 if ((result = EAS_HWGetWord(pEASData->hwInstData, fileHandle, &usTemp, EAS_FALSE)) != EAS_FALSE)
635 return result;
636
637 /* PCM, must be 8 or 16 bit samples */
638 if (params.decoder == EAS_DECODER_PCM)
639 {
640 if (usTemp == 8)
641 pWaveData->flags |= PCM_FLAGS_8_BIT | PCM_FLAGS_UNSIGNED;
642 else if (usTemp != 16)
643 return EAS_ERROR_UNRECOGNIZED_FORMAT;
644 }
645
646 /* for IMA ADPCM, we only support mono 4-bit ADPCM */
647 else
648 {
649 if ((usTemp != 4) || (pWaveData->flags & PCM_FLAGS_STEREO))
650 return EAS_ERROR_UNRECOGNIZED_FORMAT;
651 }
652
653 break;
654
655 case CHUNK_DATA:
656 audioOffset = pos;
657 if (pWaveData->flags & PCM_FLAGS_STREAMING)
658 {
659 params.size = 0x7fffffff;
660 parseDone = EAS_TRUE;
661 }
662 else
663 {
664 params.size = (EAS_I32) size;
665 params.loopStart = size;
666 /* use more accurate method if possible */
667 if (size <= (0x7fffffff / 1000))
668 pWaveData->mediaLength = (EAS_I32) ((size * 1000) / avgBytesPerSec);
669 else
670 pWaveData->mediaLength = (EAS_I32) (size / (avgBytesPerSec / 1000));
671 }
672 break;
673
674 case CHUNK_LIST:
675 /* get the list type */
676 if ((result = EAS_HWGetDWord(pEASData->hwInstData, fileHandle, &tag, EAS_TRUE)) != EAS_FALSE)
677 return result;
678 if (tag == CHUNK_INFO)
679 {
680 pWaveData->infoChunkPos = pos + 4;
681 pWaveData->infoChunkSize = (EAS_I32) size - 4;
682 }
683 break;
684
685 default:
686 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "WaveParseHeader: %c%c%c%c chunk - %d byte(s) ignored\n",
687 (char) (tag >> 24), (char) (tag >> 16), (char) (tag >> 8), (char) tag, size); */ }
688 break;
689 }
690
691 if (parseDone)
692 break;
693
694 /* subtract header size */
695 fileSize -= 8;
696
697 /* account for zero-padding on odd length chunks */
698 if (size & 1)
699 size++;
700
701 /* this check works for files with odd length last chunk and no zero-pad */
702 if (size >= fileSize)
703 {
704 if (size > fileSize)
705 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "WaveParseHeader: '%c%c%c%c' chunk size exceeds length of file or is not zero-padded\n",
706 (char) (tag >> 24), (char) (tag >> 16), (char) (tag >> 8), (char) tag, size); */ }
707 break;
708 }
709
710 /* subtract size of data chunk (including any zero-pad) */
711 fileSize -= size;
712
713 /* seek to next chunk */
714 pos += (EAS_I32) size;
715 if ((result = EAS_HWFileSeek(pEASData->hwInstData, fileHandle, pos)) != EAS_SUCCESS)
716 return result;
717 }
718
719 /* check for valid header */
720 if ((params.sampleRate == 0) || (params.size == 0))
721 return EAS_ERROR_UNRECOGNIZED_FORMAT;
722
723 /* save the pertinent information */
724 pWaveData->audioOffset = audioOffset;
725 params.flags = pWaveData->flags;
726
727 /* seek to data */
728 if ((result = EAS_HWFileSeek(pEASData->hwInstData, fileHandle, audioOffset)) != EAS_SUCCESS)
729 return result;
730
731 /* open a stream in the PCM engine */
732 return EAS_PEOpenStream(pEASData, ¶ms, &pWaveData->streamHandle);
733 }
734
735 /*----------------------------------------------------------------------------
736 * WaveGetMetaData()
737 *----------------------------------------------------------------------------
738 * Purpose:
739 * Process the INFO chunk and return metadata to host
740 *----------------------------------------------------------------------------
741 */
WaveGetMetaData(S_EAS_DATA * pEASData,EAS_VOID_PTR pInstData,EAS_I32 * pMediaLength)742 static EAS_RESULT WaveGetMetaData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 *pMediaLength)
743 {
744 S_WAVE_STATE *pWaveData;
745 EAS_RESULT result;
746 EAS_I32 pos;
747 EAS_U32 size;
748 EAS_I32 infoSize;
749 EAS_U32 tag;
750 EAS_I32 restorePos;
751 E_EAS_METADATA_TYPE metaType;
752 EAS_I32 metaLen;
753
754 /* get current position so we can restore it */
755 pWaveData = (S_WAVE_STATE*) pInstData;
756
757 /* return media length */
758 *pMediaLength = pWaveData->mediaLength;
759
760 /* did we encounter an INFO chunk? */
761 if (pWaveData->infoChunkPos < 0)
762 return EAS_SUCCESS;
763
764 if ((result = EAS_HWFilePos(pEASData->hwInstData, pWaveData->fileHandle, &restorePos)) != EAS_SUCCESS)
765 return result;
766
767 /* offset to start of first chunk in INFO chunk */
768 pos = pWaveData->infoChunkPos;
769 infoSize = pWaveData->infoChunkSize;
770
771 /* read all the chunks in the INFO chunk */
772 for (;;)
773 {
774
775 /* seek to next chunk */
776 if ((result = EAS_HWFileSeek(pEASData->hwInstData, pWaveData->fileHandle, pos)) != EAS_SUCCESS)
777 return result;
778
779 /* get tag and size for next chunk */
780 if ((result = EAS_HWGetDWord(pEASData->hwInstData, pWaveData->fileHandle, &tag, EAS_TRUE)) != EAS_FALSE)
781 return result;
782 if ((result = EAS_HWGetDWord(pEASData->hwInstData, pWaveData->fileHandle, &size, EAS_FALSE)) != EAS_FALSE)
783 return result;
784
785 /* process chunk */
786 pos += 8;
787 metaType = EAS_METADATA_UNKNOWN;
788 switch (tag)
789 {
790 case CHUNK_INAM:
791 metaType = EAS_METADATA_TITLE;
792 break;
793
794 case CHUNK_IART:
795 metaType = EAS_METADATA_AUTHOR;
796 break;
797
798 case CHUNK_ICOP:
799 metaType = EAS_METADATA_COPYRIGHT;
800 break;
801
802 default:
803 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "WaveParseHeader: %c%c%c%c chunk - %d byte(s) ignored\n",
804 (char) (tag >> 24), (char) (tag >> 16), (char) (tag >> 8), (char) tag, size); */ }
805 break;
806 }
807
808 /* process known metadata */
809 if (metaType != EAS_METADATA_UNKNOWN)
810 {
811 metaLen = pWaveData->metadata.bufferSize - 1;
812 if (metaLen > (EAS_I32) size)
813 metaLen = (EAS_I32) size;
814 if ((result = EAS_HWReadFile(pEASData->hwInstData, pWaveData->fileHandle, pWaveData->metadata.buffer, metaLen, &metaLen)) != EAS_SUCCESS)
815 return result;
816 pWaveData->metadata.buffer[metaLen] = 0;
817 pWaveData->metadata.callback(metaType, pWaveData->metadata.buffer, pWaveData->metadata.pUserData);
818 }
819
820 /* subtract this block */
821 if (size & 1)
822 size++;
823 infoSize -= (EAS_I32) size + 8;
824 if (infoSize == 0)
825 break;
826 pos += (EAS_I32) size;
827 }
828
829
830 /* restore original position */
831 return EAS_HWFileSeek(pEASData->hwInstData, pWaveData->fileHandle, restorePos);
832 }
833
834 #ifdef MMAPI_SUPPORT
835 /*----------------------------------------------------------------------------
836 * SaveFmtChunk()
837 *----------------------------------------------------------------------------
838 * Purpose:
839 * Save the fmt chunk for the MMAPI library
840 *----------------------------------------------------------------------------
841 */
SaveFmtChunk(S_EAS_DATA * pEASData,EAS_FILE_HANDLE fileHandle,S_WAVE_STATE * pWaveData,EAS_I32 fmtSize)842 static EAS_RESULT SaveFmtChunk (S_EAS_DATA *pEASData, EAS_FILE_HANDLE fileHandle, S_WAVE_STATE *pWaveData, EAS_I32 fmtSize)
843 {
844 EAS_RESULT result;
845 EAS_I32 pos;
846 EAS_I32 count;
847
848 /* save current file position */
849 if ((result = EAS_HWFilePos(pEASData->hwInstData, fileHandle, &pos)) != EAS_SUCCESS)
850 return result;
851
852 /* allocate a chunk of memory */
853 pWaveData->fmtChunk = EAS_HWMalloc(pEASData->hwInstData, fmtSize);
854 if (!pWaveData->fmtChunk)
855 return EAS_ERROR_MALLOC_FAILED;
856
857 /* read the fmt chunk into memory */
858 if ((result = EAS_HWReadFile(pEASData->hwInstData, fileHandle, pWaveData->fmtChunk, fmtSize, &count)) != EAS_SUCCESS)
859 return result;
860 if (count != fmtSize)
861 return EAS_ERROR_FILE_READ_FAILED;
862
863 /* restore file position */
864 return EAS_HWFileSeek(pEASData->hwInstData, fileHandle, pos);
865 }
866 #endif
867
868