| /*---------------------------------------------------------------------------- |
| * |
| * File: |
| * eas_wavefile.c |
| * |
| * Contents and purpose: |
| * This file implements the wave file parser. |
| * |
| * Copyright Sonic Network Inc. 2005 |
| |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| *---------------------------------------------------------------------------- |
| * Revision Control: |
| * $Revision: 852 $ |
| * $Date: 2007-09-04 11:43:49 -0700 (Tue, 04 Sep 2007) $ |
| *---------------------------------------------------------------------------- |
| */ |
| |
| #include "eas_data.h" |
| #include "eas_report.h" |
| #include "eas_host.h" |
| #include "eas_config.h" |
| #include "eas_parser.h" |
| #include "eas_pcm.h" |
| #include "eas_wavefile.h" |
| |
| /* lint is choking on the ARM math.h file, so we declare the log10 function here */ |
| extern double log10(double x); |
| |
| /* increase gain to compensate for loss in mixer */ |
| #define WAVE_GAIN_OFFSET 6 |
| |
| /* constant for 1200 / log10(2.0) */ |
| #define PITCH_CENTS_CONVERSION 3986.313714 |
| |
| /*---------------------------------------------------------------------------- |
| * WAVE file defines |
| *---------------------------------------------------------------------------- |
| */ |
| /* RIFF chunks */ |
| #define CHUNK_TYPE(a,b,c,d) ( \ |
| ( ((EAS_U32)(a) & 0xFF) << 24 ) \ |
| + ( ((EAS_U32)(b) & 0xFF) << 16 ) \ |
| + ( ((EAS_U32)(c) & 0xFF) << 8 ) \ |
| + ( ((EAS_U32)(d) & 0xFF) ) ) |
| |
| #define CHUNK_RIFF CHUNK_TYPE('R','I','F','F') |
| #define CHUNK_WAVE CHUNK_TYPE('W','A','V','E') |
| #define CHUNK_FMT CHUNK_TYPE('f','m','t',' ') |
| #define CHUNK_DATA CHUNK_TYPE('d','a','t','a') |
| #define CHUNK_LIST CHUNK_TYPE('L','I','S','T') |
| #define CHUNK_INFO CHUNK_TYPE('I','N','F','O') |
| #define CHUNK_INAM CHUNK_TYPE('I','N','A','M') |
| #define CHUNK_ICOP CHUNK_TYPE('I','C','O','P') |
| #define CHUNK_IART CHUNK_TYPE('I','A','R','T') |
| |
| /* wave file format identifiers */ |
| #define WAVE_FORMAT_PCM 0x0001 |
| #define WAVE_FORMAT_IMA_ADPCM 0x0011 |
| |
| /* file size for streamed file */ |
| #define FILE_SIZE_STREAMING 0x80000000 |
| |
| /*---------------------------------------------------------------------------- |
| * prototypes |
| *---------------------------------------------------------------------------- |
| */ |
| static EAS_RESULT WaveCheckFileType (S_EAS_DATA *pEASData, EAS_FILE_HANDLE fileHandle, EAS_VOID_PTR *pHandle, EAS_I32 offset); |
| static EAS_RESULT WavePrepare (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData); |
| static EAS_RESULT WaveState (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_STATE *pState); |
| static EAS_RESULT WaveClose (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData); |
| static EAS_RESULT WaveReset (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData); |
| static EAS_RESULT WaveLocate (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 time, EAS_BOOL *pParserLocate); |
| static EAS_RESULT WavePause (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData); |
| static EAS_RESULT WaveResume (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData); |
| static EAS_RESULT WaveSetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 value); |
| static EAS_RESULT WaveGetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 *pValue); |
| static EAS_RESULT WaveParseHeader (S_EAS_DATA *pEASData, EAS_FILE_HANDLE fileHandle, S_WAVE_STATE *pWaveData); |
| static EAS_RESULT WaveGetMetaData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 *pMediaLength); |
| |
| #ifdef MMAPI_SUPPORT |
| static EAS_RESULT SaveFmtChunk (S_EAS_DATA *pEASData, EAS_FILE_HANDLE fileHandle, S_WAVE_STATE *pWaveData, EAS_I32 size); |
| #endif |
| |
| /*---------------------------------------------------------------------------- |
| * |
| * EAS_Wave_Parser |
| * |
| * This structure contains the functional interface for the Wave file parser |
| *---------------------------------------------------------------------------- |
| */ |
| const S_FILE_PARSER_INTERFACE EAS_Wave_Parser = |
| { |
| WaveCheckFileType, |
| WavePrepare, |
| NULL, |
| NULL, |
| WaveState, |
| WaveClose, |
| WaveReset, |
| WavePause, |
| WaveResume, |
| WaveLocate, |
| WaveSetData, |
| WaveGetData, |
| WaveGetMetaData |
| }; |
| |
| /*---------------------------------------------------------------------------- |
| * WaveCheckFileType() |
| *---------------------------------------------------------------------------- |
| * Purpose: |
| * Check the file type to see if we can parse it |
| * |
| * Inputs: |
| * pEASData - pointer to overall EAS data structure |
| * handle - pointer to file handle |
| * |
| * Outputs: |
| * |
| * |
| * Side Effects: |
| * |
| *---------------------------------------------------------------------------- |
| */ |
| static EAS_RESULT WaveCheckFileType (S_EAS_DATA *pEASData, EAS_FILE_HANDLE fileHandle, EAS_VOID_PTR *pHandle, EAS_I32 offset) |
| { |
| S_WAVE_STATE *pWaveData; |
| |
| /* zero the memory to insure complete initialization */ |
| *pHandle = NULL; |
| |
| /* read the file header */ |
| if (WaveParseHeader(pEASData, fileHandle, NULL) == EAS_SUCCESS) |
| { |
| |
| /* check for static memory allocation */ |
| if (pEASData->staticMemoryModel) |
| pWaveData = EAS_CMEnumData(EAS_CM_WAVE_DATA); |
| else |
| pWaveData = EAS_HWMalloc(pEASData->hwInstData, sizeof(S_WAVE_STATE)); |
| if (!pWaveData) |
| return EAS_ERROR_MALLOC_FAILED; |
| EAS_HWMemSet(pWaveData, 0, sizeof(S_WAVE_STATE)); |
| |
| /* return a pointer to the instance data */ |
| pWaveData->fileHandle = fileHandle; |
| pWaveData->fileOffset = offset; |
| *pHandle = pWaveData; |
| } |
| |
| return EAS_SUCCESS; |
| } |
| |
| /*---------------------------------------------------------------------------- |
| * WavePrepare() |
| *---------------------------------------------------------------------------- |
| * Purpose: |
| * Prepare to parse the file. |
| * |
| * Inputs: |
| * pEASData - pointer to overall EAS data structure |
| * handle - pointer to file handle |
| * |
| * Outputs: |
| * |
| * |
| * Side Effects: |
| * |
| *---------------------------------------------------------------------------- |
| */ |
| static EAS_RESULT WavePrepare (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData) |
| { |
| S_WAVE_STATE *pWaveData; |
| EAS_RESULT result; |
| |
| /* validate parser state */ |
| pWaveData = (S_WAVE_STATE*) pInstData; |
| if (pWaveData->streamHandle != NULL) |
| return EAS_ERROR_NOT_VALID_IN_THIS_STATE; |
| |
| /* back to start of file */ |
| pWaveData->time = 0; |
| if ((result = EAS_HWFileSeek(pEASData->hwInstData, pWaveData->fileHandle, pWaveData->fileOffset)) != EAS_SUCCESS) |
| return result; |
| |
| /* parse the file header */ |
| if ((result = WaveParseHeader(pEASData, pWaveData->fileHandle, pWaveData)) != EAS_SUCCESS) |
| return result; |
| |
| return EAS_SUCCESS; |
| } |
| |
| /*---------------------------------------------------------------------------- |
| * WaveState() |
| *---------------------------------------------------------------------------- |
| * Purpose: |
| * Returns the current state of the stream |
| * |
| * Inputs: |
| * pEASData - pointer to overall EAS data structure |
| * handle - pointer to file handle |
| * pState - pointer to variable to store state |
| * |
| * Outputs: |
| * |
| * |
| * Side Effects: |
| * |
| * Notes: |
| * This interface is also exposed in the internal library for use by the other modules. |
| *---------------------------------------------------------------------------- |
| */ |
| static EAS_RESULT WaveState (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_STATE *pState) |
| { |
| S_WAVE_STATE *pWaveData; |
| |
| /* return current state */ |
| pWaveData = (S_WAVE_STATE*) pInstData; |
| if (pWaveData->streamHandle) |
| return EAS_PEState(pEASData, pWaveData->streamHandle, pState); |
| |
| /* if no stream handle, and time is not zero, we are done */ |
| if (pWaveData->time > 0) |
| *pState = EAS_STATE_STOPPED; |
| else |
| *pState = EAS_STATE_OPEN; |
| return EAS_SUCCESS; |
| } |
| |
| /*---------------------------------------------------------------------------- |
| * WaveClose() |
| *---------------------------------------------------------------------------- |
| * Purpose: |
| * Close the file and clean up |
| * |
| * Inputs: |
| * pEASData - pointer to overall EAS data structure |
| * handle - pointer to file handle |
| * |
| * Outputs: |
| * |
| * |
| * Side Effects: |
| * |
| *---------------------------------------------------------------------------- |
| */ |
| static EAS_RESULT WaveClose (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData) |
| { |
| S_WAVE_STATE *pWaveData; |
| EAS_RESULT result; |
| |
| pWaveData = (S_WAVE_STATE*) pInstData; |
| |
| /* close the stream */ |
| if (pWaveData->streamHandle) |
| { |
| if ((result = EAS_PEClose(pEASData, pWaveData->streamHandle)) != EAS_SUCCESS) |
| return result; |
| pWaveData->streamHandle = NULL; |
| } |
| |
| /* if using dynamic memory, free it */ |
| if (!pEASData->staticMemoryModel) |
| { |
| |
| #ifdef MMAPI_SUPPORT |
| /* need to free the fmt chunk */ |
| if (pWaveData->fmtChunk != NULL) |
| EAS_HWFree(pEASData->hwInstData, pWaveData->fmtChunk); |
| #endif |
| |
| /* free the instance data */ |
| EAS_HWFree(pEASData->hwInstData, pWaveData); |
| |
| } |
| return EAS_SUCCESS; |
| } |
| |
| /*---------------------------------------------------------------------------- |
| * WaveReset() |
| *---------------------------------------------------------------------------- |
| * Purpose: |
| * Reset the sequencer. Used for locating backwards in the file. |
| * |
| * Inputs: |
| * pEASData - pointer to overall EAS data structure |
| * handle - pointer to file handle |
| * |
| * Outputs: |
| * |
| * |
| * Side Effects: |
| * |
| *---------------------------------------------------------------------------- |
| */ |
| static EAS_RESULT WaveReset (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData) |
| { |
| EAS_PCM_HANDLE streamHandle; |
| |
| /* reset to first byte of data in the stream */ |
| streamHandle = ((S_WAVE_STATE*)pInstData)->streamHandle; |
| if (streamHandle) |
| return EAS_PEReset(pEASData, streamHandle); |
| return EAS_ERROR_NOT_VALID_IN_THIS_STATE; |
| } |
| |
| /*---------------------------------------------------------------------------- |
| * WaveLocate() |
| *---------------------------------------------------------------------------- |
| * Purpose: |
| * Rewind/fast-forward in file. |
| * |
| * Inputs: |
| * pEASData - pointer to overall EAS data structure |
| * handle - pointer to file handle |
| * time - time (in msecs) |
| * |
| * Outputs: |
| * |
| * |
| * Side Effects: |
| * |
| *---------------------------------------------------------------------------- |
| */ |
| /*lint -esym(715, pParserLocate) reserved for future use */ |
| static EAS_RESULT WaveLocate (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 time, EAS_BOOL *pParserLocate) |
| { |
| EAS_PCM_HANDLE streamHandle; |
| |
| /* reset to first byte of data in the stream */ |
| streamHandle = ((S_WAVE_STATE*)pInstData)->streamHandle; |
| if (streamHandle) |
| return EAS_PELocate(pEASData, streamHandle, time); |
| return EAS_ERROR_NOT_VALID_IN_THIS_STATE; |
| } |
| |
| /*---------------------------------------------------------------------------- |
| * WavePause() |
| *---------------------------------------------------------------------------- |
| * Purpose: |
| * Mute and stop rendering a PCM stream. Sets the gain target to zero and stops the playback |
| * at the end of the next audio frame. |
| * |
| * Inputs: |
| * pEASData - pointer to EAS library instance data |
| * handle - pointer to S_WAVE_STATE for this stream |
| * |
| * Outputs: |
| * |
| * |
| * Side Effects: |
| * |
| *---------------------------------------------------------------------------- |
| */ |
| /*lint -esym(715, pEASData) reserved for future use */ |
| static EAS_RESULT WavePause (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData) |
| { |
| EAS_PCM_HANDLE streamHandle; |
| |
| /* pause the stream */ |
| streamHandle = ((S_WAVE_STATE*)pInstData)->streamHandle; |
| if (streamHandle) |
| return EAS_PEPause(pEASData, streamHandle); |
| return EAS_ERROR_NOT_VALID_IN_THIS_STATE; |
| } |
| |
| /*---------------------------------------------------------------------------- |
| * WaveResume() |
| *---------------------------------------------------------------------------- |
| * Purpose: |
| * Resume rendering a PCM stream. Sets the gain target back to its |
| * previous setting and restarts playback at the end of the next audio |
| * frame. |
| * |
| * Inputs: |
| * pEASData - pointer to EAS library instance data |
| * handle - pointer to S_WAVE_STATE for this stream |
| * |
| * Outputs: |
| * |
| * |
| * Side Effects: |
| * |
| *---------------------------------------------------------------------------- |
| */ |
| /*lint -esym(715, pEASData) reserved for future use */ |
| static EAS_RESULT WaveResume (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData) |
| { |
| EAS_PCM_HANDLE streamHandle; |
| |
| /* resume the stream */ |
| streamHandle = ((S_WAVE_STATE*)pInstData)->streamHandle; |
| if (streamHandle) |
| return EAS_PEResume(pEASData, streamHandle); |
| return EAS_ERROR_NOT_VALID_IN_THIS_STATE; |
| } |
| |
| /*---------------------------------------------------------------------------- |
| * WaveSetData() |
| *---------------------------------------------------------------------------- |
| * Purpose: |
| * |
| * Inputs: |
| * pEASData - pointer to EAS library instance data |
| * handle - pointer to S_WAVE_STATE for this stream |
| * |
| * Outputs: |
| * |
| * |
| * Side Effects: |
| * |
| *---------------------------------------------------------------------------- |
| */ |
| static EAS_RESULT WaveSetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 value) |
| { |
| S_WAVE_STATE *pWaveData = (S_WAVE_STATE*) pInstData; |
| |
| switch (param) |
| { |
| /* set metadata callback */ |
| case PARSER_DATA_METADATA_CB: |
| EAS_HWMemCpy(&pWaveData->metadata, (void*) value, sizeof(S_METADATA_CB)); |
| return EAS_SUCCESS; |
| |
| case PARSER_DATA_PLAYBACK_RATE: |
| value = (EAS_I32) (PITCH_CENTS_CONVERSION * log10((double) value / (double) (1 << 28))); |
| return EAS_PEUpdatePitch(pEASData, pWaveData->streamHandle, (EAS_I16) value); |
| |
| case PARSER_DATA_VOLUME: |
| return EAS_PEUpdateVolume(pEASData, pWaveData->streamHandle, (EAS_I16) value); |
| |
| default: |
| return EAS_ERROR_INVALID_PARAMETER; |
| } |
| } |
| |
| /*---------------------------------------------------------------------------- |
| * WaveGetData() |
| *---------------------------------------------------------------------------- |
| * Purpose: |
| * |
| * Inputs: |
| * pEASData - pointer to EAS library instance data |
| * handle - pointer to S_WAVE_STATE for this stream |
| * |
| * Outputs: |
| * |
| * |
| * Side Effects: |
| * |
| *---------------------------------------------------------------------------- |
| */ |
| /*lint -esym(715, pEASData) reserved for future use */ |
| static EAS_RESULT WaveGetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 *pValue) |
| { |
| S_WAVE_STATE *pWaveData; |
| |
| pWaveData = (S_WAVE_STATE*) pInstData; |
| switch (param) |
| { |
| /* return file type as WAVE */ |
| case PARSER_DATA_FILE_TYPE: |
| *pValue = pWaveData->fileType; |
| break; |
| |
| #ifdef MMAPI_SUPPORT |
| /* return pointer to 'fmt' chunk */ |
| case PARSER_DATA_FORMAT: |
| *pValue = (EAS_I32) pWaveData->fmtChunk; |
| break; |
| #endif |
| |
| case PARSER_DATA_GAIN_OFFSET: |
| *pValue = WAVE_GAIN_OFFSET; |
| break; |
| |
| default: |
| return EAS_ERROR_INVALID_PARAMETER; |
| } |
| |
| return EAS_SUCCESS; |
| } |
| |
| /*---------------------------------------------------------------------------- |
| * WaveParseHeader() |
| *---------------------------------------------------------------------------- |
| * Purpose: |
| * Parse the WAVE file header. |
| * |
| * Inputs: |
| * pEASData - pointer to EAS library instance data |
| * handle - pointer to S_WAVE_STATE for this stream |
| * |
| * Outputs: |
| * |
| * |
| * Side Effects: |
| * |
| *---------------------------------------------------------------------------- |
| */ |
| static EAS_RESULT WaveParseHeader (S_EAS_DATA *pEASData, EAS_FILE_HANDLE fileHandle, S_WAVE_STATE *pWaveData) |
| { |
| S_PCM_OPEN_PARAMS params; |
| EAS_RESULT result; |
| EAS_U32 tag; |
| EAS_U32 fileSize; |
| EAS_U32 size; |
| EAS_I32 pos; |
| EAS_I32 audioOffset; |
| EAS_U16 usTemp; |
| EAS_BOOL parseDone; |
| EAS_U32 avgBytesPerSec; |
| |
| /* init some data (and keep lint happy) */ |
| params.sampleRate = 0; |
| params.size = 0; |
| audioOffset = 0; |
| params.decoder = 0; |
| params.blockSize = 0; |
| params.pCallbackFunc = NULL; |
| params.cbInstData = NULL; |
| params.loopSamples = 0; |
| params.fileHandle = fileHandle; |
| params.volume = 0x7fff; |
| params.envData = 0; |
| avgBytesPerSec = 8000; |
| |
| /* check for 'RIFF' tag */ |
| if ((result = EAS_HWGetDWord(pEASData->hwInstData, fileHandle, &tag, EAS_TRUE)) != EAS_FALSE) |
| return result; |
| if (tag != CHUNK_RIFF) |
| return EAS_ERROR_UNRECOGNIZED_FORMAT; |
| |
| /* get size */ |
| if ((result = EAS_HWGetDWord(pEASData->hwInstData, fileHandle, &fileSize, EAS_FALSE)) != EAS_FALSE) |
| return result; |
| |
| /* check for 'WAVE' tag */ |
| if ((result = EAS_HWGetDWord(pEASData->hwInstData, fileHandle, &tag, EAS_TRUE)) != EAS_FALSE) |
| return result; |
| if (tag != CHUNK_WAVE) |
| return EAS_ERROR_UNRECOGNIZED_FORMAT; |
| |
| /* this is enough to say we recognize the file */ |
| if (pWaveData == NULL) |
| return EAS_SUCCESS; |
| |
| /* check for streaming mode */ |
| pWaveData->flags = 0; |
| pWaveData->mediaLength = -1; |
| pWaveData->infoChunkPos = -1; |
| pWaveData->infoChunkSize = -1; |
| if (fileSize== FILE_SIZE_STREAMING) |
| { |
| pWaveData->flags |= PCM_FLAGS_STREAMING; |
| fileSize = 0x7fffffff; |
| } |
| |
| /* find out where we're at */ |
| if ((result = EAS_HWFilePos(pEASData->hwInstData, fileHandle, &pos)) != EAS_SUCCESS) |
| return result; |
| fileSize -= 4; |
| |
| parseDone = EAS_FALSE; |
| for (;;) |
| { |
| /* get tag and size for next chunk */ |
| if ((result = EAS_HWGetDWord(pEASData->hwInstData, fileHandle, &tag, EAS_TRUE)) != EAS_FALSE) |
| return result; |
| if ((result = EAS_HWGetDWord(pEASData->hwInstData, fileHandle, &size, EAS_FALSE)) != EAS_FALSE) |
| return result; |
| |
| /* process chunk */ |
| pos += 8; |
| switch (tag) |
| { |
| case CHUNK_FMT: |
| |
| #ifdef MMAPI_SUPPORT |
| if ((result = SaveFmtChunk(pEASData, fileHandle, pWaveData, (EAS_I32) size)) != EAS_SUCCESS) |
| return result; |
| #endif |
| |
| /* get audio format */ |
| if ((result = EAS_HWGetWord(pEASData->hwInstData, fileHandle, &usTemp, EAS_FALSE)) != EAS_FALSE) |
| return result; |
| if (usTemp == WAVE_FORMAT_PCM) |
| { |
| params.decoder = EAS_DECODER_PCM; |
| pWaveData->fileType = EAS_FILE_WAVE_PCM; |
| } |
| else if (usTemp == WAVE_FORMAT_IMA_ADPCM) |
| { |
| params.decoder = EAS_DECODER_IMA_ADPCM; |
| pWaveData->fileType = EAS_FILE_WAVE_IMA_ADPCM; |
| } |
| else |
| return EAS_ERROR_UNRECOGNIZED_FORMAT; |
| |
| /* get number of channels */ |
| if ((result = EAS_HWGetWord(pEASData->hwInstData, fileHandle, &usTemp, EAS_FALSE)) != EAS_FALSE) |
| return result; |
| if (usTemp == 2) |
| pWaveData->flags |= PCM_FLAGS_STEREO; |
| else if (usTemp != 1) |
| return EAS_ERROR_UNRECOGNIZED_FORMAT; |
| |
| /* get sample rate */ |
| if ((result = EAS_HWGetDWord(pEASData->hwInstData, fileHandle, ¶ms.sampleRate, EAS_FALSE)) != EAS_FALSE) |
| return result; |
| |
| /* get stream rate */ |
| if ((result = EAS_HWGetDWord(pEASData->hwInstData, fileHandle, &avgBytesPerSec, EAS_FALSE)) != EAS_FALSE) |
| return result; |
| |
| /* get block alignment */ |
| if ((result = EAS_HWGetWord(pEASData->hwInstData, fileHandle, &usTemp, EAS_FALSE)) != EAS_FALSE) |
| return result; |
| params.blockSize = usTemp; |
| |
| /* get bits per sample */ |
| if ((result = EAS_HWGetWord(pEASData->hwInstData, fileHandle, &usTemp, EAS_FALSE)) != EAS_FALSE) |
| return result; |
| |
| /* PCM, must be 8 or 16 bit samples */ |
| if (params.decoder == EAS_DECODER_PCM) |
| { |
| if (usTemp == 8) |
| pWaveData->flags |= PCM_FLAGS_8_BIT | PCM_FLAGS_UNSIGNED; |
| else if (usTemp != 16) |
| return EAS_ERROR_UNRECOGNIZED_FORMAT; |
| } |
| |
| /* for IMA ADPCM, we only support mono 4-bit ADPCM */ |
| else |
| { |
| if ((usTemp != 4) || (pWaveData->flags & PCM_FLAGS_STEREO)) |
| return EAS_ERROR_UNRECOGNIZED_FORMAT; |
| } |
| |
| break; |
| |
| case CHUNK_DATA: |
| audioOffset = pos; |
| if (pWaveData->flags & PCM_FLAGS_STREAMING) |
| { |
| params.size = 0x7fffffff; |
| parseDone = EAS_TRUE; |
| } |
| else |
| { |
| params.size = (EAS_I32) size; |
| params.loopStart = size; |
| /* use more accurate method if possible */ |
| if (size <= (0x7fffffff / 1000)) |
| pWaveData->mediaLength = (EAS_I32) ((size * 1000) / avgBytesPerSec); |
| else |
| pWaveData->mediaLength = (EAS_I32) (size / (avgBytesPerSec / 1000)); |
| } |
| break; |
| |
| case CHUNK_LIST: |
| /* get the list type */ |
| if ((result = EAS_HWGetDWord(pEASData->hwInstData, fileHandle, &tag, EAS_TRUE)) != EAS_FALSE) |
| return result; |
| if (tag == CHUNK_INFO) |
| { |
| pWaveData->infoChunkPos = pos + 4; |
| pWaveData->infoChunkSize = (EAS_I32) size - 4; |
| } |
| break; |
| |
| default: |
| { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "WaveParseHeader: %c%c%c%c chunk - %d byte(s) ignored\n", |
| (char) (tag >> 24), (char) (tag >> 16), (char) (tag >> 8), (char) tag, size); */ } |
| break; |
| } |
| |
| if (parseDone) |
| break; |
| |
| /* subtract header size */ |
| fileSize -= 8; |
| |
| /* account for zero-padding on odd length chunks */ |
| if (size & 1) |
| size++; |
| |
| /* this check works for files with odd length last chunk and no zero-pad */ |
| if (size >= fileSize) |
| { |
| if (size > fileSize) |
| { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "WaveParseHeader: '%c%c%c%c' chunk size exceeds length of file or is not zero-padded\n", |
| (char) (tag >> 24), (char) (tag >> 16), (char) (tag >> 8), (char) tag, size); */ } |
| break; |
| } |
| |
| /* subtract size of data chunk (including any zero-pad) */ |
| fileSize -= size; |
| |
| /* seek to next chunk */ |
| pos += (EAS_I32) size; |
| if ((result = EAS_HWFileSeek(pEASData->hwInstData, fileHandle, pos)) != EAS_SUCCESS) |
| return result; |
| } |
| |
| /* check for valid header */ |
| if ((params.sampleRate == 0) || (params.size == 0)) |
| return EAS_ERROR_UNRECOGNIZED_FORMAT; |
| |
| /* save the pertinent information */ |
| pWaveData->audioOffset = audioOffset; |
| params.flags = pWaveData->flags; |
| |
| /* seek to data */ |
| if ((result = EAS_HWFileSeek(pEASData->hwInstData, fileHandle, audioOffset)) != EAS_SUCCESS) |
| return result; |
| |
| /* open a stream in the PCM engine */ |
| return EAS_PEOpenStream(pEASData, ¶ms, &pWaveData->streamHandle); |
| } |
| |
| /*---------------------------------------------------------------------------- |
| * WaveGetMetaData() |
| *---------------------------------------------------------------------------- |
| * Purpose: |
| * Process the INFO chunk and return metadata to host |
| *---------------------------------------------------------------------------- |
| */ |
| static EAS_RESULT WaveGetMetaData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 *pMediaLength) |
| { |
| S_WAVE_STATE *pWaveData; |
| EAS_RESULT result; |
| EAS_I32 pos; |
| EAS_U32 size; |
| EAS_I32 infoSize; |
| EAS_U32 tag; |
| EAS_I32 restorePos; |
| E_EAS_METADATA_TYPE metaType; |
| EAS_I32 metaLen; |
| |
| /* get current position so we can restore it */ |
| pWaveData = (S_WAVE_STATE*) pInstData; |
| |
| /* return media length */ |
| *pMediaLength = pWaveData->mediaLength; |
| |
| /* did we encounter an INFO chunk? */ |
| if (pWaveData->infoChunkPos < 0) |
| return EAS_SUCCESS; |
| |
| if ((result = EAS_HWFilePos(pEASData->hwInstData, pWaveData->fileHandle, &restorePos)) != EAS_SUCCESS) |
| return result; |
| |
| /* offset to start of first chunk in INFO chunk */ |
| pos = pWaveData->infoChunkPos; |
| infoSize = pWaveData->infoChunkSize; |
| |
| /* read all the chunks in the INFO chunk */ |
| for (;;) |
| { |
| |
| /* seek to next chunk */ |
| if ((result = EAS_HWFileSeek(pEASData->hwInstData, pWaveData->fileHandle, pos)) != EAS_SUCCESS) |
| return result; |
| |
| /* get tag and size for next chunk */ |
| if ((result = EAS_HWGetDWord(pEASData->hwInstData, pWaveData->fileHandle, &tag, EAS_TRUE)) != EAS_FALSE) |
| return result; |
| if ((result = EAS_HWGetDWord(pEASData->hwInstData, pWaveData->fileHandle, &size, EAS_FALSE)) != EAS_FALSE) |
| return result; |
| |
| /* process chunk */ |
| pos += 8; |
| metaType = EAS_METADATA_UNKNOWN; |
| switch (tag) |
| { |
| case CHUNK_INAM: |
| metaType = EAS_METADATA_TITLE; |
| break; |
| |
| case CHUNK_IART: |
| metaType = EAS_METADATA_AUTHOR; |
| break; |
| |
| case CHUNK_ICOP: |
| metaType = EAS_METADATA_COPYRIGHT; |
| break; |
| |
| default: |
| { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "WaveParseHeader: %c%c%c%c chunk - %d byte(s) ignored\n", |
| (char) (tag >> 24), (char) (tag >> 16), (char) (tag >> 8), (char) tag, size); */ } |
| break; |
| } |
| |
| /* process known metadata */ |
| if (metaType != EAS_METADATA_UNKNOWN) |
| { |
| metaLen = pWaveData->metadata.bufferSize - 1; |
| if (metaLen > (EAS_I32) size) |
| metaLen = (EAS_I32) size; |
| if ((result = EAS_HWReadFile(pEASData->hwInstData, pWaveData->fileHandle, pWaveData->metadata.buffer, metaLen, &metaLen)) != EAS_SUCCESS) |
| return result; |
| pWaveData->metadata.buffer[metaLen] = 0; |
| pWaveData->metadata.callback(metaType, pWaveData->metadata.buffer, pWaveData->metadata.pUserData); |
| } |
| |
| /* subtract this block */ |
| if (size & 1) |
| size++; |
| infoSize -= (EAS_I32) size + 8; |
| if (infoSize == 0) |
| break; |
| pos += (EAS_I32) size; |
| } |
| |
| |
| /* restore original position */ |
| return EAS_HWFileSeek(pEASData->hwInstData, pWaveData->fileHandle, restorePos); |
| } |
| |
| #ifdef MMAPI_SUPPORT |
| /*---------------------------------------------------------------------------- |
| * SaveFmtChunk() |
| *---------------------------------------------------------------------------- |
| * Purpose: |
| * Save the fmt chunk for the MMAPI library |
| *---------------------------------------------------------------------------- |
| */ |
| static EAS_RESULT SaveFmtChunk (S_EAS_DATA *pEASData, EAS_FILE_HANDLE fileHandle, S_WAVE_STATE *pWaveData, EAS_I32 fmtSize) |
| { |
| EAS_RESULT result; |
| EAS_I32 pos; |
| EAS_I32 count; |
| |
| /* save current file position */ |
| if ((result = EAS_HWFilePos(pEASData->hwInstData, fileHandle, &pos)) != EAS_SUCCESS) |
| return result; |
| |
| /* allocate a chunk of memory */ |
| pWaveData->fmtChunk = EAS_HWMalloc(pEASData->hwInstData, fmtSize); |
| if (!pWaveData->fmtChunk) |
| return EAS_ERROR_MALLOC_FAILED; |
| |
| /* read the fmt chunk into memory */ |
| if ((result = EAS_HWReadFile(pEASData->hwInstData, fileHandle, pWaveData->fmtChunk, fmtSize, &count)) != EAS_SUCCESS) |
| return result; |
| if (count != fmtSize) |
| return EAS_ERROR_FILE_READ_FAILED; |
| |
| /* restore file position */ |
| return EAS_HWFileSeek(pEASData->hwInstData, fileHandle, pos); |
| } |
| #endif |
| |