/*---------------------------------------------------------------------------- | |
* | |
* 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 | |