/*----------------------------------------------------------------------------
*
* File:
* eas_wave.c
*
* Contents and purpose:
* This module contains .WAV file functions for the EAS synthesizer
* test harness.
*
* 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: 658 $
* $Date: 2007-04-24 13:35:49 -0700 (Tue, 24 Apr 2007) $
*----------------------------------------------------------------------------
*/
/* lint complaints about most C library headers, so we use our own during lint step */
#ifdef _lint
#include "lint_stdlib.h"
#else
#include <stdio.h>
#include <stdlib.h>
#endif
#include "eas_wave.h"
/* .WAV file format tags */
const EAS_U32 riffTag = 0x46464952;
const EAS_U32 waveTag = 0x45564157;
const EAS_U32 fmtTag = 0x20746d66;
const EAS_U32 dataTag = 0x61746164;
#ifdef _BIG_ENDIAN
/*----------------------------------------------------------------------------
* FlipDWord()
*----------------------------------------------------------------------------
* Purpose: Endian flip a DWORD for big-endian processors
*
* Inputs:
*
* Outputs:
*
*----------------------------------------------------------------------------
*/
static void FlipDWord (EAS_U32 *pValue)
{
EAS_U8 *p;
EAS_U32 temp;
p = (EAS_U8*) pValue;
temp = (((((p[3] << 8) | p[2]) << 8) | p[1]) << 8) | p[0];
*pValue = temp;
}
/*----------------------------------------------------------------------------
* FlipWord()
*----------------------------------------------------------------------------
* Purpose: Endian flip a WORD for big-endian processors
*
* Inputs:
*
* Outputs:
*
*----------------------------------------------------------------------------
*/
static void FlipWord (EAS_U16 *pValue)
{
EAS_U8 *p;
EAS_U16 temp;
p = (EAS_U8*) pValue;
temp = (p[1] << 8) | p[0];
*pValue = temp;
}
/*----------------------------------------------------------------------------
* FlipWaveHeader()
*----------------------------------------------------------------------------
* Purpose: Endian flip the wave header for big-endian processors
*
* Inputs:
*
* Outputs:
*
*----------------------------------------------------------------------------
*/
static void FlipWaveHeader (WAVE_HEADER *p)
{
FlipDWord(&p->nRiffTag);
FlipDWord(&p->nRiffSize);
FlipDWord(&p->nWaveTag);
FlipDWord(&p->nFmtTag);
FlipDWord(&p->nFmtSize);
FlipDWord(&p->nDataTag);
FlipDWord(&p->nDataSize);
FlipWord(&p->fc.wFormatTag);
FlipWord(&p->fc.nChannels);
FlipDWord(&p->fc.nSamplesPerSec);
FlipDWord(&p->fc.nAvgBytesPerSec);
FlipWord(&p->fc.nBlockAlign);
FlipWord(&p->fc.wBitsPerSample);
}
#endif
/*----------------------------------------------------------------------------
* WaveFileCreate()
*----------------------------------------------------------------------------
* Purpose: Opens a wave file for writing and writes the header
*
* Inputs:
*
* Outputs:
*
*----------------------------------------------------------------------------
*/
WAVE_FILE *WaveFileCreate (const char *filename, EAS_I32 nChannels, EAS_I32 nSamplesPerSec, EAS_I32 wBitsPerSample)
{
WAVE_FILE *wFile;
/* allocate memory */
wFile = malloc(sizeof(WAVE_FILE));
if (!wFile)
return NULL;
wFile->write = EAS_TRUE;
/* create the file */
wFile->file = fopen(filename,"wb");
if (!wFile->file)
{
free(wFile);
return NULL;
}
/* initialize PCM format .WAV file header */
wFile->wh.nRiffTag = riffTag;
wFile->wh.nRiffSize = sizeof(WAVE_HEADER) - 8;
wFile->wh.nWaveTag = waveTag;
wFile->wh.nFmtTag = fmtTag;
wFile->wh.nFmtSize = sizeof(FMT_CHUNK);
/* initalize 'fmt' chunk */
wFile->wh.fc.wFormatTag = 1;
wFile->wh.fc.nChannels = (EAS_U16) nChannels;
wFile->wh.fc.nSamplesPerSec = (EAS_U32) nSamplesPerSec;
wFile->wh.fc.wBitsPerSample = (EAS_U16) wBitsPerSample;
wFile->wh.fc.nBlockAlign = (EAS_U16) (nChannels * (EAS_U16) (wBitsPerSample / 8));
wFile->wh.fc.nAvgBytesPerSec = wFile->wh.fc.nBlockAlign * (EAS_U32) nSamplesPerSec;
/* initialize 'data' chunk */
wFile->wh.nDataTag = dataTag;
wFile->wh.nDataSize = 0;
#ifdef _BIG_ENDIAN
FlipWaveHeader(&wFile->wh);
#endif
/* write the header */
if (fwrite(&wFile->wh, sizeof(WAVE_HEADER), 1, wFile->file) != 1)
{
fclose(wFile->file);
free(wFile);
return NULL;
}
#ifdef _BIG_ENDIAN
FlipWaveHeader(&wFile->wh);
#endif
/* return the file handle */
return wFile;
} /* end WaveFileCreate */
/*----------------------------------------------------------------------------
* WaveFileWrite()
*----------------------------------------------------------------------------
* Purpose: Writes data to the wave file
*
* Inputs:
*
* Outputs:
*
*----------------------------------------------------------------------------
*/
EAS_I32 WaveFileWrite (WAVE_FILE *wFile, void *buffer, EAS_I32 n)
{
EAS_I32 count;
/* make sure we have an open file */
if (wFile == NULL)
{
return 0;
}
#ifdef _BIG_ENDIAN
{
EAS_I32 i;
EAS_U16 *p;
p = buffer;
i = n >> 1;
while (i--)
FlipWord(p++);
}
#endif
/* write the data */
count = (EAS_I32) fwrite(buffer, 1, (size_t) n, wFile->file);
/* add the number of bytes written */
wFile->wh.nRiffSize += (EAS_U32) count;
wFile->wh.nDataSize += (EAS_U32) count;
/* return the count of bytes written */
return count;
} /* end WriteWaveHeader */
/*----------------------------------------------------------------------------
* WaveFileClose()
*----------------------------------------------------------------------------
* Purpose: Opens a wave file for writing and writes the header
*
* Inputs:
*
* Outputs:
*
*----------------------------------------------------------------------------
*/
EAS_BOOL WaveFileClose (WAVE_FILE *wFile)
{
EAS_I32 count = 1;
/* return to beginning of file and write the header */
if (wFile->write)
{
if (fseek(wFile->file, 0L, SEEK_SET) == 0)
{
#ifdef _BIG_ENDIAN
FlipWaveHeader(&wFile->wh);
#endif
count = (EAS_I32) fwrite(&wFile->wh, sizeof(WAVE_HEADER), 1, wFile->file);
#ifdef _BIG_ENDIAN
FlipWaveHeader(&wFile->wh);
#endif
}
}
/* close the file */
if (fclose(wFile->file) != 0)
count = 0;
/* free the memory */
free(wFile);
/* return the file handle */
return (count == 1 ? EAS_TRUE : EAS_FALSE);
} /* end WaveFileClose */
#ifdef _WAVE_FILE_READ
#ifdef _BIG_ENDIAN
#error "WaveFileOpen not currently supported on big-endian processors"
#endif
/*----------------------------------------------------------------------------
* WaveFileOpen()
*----------------------------------------------------------------------------
* Purpose: Opens a wave file for reading and reads the header
*
* Inputs:
*
* Outputs:
*
*----------------------------------------------------------------------------
*/
WAVE_FILE *WaveFileOpen (const char *filename)
{
WAVE_FILE *wFile;
struct
{
EAS_U32 tag;
EAS_U32 size;
} chunk;
EAS_U32 tag;
EAS_I32 startChunkPos;
EAS_INT state;
EAS_BOOL done;
/* allocate memory */
wFile = malloc(sizeof(WAVE_FILE));
if (!wFile)
return NULL;
/* open the file */
wFile->write = EAS_FALSE;
wFile->file = fopen(filename,"rb");
if (!wFile->file)
{
free(wFile);
return NULL;
}
/* make lint happy */
chunk.tag = chunk.size = 0;
startChunkPos = 0;
/* read the RIFF tag and file size */
state = 0;
done = EAS_FALSE;
while (!done)
{
switch(state)
{
/* read the RIFF tag */
case 0:
if (fread(&chunk, sizeof(chunk), 1, wFile->file) != 1)
done = EAS_TRUE;
else
{
if (chunk.tag != riffTag)
done = EAS_TRUE;
else
state++;
}
break;
/* read the WAVE tag */
case 1:
if (fread(&tag, sizeof(tag), 1, wFile->file) != 1)
done = EAS_TRUE;
else
{
if (tag != waveTag)
done = EAS_TRUE;
else
state++;
}
break;
/* looking for fmt chunk */
case 2:
if (fread(&chunk, sizeof(chunk), 1, wFile->file) != 1)
done = EAS_TRUE;
else
{
startChunkPos = ftell(wFile->file);
/* not fmt tag, skip it */
if (chunk.tag != fmtTag)
fseek(wFile->file, startChunkPos + (EAS_I32) chunk.size, SEEK_SET);
else
state++;
}
break;
/* read fmt chunk */
case 3:
if (fread(&wFile->wh.fc, sizeof(FMT_CHUNK), 1, wFile->file) != 1)
done = EAS_TRUE;
else
{
fseek(wFile->file, startChunkPos + (EAS_I32) chunk.size, SEEK_SET);
state++;
}
break;
/* looking for data chunk */
case 4:
if (fread(&chunk, sizeof(chunk), 1, wFile->file) != 1)
done = EAS_TRUE;
else
{
startChunkPos = ftell(wFile->file);
/* not data tag, skip it */
if (chunk.tag != dataTag)
fseek(wFile->file, startChunkPos + (EAS_I32) chunk.size, SEEK_SET);
else
{
wFile->dataSize = chunk.size;
state++;
done = EAS_TRUE;
}
}
break;
default:
done = EAS_TRUE;
break;
}
}
/* if not final state, an error occurred */
if (state != 5)
{
fclose(wFile->file);
free(wFile);
return NULL;
}
/* return the file handle */
return wFile;
} /* end WaveFileOpen */
#endif