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