_source/AIFF.c

/*
    File:       AIFF.c
 
    Contains:   Routine demonstrating how to parse AIFF 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 "MyAIFF.h"
 
/*
    need to substitute this code where using long doubles
 
typedef struct myLongDouble {
    double  firstHalf;
    double  secondHalf;
} myLongDouble;
 
*/
 
/* Given an AIFF 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   ASoundGetAIFFHeader     (SoundInfoPtr theSoundInfo,
                                        long *dataStart,
                                        long *length)
/*-----------------------------------------------------------------------*/
{
    ChunkTemplatePtr        chunkTemplate       = nil;          /* ...for ease of access */
    unsigned long           type                = kInit;
    long                    filePosition        = kInit,
                            byteCount           = kInit;
    Fixed                   sampleRate          = kInit;
    unsigned short          chunkFlags          = kInit;        /* remember chunks we've seen */
    OSErr                   err                 = noErr;
    char                    chunkBuffer[kChunkBufferSize];
 
    *dataStart = kInit;
 
    /*  Parse the AIFF (or AIFC) header */
 
    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 = kChunkBufferSize;
        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... */
        chunkTemplate = (ChunkTemplatePtr) 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 = badFileFormat;                                
        switch (chunkTemplate->generic.ckID) {
            case FORMID:                                                                    /* Format Version Chunk? */
                /*  make sure that this is a standard, noncompressed AIFF file. */
                if ((chunkFlags & kFORM) == false) {                                        /* see if this chunk already exists */
                    chunkFlags |= kFORM;                                                    /* otherwise mark it found */
                    *length = chunkTemplate->container.ckSize;
                    filePosition += sizeof (ContainerChunk);        /* Can't use ckSize because it's the size of the file, not this header */
                    type = chunkTemplate->container.formType;
                    err = noErr;
                }
                break;
            case FormatVersionID:                                                           /* Format Version Chunk? */
                if ((chunkFlags & kFormatVersion) == false) {                               /* see if this chunk already exists */
                    chunkFlags |= kFormatVersion;                                           /* otherwise mark it found */
                    filePosition += chunkTemplate->formatVersion.ckSize + kChunkHeaderSize; /* calculate next chunk's position */
                    if (chunkTemplate->formatVersion.timestamp != AIFCVersion1) {
                        err = kUnknownFormat;
                    }
                    else {
                        err = noErr;
                    }
                }
                break;
            case CommonID:
                if ((chunkFlags & kCommon) == false) {                                      /* see if this chunk already exists */
                    long double     tempLD  = 0.0;
 
                    chunkFlags |= kCommon;                                                  /* otherwise mark it found */
                    filePosition += chunkTemplate->common.ckSize + kChunkHeaderSize;        /* calculate next chunk's position */
 
                    #ifdef powerc
                        x80told (&(chunkTemplate->common.sampleRate), &tempLD);
                    #else
                        tempLD = chunkTemplate->common.sampleRate;
                    #endif
                    sampleRate = ASoundLongDoubleToFix (tempLD);
 
                    if (type == AIFCID) { 
                        err = SetupDBHeader (theSoundInfo,
                                            sampleRate,
                                            chunkTemplate->common.sampleSize,
                                            chunkTemplate->common.numChannels,
                                            fixedCompression,
                                            chunkTemplate->extCommon.compressionType);
                        theSoundInfo->needsMasking = false;
                    }
                    else {
                        err = SetupDBHeader (theSoundInfo,
                                            sampleRate,
                                            chunkTemplate->common.sampleSize,
                                            chunkTemplate->common.numChannels,
                                            notCompressed,
                                            NoneType);
                        if (chunkTemplate->common.sampleSize == kSixteen) {
                            theSoundInfo->needsMasking = false;
                        }
                        else {
                            theSoundInfo->needsMasking = true;
                        }
                    }
                }
                break;
            case SoundDataID:
                if ((chunkFlags & kSoundData) == false) {                                               /* see if this chunk already exists */
 
                    /*  Let's remember where the Sound Data starts, so we can find the position in the file later.
                        The mark will be positioned at the beginning of the chunk, so move 16 bytes past to get past
                        the header information to the data. */
                    *dataStart = filePosition + 16;
                    chunkFlags |= kSoundData;                                                           /* otherwise mark it found */
                    filePosition += chunkTemplate->soundData.ckSize + kChunkHeaderSize;                 /* calculate next chunk's position */
                    err = noErr;
                }
                break;
            /*
                The following list of chunks we don't care about, so we'll just skip over them.
            */
            case MarkerID:
                if ((chunkFlags & kMarker) == false) {                                                  /* see if this chunk already exists */
                    chunkFlags |= kMarker;                                                              /* otherwise mark it found */
                    filePosition += chunkTemplate->marker.ckSize + kChunkHeaderSize;                    /* calculate next chunk's position */
                    err = noErr;
                }
                break;
            case CommentID:
                if ((chunkFlags & kComment) == false) {                                                 /* see if this chunk already exists */
                    chunkFlags |= kComment;                                                             /* otherwise mark it found */
                    filePosition += chunkTemplate->marker.ckSize + kChunkHeaderSize;                    /* calculate next chunk's position */
                    err = noErr;
                }
                break;
            case InstrumentID:
                if ((chunkFlags & kInstrument) == false) {                                              /* see if this chunk already exists */
                    chunkFlags |= kInstrument;                                                          /* otherwise mark it found */
                    filePosition += chunkTemplate->instrument.ckSize + kChunkHeaderSize;                /* calculate next chunk's position */
                    err = noErr;
                }
                break;
            case MIDIDataID:
                if ((chunkFlags & kMIDIData) == false) {                                                /* see if this chunk already exists */
                    chunkFlags |= kMIDIData;                                                            /* otherwise mark it found */
                    filePosition += chunkTemplate->midiData.ckSize + kChunkHeaderSize;                  /* calculate next chunk's position */
                    err = noErr;
                }
                break;
            case AudioRecordingID:
                if ((chunkFlags & kAudioRecording) == false) {                                          /* see if this chunk already exists */
                    chunkFlags |= kAudioRecording;                                                      /* otherwise mark it found */
                    filePosition += chunkTemplate->audioRecording.ckSize + kChunkHeaderSize;            /* calculate next chunk's position */
                    err = noErr;
                }
                break;
            case ApplicationSpecificID:
                if ((chunkFlags & kApplicationSpecific) == false) {                                     /* see if this chunk already exists */
                    chunkFlags |= kApplicationSpecific;                                                 /* otherwise mark it found */
                    filePosition += chunkTemplate->generic.ckSize + kChunkHeaderSize;                   /* calculate next chunk's position */
                    err = noErr;
                }
                break;
            case NameID:
                if ((chunkFlags & kName) == false) {                                                    /* see if this chunk already exists */
                    chunkFlags |= kName;                                                                /* otherwise mark it found */
                    filePosition += chunkTemplate->generic.ckSize + kChunkHeaderSize;                   /* calculate next chunk's position */
                    err = noErr;
                }
                break;
            case AuthorID:
                if ((chunkFlags & kAuthor) == false) {                                                  /* see if this chunk already exists */
                    chunkFlags |= kAuthor;                                                              /* otherwise mark it found */
                    filePosition += chunkTemplate->generic.ckSize + kChunkHeaderSize;                   /* calculate next chunk's position */
                    err = noErr;
                }
                break;
            case CopyrightID:
                if ((chunkFlags & kCopyright) == false) {                                               /* see if this chunk already exists */
                    chunkFlags |= kCopyright;                                                           /* otherwise mark it found */
                    filePosition += chunkTemplate->generic.ckSize + kChunkHeaderSize;                   /* calculate next chunk's position */
                    err = noErr;
                }
                break;
            case AnnotationID:
                if ((chunkFlags & kAnnotation) == false) {                                              /* see if this chunk already exists */
                    chunkFlags |= kAnnotation;                                                          /* otherwise mark it found */
                    filePosition += chunkTemplate->generic.ckSize + kChunkHeaderSize;                   /* calculate next chunk's position */
                    err = noErr;
                }
                break;
            default :                                                                                   /* Unrecognized?? croak. */
                DebugPrint ("\pran into an undefined chunk!!");
 
                /*  If we hit an unrecognized chunk, clear chunkFlags to drop out of the loop
                    with the assumed error code. */
                chunkFlags = kInit;
                break;
        }
 
    /*  We're only done when the the FORM, FormatVersion, Common and Sound Data chunks.  This is only
        true of this incarnation - your needs may vary, so you'll have to modify the following statement
        accordingly...
        I know, this clause is a pain in the ass:  what it really says is this:
            As long as we have a FORM Chunk and we don't have all of the {Formatversion, Common, SoundData} chunks
            keep going... AND we haven't gotten an error from the file system. */
    } while (stillMoreDataToRead);
 
    if (err != noErr) {
        DebugPrint ("\pError in ASoundGetAIFFHeader");
    }
 
    return err;
}