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/Interrupt_Routines.c
/* |
File: Interrupt_Routines.c |
Contains: Routines demonstrating how to write interrupt routines to deal with |
sound and I/O callbacks. |
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 "Interrupt_Routines.h" |
#include <Devices.h> |
/* This gets called when the requested read has completed. |
This is a good place to add any conversion routines to convert |
from your sound format into the format expected by the Sound Manager. |
It's important to note that the conversions I am doing here is to help |
show you how to do something more complicated. The Sound Manager 3.2 has |
an Endian converter ('sowt'), and you can specify if a sound is 'raw ' |
or 'twos' to have the Sound Manager do the conversion. We do the work |
ourselves for entertainment and education. |
*/ |
/*-----------------------------------------------------------------------*/ |
#ifdef powerc |
pascal void ASoundFileCallBack (myParmBlkPtr passedPB) |
#else |
pascal void ASoundFileCallBack (myParmBlkPtr passedPB:__a0) |
#endif |
/*-----------------------------------------------------------------------*/ |
{ |
long i = kInit; |
myParmBlkPtr myPB = nil; /* just in case a0 |
gets used for something else while we're here */ |
OSErr theErr = noErr; |
char dataFormat = kInit; |
#ifndef powerc |
long myA5; |
myA5 = SetA5 (passedPB->myA5); |
#endif |
if (passedPB == nil) { |
DebugPrint ("\pmyPB is nil!"); |
theErr = kNilPtrErr; |
} |
else { |
myPB = passedPB; |
if (myPB->pb.ioParam.ioResult == noErr) { |
if (myPB->theSoundInfo->doubleHeader.dbhSampleSize == kSixteen) { |
dataFormat += kIs16Bit; |
} |
if (myPB->theSoundInfo->doubleHeader.dbhNumChannels == kStereo) { |
dataFormat += kIsStereo; |
} |
if (myPB->theSoundInfo->needsMasking == true) { |
for (i = kInit; i <= myPB->pb.ioParam.ioActCount / sizeof (long); i++) { |
((long *)myPB->pb.ioParam.ioBuffer)[i] ^= kLongMask; /* Convert from raw to 2's complement */ |
} |
} |
if (myPB->theSoundInfo->fileType == 'WAVE' && myPB->theSoundInfo->doubleHeader.dbhFormat == 'NONE') { /* We will have to rearange the data so that the Mac can play it properly */ |
switch (dataFormat) { |
case kMono8Bit: /* Do nothing (endian only effects words, not bytes) */ |
case kStereo8Bit: |
break; |
case kMono16Bit: /* Do endian conversion (i.e. 0x1234 5678 becomes 0x2143 6587) */ |
case kStereo16Bit: |
Endian16BitBuffer ((Ptr)myPB->pb.ioParam.ioBuffer, myPB->pb.ioParam.ioActCount); |
break; |
default: |
DebugPrint ("\pBad header passed to ASoundFileCallBack"); |
} |
} |
if (ASoundIsBackwards (myPB->theSoundInfo) == true) { /* reverse the sound in the buffer so it sounds like we are playing backwards */ |
switch (dataFormat) { |
case kMono8Bit: |
ReverseMono8BitBuffer (myPB->pb.ioParam.ioBuffer, myPB->pb.ioParam.ioActCount); |
break; |
case kMono16Bit: |
ReverseMono16BitBuffer (myPB->pb.ioParam.ioBuffer, myPB->pb.ioParam.ioActCount); |
break; |
case kStereo8Bit: |
ReverseStereo8BitBuffer (myPB->pb.ioParam.ioBuffer, myPB->pb.ioParam.ioActCount); |
break; |
case kStereo16Bit: |
ReverseStereo16BitBuffer (myPB->pb.ioParam.ioBuffer, myPB->pb.ioParam.ioActCount); |
break; |
default: |
DebugPrint ("\pBad header passed to ASoundFileCallBack"); |
} |
ASoundPlayBackwards (myPB->theSoundInfo, false); |
} |
} |
else { |
DebugPrint("\pError in ASoundFileCallBack, ioResult was not zero!"); |
theErr = myPB->pb.ioParam.ioResult; |
} |
} |
if (theErr == noErr) { |
myPB->theBuffer->dbFlags |= dbBufferReady; |
} |
myPB->pbInUse = false; |
#ifndef powerc |
myA5 = SetA5 (myA5); |
#endif |
return; |
} |
/* This gets called when the Sound Manager has finished playing the buffer and |
wants you to refill it. |
Please note: It may look like this routine reuses the paramBlock, but |
it does |
not, there is a paramBlock for each buffer. This eliminates race conditions |
while priming the buffer, and makes sure that the paramBlock is not reused. |
You CANNOT reuse a paramBlock from within an ioCompletion routine! This |
will (ok, may) cause terrible problems, especially if you have File Sharing |
turned on. |
*/ |
/*-----------------------------------------------------------------------*/ |
pascal void ASoundDoubleBackProc (SndChannelPtr chan, |
SndDoubleBufferPtr doubleBuffer) |
/*-----------------------------------------------------------------------*/ |
{ |
#ifndef __SC__ |
#pragma unused (chan) |
#endif |
SoundInfoPtr theSoundInfo = nil; |
myParamBlockRec *myPB = nil; |
long bytesToCopy = kInit; |
OSErr theErr = noErr; |
#ifndef powerc |
long myA5; |
#endif |
theSoundInfo = (SoundInfoPtr)doubleBuffer->dbUserInfo[kSndInfoPtr]; |
if (IsValid (theSoundInfo) == false) { |
DebugPrint ("\pbad user field, dbUserInfo[0] is probably nil!"); |
theErr = kNilPtrErr; |
} |
else { |
myPB = (myParmBlkPtr)doubleBuffer->dbUserInfo[kPBPtr]; |
if (myPB == nil) { |
DebugPrint ("\pbad user field, myPB is nil!"); |
theErr = kNilPtrErr; |
} |
} |
#ifndef powerc |
myA5 = SetA5 (myPB->myA5); |
#endif |
/* The pbInUse field was added because of a race condition caused by the fact that |
the Sound Manager calls this routine one last time after the sound has stopped, |
and in some cases when the sound has stopped we want to read from the start of |
the file right away. This seems to fix the problem of file errors when stopping |
and starting a sound rapidly. |
If the PB is in use we just don't do the requested read. |
The pbInUse flag is cleared at the end of ASoundFileCallBack. */ |
if (theErr == noErr && myPB->pbInUse == false && theSoundInfo->stopping == false) { |
myPB->pb.ioParam.ioBuffer = (Ptr)doubleBuffer->dbSoundData; |
bytesToCopy = ASoundGetNumTotalBytes(theSoundInfo) - ASoundGetBytesCopied (theSoundInfo); |
if (bytesToCopy > ASoundGetBufferSize (theSoundInfo)) { |
bytesToCopy = ASoundGetBufferSize (theSoundInfo); |
} |
myPB->pb.ioParam.ioReqCount = bytesToCopy; |
myPB->pb.ioParam.ioPosOffset = ASoundGetBytesCopied (theSoundInfo); |
if (myPB->pb.ioParam.ioPosOffset < theSoundInfo->dataStart) { /* A little extra sanity checking */ |
myPB->pb.ioParam.ioPosOffset = theSoundInfo->dataStart; |
} |
myPB->theBuffer = doubleBuffer; /* Which buffer are we filling? */ |
if (bytesToCopy > kInit) { /* Do we really need to read more sound?*/ |
myPB->pbInUse = true; |
theErr = PBReadAsync(&myPB->pb); /* Do an async read of more sound */ |
} |
if (theErr >= noErr) { |
theErr = noErr; /* positive errors are not real errors */ |
theSoundInfo->bytesCopied += bytesToCopy; |
if (theSoundInfo->bytesPerFrame > kInit) { |
doubleBuffer->dbNumFrames = bytesToCopy / theSoundInfo->bytesPerFrame; |
} |
else { |
DebugPrint ("\pbytesPerFrame is a bad value!"); |
} |
if (theSoundInfo->bytesCopied == theSoundInfo->bytesTotal) |
doubleBuffer->dbFlags |= dbLastBuffer; |
theSoundInfo->currentBuffer++; |
} |
else { |
DebugPrint ("\pPBReadAsync error!"); |
} |
} |
#ifndef powerc |
(void)SetA5 (myA5); |
#endif |
return; |
} |
/* This gets called when the sound is finally done playing. |
It sets a flag that we check in our event loop because you can't clean up |
everything at interrupt time. |
*/ |
/*-----------------------------------------------------------------------*/ |
pascal void ASoundDoneCallBack (SndChannelPtr chan, |
SndCommand *cmd) |
/*-----------------------------------------------------------------------*/ |
{ |
#ifndef __SC__ |
#ifdef powerc |
#pragma unused (cmd) |
#endif |
#endif |
SoundInfoPtr theSoundInfo = nil; |
#ifndef powerc |
long myA5; |
myA5 = SetA5 (cmd->param2); |
#endif |
theSoundInfo = (SoundInfoPtr)chan->userInfo; |
if (StrictIsValid (theSoundInfo)) { |
theSoundInfo->soundDone = true; |
} |
else { |
DebugPrint ("\pbad sound channel pointer passed to ASoundDoneCallBack"); |
} |
#ifndef powerc |
myA5 = SetA5 (myA5); |
#endif |
return; |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-03-14