home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1998 November: Tool Chest / Dev.CD Nov 98 TC.toast / Sample Code / Snippets / Sound / SndPlayDoubleBuffer / _source / Interrupt_Routines.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-11-22  |  8.3 KB  |  308 lines  |  [TEXT/BOBO]

  1. /*
  2. **    Apple Macintosh Developer Technical Support
  3. **
  4. **    Routines demonstrating how to write interrupt routines to deal with
  5. sound and I/O callbacks.
  6. **
  7. **    by Mark Cookson, Apple Developer Technical Support
  8. **
  9. **    File:    Interrupt_Routines.c
  10. **
  11. **    Copyright ©1996 Apple Computer, Inc.
  12. **    All rights reserved.
  13. **
  14. **    You may incorporate this sample code into your applications without
  15. **    restriction, though the sample code has been provided "AS IS" and the
  16. **    responsibility for its operation is 100% yours.  However, what you are
  17. **    not permitted to do is to redistribute the source as "Apple Sample
  18. **    Code" after having made changes. If you're going to re-distribute the
  19. **    source, we require that you make it clear in the source that the code
  20. **    was descended from Apple Sample Code, but that you've made changes.
  21. */
  22.  
  23. #include "Interrupt_Routines.h"
  24.  
  25. /* This gets called when the requested read has completed.
  26.    This is a good place to add any conversion routines to convert
  27.    from your sound format into the format expected by the Sound Manager.
  28.  
  29.    It's important to note that the conversions I am doing here is to help
  30.    show you how to do something more complicated.  The Sound Manager 3.2 has
  31.    an Endian converter ('sowt'), and you can specify if a sound is 'raw '
  32.    or 'twos' to have the Sound Manager do the conversion.  We do the work
  33.    ourselves for entertainment and education.
  34. */
  35. /*-----------------------------------------------------------------------*/
  36. #ifdef powerc
  37. pascal    void    ASoundFileCallBack        (myParmBlkPtr passedPB)
  38. #else
  39. pascal    void    ASoundFileCallBack        (myParmBlkPtr passedPB:__a0)
  40. #endif
  41. /*-----------------------------------------------------------------------*/
  42. {
  43.     long            i            = kInit;
  44.     myParmBlkPtr    myPB        = nil;        /* just in case a0
  45. gets used for something else while we're here */
  46.     OSErr            theErr        = noErr;
  47.     char            dataFormat    = kInit;
  48.  
  49.  
  50.   #ifndef powerc
  51.     long    myA5;
  52.     myA5 = SetA5 (passedPB->myA5);
  53.   #endif
  54.  
  55.     if (passedPB == nil) {
  56.         DebugPrint ("\pmyPB is nil!");
  57.         theErr = kNilPtrErr;
  58.     }
  59.     else {
  60.         myPB = passedPB;
  61.  
  62.         if (myPB->pb.ioParam.ioResult == noErr) {
  63.             if (myPB->theSoundInfo->doubleHeader.dbhSampleSize
  64. == kSixteen) {
  65.                 dataFormat += kIs16Bit;
  66.             }
  67.             if (myPB->theSoundInfo->doubleHeader.dbhNumChannels
  68. == kStereo) {
  69.                 dataFormat += kIsStereo;
  70.             }
  71.  
  72.             if (myPB->theSoundInfo->needsMasking == true) {
  73.                 for (i = kInit; i <=
  74. myPB->pb.ioParam.ioActCount / sizeof (long); i++) {
  75.                     ((long
  76. *)myPB->pb.ioParam.ioBuffer)[i] ^= kLongMask;    /* Convert from raw to 2's
  77. complement */
  78.                 }
  79.             }
  80.  
  81.             if (myPB->theSoundInfo->fileType == 'WAVE' &&
  82. myPB->theSoundInfo->doubleHeader.dbhFormat == 'NONE') {    /* We will have to
  83. rearange the data so that the Mac can play it properly */
  84.                 switch (dataFormat) {
  85.                     case kMono8Bit:        /* Do
  86. nothing (endian only effects words, not bytes) */
  87.                     case kStereo8Bit:
  88.                         break;
  89.                     case kMono16Bit:    /* Do
  90. endian conversion (i.e. 0x1234 5678 becomes 0x2143 6587) */
  91.                     case kStereo16Bit:
  92.                             Endian16BitBuffer
  93. ((Ptr)myPB->pb.ioParam.ioBuffer, myPB->pb.ioParam.ioActCount);
  94.                         break;
  95.                     default:
  96.                         DebugPrint ("\pBad header
  97. passed to ASoundFileCallBack");
  98.                 }
  99.             }
  100.  
  101.             if (ASoundIsBackwards (myPB->theSoundInfo) == true)
  102. {    /* reverse the sound in the buffer so it sounds like we are playing
  103. backwards */
  104.                 switch (dataFormat) {
  105.                     case kMono8Bit:
  106.                         ReverseMono8BitBuffer
  107. (myPB->pb.ioParam.ioBuffer, myPB->pb.ioParam.ioActCount);
  108.                         break;
  109.                     case kMono16Bit:
  110.                         ReverseMono16BitBuffer
  111. (myPB->pb.ioParam.ioBuffer, myPB->pb.ioParam.ioActCount);
  112.                         break;
  113.                     case kStereo8Bit:
  114.                         ReverseStereo8BitBuffer
  115. (myPB->pb.ioParam.ioBuffer, myPB->pb.ioParam.ioActCount);
  116.                         break;
  117.                     case kStereo16Bit:
  118.                         ReverseStereo16BitBuffer
  119. (myPB->pb.ioParam.ioBuffer, myPB->pb.ioParam.ioActCount);
  120.                         break;
  121.                     default:
  122.                         DebugPrint ("\pBad header
  123. passed to ASoundFileCallBack");
  124.                 }
  125.                 ASoundPlayBackwards (myPB->theSoundInfo,
  126. false);
  127.             }
  128.         }
  129.         else {
  130.             DebugPrint("\pError in ASoundFileCallBack, ioResult
  131. was not zero!");
  132.             theErr = myPB->pb.ioParam.ioResult;
  133.         }
  134.     }
  135.  
  136.     if (theErr == noErr) {
  137.         myPB->theBuffer->dbFlags |= dbBufferReady;
  138.     }
  139.  
  140.     myPB->pbInUse = false;
  141.  
  142.   #ifndef powerc
  143.     myA5 = SetA5 (myA5);
  144.   #endif
  145.  
  146.     return;
  147. }
  148.  
  149. /* This gets called when the Sound Manager has finished playing the buffer and
  150.    wants you to refill it.
  151.  
  152.    Please note:  It may look like this routine reuses the paramBlock, but
  153. it does
  154.    not, there is a paramBlock for each buffer.  This eliminates race conditions
  155.    while priming the buffer, and makes sure that the paramBlock is not reused.
  156.  
  157.    You CANNOT reuse a paramBlock from within an ioCompletion routine!  This
  158.    will (ok, may) cause terrible problems, especially if you have File Sharing
  159.    turned on.
  160. */
  161. /*-----------------------------------------------------------------------*/
  162. pascal    void    ASoundDoubleBackProc    (SndChannelPtr chan,
  163.  
  164.     SndDoubleBufferPtr doubleBuffer)
  165. /*-----------------------------------------------------------------------*/
  166. {
  167. #ifndef __SC__
  168. #pragma unused (chan)
  169. #endif
  170.  
  171.     SoundInfoPtr    theSoundInfo    = nil;
  172.     myParamBlockRec    *myPB            = nil;
  173.     long            bytesToCopy        = kInit;
  174.     OSErr            theErr            = noErr;
  175.  
  176.   #ifndef powerc
  177.     long    myA5;
  178.   #endif
  179.  
  180.     theSoundInfo = (SoundInfoPtr)doubleBuffer->dbUserInfo[kSndInfoPtr];
  181.     if (IsValid (theSoundInfo) == false) {
  182.         DebugPrint ("\pbad user field, dbUserInfo[0] is probably
  183. nil!");
  184.         theErr = kNilPtrErr;
  185.     }
  186.     else {
  187.         myPB = (myParmBlkPtr)doubleBuffer->dbUserInfo[kPBPtr];
  188.         if (myPB == nil) {
  189.             DebugPrint ("\pbad user field, myPB is nil!");
  190.             theErr = kNilPtrErr;
  191.         }
  192.     }
  193.  
  194.   #ifndef powerc
  195.     myA5 = SetA5 (myPB->myA5);
  196.   #endif
  197.  
  198.     /* The pbInUse field was added because of a race condition caused
  199. by the fact that
  200.        the Sound Manager calls this routine one last time after the
  201. sound has stopped,
  202.        and in some cases when the sound has stopped we want to read
  203. from the start of
  204.        the file right away.  This seems to fix the problem of file
  205. errors when stopping
  206.        and starting a sound rapidly.
  207.  
  208.        If the PB is in use we just don't do the requested read.
  209.        The pbInUse flag is cleared at the end of ASoundFileCallBack. */
  210.     if (theErr == noErr && myPB->pbInUse == false &&
  211. theSoundInfo->stopping == false) {
  212.         myPB->pb.ioParam.ioBuffer = (Ptr)doubleBuffer->dbSoundData;
  213.  
  214.         bytesToCopy = ASoundGetNumTotalBytes(theSoundInfo) -
  215. ASoundGetBytesCopied (theSoundInfo);
  216.  
  217.         if (bytesToCopy > ASoundGetBufferSize (theSoundInfo)) {
  218.             bytesToCopy = ASoundGetBufferSize (theSoundInfo);
  219.         }
  220.  
  221.         myPB->pb.ioParam.ioReqCount = bytesToCopy;
  222.         myPB->pb.ioParam.ioPosOffset = ASoundGetBytesCopied
  223. (theSoundInfo);
  224.  
  225.         if (myPB->pb.ioParam.ioPosOffset < theSoundInfo->dataStart)
  226.     {    /* A little extra sanity checking */
  227.             myPB->pb.ioParam.ioPosOffset = theSoundInfo->dataStart;
  228.         }
  229.  
  230.         myPB->theBuffer = doubleBuffer;                /*
  231. Which buffer are we filling? */
  232.         if (bytesToCopy > kInit) {
  233.     /* Do we really need to read more sound?*/
  234.             myPB->pbInUse = true;
  235.             theErr = PBReadAsync (&myPB->pb);        /*
  236. Do an async read of more sound */
  237.         }
  238.  
  239.         if (theErr >= noErr) {
  240.             theErr = noErr;        /* positive errors are not
  241. real errors */
  242.             theSoundInfo->bytesCopied += bytesToCopy;
  243.             if (theSoundInfo->bytesPerFrame > kInit) {
  244.                 doubleBuffer->dbNumFrames = bytesToCopy /
  245. theSoundInfo->bytesPerFrame;
  246.             }
  247.             else {
  248.                 DebugPrint ("\pbytesPerFrame is a bad value!");
  249.             }
  250.  
  251.             if (theSoundInfo->bytesCopied ==
  252. theSoundInfo->bytesTotal)
  253.                 doubleBuffer->dbFlags |= dbLastBuffer;
  254.  
  255.             theSoundInfo->currentBuffer++;
  256.         }
  257.         else {
  258.             DebugPrint ("\pPBReadAsync error!");
  259.         }
  260.     }
  261.  
  262.   #ifndef powerc
  263.     (void)SetA5 (myA5);
  264.   #endif
  265.  
  266.     return;
  267. }
  268.  
  269. /* This gets called when the sound is finally done playing.
  270.    It sets a flag that we check in our event loop because you can't clean up
  271.    everything at interrupt time.
  272. */
  273. /*-----------------------------------------------------------------------*/
  274. pascal    void    ASoundDoneCallBack        (SndChannelPtr chan,
  275.  
  276.     SndCommand *cmd)
  277. /*-----------------------------------------------------------------------*/
  278. {
  279. #ifndef __SC__
  280. #ifdef powerc
  281. #pragma unused (cmd)
  282. #endif
  283. #endif
  284.  
  285.     SoundInfoPtr        theSoundInfo = nil;
  286.  
  287.   #ifndef powerc
  288.     long    myA5;
  289.     myA5 = SetA5 (cmd->param2);
  290.   #endif
  291.  
  292.     theSoundInfo = (SoundInfoPtr)chan->userInfo;
  293.     if (StrictIsValid (theSoundInfo)) {
  294.         theSoundInfo->soundDone = true;
  295.     }
  296.     else {
  297.         DebugPrint ("\pbad sound channel pointer passed to
  298. ASoundDoneCallBack");
  299.     }
  300.  
  301.   #ifndef powerc
  302.     myA5 = SetA5 (myA5);
  303.   #endif
  304.  
  305.     return;
  306. }
  307.  
  308.