home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / C / Libraries / dsound / dsound.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-01-11  |  11.6 KB  |  362 lines  |  [TEXT/KAHL]

  1. /* dsound.c */
  2. /*
  3. DS_soundLib is a small, simple sound-utilities library created so I don't have to
  4. re-invent the wheel each time I wan't to incorporate asynchronous sound into a
  5. program.
  6.  
  7. This library includes the ability to play asynchronous sounds, including a sound
  8. track (background music that endlessly repeats).  Beyond the sound track, only one 
  9. sound can be played at a time.  If this isn't enough, it shouldn't be difficult to 
  10. hack this code to give more.
  11.  
  12. An asynchronous sound is when the mac will play a sound in tandem with other processing,
  13. so your program could play a sound and keep working while the sound is playing.
  14. This is especially useful for game development, where the action doesn't stop while
  15. waiting for a sound to finish.
  16.  
  17. Though functional, this library leaves much to be desired: with the exception of the 
  18. music, each time you play a sound (via DS_SoundPlay), it is loaded in from the resource
  19. fork.  It works, but if a sound is used frequently it would be more efficient to load
  20. it early and just reference it for playing (rather that loading/disposing of it each time).
  21.  
  22. In any case, hopefully this code is clear enough that you can adapt it to your needs.
  23. */
  24.  
  25. /* INCLUDES */
  26. #include <sound.h>
  27.  
  28. /* DEFINES */
  29. #define    NoSynth        0        // no specified synth for channel 
  30. #define    InitNone    0        // no specified init parameters for channel 
  31.  
  32. /* MY MASTER DATA STRUCTURE */
  33. struct ds_data
  34. {
  35.     SndChannelPtr        soundChannel;        // the standard sound channel
  36.     SndChannelPtr        musicChannel;        // the soundtrack sound channel
  37.     
  38.     Handle                musicHandle;        // a handle to a loaded music snd
  39.     
  40.     Boolean                soundChannelActive;    // sound playing now?
  41.     Handle                soundHandle;        // a handle to the sound being played
  42.     
  43.     Boolean                musicFlag;            // music on/off
  44.     Boolean                soundFlag;            // sound on/off
  45. };
  46.  
  47. /* GLOBALS */
  48. struct ds_data gDS_DATA;
  49.  
  50. /* FUNCTION PROTOTYPES */
  51. OSErr    DS_InitSound( void );
  52. void    DS_DisposeSound( void );
  53. void    DS_SetSoundFlag( short flag );
  54. void    DS_SetMusicFlag( short flag );            // start music?
  55. void    DS_LoadMusic( Str255 musicRsrcName );    // clobber existing?
  56. void    DS_MusicPlay( void );                    // called internally, don't use!!
  57. void    DS_SoundPlay( Str255 soundRsrcName, short clobber );
  58. Boolean    DS_SoundBusy( void );
  59. void    DS_WaitForQuiet( void );
  60. void    DS_ClobberSound( void );
  61. void    DS_ClobberMusic( void );
  62. pascal void     DS_SoundCallBack( SndChannel *Chan, SndCommand cmd );
  63. pascal void     DS_MusicCallBack( SndChannel *Chan, SndCommand cmd );
  64.  
  65. /* FUNCTIONS */
  66.  
  67. //************************************************************************************************
  68. //***
  69. //***    DS_InitSound
  70. //***
  71. //***    Allocate sound channels (checking for err!), and set defaults.
  72. //***
  73. OSErr DS_InitSound( void )
  74. {
  75.     OSErr err;
  76.         
  77.         // allocate the sound channel.  if an err occurs, return it.
  78.         // note the final parameter.  this is a 'callback,' a pointer
  79.         // to a function which is called after a sound finishes.  in this
  80.         // case, the callback releases the sound and frees the channel
  81.         // for another sound to play on it. (see DS_SoundCallBack for
  82.         // specifics.)
  83.     err = SndNewChannel( &gDS_DATA.soundChannel, NoSynth, InitNone, 
  84.                                 (SndCallBackProcPtr)DS_SoundCallBack );
  85.     if( err != noErr )
  86.         return( err );
  87.         
  88.         // allocate the music channel.  if an err occurs, return it.
  89.         // this callback routing merely starts the sound again, thus
  90.         // once the sound starts playing, it keeps looping forever
  91.         // without any fuss.  to stop it, use DS_SetMusicFlag to set
  92.           // note that the sound doesn't actually get played until
  93.           // DS_MusicPlay starts it up.
  94.       err = SndNewChannel( &gDS_DATA.musicChannel, NoSynth, InitNone, 
  95.                                 (SndCallBackProcPtr)DS_MusicCallBack );
  96.     if( err != noErr )
  97.         return( err );
  98.  
  99.     gDS_DATA.soundChannelActive = false;    // set defaults.
  100.     gDS_DATA.musicFlag = false;        
  101.     gDS_DATA.soundFlag = true;
  102.     gDS_DATA.musicHandle = nil;
  103.     
  104.     return( noErr );                        // if we got this far, everything is cool.
  105. }
  106.     
  107. //************************************************************************************************
  108. //***
  109. //***    DS_DisposeSound
  110. //***
  111. //***    quiet the sound, release memory, ignore errors (not much you can do at this point!).
  112. //***
  113. void DS_DisposeSound( void )
  114. {
  115.     OSErr err;
  116.     SndCommand    mySndCmd;
  117.     
  118.     mySndCmd.cmd = 3;            // quietCmd 
  119.     
  120.     err = SndDoCommand( gDS_DATA.soundChannel, &mySndCmd, false );
  121.     err = SndDisposeChannel( gDS_DATA.soundChannel, true );
  122.     
  123.     err = SndDoCommand( gDS_DATA.musicChannel, &mySndCmd, false );
  124.     err = SndDisposeChannel( gDS_DATA.musicChannel, true );
  125. }
  126.  
  127. //************************************************************************************************
  128. //***
  129. //***    DS_SetSoundFlag
  130. //***
  131. //***    easy on/off sound toggle (only the DS_ library obeys this!)
  132. //***
  133. void DS_SetSoundFlag( short flag )
  134. {
  135.     gDS_DATA.soundFlag = flag;
  136. }
  137.  
  138. //************************************************************************************************
  139. //***
  140. //***    DS_SetMusicFlag
  141. //***
  142. //***    music on/off, starts or ends music.  note: use DS_LoadMusic to prep for this!
  143. //***    note: this will wait for the sound peice to end before silencing, use
  144. //***    DS_ClobberMusic to kill it immediately.
  145. //***
  146. void DS_SetMusicFlag( short flag )
  147. {
  148.     SndCommand        mySndCmd;
  149.     OSErr            err;
  150.  
  151.     gDS_DATA.musicFlag = flag;
  152.     
  153.         // if the flag is set to true, start up the sound.  it will automatically repeat.
  154.     if( flag )
  155.         DS_MusicPlay();                // crank it up!
  156. }
  157.  
  158. //************************************************************************************************
  159. //***
  160. //***    DS_LoadMusic
  161. //***
  162. //***    load in the music rsrc, set up for play.  use DS_SetMusicFlag to toggle music on/off.
  163. //***
  164. void DS_LoadMusic( Str255 musicRsrcName )
  165. {
  166.     DS_ClobberMusic();                // unlock any current music.. (it does err checking)
  167.     
  168.         // load in the music from the sound resource passed in (use "\pSoundName" convention).
  169.     gDS_DATA.musicHandle = GetNamedResource( 'snd ', musicRsrcName );
  170.  
  171.         // assuming the resource was loaded successfully (musicHandle isn't nil),
  172.         // load it into high memory and lock it down.
  173.     if( gDS_DATA.musicHandle )    // watch out for nil! (eg. rsrc doesn't exist...)
  174.     {
  175.         LoadResource( gDS_DATA.musicHandle );    // load it in..
  176.         MoveHHi( gDS_DATA.musicHandle );        // get it out of the way..
  177.         HLock( gDS_DATA.musicHandle );            // lock it down..
  178.         HPurge( gDS_DATA.musicHandle );            // once it's unlocked its free to go.
  179.     }
  180. }
  181.  
  182. //************************************************************************************************
  183. //***
  184. //***    DS_MusicPlay
  185. //***
  186. //***    this is the recursive music play routine.  it should only be called internally!
  187. //***
  188. void DS_MusicPlay( void )
  189. {
  190.     SndCommand        mySndCmd;
  191.     OSErr            err;
  192.         
  193.         // check to see that music is on (musicFlag) and that it was loaded 
  194.         // (musicHandle).  If so, play it with instructions to use the callback
  195.         // command.
  196.     if( gDS_DATA.musicFlag && gDS_DATA.musicHandle )    // music on and exists?
  197.     {
  198.             // begin playing the sound.
  199.         err = SndPlay( gDS_DATA.musicChannel, gDS_DATA.musicHandle, true );
  200.         mySndCmd.cmd = 13;            // callBackCmd 
  201.         mySndCmd.param1 = 99;         // arbitrary 
  202.         mySndCmd.param2 = SetCurrentA5();    
  203.             // instruct the sound to use the callback when done.
  204.         err = SndDoCommand( gDS_DATA.musicChannel, &mySndCmd, false );
  205.     }
  206. }
  207.  
  208. //************************************************************************************************
  209. //***
  210. //***    DS_SoundPlay
  211. //***
  212. //*** quiet any current sound, set up for new, and go!
  213. //***
  214. void DS_SoundPlay( Str255 soundRsrcName, short clobber )
  215. {
  216.     SndCommand        mySndCmd;
  217.     OSErr            err;
  218.  
  219.     if( gDS_DATA.soundFlag )            // make sure sound is on before using it.
  220.     {
  221.         if( (gDS_DATA.soundChannelActive && clobber) 
  222.             || !(gDS_DATA.soundChannelActive) )    // release (unlock) mem before we forget!
  223.         {
  224.             DS_ClobberSound();            // if there is an existing sound, quiet it.
  225.         
  226.                 // load in the sound resource. the Str255 should look like "\pSoundName"
  227.             gDS_DATA.soundHandle = GetNamedResource( 'snd ', soundRsrcName );
  228.         
  229.                 // if it exists, load it into high memory and lock it down.  call HPurge
  230.                 // so that once it's unlocked it can be removed from the heap.
  231.                 // play the sound and instruct it to use the callback when done.
  232.             if( gDS_DATA.soundHandle )    // watch out for nil! (eg. rsrc doesn't exist...)
  233.             {
  234.                 LoadResource( gDS_DATA.soundHandle );        // load it in..
  235.                 MoveHHi( gDS_DATA.soundHandle );            // move it out of the way..
  236.                 HLock( gDS_DATA.soundHandle );                // lock it down..
  237.                 HPurge( gDS_DATA.soundHandle );                // once its unlocked, its free to go.
  238.                 err = SndPlay( gDS_DATA.soundChannel, gDS_DATA.soundHandle, true );
  239.                 gDS_DATA.soundChannelActive = true;
  240.                 mySndCmd.cmd = 13;            // callBackCmd 
  241.                 mySndCmd.param1 = 99;         // arbitrary 
  242.                 mySndCmd.param2 = SetCurrentA5();
  243.                 err = SndDoCommand( gDS_DATA.soundChannel, &mySndCmd, false );
  244.             }
  245.         }
  246.     }
  247. }
  248.  
  249. //************************************************************************************************
  250. //***
  251. //***    DS_SoundBusy
  252. //***
  253. //***     is a sound currently being produced?
  254. //***
  255. Boolean DS_SoundBusy( void )
  256. {
  257.     return( gDS_DATA.soundChannelActive );
  258. }
  259.  
  260. //************************************************************************************************
  261. //***
  262. //***    DS_WaitForQuiet
  263. //***
  264. //***     wait for the sound to end.
  265. //***
  266. void    DS_WaitForQuiet( void )
  267. {
  268.     while( gDS_DATA.soundChannelActive )
  269.         ;
  270. }
  271.  
  272.  
  273. //************************************************************************************************
  274. //***
  275. //***    DS_ClobberSound
  276. //***
  277. //***     if sound is active, silence it, then unlock the sound for purging.
  278. //***
  279. void DS_ClobberSound( void )
  280. {
  281.     SndCommand    mySndCmd;
  282.     OSErr        err;
  283.     
  284.     if( gDS_DATA.soundChannelActive )        // is a sound being played at the moment?
  285.     {
  286.         mySndCmd.cmd = 3;                    // quietCmd 
  287.         
  288.             // quiet the sound channel
  289.         err = SndDoImmediate( gDS_DATA.soundChannel, &mySndCmd );
  290.         
  291.             // unlock the memory, so that it can be purged when the heap is cleaned up.
  292.         HUnlock( gDS_DATA.soundHandle );    // unlock the mem, hopefully the user is careful..
  293.         
  294.         gDS_DATA.soundChannelActive = false;
  295.     }
  296. }
  297.  
  298. //************************************************************************************************
  299. //***
  300. //***    DS_ClobberMusic
  301. //***
  302. //***     unlock the music for purging.
  303. //***
  304. void DS_ClobberMusic( void )
  305. {    
  306.     SndCommand    mySndCmd;
  307.     OSErr        err;
  308.     
  309.     if( gDS_DATA.musicHandle )
  310.     {
  311.         mySndCmd.cmd = 3;                    // quietCmd 
  312.         err = SndDoImmediate( gDS_DATA.musicChannel, &mySndCmd );
  313.     
  314.         HUnlock( gDS_DATA.musicHandle );    // unlock the mem, hopefully the user is careful..    
  315.         
  316.         gDS_DATA.musicFlag = false;
  317.     }
  318. }
  319.  
  320. //************************************************************************************************
  321. //***
  322. //*** SoundCallBack
  323. //***
  324. //*** this is the function that gets called when a sound is done playing.
  325. //*** the SndCommand should containt these parameters for this callback:
  326. //***        mySndCmd.cmd = 13;            // callBackCmd 
  327. //***        mySndCmd.param1 = 99;         // arbitrary 
  328. //***        mySndCmd.param2 = SetCurrentA5();    
  329. //***
  330. pascal void DS_SoundCallBack( SndChannel *Chan, SndCommand cmd )
  331. {        
  332.     long    myA5;
  333.     if( cmd.param1 == 99 )
  334.     {
  335.                 myA5 = SetA5(cmd.param2);
  336.                 HUnlock( gDS_DATA.soundHandle );        // unlock the memory.
  337.                 gDS_DATA.soundChannelActive = false;    // sound is officially done.
  338.                 myA5 = SetA5(myA5);
  339.     }
  340. }
  341.  
  342. //************************************************************************************************
  343. //***
  344. //*** MusicCallBack
  345. //***
  346. //*** this gets called when the music is done.  It just starts it up again!
  347. //*** the SndCommand should containt these parameters for this callback:
  348. //***        mySndCmd.cmd = 13;            // callBackCmd 
  349. //***        mySndCmd.param1 = 99;         // arbitrary 
  350. //***        mySndCmd.param2 = SetCurrentA5();    
  351. //***
  352. pascal void DS_MusicCallBack( SndChannel *Chan, SndCommand cmd )
  353. {    
  354.     long    myA5;
  355.     if( cmd.param1 == 99 )
  356.     {
  357.                 myA5 = SetA5(cmd.param2);
  358.                 DS_MusicPlay();                            // play it again sam!
  359.                 myA5 = SetA5(myA5);
  360.     }
  361. }
  362.