Home | History | Annotate | Download | only in lib_src
      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 */
    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 */
    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 */
    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 */
    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 */
    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 */
    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 */
    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 */
    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 */
    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 */
    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 */
    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 */
    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 */
    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