home *** CD-ROM | disk | FTP | other *** search
- /*****************************************************************************
- * FILE: CMSoundSystem.c
- * AUTHOR: David Hay
- * CREATED: March 9, 1995
- * DESCRIPTION: Routines for playing sound and music.
- *
- * Copyright © 1995-1997 David Hay
- *
- * Permission to use, copy, and distribute this software and its documentation
- * for any purpose is hereby granted without fee, provided that (i) the above
- * copyright notices and this permission notice appear in all copies of the
- * software and related documentation, and (ii) the names of David Hay and
- * Caveman Creations may not be used in any advertising or publicity relating
- * to the software without the specific, prior written permission of David Hay
- *
- * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, EXPRESS,
- * IMPLIED OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, ANY WARRANTY OF
- * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL
- * DAVID HAY OR CAVEMAN CREATIONS BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
- * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER
- * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF THE
- * POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- *
- * Version History:
- *
- * 1.0 (09/11/95)
- * - Initial release
- *
- * 1.1 (03/05/96)
- * - Incorporated changes by Dario Accornero (dappsoft@mix.it) Thanks
- * Dario for all your work! (look for "DA on 01/20/96")
- * • added PPC support in the callback routine
- * • added stero Macs support in the channel allocation routines
- * • added double buffer support in order to speed up sound output
- * on all Macs
- * • chaned SoundSystem allocation, in order to lower global vars
- * space memory charge; now, the SoundSystem is accessed thru a
- * pointer, so it is *necessare* to allocate the system before
- * using it, otherwise the code will crash…
- * • added priorities support
- * • added channel volume control (get/set)
- * • CodeWarrior is now supported
- * - Cleaned up the code a bit.
- * - Added some internal routines for code that was getting repeated by
- * both sound and music routines.
- * 1.1.1 (06/14/97)
- * - Fixed a bug in CMSRegisterSound() that resulted in a memory leak and
- * could possibly cause a random system crash.
- *****************************************************************************/
-
-
- #ifndef __GESTALT__
- #include <Gestalt.h>
- #endif
- #ifndef __SOUND__
- #include <Sound.h>
- #endif
- #ifndef __TRAPS__
- #include <Traps.h>
- #endif
- #ifndef __CMSOUNDSYSTEM__
- #include "CMSoundSystem.h"
- #endif
-
-
- #ifndef PUBLIC
- #define PUBLIC
- #endif
- #ifndef PRIVATE
- #define PRIVATE static
- #endif
-
- /* DA on 01/20/96: previously defined as long quantities, but SndCommand.param1
- ** is short; this could have caused troubles on the PPC, which is a little endian,
- ** whereas the 68K is a big endian.
- */
- #define kSoundDone 99 /* a sound is done playing */
- #define kMusicDone 100 /* The music block is done playing */
- #define kSoundResourceDone 101 /* a sound resource is done playing */
- #define kDoubleBufferSize 4096 /* size of each double buffer (in bytes) */
-
- /*****************************************************************************
- ** DBLocal -- Double buffer local variables DA on 01/20/96
- **
- ** header - information about the sound
- **
- ** bytesTotal - total number of samples in the sound sample.
- **
- ** bytesCopied - number of samples that have been copied into the
- ** buffer so far.
- **
- ** dataPtr - pointer to the data of the sound sample to copy
- */
- typedef struct DBLocal
- {
- SoundHeaderPtr header;
- long bytesTotal;
- long bytesCopied;
- Ptr dataPtr;
-
- } DBLocal, *DBLocalPtr;
-
-
- /*****************************************************************************
- ** ExtSndChannel -- Describes an extended sound channel
- **
- ** channel - the Sound Manager channel data. NOTE: this field
- ** MUST be first! This makes it possible to simply
- ** simply cast the extended sound channel to a sound
- ** channel for passing to Sound Manager routines.
- **
- ** soundPlaying - The reference number of the sound currently playing
- ** on this sound channel.
- **
- ** isValid - Is the sound channel valid? If not a call to
- ** SndNewChannel() is needed to make the channel valid
- ** again. Otherwise, the channel has already been
- ** allocated and sound may be played on it.
- **
- ** soundData - if the sound playing is an unregistered sound
- ** resource, this is the sound resource playing on the
- ** sound channel.
- */
- typedef struct ExtSndChannel
- {
- SndChannel channel;
- volatile short soundPlaying; /* See CMSWaitForSilence() for the reason */
- /* why 'volatile' is required here */
- Boolean isValid;
- SndListHandle soundData;
-
- } ExtSndChannel, *ExtSndChannelPtr;
-
-
- /*****************************************************************************
- ** SoundSystem -- Describes private data the sound system
- **
- ** numChannels - the number of sound channels open
- ** soundChannel - The list of available channels.
- **
- ** // DA on 01/20/96: double buffers support
- **
- ** doubleBuffer - buffers used to play sound data
- ** doubleHeader - necessary headers for the above buffers
- ** doubleVars - double buffers state tracking/data loading areas
- **
- ** // DA on 01/20/96: priorities support
- **
- ** priorities - table used to track priorities on all channels
- **
- ** numSounds - the number of sound that have been registered with
- ** the sound system.
- ** soundList - A list of sound handles that have been registered.
- ** soundData - Every time a sound handle is registered with the
- ** sound system, the sound header is found and placed
- ** in the corresponding slot in this list. This allows
- ** the sound system to play the sounds more efficently
- ** with bufferCmd's rather than using SndPlay(). // DA on 01/20/96
- ** // it was SoundPlay()
- ** // a non-existent call
- ** musicSequence - describes which sounds to play and in what order
- ** for a music sequence
- ** musicPosition - the current position in the music sequence
- **
- ** smVersion - the version of the sound manager used.
- ** smHasStereo - is stereo sound is available?
- ** smHasStereoMixing
- ** - can the stereo be mixed into a single sound channel?
- ** smHasDoubleBuffer
- ** - Are double buffering routines available?
- ** smHasMultiChannels
- ** - Are we allowed to have more than one channel open
- ** at a time?
- */
- typedef struct SoundSystem
- {
-
- short numChannels;
- ExtSndChannelPtr soundChannel[kMaxChannels];
-
- SndDoubleBufferPtr doubleBuffer[kMaxChannels * 2];
- SndDoubleBufferHeader doubleHeader[kMaxChannels];
- DBLocal doubleVars[kMaxChannels];
-
- short priorities[kMaxChannels];
-
- short numSounds;
- SndListHandle soundList[kMaxSounds];
- SoundHeaderPtr soundData[kMaxSounds];
-
- PlaySequencePtr musicSequence;
- short musicPosition;
-
- NumVersion smVersion;
- Boolean smHasStereo :1;
- Boolean smHasStereoMixing :1;
- Boolean smHasDoubleBuffer :1;
- Boolean smHasMultiChannels :1;
-
- } SoundSystem, *SoundSystemPtr;
-
-
- /************************ PRIVATE FUNCTION PROTOTYPES ************************/
-
-
- PRIVATE OSErr PlayMusicBlock( short channelNum );
-
-
- /* DA on 01/20/96: a sound callback procedure accepts a _pointer_ to a SndCommand,
- ** rather than a SndCommand, if it's written in C; previously, it accepted a whole
- ** SndCommand, making it impossible to be called on PPC machines.
- */
- PRIVATE pascal void SoundCallBack( SndChannelPtr theChannel, SndCommand *theCmd );
-
-
- /* DA on 01/20/96: local functions used to allocate the SoundSystem, and also to
- ** support the use of double buffers.
- */
- PRIVATE OSErr AllocateSoundSystem(void);
- PRIVATE OSErr DBSndPlay(SndChannelPtr channel, short channelNum, SoundHeaderPtr sound);
- PRIVATE pascal void DADoubleBack(SndChannelPtr channel, SndDoubleBufferPtr buffer);
-
-
- /* DJH on 03/03/96: Common functions to play sounds used by the sound playing and
- ** music routines.
- */
- PRIVATE OSErr StartSound( SndChannelPtr theChannel,
- short channelNum,
- short refNum,
- short param );
- PRIVATE OSErr PrepareChannel( short channelNum );
-
-
- /************************** PRIVATE GLOBAL VARIABLES *************************/
-
- PRIVATE SndCallBackUPP soundCallBackUPP; /* callback when sounds are done */
-
- PRIVATE SoundSystemPtr snd = NULL; /* sound system state information */
- /* DA on 01/20/96: previously defined */
- /* as a variable rather than a pointer, */
- /* eating up as much as 500+ bytes in */
- /* global vars space… */
-
- PRIVATE SndDoubleBackUPP doubleBackUPP; /* DA on 01/20/96: double buffer */
- /* callback procedure */
-
- /*===========================================================================*/
- /* PUBLIC FUNCTION DEFINITIONS */
- /*===========================================================================*/
-
-
- /*----------------------- INITIALIZATION/DEALLOCATION -----------------------*/
-
-
- PUBLIC OSErr
- CMSInitSound( short numChannels )
- {
- ExtSndChannelPtr aChannel; /* A channel to initialize */
- short ii; /* misc counter */
- long feature; /* result value from Gestalt() */
- OSErr err;
-
- /* DA on 01/20/96: allocate the SoundSystem */
-
- if ((err = AllocateSoundSystem()) != noErr)
- return err;
-
-
-
- /* Get some information about the current sound manager. First we
- ** find out what version of the sound manager is available. To do
- ** this we first have to check if the _SoundDispatch trap is
- ** available. If so, then we can call SndSoundManagerVersion() to
- ** get the version number. Otherwise, the enhanced sound manager
- ** is not present so set the version number to 1.0
- */
- if ( GetToolTrapAddress(_Unimplemented) != GetToolTrapAddress(_SoundDispatch) )
- {
-
- /* DA on 01/20/96: Universal Interfaces 2.x and later are returning
- ** a long quantity from SndSoundManagerVersion()
- **
- ** DJH on 03/03/96: Huh? No it doesn't.
- **
- ** DJH on 05/21/96: The Metrowerks compiler includes headers where
- ** SndSoundManagerVersion returns a long, so check for the compiler
- ** and use the appropriate call according to the headers in use.
- **
- ** DA on 07/24/96: Metrowerks CodeWarrior 9 and later headers are
- ** now returning a NumVersion struct. Removed the __MWERKS__ checks
- **
- */
- // #ifdef __MWERKS__
- // long v = (long *)&snd->smVersion;
- // *v = SndSoundManagerVersion();
- // #else
- snd->smVersion = SndSoundManagerVersion();
- // #endif
- }
- else
- {
- snd->smVersion.majorRev = 1;
- snd->smVersion.minorAndBugRev = 0;
- snd->smVersion.stage = finalStage;
- snd->smVersion.nonRelRev = 0;
- }
-
-
-
- /* Now that we have the version number, check for some capabilities
- ** on the current system using gestalt. To check if double buffering
- ** or multiple channels are available, we check two ways. If Sound
- ** Manager 3.0 is available, we can simply look at the result
- ** returned from Gestalt. Otherwise, we see if the ASC chip is
- ** present to determine if the desired features are available.
- */
- err = Gestalt( gestaltSoundAttr, &feature );
- if ( err == noErr )
- {
- snd->smHasStereo = ((feature & (1L << gestaltStereoCapability)) != 0);
- snd->smHasStereoMixing = ((feature & (1L << gestaltStereoMixing)) != 0);
- if ( snd->smVersion.majorRev >= 3 )
- {
- snd->smHasDoubleBuffer =
- ((feature & (1L << gestaltSndPlayDoubleBuffer)) != 0);
- snd->smHasMultiChannels =
- ((feature & (1L << gestaltMultiChannels)) != 0);
- }
- else
- {
- err = Gestalt( gestaltHardwareAttr, &feature );
- if ( err == noErr )
- {
- snd->smHasDoubleBuffer =
- ((feature & (1L << gestaltHasASC)) != 0);
- snd->smHasMultiChannels = snd->smHasDoubleBuffer;
- }
- }
- }
-
-
- /* Initialize the sound channel information so we know
- ** what to dispose of if an error occurs.
- */
- snd->numChannels = 0;
- for ( ii = 0; ii < kMaxChannels; ii++ )
- {
- snd->soundChannel[ii] = NULL;
- snd->priorities[ii] = 0;
- }
-
-
- /* Initialize the sound data information to indicate that
- ** no sounds have been registered with the sound system
- */
- snd->numSounds = 0;
- for ( ii = 0; ii < kMaxSounds; ii++ )
- {
- snd->soundData[ii] = NULL;
- snd->soundList[ii] = NULL;
- }
-
-
- /* Initialize the music information to indicate that no
- ** music has been loaded
- */
- snd->musicPosition = kNoSound;
- snd->musicSequence = NULL;
-
-
- /* Setup the callback routine pointer for sounds. We will use the
- ** same routine to handle sounds and music by putting a different
- ** number in the callback command to determine what to do.
- */
- soundCallBackUPP = NewSndCallBackProc( SoundCallBack );
-
-
-
- /* DA on 01/20/96: create a routine descriptor for our doublebuffer
- ** callback if the system is supporting double buffers
- */
- if (snd->smHasDoubleBuffer)
- doubleBackUPP = NewSndDoubleBackProc(DADoubleBack);
- else
- doubleBackUPP = nil;
-
-
-
- /* Allocate the sound channels. If more than one sound channel is
- ** requested and multiple sound channels are not available, then
- ** report the error.
- */
- for ( ii = 0; ii < numChannels; ii++ )
- {
- aChannel = (ExtSndChannelPtr) NewPtr( sizeof( ExtSndChannel ) );
- if ( !aChannel )
- {
- err = MemError();
- break;
- }
- else
- {
- aChannel->channel.qLength = stdQLength;
- aChannel->channel.userInfo = ii;
- aChannel->soundPlaying = kNoSound;
- aChannel->isValid = false;
- aChannel->soundData = NULL;
- snd->soundChannel[ii] = aChannel;
- }
- }
- if ( err == noErr )
- {
- snd->numChannels = numChannels;
- }
- else
- {
- CMSDisposeSound();
- }
-
- return err;
- }
-
- /*---------------------------------------------------------------------------*/
- /* DA on 01/20/96: simplified initialization of the whole package */
-
- PUBLIC OSErr
- CMSOpenSoundTool( short numChannels )
- {
- OSErr err;
-
- /* safety check on number of channels */
-
- if ( numChannels < 0 )
- {
- numChannels = 1;
- }
- else if ( numChannels > kMaxChannels )
- {
- numChannels = kMaxChannels;
- }
-
-
- /* initialize SoundSystem */
-
- err = CMSInitSound( numChannels );
-
-
- /* open all sound channels requested */
-
- if ( err == noErr )
- {
- err = CMSOpenAllChannels();
- }
-
- return err;
- }
-
- /*---------------------------------------------------------------------------*/
- /* DA on 01/20/96: allocate space for the sound system */
-
- PRIVATE OSErr
- AllocateSoundSystem( void )
- {
- if ( snd == NULL )
- {
- snd = (SoundSystemPtr) NewPtrClear( sizeof( SoundSystem ) );
- if ( !snd )
- {
- return MemError();
- }
- }
-
- return noErr;
- }
-
- /*---------------------------------------------------------------------------*/
-
- PUBLIC void
- CMSDisposeSound( void )
- {
- short ii;
-
- /* DA on 01/20/96: if the SoundSystem wasn't allocated, just return;
- ** this is necessary to avoid accessing a null pointer
- */
- if ( snd == NULL ) return;
-
-
- /* Destroy all of the sound channels */
-
- for ( ii = 0; ii < snd->numChannels; ii++ )
- {
- if ( snd->soundChannel[ii] )
- {
- CMSCloseChannel( ii );
- DisposePtr( (Ptr) snd->soundChannel[ii] );
- snd->soundChannel[ii] = NULL;
- }
- }
-
-
- DisposeRoutineDescriptor( soundCallBackUPP );
-
-
- /* DA on 01/20/96: if we were using double buffers, dispose of
- ** the relative routine descriptor
- */
- if ( snd->smHasDoubleBuffer )
- {
- DisposeRoutineDescriptor(doubleBackUPP);
- }
-
-
- /* Dispose of the registered sound data */
-
- for ( ii = 0; ii < snd->numSounds; ii++ )
- {
- if ( snd->soundList[ii] )
- {
- HUnlock( (Handle) snd->soundList[ii] );
- DisposeHandle( (Handle) snd->soundList[ii] );
- }
- snd->soundData[ii] = NULL;
- snd->soundList[ii] = NULL;
- }
-
-
- /* Dispose of the music (if any) */
-
- if ( snd->musicSequence )
- {
- DisposePtr( (Ptr) snd->musicSequence );
- snd->musicSequence = NULL;
- }
-
- /* DA on 01/20/96: dispose of the SoundSystem */
-
- DisposePtr( (Ptr)snd );
- }
-
-
-
- /*---------------------------- CHANNEL MANAGEMENT ---------------------------*/
-
- PUBLIC OSErr
- CMSCloseChannel( short channelNum )
- {
- OSErr err;
-
- err = noErr;
- if ( snd->soundChannel[channelNum]->isValid )
- {
- err = SndDisposeChannel( (SndChannelPtr) snd->soundChannel[channelNum],
- true );
-
- /* DA on 01/20/96: dispose of the double buffers if the system
- ** supports them
- */
- if ( snd->smHasDoubleBuffer )
- {
- DisposePtr( (Ptr)snd->doubleBuffer[channelNum * 2] );
- DisposePtr( (Ptr)snd->doubleBuffer[(channelNum * 2) + 1] );
- }
-
- snd->soundChannel[channelNum]->isValid = false;
- }
-
- return err;
- }
-
- /*---------------------------------------------------------------------------*/
-
- PUBLIC OSErr
- CMSCloseAllChannels( void )
- {
- short ii;
- OSErr err;
-
- err = noErr;
- for ( ii = 0; ii < snd->numChannels; ii++ )
- {
- err = CMSCloseChannel( ii );
- }
-
- return err;
- }
-
- /*---------------------------------------------------------------------------*/
-
- PUBLIC OSErr
- CMSOpenChannel( short channelNum )
- {
- OSErr err;
- SndDoubleBufferPtr doubleBuffer;
-
- err = noErr;
- if ( !snd->soundChannel[channelNum]->isValid )
- {
- /* DA on 01/20/96: create a sound channel; take advantage of
- ** stereo output on systems supporting it
- */
- err = SndNewChannel( (SndChannelPtr*) &snd->soundChannel[channelNum],
- sampledSynth, initNoInterp + (snd->smHasStereo ? initStereo : initMono),
- soundCallBackUPP );
-
- if ( err == noErr )
- {
- snd->soundChannel[channelNum]->isValid = true;
- }
-
-
- /* DA on 01/20/96: create double buffer information if the
- ** system supports it.
- */
- if (err == noErr)
- {
- if (snd->smHasDoubleBuffer)
- {
- /* DA on 01/20/96: create double buffers for specified channel */
-
- doubleBuffer = (SndDoubleBufferPtr) NewPtr( sizeof( SndDoubleBuffer ) +
- kDoubleBufferSize);
- if ( !doubleBuffer)
- {
- return MemError();
- }
-
- snd->doubleBuffer[channelNum * 2] = doubleBuffer;
- snd->doubleHeader[channelNum].dbhBufferPtr[0] = doubleBuffer;
-
- doubleBuffer = (SndDoubleBufferPtr) NewPtr( sizeof( SndDoubleBuffer ) +
- kDoubleBufferSize );
- if ( doubleBuffer == NULL )
- {
- DisposePtr( (Ptr)snd->doubleBuffer[channelNum * 2] );
- return MemError();
- }
-
- snd->doubleBuffer[(channelNum * 2) + 1] = doubleBuffer;
- snd->doubleHeader[channelNum].dbhBufferPtr[1] = doubleBuffer;
-
-
- /* DA on 01/20/96
- ** Setup common information in the double buffer headers
- ** WARNING: the following lines are assuming that _only_ this
- ** type of sounds will be used: mono, 8 bits, uncompressed,
- ** any frequency
- **
- ** DJH on 03/03/96: Took this warning to heart and moved it
- ** to DBSndPlay where we take other kindes of sounds into
- ** account. (e.g. 16-bit, compressed, etc.)
- */
-
-
- /* DA on 01/20/96 assign doubleback procedure to the
- ** double buffer
- */
- snd->doubleHeader[channelNum].dbhDoubleBack = doubleBackUPP;
-
- }
- }
- }
-
- return err;
- }
-
- /*---------------------------------------------------------------------------*/
-
- PUBLIC OSErr
- CMSOpenAllChannels( void )
- {
- short ii;
- OSErr err;
-
- err = noErr;
- for ( ii = 0; ii < snd->numChannels; ii++ )
- err = CMSOpenChannel( ii );
-
- return err;
- }
-
- /*---------------------------------------------------------------------------*/
-
- PUBLIC OSErr
- CMSStopSound( short channelNum )
- {
- SndCommand theCommand;
- ExtSndChannelPtr sndChannel;
- OSErr err;
-
- sndChannel = snd->soundChannel[channelNum];
-
- /* Flush the sound channel of any other sound commands */
-
- theCommand.cmd = flushCmd;
- theCommand.param1 = 0;
- theCommand.param2 = 0L;
- err = SndDoImmediate( (SndChannelPtr) sndChannel, &theCommand );
-
-
- /* Send a quiet command to stop any currently playing sounds */
-
- if ( err == noErr )
- {
- theCommand.cmd = quietCmd;
- theCommand.param1 = 0;
- theCommand.param2 = 0L;
- err = SndDoImmediate( (SndChannelPtr) sndChannel, &theCommand );
- }
-
- sndChannel = snd->soundChannel[channelNum];
- sndChannel->soundPlaying = kNoSound;
- if ( sndChannel->soundData )
- {
- HUnlock( (Handle) sndChannel->soundData );
- sndChannel->soundData = NULL;
- }
-
- return err;
- }
-
- /*---------------------------------------------------------------------------*/
-
- PUBLIC void
- CMSIdleSoundTask( void )
- {
- short ii;
- ExtSndChannelPtr sndChannel;
-
- /* Loop over the available channels checking to see if one has
- ** fallen silent. If so and there is a sound resource attached
- ** to the channel, then unlock the sound so that it may be
- ** purged. Also detach the sound resource from the channel so
- */
- for ( ii = 0; ii < snd->numChannels; ii++ )
- {
- sndChannel = snd->soundChannel[ii];
- if ( sndChannel->soundPlaying == kNoSound && sndChannel->soundData )
- {
- HUnlock( (Handle) sndChannel->soundData );
- sndChannel->soundData = NULL;
- }
- }
- }
-
- /*---------------------------------------------------------------------------*/
-
- PUBLIC OSErr
- CMSGetSoundPlaying( short channelNum, short* refNum )
- {
- if ( channelNum >= 0 && channelNum < snd->numChannels )
- {
- *refNum = snd->soundChannel[channelNum]->soundPlaying;
- return noErr;
- }
-
- return badChannel;
- }
-
- /*---------------------------------------------------------------------------*/
-
- PUBLIC OSErr
- CMSWaitForSilence( short channelNum )
- {
- if ( channelNum >= 0 && channelNum < snd->numChannels )
- {
- /* We turn off global optimizer to keep the compiler from putting
- ** putting our loop control variable into a register. Since we
- ** are waiting for an interrupt to occur, putting the LCV in a
- ** register would put us in an infinite loop. By disabling the
- ** compiler's ability to assign variables to registers, we are
- ** able to avoid this nasty problem. I don't know if this hack
- ** will work on the PPC since I don't have a PPC compiler.
- */
-
- /* DJH on 07/03/96: The #pragma has been removed since I was
- ** reminded that making the 'soundPlaying' variable 'volatile'
- ** tells the compiler to avoid aggressive optimiation on that
- ** object. Note that since this is only a suggestion, I would
- ** recommend that you check the assembly generated by your
- ** to make sure that this value is not stuffed into a register
- ** before the loop is entered. Think C 5.0 does it right, but
- ** I don't know about other compilers.
- */
-
- //#ifdef THINK_C
- //# pragma options( !global_optimizer )
- //#endif
-
- while( snd->soundChannel[channelNum]->soundPlaying != kNoSound )
- {} /* Do nothing, just wait for the sound to complete */
-
- return noErr;
- }
-
- return badChannel; /* invalid channel specified */
- }
-
-
-
- /*-------------------------- SOUND DATA MANAGEMENT --------------------------*/
-
-
- PUBLIC OSErr
- CMSRegisterSound( SndListHandle sndHandle, short* refNum )
- {
- short ii;
- SoundHeaderPtr sndHeader;
- OSErr err;
-
- err = noErr;
- *refNum = kNoSound;
- if ( sndHandle != NULL )
- {
- /* First we get the sound header from the sound handle so
- ** that the sound manager does not have to parse the sound
- ** resource each time the sound is played. The handle is
- ** locked by CMSGetSoundHeader(), so we don't have to worry
- ** about locking it ourselves.
- */
- sndHeader = CMSGetSoundHeader( sndHandle );
-
- /* Now we need to find a free slot in the list of available
- ** sounds. Once we find a slot, insert the sound data into
- ** that slot and return the slot number as the sound
- ** reference.
- */
- for ( ii = 0; ii < snd->numSounds; ii++ )
- {
- if ( snd->soundData[ii] == NULL )
- {
- *refNum = ii;
- snd->soundData[ii] = sndHeader;
- snd->soundList[ii] = sndHandle;
- break;
- }
- }
-
- /* There aren't enough slots, so bump the number of slots
- ** if possible and insert the sound at the end of the sound
- ** list. If there simply isn't any more room, return an error.
- **
- ** 1.1.1: DJH on 06/14/97: Don't forget to update sndList here
- ** as well (we did it right in the above case)
- **
- ** 1.1.1: DJH on 06/20/97: Now if we find an open slot in the
- ** loop above, we won't return an error.
- */
- if ( *refNum == kNoSound )
- {
- if ( snd->numSounds < kMaxSounds )
- {
- *refNum = snd->numSounds;
- snd->soundData[snd->numSounds] = sndHeader;
- snd->soundList[snd->numSounds] = sndHandle; /* 1.1.1: DJH */
- snd->numSounds++;
- }
- else
- {
- err = notEnoughBufferSpace;
- }
- }
- }
-
- return err;
- }
- /*---------------------------------------------------------------------------*/
-
- PUBLIC OSErr
- CMSRemoveSound( short refNum )
- {
- short ii;
- OSErr err;
-
- err = noErr;
- if ( refNum >= 0 && refNum < snd->numSounds )
- {
- /* Stop the sound if it is playing on one
- ** of the sound channels
- */
- for ( ii = 0; ii < snd->numChannels; ii++ )
- {
- if ( snd->soundChannel[ii]->soundPlaying == refNum )
- {
- err = CMSStopSound( ii );
- }
- if ( err != noErr ) break;
- }
-
- /* Dispose of the sound handle and mark it's slot in the
- ** list of registered sounds as now available.
- **
- ** 1.1.1: DJH on 06/20/97: Why don't we use refNum to index into
- ** soundData and soundList rather than ii?
- */
- if ( err == noErr )
- {
- HUnlock( (Handle) snd->soundList[refNum] );
- DisposeHandle( (Handle) snd->soundList[refNum] );
- snd->soundData[refNum] = NULL;
- snd->soundList[refNum] = NULL;
- }
- }
-
- return err;
- }
-
- /*---------------------------------------------------------------------------*/
-
- PUBLIC OSErr
- CMSLoadSound( short soundID, short* refNum )
- {
- Handle sndHandle;
- OSErr err;
-
- err = noErr;
-
- /* Get the sound resource and detach it from the resource file. */
-
- sndHandle = GetResource( 'snd ', soundID );
- if ( sndHandle == NULL )
- err = ResError();
-
- if ( err == noErr )
- {
- DetachResource( (Handle) sndHandle );
- err = ResError();
- }
-
- /* Now that we have the sound handle, register the sound
- ** with the sound system.
- */
- if ( err == noErr )
- err = CMSRegisterSound( (SndListHandle) sndHandle, refNum );
-
- return err;
- }
-
- /*---------------------------------------------------------------------------*/
-
- PUBLIC OSErr
- CMSPlaySound( short refNum, short channelNum )
- {
- OSErr err;
-
- err = noErr;
- if ( channelNum >= 0 && channelNum < snd->numChannels &&
- refNum >= 0 && refNum < snd->numSounds )
- {
- /* DJH on 03/03/96: Moved the code that was here into a function
- ** since CMSPlayMusic() does the same thing.
- */
- err = PrepareChannel( channelNum );
-
-
- /* Play the sound and install a completion callback routine */
-
- if ( err == noErr )
- {
- /* DJH on 03/03/96: Moved the code that was here into a
- ** function since PlayMusicBlock() does nearly the same
- ** thing.
- */
- err = StartSound( (SndChannelPtr) snd->soundChannel[channelNum],
- channelNum, refNum, kSoundDone );
- }
- }
-
- return err;
- }
-
- /*---------------------------------------------------------------------------*/
- /* DJH on 03/03/96: Make sure a channel is ready to play a sound */
-
- PRIVATE OSErr
- PrepareChannel( short channelNum )
- {
- OSErr err;
-
-
- /* Open the sound channel if it is not already. Otherwise, stop
- ** any sound that may be playing on that sound channel.
- */
- if ( !snd->soundChannel[channelNum]->isValid )
- {
- err = CMSOpenChannel( channelNum );
- }
- else
- {
- err = CMSStopSound( channelNum );
- }
-
- return err;
- }
-
- /*---------------------------------------------------------------------------*/
- /* DJH on 03/03/96: Start a sound going on a channel */
-
- PRIVATE OSErr
- StartSound( SndChannelPtr theChannel, short channelNum, short refNum, short param )
- {
- SndCommand theCommand;
- OSErr err;
-
- /* DA on 01/20/96: if the system is supporting double buffers, take
- ** advantage of them this results in much faster sound output on all Macs
- */
- if ( snd->smHasDoubleBuffer )
- {
- err = DBSndPlay( theChannel, channelNum, snd->soundData[refNum] );
- }
- else /* just do a simple buffer command */
- {
- theCommand.cmd = bufferCmd;
- theCommand.param1 = 0;
- theCommand.param2 = (long) snd->soundData[refNum];
- err = SndDoImmediate( (SndChannelPtr) theChannel, &theCommand );
- }
-
-
- /* attach a callback so we know when the sound is done. */
-
- if ( err == noErr )
- {
- theCommand.cmd = callBackCmd;
- theCommand.param1 = param;
- theCommand.param2 = SetCurrentA5();
- err = SndDoCommand( (SndChannelPtr) theChannel, &theCommand, true );
- }
-
- if ( err == noErr )
- {
- ((ExtSndChannelPtr)theChannel)->soundPlaying = refNum;
- snd->priorities[channelNum] = 0;
- }
-
- return err;
- }
-
- /*---------------------------------------------------------------------------*/
-
- PUBLIC OSErr
- CMSPlaySoundResource( short resID, short channelNum, Boolean async )
- {
- Handle sndHandle;
- SndCommand theCommand;
- OSErr err;
-
- err = noErr;
-
- if ( channelNum >= 0 && channelNum < snd->numChannels )
- {
- /* Open the sound channel if it is not already. Otherwise, stop
- ** any sound that may be playing on that sound channel.
- */
- if ( !snd->soundChannel[channelNum]->isValid )
- {
- err = CMSOpenChannel( channelNum );
- }
- else
- {
- err = CMSStopSound( channelNum );
- }
-
- /* Get the sound resource. Then lock it down and make it purgeable
- ** so that it can be freed after the sound is finished playing.
- */
- sndHandle = GetResource( 'snd ', resID );
- if ( sndHandle == NULL )
- return ResError();
-
- HLockHi( sndHandle );
- HPurge( sndHandle );
-
- /* Play the sound resource and install a callback routine to
- ** handle the completion of the sound
- */
- err = SndPlay( (SndChannelPtr) snd->soundChannel[channelNum],
- (SndListHandle) sndHandle, async );
- if ( err == noErr )
- {
- theCommand.cmd = callBackCmd;
- theCommand.param1 = kSoundResourceDone;
- theCommand.param2 = SetCurrentA5();
- err = SndDoCommand( (SndChannelPtr) snd->soundChannel[channelNum],
- &theCommand, true );
-
- snd->soundChannel[channelNum]->soundData = (SndListHandle) sndHandle;
- snd->soundChannel[channelNum]->soundPlaying = kSoundResource;
- }
- }
- return err;
- }
-
-
-
- /*----------------------------- MUSIC MANAGEMENT ----------------------------*/
-
-
- PUBLIC OSErr
- CMSLoadMusic( short musicID )
- {
- PlaySequenceHandle sequenceH; /* Play sequence resource handle */
- PlaySequencePtr sequencePtr; /* for derefing the resource */
- Size sequenceSize; /* number of entries in the sequence */
- short ii, jj; /* misc counters */
- short soundLoaded[kMaxSounds]; /* ref nums of loaded sounds */
- short soundRef; /* current sound ref num */
- short firstSound; /* resource # of first sound */
- short lastSound; /* resource # of last sound */
- OSErr err; /* error code */
-
- /* First we get the music list resource and copy it into
- ** a newly allocated block of memory
- */
- sequenceH = (PlaySequenceHandle) GetResource( kMusicResType, musicID );
- if ( !sequenceH ) return ResError();
-
- sequenceSize = GetHandleSize( (Handle) sequenceH );
- err = MemError();
-
- if ( err == noErr )
- {
- sequencePtr = (PlaySequencePtr) NewPtr( sequenceSize );
- if ( sequencePtr == NULL )
- err = MemError();
- }
-
- if ( err == noErr && sequencePtr )
- {
- HLock( (Handle) sequenceH );
- BlockMove( (Ptr)(*sequenceH),(Ptr)(sequencePtr), sequenceSize );
- HUnlock( (Handle) sequenceH );
- }
-
- ReleaseResource( (Handle) sequenceH );
-
- if ( err == noErr )
- {
- /* Figure out the range of sound resources we are dealing with so
- ** that we know which sounds to load in. It is assumed that the
- ** sounds in the music sequence are contiguous and no other sounds
- ** are involved.
- */
- firstSound = sequencePtr->sequence[0];
- lastSound = sequencePtr->sequence[0];
- for ( ii = 1; ii < sequencePtr->sequenceLength; ii++ )
- {
- if ( sequencePtr->sequence[ii] < firstSound )
- firstSound = sequencePtr->sequence[ii];
-
- if ( sequencePtr->sequence[ii] > lastSound )
- lastSound = sequencePtr->sequence[ii];
- }
-
- /* Load the sound data for the music. The sequence resource lists
- ** the 'snd ' resources to play, so load each one in and update
- ** the music sequence to refer to the new sound, rather than the
- ** resource number.
- */
- for ( ii = firstSound; ii <= lastSound; ii++ )
- {
- err = CMSLoadSound( ii, &soundRef );
- if ( err != noErr ) break;
-
- soundLoaded[ii - firstSound] = soundRef;
- for ( jj = 0; jj < sequencePtr->sequenceLength; jj++ )
- {
- if ( sequencePtr->sequence[jj] == ii )
- sequencePtr->sequence[jj] = soundRef;
- }
- }
- }
-
-
- /* Cleanup if there were any errors */
-
- if ( err == noErr )
- {
- sequencePtr->loopStart--;
- snd->musicSequence = sequencePtr;
- }
- else if ( sequencePtr )
- {
- /* Dispose of the sounds we could load before the error occured. */
-
- ii -= firstSound;
- while ( --ii >= 0 )
- CMSRemoveSound( soundLoaded[ii] );
-
- DisposePtr( (Ptr) sequencePtr );
- snd->musicSequence = NULL;
- }
-
- return err;
- }
-
-
- /*---------------------------------------------------------------------------*/
-
- PUBLIC OSErr
- CMSRemoveMusic( void )
- {
- PlaySequencePtr sequencePtr;
- short ii;
- OSErr err;
-
- err = noErr;
- sequencePtr = snd->musicSequence;
- if ( sequencePtr )
- {
- /* Loop through the entire play sequence and release each
- ** sound. If the sound has already been released, skip it
- ** and move on to the next one.
- */
- for ( ii = 0; ii < sequencePtr->sequenceLength; ii++ )
- {
- if ( snd->soundData[sequencePtr->sequence[ii]] )
- CMSRemoveSound( sequencePtr->sequence[ii] );
- }
-
- DisposePtr( (Ptr) sequencePtr );
- snd->musicSequence = NULL;
- snd->musicPosition = 0;
- }
- return err;
- }
-
- /*---------------------------------------------------------------------------*/
-
- PUBLIC OSErr
- CMSPlayMusic( short channelNum )
- {
- OSErr err;
-
- err = noErr;
- if ( snd->musicSequence != NULL )
- {
- /* DJH on 03/03/96: Use the common routine that CMSPlaySound uses */
-
- err = PrepareChannel( channelNum );
-
- /* Reset the music position and then play the first
- ** block of music.
- */
- if ( err == noErr )
- {
- snd->musicPosition = 0;
- err = PlayMusicBlock( channelNum );
- }
- }
-
- return err;
- }
-
-
-
- /*----------------------------- UTILITY ROUTINES ----------------------------*/
-
-
- PUBLIC SoundHeaderPtr
- CMSGetSoundHeader( SndListHandle sndHandle )
- {
- long offset; /* offset to sound header */
- OSErr err;
-
- HLockHi( (Handle) sndHandle );
-
- /* compute offset to sound header and use that offset to
- ** return a pointer into the sound handle.
- */
- err = CMSGetSoundHeaderOffset( sndHandle, &offset );
- if ( err != noErr ) /* no sound header in resource */
- return NULL;
- else /* compute address of sound header */
- return ((SoundHeaderPtr)((Ptr)(*sndHandle) + offset));
- }
-
- /*---------------------------------------------------------------------------*/
-
- PUBLIC OSErr
- CMSGetSoundHeaderOffset( SndListHandle soundHandle, long* theOffset )
- {
- Ptr sndPtr; /* to navigate resource */
- long offset; /* offset into resource */
- short numSynths; /* info about resource */
- short numCmds; /* info about resource */
- Boolean isDone; /* are we done yet? */
- OSErr err;
-
- /* If we have Sound Manager 3.0 or greater, then let it do
- ** the work for us. Otherwise, we have to parse the sound
- ** resource ourselves.
- */
- if ( snd->smVersion.majorRev >= 3 )
- return GetSoundHeaderOffset( soundHandle, theOffset );
-
- /* Initialize variables. */
-
- offset = 0L; /* return 0 if no sound header found */
- sndPtr = (Ptr)*soundHandle; /* point to start of resource data */
- isDone = false; /* haven't yet found sound header */
- err = noErr;
-
- /* Skip everything before sound commands. */
-
- switch ( ((SndListPtr) sndPtr)->format )
- {
- case firstSoundFormat: /* format 1 'snd ' resource */
- numSynths = ((SndListPtr) sndPtr)->numModifiers;
- sndPtr += 2 * sizeof( short ) + numSynths * sizeof( ModRef );
- break;
-
- secondSoundFormat: /* format 2 'snd ' resource */
- sndPtr += 2 * sizeof( short );
- break;
-
- default: /* unrecognized resource format */
- err = badFormat;
- isDone = true;
- break;
- }
-
- /* Find number of commands and move to start of first command. */
-
- numCmds = *(short*)sndPtr;
- sndPtr += sizeof( short );
-
-
- /* Search for bufferCmd or soundCmd to obtain sound header. */
-
- while ( numCmds >= 1 && !isDone )
- {
- if ( (*(short*)sndPtr) == bufferCmd + dataOffsetFlag ||
- (*(short*)sndPtr) == soundCmd + dataOffsetFlag )
- {
- /* bufferCmd or soundCmd found, copy offset
- ** from sound command
- */
- offset = ((SndCommand*)sndPtr)->param2;
- isDone = true; /* get out of loop */
- }
- else
- {
- /* soundCmd or bufferCmd not found move to next command
- */
- sndPtr += sizeof( SndCommand );
- --numCmds;
- }
- }
-
- *theOffset = offset; /* return offset */
- return err; /* return result code */
- }
-
-
-
- /*----------------------------- INQUIRY ROUTINES ----------------------------*/
-
-
- PUBLIC Boolean
- CMSHasNewSoundManager( void )
- {
- NumVersion sndVersion;
-
- if ( GetToolTrapAddress(_Unimplemented) != GetToolTrapAddress(_SoundDispatch) )
- {
- // #ifdef __MWERKS__
- // long* v = (long*)&sndVersion;
- // *v = SndSoundManagerVersion();
- // #else
- sndVersion = SndSoundManagerVersion();
- // #endif
-
- return (sndVersion.majorRev >= 2);
- }
-
- return false;
- }
-
- /*---------------------------------------------------------------------------*/
-
- PUBLIC Boolean
- CMSHasMultipleChannels( void )
- {
- OSErr err;
- NumVersion numv;
- long feature;
-
- err = Gestalt( gestaltSoundAttr, &feature );
- if ( err == noErr )
- {
- // #ifdef __MWERKS__
- // long* v = (long*)&numv;
- // *v = SndSoundManagerVersion();
- // #else
- numv = SndSoundManagerVersion();
- // #endif
- if ( CMSHasNewSoundManager() && numv.majorRev >= 3 )
- {
- return ((feature & (1L << gestaltMultiChannels)) != 0);
- }
- else
- {
- err = Gestalt( gestaltHardwareAttr, &feature );
- if ( err == noErr )
- return ((feature & (1L << gestaltHasASC)) != 0);
- }
- }
- return false;
- }
-
-
- /*===========================================================================*/
- /* PRIVATE FUNCTION DEFINITIONS */
- /*===========================================================================*/
-
-
- PRIVATE OSErr
- PlayMusicBlock( short channelNum )
- {
- short soundRef;
- OSErr err;
-
- if ( snd->musicPosition < 0 )
- return noErr;
-
- soundRef = snd->musicSequence->sequence[snd->musicPosition];
- if ( soundRef < 0 )
- return noErr;
-
- /* DJH on 03/03/96: use the common routine shared by CMSPlaySound() */
-
- err = StartSound( (SndChannelPtr) snd->soundChannel[channelNum],
- channelNum, soundRef, kMusicDone );
-
- return err;
- }
-
- /*---------------------------------------------------------------------------*/
- /* DA on 01/20/96: redeclared theCmd as a pointer rather than a whole
- ** structure, to enable this routine to be called on PowerMacs too
- */
-
- PRIVATE pascal void
- SoundCallBack( SndChannelPtr theChannel, SndCommand *theCmd )
- {
- long myA5;
- short channelNum;
-
- myA5 = SetA5( theCmd->param2 );
-
- switch( theCmd->param1 )
- {
- case kSoundDone:
- case kSoundResourceDone:
-
- /* A sound has finished playing on the sound channel so
- ** mark the sound channel as no longer playing any sound
- ** and return.
- */
- channelNum = theChannel->userInfo;
- snd->soundChannel[channelNum]->soundPlaying = kNoSound;
- break;
-
-
-
- case kMusicDone:
-
- /* Advance the music position counter. If we have reached
- ** the end of the music, loop back to the appropriate place
- ** in the sequence.
- */
- if ( (++snd->musicPosition) >= snd->musicSequence->sequenceLength )
- snd->musicPosition = snd->musicSequence->loopStart;
-
- /* Extract the channel number and play the next music
- ** block on that channel.
- */
- channelNum = theChannel->userInfo;
- PlayMusicBlock( channelNum );
- break;
- }
-
- myA5 = SetA5( myA5 );
-
- }
-
- /*---------------------------------------------------------------------------*/
- /* DA on 01/20/96: reads the volume of a specific channel */
-
- PUBLIC OSErr
- CMSGetChannelVolume( short channelNum, unsigned short *sndVolume )
- {
- SndChannelPtr chan; /* the channel to inspect */
- SndCommand sndCmd; /* command to issue to the channel */
- unsigned long volume; /* the volume of left and right channels */
- OSErr err; /* error code */
-
- /* get the channel to inspect */
-
- chan = (SndChannelPtr)snd->soundChannel[channelNum];
-
-
- /* fill in the command structure to get the volume of the channel */
-
- sndCmd.cmd = getVolumeCmd;
- sndCmd.param1 = 0;
- sndCmd.param2 = (long) &volume;
-
- err = SndDoImmediate(chan, &sndCmd);
-
-
- /* extract the volume. Try the right channel first, then left */
-
- if ( err == noErr )
- {
- *sndVolume = (unsigned short)(volume & 0x0000FFFF);
- if ( *sndVolume == 0 )
- *sndVolume = (unsigned short)((volume & 0xFFFF0000) >> 16);
- }
- else
- {
- *sndVolume = 0;
- }
-
- return err;
- }
-
- /*---------------------------------------------------------------------------*/
- /* DA on 01/20/96: assigns selected volume to a specific channel */
-
- PUBLIC OSErr
- CMSSetChannelVolume( short channelNum, unsigned short volume )
- {
- SndChannelPtr chan; /* channel to modify */
- SndCommand sndCmd; /* command to set the volume */
- OSErr err; /* error code */
-
- chan = (SndChannelPtr)snd->soundChannel[channelNum];
-
- /* Fill in the command data. Assign volume to both left and right
- ** channels to provide compatibility on stereo machines.
- */
- sndCmd.cmd = volumeCmd;
- sndCmd.param1 = 0;
- sndCmd.param2 = ((long)volume << 16) | (long)volume;
-
- err = SndDoImmediate(chan, &sndCmd);
-
-
- return err;
- }
-
- /*---------------------------------------------------------------------------*/
- /* DA on 01/20/96: local function play a sound using a double buffered scheme*/
-
- PRIVATE OSErr
- DBSndPlay( SndChannelPtr chan, short channelNum, SoundHeaderPtr sndHeader )
- {
- OSErr err; /* error code */
- DBLocalPtr doubleVars; /* local double buffer info */
- SndDoubleBufferPtr doubleBuffer; /* one of the buffers */
- SndDoubleBufferHeader* doubleHeader; /* header for the double buffer */
- short i; /* misc counter */
-
- /* obtain pointers to double buffer structures for faster access */
-
- doubleVars = &snd->doubleVars[channelNum];
- doubleHeader = &snd->doubleHeader[channelNum];
-
-
- /* fill our local structure used to control the double buffering */
-
- doubleVars->header = sndHeader;
- doubleVars->bytesCopied = 0; /* no samples copied yet */
- doubleVars->dataPtr = (Ptr)&sndHeader->sampleArea[0]; /* pointer to the first sample */
-
-
- /* sampling rate in kHz. This works for all sound headers. */
-
- doubleHeader->dbhSampleRate = sndHeader->sampleRate;
-
-
- /* DJH on 02/06/96: fill in the double buffer header at _play_ time
- ** rather than _init_ time based on the kind of sound header passed.
- */
- switch ( sndHeader->encode )
- {
- case stdSH: /* Standard sound header (8-bit, 1 channel, no compression) */
-
- doubleHeader->dbhNumChannels = 1;
- doubleHeader->dbhSampleSize = 8;
- doubleHeader->dbhCompressionID = notCompressed;
- doubleHeader->dbhPacketSize = 0;
-
- doubleVars->bytesTotal = sndHeader->length; // sound data length
-
- break;
-
-
- case extSH: /* Extended sound header */
- {
- ExtSoundHeaderPtr extHeader = (ExtSoundHeaderPtr) sndHeader;
-
- doubleHeader->dbhNumChannels = extHeader->numChannels;
- doubleHeader->dbhSampleSize = extHeader->sampleSize;
- doubleHeader->dbhCompressionID = notCompressed;
- doubleHeader->dbhPacketSize = 0;
-
- doubleVars->bytesTotal =
- extHeader->numFrames * extHeader->sampleSize * extHeader->numChannels / 8;
-
- break;
- }
-
-
- case cmpSH: /* Compressed sound header */
- {
- CmpSoundHeaderPtr cmpHeader = (CmpSoundHeaderPtr) sndHeader;
-
- doubleHeader->dbhNumChannels = cmpHeader->numChannels;
- doubleHeader->dbhSampleSize = cmpHeader->sampleSize;
- doubleHeader->dbhCompressionID = cmpHeader->compressionID;
- doubleHeader->dbhPacketSize = cmpHeader->packetSize;
- break;
- }
- }
-
-
- /* initialize both buffers */
-
- for ( i = 0; i < 2; i++ )
- {
- doubleBuffer = snd->doubleBuffer[(channelNum * 2) + i];
-
-
- doubleBuffer->dbNumFrames = 0; // no frames yet
- doubleBuffer->dbFlags = 0; // buffer is empty
- doubleBuffer->dbUserInfo[0] = (long)doubleVars; // pointer to our vars
-
- /* Fill buffer with samples. It is _necessary_ to call the doubleback
- ** function this way, otherwise it would crash on PPCs
- */
-
- CallSndDoubleBackProc( doubleBackUPP, chan, doubleBuffer );
-
- }
-
-
- /* start the sound playing */
-
- err = SndPlayDoubleBuffer( chan, doubleHeader );
-
-
- return err;
- }
-
- /*---------------------------------------------------------------------------*/
- /* DA on 01/20/96: local function double buffering callback procedure used to
- ** feed the Sound Manager with sound data
- */
-
- PRIVATE pascal void
- DADoubleBack( SndChannelPtr, SndDoubleBufferPtr doubleBuffer )
- {
- DBLocalPtr doubleVarsPtr;
- long bytesToCopy;
-
-
- /* get pointer to our local variables */
-
- doubleVarsPtr = (DBLocalPtr)doubleBuffer->dbUserInfo[0];
-
-
-
- /* get number of bytes left to copy */
-
- bytesToCopy = doubleVarsPtr->bytesTotal - doubleVarsPtr->bytesCopied;
-
-
-
- /* If the amount left is greater than double-buffer size, then
- ** limit the number of bytes to copy to the size of the buffer.
- */
- if ( bytesToCopy > kDoubleBufferSize )
- {
- bytesToCopy = kDoubleBufferSize;
- }
-
-
-
- /* copy samples to double buffer
- ** uses BlockMoveData to avoid flushing the cache on 040 machines
- */
- BlockMoveData(doubleVarsPtr->dataPtr, &doubleBuffer->dbSoundData[0], bytesToCopy);
-
-
- /* store the number of samples in buffer */
-
- switch ( doubleVarsPtr->header->encode )
- {
- case stdSH: /* Standard sound header */
-
- doubleBuffer->dbNumFrames = bytesToCopy;
- break;
-
-
- case extSH: /* Extended sound header */
- {
- ExtSoundHeaderPtr extHeader = (ExtSoundHeaderPtr) doubleVarsPtr->header;
-
- doubleBuffer->dbNumFrames =
- (bytesToCopy * 8) / (extHeader->numChannels * extHeader->sampleSize);
-
- break;
- }
-
-
- case cmpSH: /* Compressed sound header */
- {
- /* Do I need to decompress the data myself? */
-
- CmpSoundHeaderPtr cmpHeader = (CmpSoundHeaderPtr) doubleVarsPtr->header;
-
- doubleBuffer->dbNumFrames =
- (bytesToCopy * 8) / (cmpHeader->numChannels * cmpHeader->sampleSize);
-
- break;
- }
-
-
- default:
- break;
- }
-
-
-
- /* Mark the buffer as ready */
-
- doubleBuffer->dbFlags |= dbBufferReady;
-
-
-
- /* Update data pointer and number of bytes copied */
-
- doubleVarsPtr->dataPtr += bytesToCopy;
- doubleVarsPtr->bytesCopied += bytesToCopy;
-
-
-
- /* If all samples have been copied, then this is the last buffer. */
-
- if ( doubleVarsPtr->bytesCopied == doubleVarsPtr->bytesTotal )
- {
- doubleBuffer->dbFlags |= dbLastBuffer;
- }
- }
-
- /*---------------------------------------------------------------------------*/
- /* DA on 01/20/96: play a sound with specified priority */
-
- PUBLIC OSErr
- CMSPlaySoundPriority( short sound, short priority )
- {
- short i; /* misc index */
- short free = -1; /* index of a channel that is free */
- short sndRef; /* sound currently playing */
- short minPriority; /* the minimum priority on all channels */
- short minPTrack; /* channel that has the minimum priority */
- short tracksUsed; /* number of channels currently open */
- OSErr err = noErr; /* error code */
-
- tracksUsed = snd->numChannels; /* number of open channels */
-
-
- /* First, rebuild priorites list and search for a free channel */
-
- for (i = 0; i < tracksUsed; i++)
- {
- err = CMSGetSoundPlaying( i, &sndRef );
-
- if ( sndRef == kNoSound )
- {
- snd->priorities[i] = 0;
- free = i;
- break;
- }
-
- }
-
-
-
- /* If there's a free channel, use that one, otherwise search
- ** for the channel with the lowest priority and see if we can
- ** play the sound on that one
- */
- if ( free >= 0 )
- {
- err = CMSPlaySound( sound, free );
- snd->priorities[free] = priority;
- }
- else
- {
- /* search for the lowest priority of any sound playing */
-
- for ( i = minPTrack = 0, minPriority = snd->priorities[0]; i < tracksUsed; i++ )
- {
- if ( snd->priorities[i] < minPriority )
- {
- minPriority = snd->priorities[i];
- minPTrack = i;
- }
- }
-
- if ( minPriority < priority ) /* ok to play the sound */
- {
- err = CMSPlaySound( sound, minPTrack );
-
-
- /* NOTE: We MUST set the priority after we play the sound since
- ** CMSPlaySound gives the sound a priority of 0 by default.
- */
- snd->priorities[minPTrack] = priority;
-
- if ( err == noErr )
- err = kPriorityOverride; /* a lower priority was found */
- }
- else
- {
- err = kPriorityDenied; /* no lower priority found: sound not played */
- }
- }
-
- return err;
- }
-
- /*---------------------------------------------------------------------------*/
- /* DA on 01/20/96: change the volume for all open channels */
-
- PUBLIC OSErr
- CMSSetToolVolume( short volume )
- {
- short channel, tracksUsed;
- unsigned short setting;
- OSErr err;
-
- /* number of open channels */
-
- tracksUsed = snd->numChannels;
-
-
- /* operate the change on all channels */
-
- for ( channel = 0; channel < tracksUsed; channel++ )
- {
- /* read current volume */
-
- err = CMSGetChannelVolume( channel, &setting );
- if ( err != noErr )
- return err;
-
-
-
- /* change it according to specified parameter */
-
- setting += volume;
-
- if ( volume > 0 )
- {
- if ( setting > kMaxVolume )
- setting = kMaxVolume;
- }
- else if ( volume < 0 )
- {
- if ( ((short)setting) < kMinVolume )
- setting = kMinVolume;
- }
-
- err = CMSSetChannelVolume( channel, setting );
- if ( err != noErr )
- return err;
- }
-
- return noErr;
- }
-