• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, &params.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, &params, &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