C++程序  |  424行  |  9.96 KB

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