Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
_source/WAVE.c
/* |
File: WAVE.c |
Contains: Routines demonstrating how to parse Microsoft's WAVE sound files. |
Written by: Mark Cookson |
Copyright: Copyright ©1996-1999 by Apple Computer, Inc., All Rights Reserved. |
You may incorporate this Apple sample source code into your program(s) without |
restriction. This Apple sample source code has been provided "AS IS" and the |
responsibility for its operation is yours. You are not permitted to redistribute |
this Apple sample source code as "Apple sample source code" after having made |
changes. If you're going to re-distribute the source, we require that you make |
it clear in the source that the code was descended from Apple sample source |
code, but that you've made changes. |
Change History (most recent first): |
8/31/1999 Karl Groethe Updated for Metrowerks Codewarror Pro 2.1 |
*/ |
#include "WAVE.h" |
/* Please note that this code was written in less than 6 hours, does very little error checking |
and probably will not work for all files. I have tested it with the WAVE files at my |
disposal and it worked with them. Your milage may vary. |
I do know that it will probably be impossible to get the Mac's IMA decompressor to |
decompress IMA (and ADPCM) compressed WAVE files because the Mac's IMA has a different |
file format from the WAVE file and the decompressor cannot correctly decompress WAVE files. |
The Mac IMA compressed file has predictor bytes in the data stream that the WAVE files |
do not have. |
*/ |
/* Given an WAVE File gets the sample rate and other relavent pieces of information. |
This has been modified from its orignal form found in the Develop 11 MultiBuffer source. */ |
/*-----------------------------------------------------------------------*/ |
OSErr ASoundGetWAVEHeader (SoundInfoPtr theSoundInfo, |
long *dataStart, |
long *length) |
/*-----------------------------------------------------------------------*/ |
{ |
WAVETemplatePtr WAVETemplate; /* ...for ease of access */ |
long filePosition = kInit, |
byteCount = kInit; |
short format = kInit; |
unsigned short chunkFlags = kInit; /* remember chunks we've seen */ |
OSErr err = noErr; |
char chunkBuffer[kWAVEChunkBufferSize]; |
*dataStart = kInit; |
/* Now for the fun part! We're going to assume that these are generic, non-compressed |
WAVE files and parse accordingly... */ |
do { |
/* Position ourselves at the beginning of the next chunk and read in |
a hunk-o-data... */ |
err = SetFPos (theSoundInfo->refNum, fsFromStart, filePosition); |
if (err != noErr) { |
DebugPrint ("\pSetFPos failed!"); |
break; |
} |
byteCount = kWAVEChunkBufferSize; |
err = FSRead (theSoundInfo->refNum, &byteCount, chunkBuffer); /* read a chunk */ |
if ((err != noErr) && (err != eofErr)) { |
DebugPrint ("\pFSRead failed!"); |
break; |
} |
/* Now, position the template over the data... */ |
WAVETemplate = (WAVETemplatePtr) chunkBuffer; |
/* assume a failure and break out of the do {} while loop if the next case isn't found. |
if the case is found and no other error is detected, then each case needs to set noErr */ |
err = kBadWAVEFileFormat; |
switch (WAVETemplate->generic.ckID) { |
case WAVEFORMID: |
chunkFlags |= kWAVEFORMID; /* otherwise mark it found */ |
filePosition += 12; |
err = noErr; |
break; |
case FormatID: |
chunkFlags |= kFormatID; /* otherwise mark it found */ |
format = SwapShort (WAVETemplate->fmt.wFormatTag); |
theSoundInfo->needsMasking = false; |
switch (format) { |
case WAVE_FORMAT_PCM: |
err = SetupDBHeader (theSoundInfo, |
SwapLong (WAVETemplate->fmt.dwSamplesPerSec), |
((SwapShort (WAVETemplate->fmt.wBlockAlign) / SwapShort (WAVETemplate->fmt.wChannels)) * 8), |
SwapShort (WAVETemplate->fmt.wChannels), |
notCompressed, |
NoneType); |
break; |
case WAVE_FORMAT_MULAW: |
case IBM_FORMAT_MULAW: |
err = SetupDBHeader (theSoundInfo, |
SwapLong (WAVETemplate->fmt.dwSamplesPerSec), |
((SwapShort (WAVETemplate->fmt.wBlockAlign) / SwapShort (WAVETemplate->fmt.wChannels)) * 8), |
SwapShort (WAVETemplate->fmt.wChannels), |
fixedCompression, |
kULawCompression); |
filePosition += 2; /* This is here because the only µlaw compressed file I ever saw needed it. */ |
break; |
default: |
DebugPrint ("\pCan't deal with this file format"); |
err = kUnknownFormat; |
break; |
} |
filePosition += 24; |
break; |
case WAVEDataID: |
chunkFlags |= kWAVEDataID; |
*length = ReverseLong (WAVETemplate->waveData.ckSize); |
filePosition += 8; |
*dataStart = filePosition; |
err = noErr; |
break; |
/* We don't care about any of these, and my handling of them is probably not correct */ |
case WAVEID: |
err = noErr; |
filePosition += 4; |
break; |
case WAVEListID: |
err = noErr; |
filePosition += 4; |
break; |
case SilenceID: |
err = noErr; |
filePosition += 4; |
break; |
case CueID: |
err = noErr; |
filePosition += 24; |
break; |
case FactID: |
err = noErr; |
filePosition += 4; |
break; |
case PlaylistID: |
err = noErr; |
filePosition += 12; |
break; |
case AssocDataID: |
err = noErr; |
filePosition += 32; |
break; |
case LabelID: |
err = noErr; |
filePosition += 4; |
break; |
case NoteID: |
err = noErr; |
filePosition += 4; |
break; |
case TextWithLenID: |
err = noErr; |
filePosition += 16; |
break; |
case EmbededFileID: |
err = noErr; |
filePosition += 8; |
break; |
default: |
//DebugPrint ("\pSaw a field we didn't know about, this doesn't have to be bad"); |
/* Start walking through the file looking for an ID we know about. |
This could get ugly, but what the heck... */ |
filePosition += 1; |
err = noErr; |
break; |
} |
} while (stillMoreDataWAVEToRead); |
if (err != noErr) { |
DebugPrint ("\pError in ASoundGetWAVEHeader"); |
} |
return err; |
} |
/* These are here for the endian conversion required to parse the header. |
The endian conversion used when playing a WAVE file is much better, don't worry. |
*/ |
short SwapShort (const short theShort) { |
char tempChar = kInit; |
short newShort = kInit; |
tempChar = ((char *)&theShort)[0]; |
((char *)&newShort)[0] = ((char *)&theShort)[1]; |
((char *)&newShort)[1] = tempChar; |
return newShort; |
} |
long SwapLong (const long theLong) { |
short tempShort = kInit; |
long newLong = kInit; |
tempShort = SwapShort (((short *)&theLong)[0]); |
((short *)&newLong)[0] = tempShort; |
tempShort = SwapShort (((short *)&theLong)[1]); |
((short *)&newLong)[1] = tempShort; |
return newLong; |
} |
long ReverseLong (const long theLong) { |
char tempChar = kInit, |
i = kInit; |
long newLong = kInit; |
for (i = nil; i < 2; i++) { |
tempChar = ((char *)&theLong)[i]; |
((char *)&newLong)[i] = ((char *)&theLong)[3-i]; |
((char *)&newLong)[3-i] = tempChar; |
} |
return newLong; |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-03-14