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 */
39extern 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*/
79static EAS_RESULT WaveCheckFileType (S_EAS_DATA *pEASData, EAS_FILE_HANDLE fileHandle, EAS_VOID_PTR *pHandle, EAS_I32 offset);
80static EAS_RESULT WavePrepare (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData);
81static EAS_RESULT WaveState (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_STATE *pState);
82static EAS_RESULT WaveClose (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData);
83static EAS_RESULT WaveReset (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData);
84static EAS_RESULT WaveLocate (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 time, EAS_BOOL *pParserLocate);
85static EAS_RESULT WavePause (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData);
86static EAS_RESULT WaveResume (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData);
87static EAS_RESULT WaveSetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 value);
88static EAS_RESULT WaveGetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 *pValue);
89static EAS_RESULT WaveParseHeader (S_EAS_DATA *pEASData, EAS_FILE_HANDLE fileHandle, S_WAVE_STATE *pWaveData);
90static EAS_RESULT WaveGetMetaData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 *pMediaLength);
91
92#ifdef MMAPI_SUPPORT
93static 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*/
103const 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*/
137static 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*/
183static 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*/
225static 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*/
259static 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*/
308static 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 */
338static 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 */
368static 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 */
399static 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*/
426static 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 */
466static 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*/
513static 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*/
742static 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*/
842static 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