_source/Private_DBFFFunctions.c

/*
    File:       Private_DBFFFunctions.c
 
    Contains:   Routines demonstrating how to set up for 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 "Private_DBFFFunctions.h"
 
/********************************************************************************************
        ** YOU SHOULD NEVER NEED TO CALL ANY OF THE FOLLOWING ROUTINES DIRECTLY **
********************************************************************************************/
 
/*  Purpose:        This sets all of local and global variables to safe values.
                    This routine is called by ASoundNew, and is called by the
                    ASoundDone function after it has cleaned up to reset things.
    Side Effects:   None.
*/
/*-----------------------------------------------------------------------*/
        OSErr   ASoundInit      (SoundInfoPtr theSoundInfo)
/*-----------------------------------------------------------------------*/
{
    NumVersion  SndManagerVer;
    OSErr       theErr          = noErr;
 
    theSoundInfo->globals.gSupports16Bit    = true;
    theSoundInfo->globals.gSupportsSPDB     = true;
 
    theSoundInfo->signature         = kDBFFSignature;
    theSoundInfo->chan              = kInit;
    theSoundInfo->rateForResume     = kInit;
    /* create a Universal Procedure Pointer (UPP) for our sound callback */
    theSoundInfo->theSoundCallBackUPP = NewSndCallBackProc(ASoundDoneCallBack);
    theSoundInfo->fileType          = kInit;
    (void)ASoundSetNumBuffers       (theSoundInfo, kStart);
    theSoundInfo->dataStart         = kInit;
    (void)ASoundSetSoundLength      (theSoundInfo, kInit);
    ASoundSetBytesCopied            (theSoundInfo, kInit);
    ASoundSetCurBuffer              (theSoundInfo, kStart);
    (void)ASoundSetBufferSize       (theSoundInfo, kInit);
    theSoundInfo->bytesPerFrame     = kInit;
    theSoundInfo->refNum            = kInit;
    theSoundInfo->vRefNum           = nil;
    theSoundInfo->compFactor        = kNoCompression;
    theSoundInfo->paused            = false;
    theSoundInfo->playing           = false;
    theSoundInfo->adjusting         = false;
    theSoundInfo->soundDone         = false;
    theSoundInfo->backwards         = false;
    theSoundInfo->hasBeenAdjusted   = false;
    theSoundInfo->needsMasking      = false;
    theSoundInfo->stopping          = false;
 
    SndManagerVer = SndSoundManagerVersion ();
    theErr = InterrogateSystem (&(theSoundInfo->globals));
    if (theErr == noErr) {
        if (SndManagerVer.majorRev >= kMinSndMgrVer) {
            if ((theSoundInfo->globals.ggestaltSoundAttr & (1 << gestaltSndPlayDoubleBuffer)) == false) {
                DebugPrint ("\pSndPlayDoubleBuffer isn't supported!");
                theSoundInfo->globals.gSupportsSPDB = false;
                theErr = kNoSPDBErr;
            }
            if ((theSoundInfo->globals.ggestaltSoundAttr & (1<< gestalt16BitAudioSupport)) == false) {
                DebugPrint ("\pThis machine doesn't support 16 bit audio!");
                theSoundInfo->globals.gSupports16Bit = false;
                theErr = notEnoughHardwareErr;  /* This doesn't have to be a fatal error */
            }
        }
        else {
            theErr = kOldSndMgrErr;
        }
    }
    else {
        theErr = kInitErr;
    }
 
    return theErr;
}
 
/*-----------------------------------------------------------------------*/
Boolean         IsValid                 (SoundInfoPtr theSoundInfo)
/*-----------------------------------------------------------------------*/
{
    return CheckValididity (theSoundInfo, false);
}
 
/*-----------------------------------------------------------------------*/
Boolean         StrictIsValid           (SoundInfoPtr theSoundInfo)
/*-----------------------------------------------------------------------*/
{
    return CheckValididity (theSoundInfo, true);
}
 
 
/*-----------------------------------------------------------------------*/
Boolean         CheckValididity         (SoundInfoPtr theSoundInfo,
                                        Boolean strict)
/*-----------------------------------------------------------------------*/
{
    Boolean     returnValue     = true;     /* Assume success */
 
    if (theSoundInfo == nil) {
        returnValue = false;
    }
    else {
        if (strict == true) {
            if (theSoundInfo->signature != kDBFFSignature) {
                returnValue = false;
                DebugPrint ("\pStrictIsValid theSoundInfo->signature is invalid");
            }
            else {
                if (theSoundInfo->doubleHeader.dbhNumChannels <= kInit) {
                    returnValue = false;
                    DebugPrint ("\pStrictIsValid theSoundInfo->doubleHeader.dbhNumChannels is invalid");
                }
                if (theSoundInfo->doubleHeader.dbhSampleSize <= kInit) {
                    returnValue = false;
                    DebugPrint ("\pStrictIsValid theSoundInfo->doubleHeader.dbhSampleSize is invalid");
                }
                if (theSoundInfo->doubleHeader.dbhPacketSize < kInit) {
                    returnValue = false;
                    DebugPrint ("\pStrictIsValid theSoundInfo->doubleHeader.dbhPacketSize is invalid");
                }
                if (theSoundInfo->theSoundCallBackUPP == nil) {
                    returnValue = false;
                    DebugPrint ("\pStrictIsValid theSoundInfo->theSoundCallBackUPP is invalid");
                }
                if (theSoundInfo->rateForResume < kInit) {
                    returnValue = false;
                    DebugPrint ("\pStrictIsValid theSoundInfo->rateForResume is invalid");
                }
                if (theSoundInfo->numBuffers <= kInit) {
                    returnValue = false;
                    DebugPrint ("\pStrictIsValid theSoundInfo->numBuffers is invalid");
                }
                if (theSoundInfo->dataStart < kInit) {
                    returnValue = false;
                    DebugPrint ("\pStrictIsValid theSoundInfo->dataStart is invalid");
                }
                if (theSoundInfo->bytesTotal <= kInit) {
                    returnValue = false;
                    DebugPrint ("\pStrictIsValid theSoundInfo->bytesTotal is invalid");
                }
                if (theSoundInfo->bytesCopied <= kInit) {
                    returnValue = false;
                    DebugPrint ("\pStrictIsValid theSoundInfo->bytesCopied is invalid");
                }
                if (theSoundInfo->currentBuffer <= kInit) {
                    returnValue = false;
                    DebugPrint ("\pStrictIsValid theSoundInfo->currentBuffer is invalid");
                }
                if (theSoundInfo->doubleBufferSize <= kInit) {
                    returnValue = false;
                    DebugPrint ("\pStrictIsValid theSoundInfo->doubleBufferSize is invalid");
                }
                if (theSoundInfo->bytesPerFrame <= kInit) {
                    returnValue = false;
                    DebugPrint ("\pStrictIsValid theSoundInfo->bytesPerFrame is invalid");
                }
                if (theSoundInfo->compFactor <= kInit) {
                    returnValue = false;
                    DebugPrint ("\pStrictIsValid theSoundInfo->compFactor is invalid");
                }
            }
        }
        else {
            if (theSoundInfo->signature != kDBFFSignature) {
                returnValue = false;
                DebugPrint ("\pIsValid theSoundInfo->signature is invalid");
            }
            else {
                if (theSoundInfo->doubleHeader.dbhNumChannels < kInit) {
                    returnValue = false;
                    DebugPrint ("\pIsValid theSoundInfo->doubleHeader.dbhNumChannels is invalid");
                }
                if (theSoundInfo->doubleHeader.dbhSampleSize < kInit) {
                    returnValue = false;
                    DebugPrint ("\pIsValid theSoundInfo->doubleHeader.dbhSampleSize is invalid");
                }
                if (theSoundInfo->doubleHeader.dbhPacketSize < kInit) {
                    returnValue = false;
                    DebugPrint ("\pIsValid theSoundInfo->doubleHeader.dbhPacketSize is invalid");
                }
                if (theSoundInfo->theSoundCallBackUPP == nil) {
                    returnValue = false;
                    DebugPrint ("\pIsValid theSoundInfo->theSoundCallBackUPP is invalid");
                }
                if (theSoundInfo->rateForResume < kInit) {
                    returnValue = false;
                    DebugPrint ("\pIsValid theSoundInfo->rateForResume is invalid");
                }
                if (theSoundInfo->numBuffers < kInit) {
                    returnValue = false;
                    DebugPrint ("\pIsValid theSoundInfo->numBuffers is invalid");
                }
                if (theSoundInfo->dataStart < kInit) {
                    returnValue = false;
                    DebugPrint ("\pIsValid theSoundInfo->dataStart is invalid");
                }
                if (theSoundInfo->bytesTotal < kInit) {
                    returnValue = false;
                    DebugPrint ("\pIsValid theSoundInfo->bytesTotal is invalid");
                }
                if (theSoundInfo->bytesCopied < kInit) {
                    returnValue = false;
                    DebugPrint ("\pIsValid theSoundInfo->bytesCopied is invalid");
                }
                if (theSoundInfo->currentBuffer < kInit) {
                    returnValue = false;
                    DebugPrint ("\pIsValid theSoundInfo->currentBuffer is invalid");
                }
                if (theSoundInfo->doubleBufferSize < kInit) {
                    returnValue = false;
                    DebugPrint ("\pIsValid theSoundInfo->doubleBufferSize is invalid");
                }
                if (theSoundInfo->bytesPerFrame < kInit) {
                    returnValue = false;
                    DebugPrint ("\pIsValid theSoundInfo->bytesPerFrame is invalid");
                }
                if (theSoundInfo->compFactor < kInit) {
                    returnValue = false;
                    DebugPrint ("\pIsValid theSoundInfo->compFactor is invalid");
                }
            }
        }
    }
 
    return returnValue;
}
 
/*
    Purpose:        Called by StandardFile for every file in a folder to
                    see if we want to display that file in the open file
                    dialog.  Return false (don't filter) if you want the
                    file displayed, true (do filter) if you don't want
                    it displayed.
 
                    This calls ASoundCanThisPlay() which calls the header
                    parsing routines.  If we can parse the header we
                    should be able to play the file.
    Side Effects:   None.
*/
/*-----------------------------------------------------------------------*/
pascal Boolean  ASoundFileFilter        (CInfoPBPtr theFileInfo)
/*-----------------------------------------------------------------------*/
{
    OSErr       theErr          = noErr;
    Boolean     returnValue     = true;     /* by default don't display the file */
 
    theErr = ASoundCanThisPlay (theFileInfo);
    if (theErr == noErr) {                  /* We can play this file. */
        returnValue = false;                /* Display this file */
    }
 
    if (theErr != noErr && theErr != kUnknownFormat) {
        DebugPrint ("\pError in ASoundFileFilter");
    }
 
    return returnValue;
}
 
/*
    Purpose:        Sets how many buffers will be needed to play the entire
                    sound.
    Side Effects:   None.
*/
/*-----------------------------------------------------------------------*/
        OSErr   ASoundSetNumBuffers     (SoundInfoPtr theSoundInfo,
                                        long newValue)
/*-----------------------------------------------------------------------*/
{
    OSErr       theErr  = noErr;
 
    if (newValue >= kInit) {
        theSoundInfo->numBuffers = newValue;
    }
    else {
        theErr = kBadValue;
    }
 
    return theErr;
}
 
/*
    Purpose:        To install a callback command into the current sound
                    channel.
    Side Effects:   None.
*/
/*-----------------------------------------------------------------------*/
        OSErr   InstallCallBack         (SoundInfoPtr theSoundInfo)
/*-----------------------------------------------------------------------*/
{
    SndCommand  mycmd   = {callBackCmd, kInit, kInit};
    OSErr       theErr  = noErr;
 
    mycmd.param2 = SetCurrentA5();
    theErr = SndDoCommand (theSoundInfo->chan, &mycmd, true);
 
    if (theErr != noErr) {
        DebugPrint ("\pError in InstallCallBack");
    }
 
    return theErr;
}
 
/*
    Purpose:        Gather the information needed (from the sound's header)
                    to setup the structures the Sound Manager will need to
                    play the sound.
    Side Effects:   This will allocate memory for the sound header strucure
                    that will be disposed of by ASoundDonePlaying.
*/
/*-----------------------------------------------------------------------*/
        OSErr   SetUpSoundHeader        (SoundInfoPtr theSoundInfo,
                                        unsigned long bufferSize)
/*-----------------------------------------------------------------------*/
{
    long double     sampleRate  = kInit;
    long            dataStart   = kInit,
                    sndLength   = kInit,
                    numBuffers  = kInit;
    short           remainder   = kInit;
    OSErr           theErr      = noErr;
 
    theSoundInfo->chan->userInfo = (long)theSoundInfo;      /* So we know who we are in the SoundCompletion routines */
 
    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 = ASoundGetSNDHeader  (theSoundInfo, &dataStart, &sndLength);
            break;
        default:
            theErr = kUnknownFormat;
            break;
    }
 
    theSoundInfo->dataStart = dataStart;
    if (theErr != noErr) {
        DebugPrint ("\pASoundGetAIFFHeader error!");
        (void)SndDisposeChannel (theSoundInfo->chan, true);
    }
    else {
        if ((theSoundInfo->doubleHeader.dbhSampleSize == kSixteen) && (theSoundInfo->globals.gSupports16Bit == false)) {
            DebugPrint ("\pThis is a 16 bit sound, this is not a 16 bit capable machine.");
            theErr = notEnoughHardwareErr;
        }
        else {  
            theErr = ASoundSetSoundLength (theSoundInfo, sndLength);
            ASoundSetBytesCopied (theSoundInfo, dataStart);             /* skip the header of the file */
 
            sampleRate = ASoundFixToLongDouble (theSoundInfo->doubleHeader.dbhSampleRate);
 
            if (bufferSize > kInit) {
                theErr = ASoundSetBufferSize (theSoundInfo, bufferSize);
            }
            else {
                theErr = ASoundSetBufferSize (theSoundInfo, (((theSoundInfo->doubleHeader.dbhSampleSize / kBitsPerByte) * theSoundInfo->doubleHeader.dbhNumChannels * sampleRate) / kBufLen) / theSoundInfo->compFactor);
            }
 
            if (ASoundGetBufferSize (theSoundInfo) > kInit) {
                numBuffers = (ASoundGetNumTotalBytes (theSoundInfo) / ASoundGetBufferSize (theSoundInfo));
                remainder = (ASoundGetNumTotalBytes (theSoundInfo) % ASoundGetBufferSize (theSoundInfo));
                if (remainder != 0) {   /* Is the last buffer only a partial buffer? */
                    numBuffers++;       /* Don't forget to account for it! */
                }
                theErr = ASoundSetNumBuffers (theSoundInfo, numBuffers);
            }
            else {
                DebugPrint ("\pThe buffer size is zero, this is bad.");
                theErr = dsZeroDivErr;
            }
 
            theSoundInfo->doubleHeader.dbhDoubleBack = NewSndDoubleBackProc (ASoundDoubleBackProc);
            theSoundInfo->doubleHeader.dbhBufferPtr[kDBBufOne] = nil;
            theSoundInfo->doubleHeader.dbhBufferPtr[kDBBufTwo] = nil;
        }
    }
 
    if (theErr != noErr) {
        DebugPrint ("\pError in SetUpSoundHeader");
    }
 
    return theErr;
}
 
/*
    Purpose:        Pause the playing of the sound.
    Side Effects:   None.
*/
/*-----------------------------------------------------------------------*/
        OSErr   PauseSound              (SoundInfoPtr theSoundInfo)
/*-----------------------------------------------------------------------*/
{
    OSErr           theErr  = noErr;
    UnsignedFixed   rateMul;
 
    rateMul = 0;
    theErr = SndSetInfo (theSoundInfo->chan, siRateMultiplier, (void*)rateMul);
 
    if (theErr == noErr) {
        theSoundInfo->paused = true;
    }
 
    if (theErr != noErr) {
        DebugPrint ("\pError in PauseSound");
    }
 
    return theErr;
}
 
/*
    Purpose:        Resume the playing of the sound.
    Side Effects:   This will reinstall the sound completion callback
                    if it was removed by the ASoundPauseForAdjust function.
*/
/*-----------------------------------------------------------------------*/
        OSErr   ResumeSound             (SoundInfoPtr theSoundInfo)
/*-----------------------------------------------------------------------*/
{
    OSErr           theErr  = noErr;
    UnsignedFixed   rateMul;
 
    rateMul = (1<<16);
    theErr = SndSetInfo (theSoundInfo->chan, siRateMultiplier, (void*)rateMul);
 
    if (theErr == noErr) {
        theSoundInfo->paused = false;
        if (theSoundInfo->hasBeenAdjusted == true) {
            theErr = SndPlayDoubleBuffer (theSoundInfo->chan, (SndDoubleBufferHeaderPtr)&(theSoundInfo->doubleHeader));
            theErr = InstallCallBack (theSoundInfo);
            theSoundInfo->hasBeenAdjusted = false;
        }
    }
 
    if (theErr != noErr) {
        DebugPrint ("\pError in ResumeSound");
    }
 
    return theErr;
}
 
/*-----------------------------------------------------------------------*/
        OSErr   ASoundSetBufferSize     (SoundInfoPtr theSoundInfo,
                                        long newValue)
/*-----------------------------------------------------------------------*/
{
    OSErr           theErr      = noErr;
 
    if (newValue >= nil) {
        /* Make sure the buffer is an integer multiple of the packet size,
           otherwise IMA will be VERY upset, and WAVE files might not sound
           too good either. */
        if (theSoundInfo->doubleHeader.dbhPacketSize > kInit) {
            newValue /= theSoundInfo->bytesPerFrame;
//          newValue /= theSoundInfo->doubleHeader.dbhPacketSize * theSoundInfo->doubleHeader.dbhNumChannels;
        }
        newValue *= theSoundInfo->bytesPerFrame;
//      newValue *= theSoundInfo->doubleHeader.dbhPacketSize * theSoundInfo->doubleHeader.dbhNumChannels;
        theSoundInfo->doubleBufferSize = newValue;
    }
    else {
        theSoundInfo->doubleBufferSize = kInit;
        DebugPrint ("\pBad value passed to ASoundSetBufferSize");
        theErr = kBadValue;
    }
 
    if (theErr != noErr) {
        DebugPrint ("\pError in ASoundSetBufferSize");
    }
 
    return theErr;
}
 
/*-----------------------------------------------------------------------*/
        OSErr   ASoundSetSoundLength    (SoundInfoPtr theSoundInfo,
                                        long newValue)
/*-----------------------------------------------------------------------*/
{
    OSErr           theErr      = noErr;
 
    if (newValue >= nil) {
        theSoundInfo->bytesTotal = newValue;
    }
    else {
        theSoundInfo->bytesTotal = kInit;
        theErr = kBadValue;
    }
 
    if (theErr != noErr) {
        DebugPrint ("\pError in ASoundSetSoundLength");
    }
 
    return theErr;
}
 
/*
    Purpose:        The purpose of this routine is to fill out the
                    SndDoubleBufferHeader structure with the information the
                    Sound Manager will need, and to fill out the
                    myParamBlockRec structure with the information the
                    ASoundDoubleBackProc interrupt routine will need.  It
                    then calls ASoundDoubleBackProc to fill the first two
                    buffers.
    Side effects:   This routine allocates two pointers as buffers for the
                    sound that will be playing.
                    This routine allocates two custom paramBlockRecs for
                    the ASoundDoubleBackProc (interrupt routine) to use.
*/
/*-----------------------------------------------------------------------*/
        OSErr   ASoundPrimeBuffers      (SoundInfoPtr theSoundInfo)
/*-----------------------------------------------------------------------*/
{
    myParmBlkPtr        myPB            = nil;
    SndDoubleBufferPtr  doubleBuffer    = nil;
    IOCompletionUPP     myIOCompletion  = nil;
    OSErr               theErr          = noErr;
    short               i               = kInit;
 
    for (i = kInit; i <= kOne; ++i) {
        if (theSoundInfo->doubleHeader.dbhBufferPtr[i] == nil) {
            doubleBuffer = (SndDoubleBufferPtr)NewPtrClear (sizeof(SndDoubleBuffer) + ASoundGetBufferSize (theSoundInfo));
            if (doubleBuffer == nil || MemError() != noErr) {
                DebugPrint("\pNo memory for double buffers!");
                theErr = memFullErr;
            }
            else {
                /* Hold the memory in case VM is on, this will help to ensure that we have no sound
                   drop outs caused by the paging of our buffers. */
                HoldMemory (doubleBuffer, sizeof(SndDoubleBuffer) + ASoundGetBufferSize (theSoundInfo));
                /* dbNumFrames gets set in the ASoundDoubleBackProc() routine */
                doubleBuffer->dbFlags = nil;
                doubleBuffer->dbUserInfo[kSndInfoPtr] = (long)theSoundInfo;     /* Make this point at our SoundInfo struct */
                myPB = (myParamBlockRec *)NewPtrClear(sizeof(myParamBlockRec));     /* Make one for each buffer so */
                if (myPB == nil || MemError() != noErr) {                           /* we don't reuse the same paramBlock in */
                    DebugPrint("\pNo memory for a new paramBlockRec!");             /* the ioCompletion routine. */
                    theErr = memFullErr;
                }
                else {
                    myPB->myA5 = SetCurrentA5 ();
                    myPB->theSoundInfo = theSoundInfo;
                    myIOCompletion = NewIOCompletionProc (ASoundFileCallBack);
                    myPB->pb.ioParam.ioCompletion = myIOCompletion;
                    myPB->pb.ioParam.ioVRefNum = theSoundInfo->vRefNum;
                    myPB->pb.ioParam.ioRefNum = theSoundInfo->refNum;
                    myPB->pb.ioParam.ioPosMode = fsFromStart | noCacheMask;             /* Set the noCacheBit since we probably won't be reading this again */
                    doubleBuffer->dbUserInfo[kPBPtr] = (long)myPB;
 
                    theSoundInfo->doubleHeader.dbhBufferPtr[i] = doubleBuffer;
                }
            }
        }
        else {
            doubleBuffer = theSoundInfo->doubleHeader.dbhBufferPtr[i];
        }
 
        ASoundDoubleBackProc (theSoundInfo->chan, doubleBuffer);    /* prime the buffers */
 
    }
 
    if (theErr != noErr) {
        DebugPrint ("\pError in ASoundPrimeBuffers");
        for (i = kInit; i <= kOne; ++i) {
            if (!theSoundInfo->doubleHeader.dbhBufferPtr[i]) {
                DisposePtr ((Ptr)myIOCompletion);
                DisposePtr ((Ptr)theSoundInfo->doubleHeader.dbhBufferPtr[i]->dbUserInfo[kSndInfoPtr]);
                DisposePtr ((Ptr)theSoundInfo->doubleHeader.dbhBufferPtr[i]);
            }
        }
    }
 
    return theErr;
}
 
/* This routine is used to calculate where to put the SFGetFile Dialog */
/*-----------------------------------------------------------------------*/
        Rect    GetMainScreenRect       (void)
/*-----------------------------------------------------------------------*/
{
    GDHandle    mainDevice;
    GrafPtr     mainPort;
    Rect        returnRect;
    long        response;
    
    Gestalt (gestaltQuickdrawVersion, &response);
    
    if (response == gestaltOriginalQD) {
        GetWMgrPort (&mainPort);
        returnRect = mainPort->portRect;
    }
    else {
        mainDevice = GetMainDevice();
        returnRect = (*mainDevice)->gdRect;
    }
 
    return returnRect;
}