867 lines
28 KiB
C
867 lines
28 KiB
C
/*----------------------------------------------------------------------------
|
|
*
|
|
* 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
|
|
|