_source/DoubleBufferFromFile.c

/*
    File:       DoubleBufferFromFile.c
 
    Contains:   Routines demonstrating how to play a sound from disk using SndPlayDoubleBuffer.
 
    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"
#include "DoubleBufferFromFile.h"
 
/*  Purpose:        This creates a new SoundInfo structure and initializes
                    it by calling the private function ASoundInit.
    Side Effects:   None.
*/
/*-----------------------------------------------------------------------*/
SoundInfoPtr    ASoundNew               (OSErr *theErr)
/*-----------------------------------------------------------------------*/
{
    SoundInfoPtr    theSoundInfo    = nil;
 
    *theErr = noErr;
    theSoundInfo = (SoundInfoPtr)NewPtrClear (sizeof (SoundInfo));
 
    if (MemError () != noErr || theSoundInfo == nil) {
        *theErr = kInitErr;
    }
    else {
        *theErr = ASoundInit (theSoundInfo);
    }
 
    if (*theErr != noErr) {
        DebugPrint ("\pError in ASoundNew");
    }
 
    return theSoundInfo;
}
 
/*
    Purpose:        Display a StandardFile dialog to select a file.
                    Opens the file selected by the user.
    Side Effects:   None.
*/
/*-----------------------------------------------------------------------*/
OSErr           ASoundGetFileToPlay     (SoundInfoPtr theSoundInfo)
/*-----------------------------------------------------------------------*/
{
    FileFilterUPP       myFilterUPP = nil;
    OSErr               theErr      = noErr;
    short               i           = 0;
    Boolean             good        = false;
 
    if (IsValid (theSoundInfo)) {
        myFilterUPP = NewFileFilterProc (ASoundFileFilter);
        if ((theSoundInfo->globals.ggestaltStandardFileAttr & gestaltStandardFile58) == false) {
            StandardFileReply   theSFReply;
            short               showTypes;
 
            if ((theSoundInfo->globals.ggestaltTranslationAttr & (1 << gestaltTranslationMgrExists)) == true) {
                showTypes = kUseOpenResourceTypes;
            }
            else {
                showTypes = kNoFirstFiltering;
            }
            StandardGetFile (myFilterUPP, showTypes, nil, &theSFReply);
            theSoundInfo->vRefNum = theSFReply.sfFile.vRefNum;
            if (theSFReply.sfGood == true) {
                switch (theSFReply.sfType) {
                    case kSNDResource:
                    case kResource:
                        theErr = FSpOpenRF (&theSFReply.sfFile, fsCurPerm, &(theSoundInfo->refNum));
                        break;
                    case kCompressedAIFFFile:
                    case kUncompressedAIFFFile:
                    case kWAVEFile:
                    case kWAVFile:
                    case kAUFile:
                        theErr = FSpOpenDF (&theSFReply.sfFile, fsCurPerm, &(theSoundInfo->refNum));
                        break;
                    default:
                        DebugPrint ("\pASoundGetFileToPlay should have never executed this line");
                }
                if (theErr != noErr) {
                    DebugPrint ("\pCouldn't open file");
                }
                else {
                    theSoundInfo->fileType = theSFReply.sfType;
                    for (i = 0; i <= theSFReply.sfFile.name[0]+1; i++)
                        theSoundInfo->theName[i] = theSFReply.sfFile.name[i];
                }
            }
            else {
                theErr = userCanceledErr;
            }
        }
        else {
            SFReply         theSFReply;
            Point           where       = {kInit, kInit};
            Rect            screenRect  = GetMainScreenRect();
            long            dirID       = kInit,
                            procID      = kInit;
 
            where.h = screenRect.right / kHorizAdjust;  /* This will put the SFGetFile dialog in the same place */
            where.v = screenRect.bottom / kVertAdjust;  /* as the StandardGetFile dialog (or really close). */
            if ((GetMBarHeight() + kOne) > where.h) {
                where.h = GetMBarHeight() + kOne;
            }
            SFGetFile (where, nil, myFilterUPP, kNoFirstFiltering, nil, nil, &theSFReply);
            good = theSFReply.good;
            if (theSFReply.good == true) {
                theErr = GetWDInfo (theSFReply.vRefNum, &(theSoundInfo->vRefNum), &dirID, &procID);
                if (theErr == noErr) {
                    switch (theSFReply.fType) {
                        case kSNDResource:
                        case kResource:
                            theErr = HOpenRF (theSoundInfo->vRefNum, dirID, theSFReply.fName, fsCurPerm, &(theSoundInfo->refNum));
                            break;
                        case kCompressedAIFFFile:
                        case kUncompressedAIFFFile:
                        case kWAVEFile:
                        case kWAVFile:
                        case kAUFile:
                            theErr = HOpenDF (theSoundInfo->vRefNum, dirID, theSFReply.fName, fsCurPerm, &(theSoundInfo->refNum));
                            break;
                        default:
                            DebugPrint ("\pASoundGetFileToPlay should have never executed this line");
                    }
                    if (theErr != noErr) {
                        DebugPrint ("\pCouldn't open file");
                    }
                    else {
                        theSoundInfo->fileType = theSFReply.fType;
                        for (i = 0; i <= theSFReply.fName[0]+1; i++)
                            theSoundInfo->theName[i] = theSFReply.fName[i];
                    }
                }
                else {
                    theErr = kFileErr;
                    DebugPrint ("\pCouldn't translate working directory");
                }
            }
            else {
                theErr = userCanceledErr;
            }
        }
        DisposeRoutineDescriptor (myFilterUPP);
    }
    else {
        theErr = kNilPtrErr;
    }
 
    if (theErr != noErr && theErr != userCanceledErr) {
        DebugPrint ("\pError in ASoundGetFileToPlay");
    }
 
    return theErr;
}
 
/*
    Purpose:        Checks a file to see if its header can be parsed
                    and the file can be played.
 
                    This will return an error if the sound will not play,
                    returning noErr means that sound will play.
 
                    Parsing a header can take some time, this routine
                    is a canidate for speed improvements.
    Side Effects:   None.
*/
/*-----------------------------------------------------------------------*/
OSErr           ASoundCanThisPlay       (CInfoPBPtr theFileInfo)
/*-----------------------------------------------------------------------*/
{
    SoundInfoPtr    theSoundInfo    = nil;
    long            dataStart       = kInit,
                    sndLength       = kInit;
    OSErr           theErr          = noErr,
                    closeErr        = noErr;
 
    if (theFileInfo  != nil) {
        theSoundInfo = ASoundNew (&theErr);
        if (theErr == noErr) {
            theSoundInfo->vRefNum = theFileInfo->hFileInfo.ioVRefNum;
            theSoundInfo->fileType = theFileInfo->hFileInfo.ioFlFndrInfo.fdType;
            theErr = HOpenDF (theFileInfo->hFileInfo.ioVRefNum, theFileInfo->hFileInfo.ioFlParID, theFileInfo->hFileInfo.ioNamePtr, fsRdPerm, &theSoundInfo->refNum);
            if (theErr == noErr) {
                switch (theSoundInfo->fileType) {
                    case kCompressedAIFFFile:
                    case kUncompressedAIFFFile:
                        theErr = ASoundGetAIFFHeader (theSoundInfo, &dataStart, &sndLength);
                        break;
                    case kWAVEFile:
                    case kWAVFile:
                        theErr = ASoundGetWAVEHeader (theSoundInfo, &dataStart, &sndLength);
                        break;
                    case kAUFile:
                        theErr = ASoundGetULAWHeader (theSoundInfo, &dataStart, &sndLength);
                        break;
                    case kSNDResource:
                    case kResource:
                        theErr = noErr;
                        break;
                    default:
                        /* The file type is not one that we can even parse. */
                        theErr = kUnknownFormat;
                        break;
                }
            }
        }
    }
    else {
        theErr = kNilPtrErr;
    }
 
    closeErr = FSClose (theSoundInfo->refNum);
    DisposeRoutineDescriptor (ASoundGetSoundCallBack (theSoundInfo));
    DisposePtr ((Ptr)theSoundInfo);
 
    if (closeErr != noErr) {
        theErr = closeErr;
    }
 
    if (theErr != noErr && theErr != kUnknownFormat) {
        DebugPrint ("\pError in ASoundCanThisPlay");
    }
 
    return theErr;
}
 
/*
    Purpose:        This function is called to get ready to play a sound.
                    Use this if you want to make sure that there is enough
                    memory to play the sound.
    Side Effects:   This will call routines that will allocate memory needed
                    to all of the various structures needed by the Sound Manager
                    and memory to be used as the sounds' buffers.
*/
/*-----------------------------------------------------------------------*/
OSErr           ASoundReadyForPlaying   (SoundInfoPtr theSoundInfo,
                                        unsigned long bufferSize)
/*-----------------------------------------------------------------------*/
{
    OSErr               theErr      = noErr;
 
    if (IsValid (theSoundInfo)) {
        if (theSoundInfo->globals.gSupportsSPDB == false) {
            DebugPrint ("\pThis machine doesn't support SoundPlayDoubleBuffer, can't play!");
            theErr = kNoSPDBErr;
        }
        else {
            /* do we want to start at the begining of the file? */
            if ((ASoundGetBytesCopied (theSoundInfo) <= kMaxAIFFHeaderSize) && theSoundInfo->paused == false) {
                theSoundInfo->soundDone = false;
                theErr = SndNewChannel (&(theSoundInfo->chan), sampledSynth, nil, ASoundGetSoundCallBack (theSoundInfo));
                if (theErr != noErr) {
                    DebugPrint ("\pSndNewChannel error!");
                }
                else {
                    theErr = SetUpSoundHeader (theSoundInfo, bufferSize);
                    if (theErr == noErr) {
                        theErr = ASoundPrimeBuffers (theSoundInfo);
                        ASoundSetCurBuffer (theSoundInfo, kStart);
                    }
                }
            }
            else {
                theErr = ASoundPrimeBuffers (theSoundInfo);
            }
        }
    }
    else {
        theErr = kNilPtrErr;
    }
 
    if (theErr != noErr) {
        DebugPrint ("\pError in ASoundReadyForPlaying!");
        (void)ASoundDonePlaying (theSoundInfo, kCloseFile + kFreeMem);
    }
 
    return theErr;
}
 
/*
    Purpose:        Call this after you have called ASoundReadyForPlaying to
                    start playing the sound you prepaired.
    Side Effects:   Starts the sound playing.
*/
/*-----------------------------------------------------------------------*/
OSErr           ASoundPlay              (SoundInfoPtr theSoundInfo)
/*-----------------------------------------------------------------------*/
{
    OSErr               theErr      = noErr;
 
    if (IsValid (theSoundInfo)) {
        if (theSoundInfo->playing == false || theSoundInfo->paused == true) {
            if (theSoundInfo->globals.gSupportsSPDB == false) {
                theErr = kNoSPDBErr;
                DebugPrint ("\pThis machine doesn't support SoundPlayDoubleBuffer, can't play!");
            }
            else {
                if (theSoundInfo->paused == false) {
                    theErr = SndPlayDoubleBuffer (theSoundInfo->chan, (SndDoubleBufferHeaderPtr)&(theSoundInfo->doubleHeader));
                    if (theErr == noErr) {
                        theErr = InstallCallBack (theSoundInfo);
                    }
                    if (theErr == noErr) {
                        theSoundInfo->playing = true;
                    }
                }
            }
        }
    }
    else {
        theErr = kNilPtrErr;
    }
 
    if (theErr != noErr) {
        DebugPrint ("\pError in ASoundPlay!");
        (void)ASoundDonePlaying (theSoundInfo, kCloseFile + kFreeMem);
    }
 
    return theErr;
}
 
/*
    Purpose:        Wrapper function called to start playing a sound.
                    Use this if you are pretty sure the sound will play, or
                    just don't care specifically what goes wrong.
    Side Effects:   This will call routines that will allocate memory needed
                    for all of the various structures needed by the Sound Manager
                    and memory to be used as the sounds' buffers.
*/
/*-----------------------------------------------------------------------*/
OSErr           ASoundStartPlaying      (SoundInfoPtr theSoundInfo,
                                        unsigned long bufferSize)
/*-----------------------------------------------------------------------*/
{
    OSErr               theErr      = noErr;
 
    theErr = ASoundReadyForPlaying (theSoundInfo, bufferSize);
    if (theErr == noErr) {
        theErr = ASoundPlay (theSoundInfo);
    }
 
    if (theErr != noErr) {
        DebugPrint ("\pError in ASoundStartPlaying!");
        (void)ASoundDonePlaying (theSoundInfo, kCloseFile + kFreeMem);
    }
 
    return theErr;
}
 
/*
    Purpose:        Stops the currently playing sound.
    Side Effects:   Stopping the currently playing sound will cause the
                    sound completion routine to run.
*/
/*-----------------------------------------------------------------------*/
OSErr           ASoundStop              (SoundInfoPtr theSoundInfo)
/*-----------------------------------------------------------------------*/
{
    SndCommand      theCmd      = {quietCmd, kInit, kInit};
    OSErr           theErr      = noErr;
 
    if (StrictIsValid (theSoundInfo)) {
        /* So that ASoundDoubleBackProc knows that it doesn't have to
           actually read any data */
        theSoundInfo->stopping = true;
        theErr = SndDoImmediate (theSoundInfo->chan, &theCmd);
        if (theErr == noErr) {
            theSoundInfo->playing = false;
            theSoundInfo->paused = false;
        }
    }
    else {
        theErr = kNilPtrErr;
    }
 
    if (theErr != noErr) {
        DebugPrint ("\pError in ASoundStop");
    }
 
    return theErr;
}
 
/*
    Purpose:        Wrapper so the user doesn't have to keep track of if
                    the sound is playing or not.
    Side Effects:   If resuming a sound and the user had also called
                    ASoundPauseForAdjust this will reinstall the sound
                    completion callback.
*/
/*-----------------------------------------------------------------------*/
OSErr           ASoundPause             (SoundInfoPtr theSoundInfo)
/*-----------------------------------------------------------------------*/
{
    OSErr           theErr      = noErr;
 
    if (StrictIsValid (theSoundInfo)) {
        if (theSoundInfo->paused == false) {
            theErr = PauseSound (theSoundInfo);
        }
        else {
            theErr = ResumeSound (theSoundInfo);
        }
    }
    else {
        theErr = kNilPtrErr;
    }
 
    if (theErr != noErr) {
        DebugPrint ("\pError in ASoundPause");
    }
 
    return theErr;
}
 
/*
    Purpose:        If the sound is paused, resume playing.  If the sound is
                    playing, pause playing.
                    This differs from ASoundPause because it actually stops
                    the sound instead of pausing it.  When the sound is
                    paused for adjusting you can reset where the sound will
                    next start playing from without having to play the
                    remainder of the current buffer.  This routine is used
                    for the QuickTime style playing.
    Side Effects:   Removes the callback from the sound channel because
                    otherwise while adjusting the sound the Sound Manager
                    would call our clean up routine.
                    When resuming a sound ASoundStartPlaying will install
                    our callback routine if necessary (if the sound wasn't
                    already paused).
*/
/*-----------------------------------------------------------------------*/
OSErr           ASoundPauseForAdjust    (SoundInfoPtr theSoundInfo)
/*-----------------------------------------------------------------------*/
{
    SndCommand      theCmd          = {kInit, kInit, kInit};
    OSErr           theErr          = noErr;
    static Fixed    oldRate         = kInit;
 
    if (StrictIsValid (theSoundInfo)) {
        if (theSoundInfo->adjusting == false) {
            theCmd.cmd = flushCmd;          /* so the sound completion callback doesn't get called */
            theErr = SndDoImmediate (theSoundInfo->chan, &theCmd);
            if (theErr == noErr) {
                theSoundInfo->adjusting = true;
                theCmd.cmd = quietCmd;
                theErr = SndDoImmediate (theSoundInfo->chan, &theCmd);
                if (theErr == noErr) {
                    theSoundInfo->hasBeenAdjusted = true;
                    theSoundInfo->playing = false;
                }
            }
        }
        else {
            theSoundInfo->adjusting = false;
            ASoundStartPlaying (theSoundInfo, nil);
        }
    }
    else {
        theErr = kNilPtrErr;
    }
 
    if (theErr != noErr) {
        DebugPrint ("\pError in ASoundPauseForAdjust");
    }
 
    return theErr;
}
 
/*
    Purpose:        Sound is done playing, dispose of the memory we no
                    longer need.
    Side Effects:   None.
*/
/*-----------------------------------------------------------------------*/
OSErr           ASoundDonePlaying       (SoundInfoPtr theSoundInfo,
                                        unsigned long options)
/*-----------------------------------------------------------------------*/
{
    myParamBlockRec *myPB           = nil;
    OSErr           theErr          = noErr;
    short           i               = kInit,
                    savedVRefNum    = kInit,
                    savedRefNum     = kInit;
 
    if (IsValid (theSoundInfo)) {
        theSoundInfo->soundDone = false;            /* so we don't get called multiple times */
        theSoundInfo->playing = false;
 
        savedVRefNum = theSoundInfo->vRefNum;
        savedRefNum = theSoundInfo->refNum;
        if ((options == kCloseFile) || (options > kCloseFile + kFreeMem)) {
            DebugPrint ("\pInvalid selector passed to ASoundDonePlaying");
            theErr = kBadValue;
        }
        else {
            theSoundInfo->stopping = false; /* Sound is done stopping, hopefully... */
            if (options > kNoOptions) {
                myPB = (myParamBlockRec*)theSoundInfo->doubleHeader.dbhBufferPtr[kDBBufOne]->dbUserInfo[kPBPtr];
                if (myPB != nil) {
                    if (options == kCloseFile + kFreeMem) {
                        theErr = FSClose (myPB->pb.ioParam.ioRefNum);
                    }
                    if (theErr == noErr) {
                        for (i = kInit; i <= kOne; i++) {
                            DisposeRoutineDescriptor (((myParamBlockRec*)theSoundInfo->doubleHeader.dbhBufferPtr[i]->dbUserInfo[kPBPtr])->pb.ioParam.ioCompletion);
                            DisposePtr ((Ptr)theSoundInfo->doubleHeader.dbhBufferPtr[i]->dbUserInfo[kPBPtr]);
                            /* Have to unhold memory that was held */
                            UnholdMemory ((Ptr)theSoundInfo->doubleHeader.dbhBufferPtr[i], sizeof(SndDoubleBuffer) + ASoundGetBufferSize (theSoundInfo));
                            DisposePtr ((Ptr)theSoundInfo->doubleHeader.dbhBufferPtr[i]);
                        }
                    }
                    else {
                        DebugPrint ("\pFSClose error!");
                    }
                    theErr = SndDisposeChannel (theSoundInfo->chan, true);
                    DisposeRoutineDescriptor (ASoundGetSoundCallBack (theSoundInfo));
                    DisposeRoutineDescriptor (theSoundInfo->doubleHeader.dbhDoubleBack);
                    if (theErr != noErr) {
                        DebugPrint ("\pSndDisposeChannel error!");
                    }
                }
            }
 
            if (options == kNoOptions) {
                ASoundSetBytesCopied (theSoundInfo, theSoundInfo->dataStart);
                ASoundSetCurBuffer (theSoundInfo, kStart);
                theSoundInfo->doubleHeader.dbhBufferPtr[kDBBufOne]->dbFlags = nil;
                theSoundInfo->doubleHeader.dbhBufferPtr[kDBBufTwo]->dbFlags = nil;
                theErr = ASoundPrimeBuffers (theSoundInfo);
            }
            if (options == kFreeMem) {
                ASoundInit (theSoundInfo);
                theSoundInfo->vRefNum = savedVRefNum;
                theSoundInfo->refNum = savedRefNum;
            }
            else {
                if (options == kCloseFile + kFreeMem) {
                    ASoundInit (theSoundInfo);
                }
            }
        }
    }
    else {
        theErr = kNilPtrErr;
    }
 
    if (theErr != noErr) {
        DebugPrint ("\pError in ASoundDonePlaying");
    }
 
    return theErr;
}
 
/*
    Purpose:        Returns the channel for the sound in case you want to
                    send it specific commands.
    Side Effects:   None.
*/
/*-----------------------------------------------------------------------*/
SndChannelPtr   ASoundGetChan           (SoundInfoPtr theSoundInfo)
/*-----------------------------------------------------------------------*/
{
    SndChannelPtr   returnValue;
 
    if (StrictIsValid (theSoundInfo)) {
        returnValue = theSoundInfo->chan;
    } else {
        returnValue = nil;
    }
 
    return returnValue;
}
 
/*
    Purpose:        Returns the name of the file containing the currently
                    playing sound.
    Side Effects:   None.
*/
/*-----------------------------------------------------------------------*/
OSErr           ASoundGetSoundName      (SoundInfoPtr theSoundInfo,
                                        Str63 theName)
/*-----------------------------------------------------------------------*/
{
    OSErr           theErr      = noErr;
    short           i           = 0;
 
    if (StrictIsValid (theSoundInfo) && theName != nil) {
        /* This should probably be a BlockMoveData */
        for (i = 0; i <= theSoundInfo->theName[0]+1; i++)
            theName[i] = theSoundInfo->theName[i];
    }
 
    return theErr;
}
 
/*
    Purpose:        Gets the number of the current buffer
                    (in the range 1 to ASoundGetNumBuffers()) of the
                    currently playing sound.
    Side Effects:   None.
*/
/*-----------------------------------------------------------------------*/
long            ASoundGetCurBuffer      (SoundInfoPtr theSoundInfo)
/*-----------------------------------------------------------------------*/
{
    long        returnValue     = kInit;
    OSErr       theErr          = noErr;
 
    if (StrictIsValid (theSoundInfo)) {
        returnValue = theSoundInfo->currentBuffer;
    }
    else {
        theErr = kNilPtrErr;
        returnValue = nil;
    }
 
    if (theErr != noErr) {
        DebugPrint ("\pError in ASoundGetCurBuffer");
    }
 
    return returnValue;
}
 
/*
    Purpose:        Sets which buffer should be the next buffer to play
                    from (in the range 1 to ASoundGetNumBuffers())
                    for the currently playing sound.
    Side Effects:   None.
*/
/*-----------------------------------------------------------------------*/
OSErr           ASoundSetCurBuffer      (SoundInfoPtr theSoundInfo,
                                        long newValue)
/*-----------------------------------------------------------------------*/
{
    OSErr       theErr          = noErr;
 
    if (IsValid (theSoundInfo)) {
        if ((newValue >= kStart) && (newValue <= ASoundGetNumBuffers (theSoundInfo))) {
            theSoundInfo->currentBuffer = newValue;
            ASoundSetBytesCopied (theSoundInfo, newValue * ASoundGetBufferSize (theSoundInfo) + theSoundInfo->dataStart);
        }
        else {
            theErr = kBadRange;
        }
    }
    else {
        theErr = kNilPtrErr;
    }
 
    return theErr;
}
 
/*
    Purpose:        Gets the number of buffers that the currently playing
                    sound will need to play in its entirety.
    Side Effects:   None.
*/
/*-----------------------------------------------------------------------*/
long            ASoundGetNumBuffers     (SoundInfoPtr theSoundInfo)
/*-----------------------------------------------------------------------*/
{
    long        returnValue     = kInit;
    OSErr       theErr          = noErr;
 
    if (IsValid (theSoundInfo)) {
        returnValue = theSoundInfo->numBuffers;
    }
    else {
        theErr = kNilPtrErr;
        returnValue = nil;
    }
 
    if (theErr != noErr) {
        DebugPrint ("\pError in ASoundGetNumBuffers");
    }
 
    return returnValue;
}
 
/*
    Purpose:        Gets the length (in bytes) of the currently playing
                    sound.  This number does not include any header bytes.
    Side Effects:   None.
*/
/*-----------------------------------------------------------------------*/
long            ASoundGetNumTotalBytes  (SoundInfoPtr theSoundInfo)
/*-----------------------------------------------------------------------*/
{
    long        returnValue     = kInit;
    OSErr       theErr          = noErr;
 
    if (IsValid (theSoundInfo)) {
        returnValue = theSoundInfo->bytesTotal;
    }
    else {
        theErr = kNilPtrErr;
        returnValue = nil;
    }
 
    if (theErr != noErr) {
        DebugPrint ("\pError in ASoundGetNumTotalBytes");
    }
 
    return returnValue;
}
 
/*
    Purpose:        Gets the number of bytes that will be played by the end
                    of the current buffer of the currently playing sound.
    Side Effects:   None.
*/
/*-----------------------------------------------------------------------*/
long            ASoundGetBytesCopied    (SoundInfoPtr theSoundInfo)
/*-----------------------------------------------------------------------*/
{
    long        returnValue     = kInit;
    OSErr       theErr          = noErr;
 
    if (IsValid (theSoundInfo)) {
        returnValue = theSoundInfo->bytesCopied;
    }
    else {
        theErr = kNilPtrErr;
        returnValue = nil;
    }
 
    if (theErr != noErr) {
        DebugPrint ("\pError in ASoundGetBytesCopied");
    }
 
    return returnValue;
}
 
/*
    Purpose:        Sets the location in the file where the next buffer
                    should be filled from for the currently playing sound.
    Side Effects:   None.
*/
/*-----------------------------------------------------------------------*/
OSErr           ASoundSetBytesCopied    (SoundInfoPtr theSoundInfo,
                                        long newValue)
/*-----------------------------------------------------------------------*/
{
    OSErr       theErr          = noErr;
 
    if (IsValid (theSoundInfo)) {
        if (newValue >= theSoundInfo->dataStart || newValue == kInit) {
            theSoundInfo->bytesCopied = newValue;
        }
        else {
            theErr = kBadValue;
        }
    }
    else {
        theErr = kNilPtrErr;
    }
 
    if (theErr != noErr) {
        DebugPrint ("\pError in ASoundSetBytesCopied");
    }
 
    return theErr;
}
 
/*
    Purpose:        Gets the size of a buffer of the currently playing
                    sound.  Multiply by two to know how much memory is
                    reserved for buffering the currently playing sound.
    Side Effects:   None.
*/
/*-----------------------------------------------------------------------*/
long            ASoundGetBufferSize     (SoundInfoPtr theSoundInfo)
/*-----------------------------------------------------------------------*/
{
    long        returnValue     = kInit;
    OSErr       theErr          = noErr;
 
    if (IsValid (theSoundInfo)) {
        returnValue = theSoundInfo->doubleBufferSize;
    }
    else {
        theErr = kNilPtrErr;
        returnValue = nil;
    }
 
    if (theErr != noErr) {
        DebugPrint ("\pError in ASoundGetBufferSize");
    }
 
    return returnValue;
}
 
/*
    Purpose:        Gets the UPP for the function that should be called when
                    the currently playing sound finishes.
    Side Effects:   None.
*/
/*-----------------------------------------------------------------------*/
SndCallBackUPP  ASoundGetSoundCallBack  (SoundInfoPtr theSoundInfo)
/*-----------------------------------------------------------------------*/
{
    SndCallBackUPP      returnValue     = nil;
    OSErr               theErr          = noErr;
 
    if (IsValid (theSoundInfo)) {
        returnValue = theSoundInfo->theSoundCallBackUPP;
    }
    else {
        theErr = kNilPtrErr;
        returnValue = nil;
    }
 
    if (theErr != noErr) {
        DebugPrint ("\pError in ASoundGetSoundCallBack");
    }
 
    return returnValue;
}
 
/*
    Purpose:        Sets the function that should be called when the the
                    currently playing sound finishes.
    Side Effects:   None.
*/
/*-----------------------------------------------------------------------*/
OSErr           ASoundSetSoundCallBack  (SoundInfoPtr theSoundInfo,
                                        void* newValue)
/*-----------------------------------------------------------------------*/
{
    OSErr       theErr      = noErr;
 
    if (IsValid (theSoundInfo)) {
        if (theSoundInfo->theSoundCallBackUPP != nil) {
            DisposeRoutineDescriptor (theSoundInfo->theSoundCallBackUPP);
        }
        if (newValue != nil) {
            theSoundInfo->theSoundCallBackUPP = NewSndCallBackProc(newValue);
        }
        else {
            theSoundInfo->theSoundCallBackUPP = NewSndCallBackProc(ASoundDoneCallBack);
            DebugPrint ("\pDid you really want the default sound callback?");
        }
    }
    else {
        theErr = kNilPtrErr;
    }
 
    if (theErr != noErr) {
        DebugPrint ("\pError in ASoundSetSoundCallBack");
    }
 
    return theErr;
}
 
/*
    Purpose:        Says whether to play the currently playing sound backwards
                    (it actually reverses the sound in the buffer).
    Side Effects:   Takes effect when the next sound buffer gets filled.
*/
/*-----------------------------------------------------------------------*/
OSErr           ASoundPlayBackwards     (SoundInfoPtr theSoundInfo,
                                        Boolean newValue)
/*-----------------------------------------------------------------------*/
{
    OSErr       theErr      = noErr;
 
    if (StrictIsValid (theSoundInfo)) {
        theSoundInfo->backwards = newValue;
    }
    else {
        theErr = kNilPtrErr;
    }
 
    if (theErr != noErr) {
        DebugPrint ("\pError in ASoundPlayBackwards");
    }
 
    return theErr;
}
 
/*
    Purpose:        Returns true if the currently playing sound's buffer
                    is set to be reversed.
    Side Effects:   None.
*/
/*-----------------------------------------------------------------------*/
Boolean         ASoundIsBackwards       (SoundInfoPtr theSoundInfo)
/*-----------------------------------------------------------------------*/
{
    OSErr       theErr          = noErr;
    Boolean     returnValue     = false;
 
    if (StrictIsValid (theSoundInfo)) {
        returnValue = theSoundInfo->backwards;
    }
    else {
        theErr = kNilPtrErr;
    }
 
    if (theErr != noErr) {
        DebugPrint ("\pError in ASoundIsBackwards");
    }
 
    return returnValue;
}
 
/*
    Purpose:        Returns true if the sound has finished playing.
    Side Effects:   None.
*/
/*-----------------------------------------------------------------------*/
Boolean         ASoundIsDone            (SoundInfoPtr theSoundInfo)
/*-----------------------------------------------------------------------*/
{
    OSErr       theErr          = noErr;
    Boolean     returnValue     = false;
 
    if (StrictIsValid (theSoundInfo)) {
        returnValue = theSoundInfo->soundDone;
    }
    else {
        theErr = kNilPtrErr;
    }
 
    if (theErr != noErr) {
        DebugPrint ("\pError in ASoundIsDone");
    }
 
    return returnValue;
}
 
/*
    Purpose:        Changes the volume of the currently playing sound.
                    The values you pass in are added to the current values.
                    Negitive values will decrease the volume, positive values
                    will increase the volume.
    Side Effects:   None.
*/
/*-----------------------------------------------------------------------*/
OSErr           ASoundChangeVolume      (SoundInfoPtr theSoundInfo,
                                        unsigned short leftVol,
                                        unsigned short rightVol)
/*-----------------------------------------------------------------------*/
{
    SndCommand      theCmd      = {volumeCmd, kInit, kInit};
    OSErr           theErr      = noErr;
    unsigned short  tempLeft    = kInit,
                    tempRight   = kInit;
 
    if (StrictIsValid (theSoundInfo)) {
        theErr = ASoundGetVolume (theSoundInfo, &tempLeft, &tempRight);
        if (theErr == noErr) {
            if ((tempLeft + leftVol) > kMaxVolume) leftVol = kMaxVolume - tempLeft;
            if ((tempRight + rightVol) > kMaxVolume) rightVol = kMaxVolume - tempRight;
            if ((tempLeft + leftVol) < kMinVolume) leftVol = -tempLeft;
            if ((tempRight + rightVol) < kMinVolume) rightVol = -tempRight;
            theErr = ASoundSetVolume (theSoundInfo, tempLeft+leftVol, tempRight+rightVol);
        }
    }
    else {
        theErr = kNilPtrErr;
    }
 
    if (theErr != noErr) {
        DebugPrint ("\pError in ASoundChangeVolume");
    }
 
    return theErr;
}
 
/*
    Purpose:        Gets the volume of the currently playing sound.
    Side Effects:   None.
*/
/*-----------------------------------------------------------------------*/
OSErr           ASoundGetVolume         (SoundInfoPtr theSoundInfo,
                                        unsigned short *leftVol,
                                        unsigned short *rightVol)
/*-----------------------------------------------------------------------*/
{
    SndCommand      theCmd      = {getVolumeCmd, kInit, kInit};
    unsigned long   theVol      = kInit;
    OSErr           theErr      = noErr;
 
    if (StrictIsValid (theSoundInfo)) {
        theCmd.param2 = (long)&theVol;
        theErr = SndDoImmediate(theSoundInfo->chan, &theCmd);
 
        if (theErr == noErr) {
            *leftVol = theVol & kLeftMask;
            *rightVol = theVol >> kSixteen;
        }
    }
    else {
        theErr = kNilPtrErr;
    }
 
    if (theErr != noErr) {
        DebugPrint ("\pError in ASoundGetVolume");
    }
 
    return theErr;
}
 
/*
    Purpose:        Sets the volume of the currently playing sound.
    Side Effects:   None.
*/
/*-----------------------------------------------------------------------*/
OSErr           ASoundSetVolume         (SoundInfoPtr theSoundInfo,
                                        unsigned short leftVol,
                                        unsigned short rightVol)
/*-----------------------------------------------------------------------*/
{
    SndCommand      theCmd      = {volumeCmd, kInit, kInit};
    OSErr           theErr      = noErr;
 
    if (StrictIsValid (theSoundInfo)) {
        theCmd.param2 = (rightVol << kSixteen) | leftVol;
        theErr = SndDoImmediate(theSoundInfo->chan, &theCmd);
    }
    else {
        theErr = kNilPtrErr;
    }
 
    if (theErr != noErr) {
        DebugPrint ("\pError in ASoundSetVolume");
    }
 
    return theErr;
}
 
/*
    Purpose:        Gets the rate multiplier of the currently playing sound.
    Side Effects:   None.
*/
/*-----------------------------------------------------------------------*/
OSErr           ASoundGetRateMul        (SoundInfoPtr theSoundInfo,
                                        UnsignedFixed *theRateMul)
/*-----------------------------------------------------------------------*/
{
    SndCommand      theCmd      = {getRateMultiplierCmd, kInit, kInit};
    OSErr           theErr      = noErr;
 
    if (StrictIsValid (theSoundInfo)) {
        theCmd.param2 = (long)theRateMul;
        theErr = SndDoImmediate(theSoundInfo->chan, &theCmd);
    }
    else {
        theErr = kNilPtrErr;
    }
 
    if (theErr != noErr) {
        DebugPrint ("\pError in ASoundGetRateMul");
    }
 
    return theErr;
}
 
/*
    Purpose:        Gets the rate multiplier of the currently playing sound.
    Side Effects:   None.
*/
/*-----------------------------------------------------------------------*/
OSErr           ASoundSetRateMul        (SoundInfoPtr theSoundInfo,
                                        UnsignedFixed theRateMul)
/*-----------------------------------------------------------------------*/
{
    SndCommand      theCmd      = {rateMultiplierCmd, kInit, kInit};
    OSErr           theErr      = noErr;
 
    if (StrictIsValid (theSoundInfo)) {
        theCmd.param2 = (long)theRateMul;
        theErr = SndDoImmediate(theSoundInfo->chan, &theCmd);
    }
    else {
        theErr = kNilPtrErr;
    }
 
    if (theErr != noErr) {
        DebugPrint ("\pError in ASoundSetRateMul");
    }
 
    return theErr;
}