_source/SND.c

/*
    File:       SND.c
 
    Contains:   Routines demonstrating how to parse 'snd ' resource 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 "SND.h"
/*
    There are lots of places for reading a resource to go wrong.  This is
    why I currently have the paranoid error checking.
 
    This routine works like the other parse header routines, except that
    since it doesn't do any Resource Manager calls, it expects the file
    pointer to be positioned right at the first byte of the data of the
    'snd ' resource.  This way the FSRead that reads the header will work.
 
    The length calculation could probably use some work, but that would
    require modifying the read routines.  Right now we just fudge the length
    so that we will read all of a resource.
*/
/*-----------------------------------------------------------------------*/
        OSErr   ASoundGetSNDHeader      (SoundInfoPtr theSoundInfo,
                                        long *dataStart,
                                        long *length)
/*-----------------------------------------------------------------------*/
{
    SoundComponentData      sndInfo;
    Ptr                     theSoundHeader  = nil;
    unsigned long           numFrames       = 0,
                            dataOffset      = 0;
    long                    headerBytes     = kMaxSNDHeaderSize;
    short                   resNum          = 0;
    OSErr                   theErr          = noErr;
 
    *dataStart = kInit;
    *length = kInit;
 
    theSoundHeader = NewPtr (headerBytes);
    theErr = MemError();
    if (theSoundHeader != nil || theErr == noErr) {
        /* This returns the number of the first resource of type 'snd ' */
        theErr = MyGetFirstResource (theSoundInfo->refNum, 'snd ', &resNum);
        if (theErr == noErr) {
            theErr = MyGetResourcePosition (theSoundInfo->refNum, 'snd ', resNum, dataStart);
            if (theErr == noErr) {
                theErr = FSRead (theSoundInfo->refNum, &headerBytes, theSoundHeader);
                if (theErr == noErr) {
                    /* Could use SM 3.2's ParseSndHeader, but this is (supposed to be) educational */
                    theErr = MyParseSndHeader (&(SndListResource*)theSoundHeader, &sndInfo, &numFrames, &dataOffset);
                    if (theErr == noErr) {
                        DisposePtr (theSoundHeader);                /* It's work is done */
                        theErr = SetupDBHeader (theSoundInfo,
                                                sndInfo.sampleRate,
                                                sndInfo.sampleSize,
                                                sndInfo.numChannels,
                                                fixedCompression,   /* fixedCompression will work for uncompressed */
                                                sndInfo.format);    /* format is the really important field */
                        theSoundInfo->needsMasking = false;         /* 'snd ' resources never need masking */
                        *length = numFrames * theSoundInfo->doubleHeader.dbhPacketSize * sndInfo.numChannels;
                        *dataStart += dataOffset;
                    }
                    else {
                        DebugPrint ("\pParseSndHeader failed");
                    }
                }
                else {
                    DebugPrint ("\pFSRead failed");
                }
            }
            else {
                DebugPrint ("\pMyGetResourcePosition failed");
            }
        }
        else {
            DebugPrint ("\pMyGetFirstResource failed");
        }
    }
    else {
        DebugPrint ("\pNewPtr failed");
    }
 
    if (theErr != noErr) {
        DebugPrint ("\pError in ASoundGetSNDHeader");
    }
 
    *length += *dataStart;  /* Otherwise we wouldn't read the last few bytes from the end of the sound. */
 
    return theErr;
}
 
/*
    This code parses a 'snd ' resource header.  While Sound Manager 3.2 will
    do this for you, this is for education (and entertainment).  Just in case
    the Sound Manager routine is better, try it first, if it's available.
*/
/*-----------------------------------------------------------------------*/
        OSErr   MyParseSndHeader        (SndListHandle theSoundHeader,
                                        SoundComponentData *sndInfo,
                                        unsigned long *numFrames,
                                        unsigned long *dataOffset)
/*-----------------------------------------------------------------------*/
{
    NumVersion              SndManagerVer;
    headerTemplate          theHeader;
    long                    headerOffset    = 0;
    OSErr                   theErr          = noErr;
    short                   numChannels     = 0,
                            numCommands     = 0,
                            i               = 0;
    Boolean                 parsed          = false;
    unsigned char           headerFormat    = 0;
 
    SndManagerVer = SndSoundManagerVersion ();
 
    /* If SM 3.2 is available, use its ParseSndHeader function */
    if (SndManagerVer.majorRev >= 3 && SndManagerVer.minorAndBugRev >= 32) {
        theErr = ParseSndHeader (theSoundHeader, sndInfo, numFrames, dataOffset);
        parsed = true;
    }
 
    /* If it's not available, or it failed, let's try it ourselves */
    if (theErr != noErr || parsed == false) {
        theErr = noErr;
        switch ((*theSoundHeader)->format) {
            case firstSoundFormat:                  /* Normal 'snd ' resource */
                if ((*theSoundHeader)->modifierPart->modNumber != kSampledSound) {
                    theErr = badFormat;             /* can only deal with sampled-sound data */
                }
                else {
                    numCommands = (*theSoundHeader)->numCommands;
                    if (numCommands != 1) {
                        theErr = badFormat;         /* can only deal with one sound per resource, for now */
                    }
                    else {
                        for (i = 0; i < numCommands; i++) {
                            if ((*theSoundHeader)->commandPart->cmd == kBufferCmd) {
                                headerOffset = (*theSoundHeader)->commandPart->param2;
                            }
                            else {
                                theErr = badFormat; /* can only deal with sampled-sound data */
                            }
                        }
                    }
                }
                break;
            case secondSoundFormat:                 /* Hypercard 'snd ' resource */
                numCommands = ((Snd2ListPtr)(*theSoundHeader))->numCommands;
                if (numCommands != 1) {
                    theErr = badFormat;             /* can only deal with one sound per resource, for now */
                }
                else {
                    for (i = 0; i < numCommands; i++) {
                        if (((Snd2ListPtr)(*theSoundHeader))->commandPart->cmd == kBufferCmd) {
                            headerOffset = ((Snd2ListPtr)(*theSoundHeader))->commandPart->param2;
                        }
                        else {
                            theErr = badFormat;     /* can only deal with sampled-sound data */
                        }
                    }
                }
                break;
            default:
                theErr = badFormat;                 /* unknown resource format */
        }
        if (theErr == noErr) {
            theHeader.standardHeaderPtr = (SoundHeaderPtr)((Ptr)(*theSoundHeader)+headerOffset);
            switch (theHeader.standardHeaderPtr->encode) {
                case stdSH:
                    theHeader.standardHeaderPtr = (SoundHeaderPtr)((Ptr)(*theSoundHeader)+headerOffset);
                    sndInfo->format         = kOffsetBinary;    /* Can only be raw sounds */
                    switch ((*theSoundHeader)->modifierPart->modInit & kChannelsMask) {
                        case initMono:
                            sndInfo->numChannels    = kMono;
                            break;
                        case initStereo:
                            sndInfo->numChannels    = kStereo;
                            break;
                        default:
                            theErr = badFormat;                 /* unknown number of channels */
                    }
                    sndInfo->sampleSize     = k8BitSample;      /* Can only be 8 bit sounds */
                    sndInfo->sampleRate     = theHeader.standardHeaderPtr->sampleRate;
                    sndInfo->sampleCount    = theHeader.standardHeaderPtr->length;
                    *dataOffset             = headerOffset + sizeof (SoundHeader) - sizeof (short);
                    break;
                case extSH:
                    theHeader.extendedHeaderPtr = (ExtSoundHeaderPtr)((Ptr)(*theSoundHeader)+headerOffset);
                    sndInfo->format         = kOffsetBinary;    /* Can only be raw sounds */
                    sndInfo->numChannels    = theHeader.extendedHeaderPtr->numChannels;
                    sndInfo->sampleSize     = theHeader.extendedHeaderPtr->sampleSize;
                    sndInfo->sampleRate     = theHeader.extendedHeaderPtr->sampleRate;
                    sndInfo->sampleCount    = theHeader.extendedHeaderPtr->numFrames;
                    *dataOffset             = headerOffset + sizeof (ExtSoundHeader) - sizeof (short);
                    break;
                case cmpSH:
                    theHeader.compressedHeaderPtr = (CmpSoundHeaderPtr)((Ptr)(*theSoundHeader)+headerOffset);
                    sndInfo->format         = theHeader.compressedHeaderPtr->format;
                    if (sndInfo->format == 0) {
                        switch (theHeader.compressedHeaderPtr->compressionID) {
                            case twoToOne:
                                sndInfo->format     = kULawCompression;
                                break;
                            case eightToThree:                  /* I don't know what compressor this is */
                                theErr = badFormat;
                                break;
                            case threeToOne:
                                sndInfo->format     = kMACE3Compression;
                                break;
                            case sixToOne:
                                sndInfo->format     = kMACE6Compression;
                                break;
                            default:
                                DebugPrint ("\pUnknown sound format");
                                theErr = badFormat;
                        }
                    }
                    sndInfo->numChannels    = theHeader.compressedHeaderPtr->numChannels;
                    sndInfo->sampleSize     = theHeader.compressedHeaderPtr->sampleSize;
                    sndInfo->sampleRate     = theHeader.compressedHeaderPtr->sampleRate;
                    sndInfo->sampleCount    = theHeader.compressedHeaderPtr->numFrames;
                    *dataOffset             = headerOffset + sizeof (CmpSoundHeader) - sizeof (short);
                    break;
                default:
                    theErr = badFormat;     /* A header format we don't know about */
            }
            *numFrames              = (unsigned long)sndInfo->sampleCount;
            sndInfo->flags          = 0;    /* ?? */
            sndInfo->buffer         = 0;    /* should always be 0, data follows header */
            sndInfo->reserved       = 0;    /* just set it to 0 */
        }
 
    }
 
    return theErr;
}