Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
_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; |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-03-14