home *** CD-ROM | disk | FTP | other *** search
- /*
- ** Apple Macintosh Developer Technical Support
- **
- ** Routines demonstrating how to write interrupt routines to deal with
- sound and I/O callbacks.
- **
- ** by Mark Cookson, Apple Developer Technical Support
- **
- ** File: Interrupt_Routines.c
- **
- ** Copyright ©1996 Apple Computer, Inc.
- ** All rights reserved.
- **
- ** You may incorporate this sample code into your applications without
- ** restriction, though the sample code has been provided "AS IS" and the
- ** responsibility for its operation is 100% yours. However, what you are
- ** not permitted to do is to redistribute the source as "Apple Sample
- ** 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 Code, but that you've made changes.
- */
-
- #include "Interrupt_Routines.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;
- }
-
-