source/AIFF.c

/*
**  Apple Macintosh Developer Technical Support
**
**  Routine demonstrating how to parse AIFF sound files.
**
**  by Mark Cookson, Apple Developer Technical Support
**
**  File:   AIFF.c
**
**  Copyright ©1996 Apple Computer, Inc.
**  All rights reserved.
**
**  You may incorporate this sample code into your applications without
**  restriction, though the sample code has been provided "AS IS" and the
**  responsibility for its operation is 100% yours.  However, what you are
**  not permitted to do is to redistribute the source as "Apple Sample
**  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 Code, but that you've made changes.
*/
 
#include "AIFF.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 *length, void *waveData)
/*-----------------------------------------------------------------------*/
{
    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];
 
    theSoundInfo->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 */
                    theSoundInfo->bytesTotal = 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. */
                    theSoundInfo->dataStart = filePosition + 16;
                    *length = chunkTemplate->soundData.ckSize - kChunkHeaderSize;
                    chunkFlags |= kSoundData;                                                           /* otherwise mark it found */
                    filePosition += chunkTemplate->soundData.ckSize + kChunkHeaderSize;                 /* calculate next chunk's position */
                    err = noErr;
                }
                break;
            case 'wave':
                filePosition += chunkTemplate->generic.ckSize + kChunkHeaderSize;
                BlockMoveData ((char*)(&(chunkTemplate->generic)) + kChunkHeaderSize, waveData, chunkTemplate->generic.ckSize);
                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->comments.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!!");
                filePosition += chunkTemplate->generic.ckSize + kChunkHeaderSize;                   /* calculate next chunk's position */
                err = noErr;
                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;
}