_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;
}