_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;
}