Source/SoundHandler.c

/*
    File:       SoundHandler.c
 
    Contains:   xxx put contents here xxx
 
    Version:    xxx put version here xxx
 
    Copyright:  © 1998-1999 by Apple Computer, Inc., all rights reserved.
 
    File Ownership:
 
        DRI:                Chris De Salvo
 
        Other Contact:      xxx put other contact here xxx
 
        Technology:         Apple Game Sprockets
 
    Writers:
 
        (cjd)   Chris De Salvo
        (cjd)   Chris DeSalvo
 
    Change History (most recent first):
 
      <SP12>      7/1/99    cjd     The camera and the point of interest were set to the same point.
                                    Bad. So we now push in the Z of the POI and pull back the Z of
                                    the camera.
      <SP11>     6/30/99    cjd     The camera and the point of interest were set to the same point.
                                    Bad. So we now push in the Z of the POI and pull back the Z of
                                    the camera.
      <SP10>     1/21/99    cjd     Removing 68K conditional
         <9>      7/1/98    cjd     Added SSp CPU load throttling
*/
 
//¥ ------------------------------------------------------------------------------------------  ¥
//¥
//¥ 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 "DSC 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.
//¥
//¥     Authors:
//¥         Chris De Salvo
//¥         Tim Carroll
//¥
//¥ ------------------------------------------------------------------------------------------  ¥
 
//¥ ------------------------------  Includes
 
#include <Resources.h>
#include <Sound.h>
#include <SoundSprocket.h>
#include <SoundComponents.h>
#include <CodeFragments.h>
 
#include "ErrorHandler.h"
#include "EventHandler.h"
#include "MemoryHandler.h"
#include "SIResources.h"
#include "SoundHandler.h"
 
//¥ ------------------------------  Private Definitions
//¥ ------------------------------  Private Types
//¥ ------------------------------  Private Variables
 
static Boolean gSoundHandlerInit = false;
static SndChannelPtr gChannels[soundNumSounds];
static Handle gSounds[soundNumSounds];
static SSpSourceReference gSource[soundNumSounds];
static SSpListenerReference gListener;
static Boolean gSoundSprocket = false;
 
//¥ ------------------------------  Private Functions
 
static void SetListenerLocation(const SInt32 inPOI_X, const SInt32 inPOI_Y);
 
//¥ ------------------------------  Public Variables
 
Boolean gSoundEffects = true;
 
UInt32  gCPULoadModifier = 0;
UInt32  gCPULoadMax = 0;
UInt32  gCPUCurrentLoad = 0;
 
//¥ --------------------    SoundHandlerInit
 
void
SoundHandlerInit(void)
{
UInt32  i;
 
    gSoundSprocket = false;
    
    //¥ Allocate the sound channels for playback
    for (i = 0; i < soundNumSounds; i++)
    {
    OSErr   theErr;
 
        theErr = SndNewChannel(&gChannels[i], sampledSynth, initMono, nil);
        if (theErr)
            FatalError("Could not allocate enough sound channels.");
    }
 
    //¥ Load the actual sound effects
    for (i = 0; i < soundNumSounds; i++)
    {
        gSounds[i] = GetResource('snd ', kSND_PlayerFire + i);
        if (! gSounds[i])
            FatalError("Could not load a required sound effect.");
            
        DetachResource(gSounds[i]);
    }
 
    gSoundHandlerInit = true;
 
    //¥ If SoundSprocket is present then create a listener and set things up
    if ((Ptr) SSpConfigureSpeakerSetup == (Ptr) kUnresolvedCFragSymbolAddress)
    {
        return;  // no sound sprocket
    }
    else    
    {
    OSStatus    theErr = noErr;
    
        theErr = SSpGetCPULoadLimit(&gCPULoadMax);
 
        if (noErr != theErr)
        {
            //¥ If we couldn't figure out the number of steps then we'll
            //¥ just set the CPU load to zero every time.  This forces maximum quality.
            gCPULoadModifier = 0;
        }
        else
        {
            gCPULoadModifier = 0xFFFFFFFF / (gCPULoadMax + 1);
        }
        
        //¥ We have sound sprocket, so now we install the filters and create source and listener objects.
    
        //¥ Create the listener
        theErr = SSpListener_New(&gListener);
        if (theErr)
            FatalError("Could not create a sound sprocket listener.");
    
        //¥ Define our unit of distance measurement
        theErr = SSpListener_SetMetersPerUnit(gListener, 0.05);
        if (theErr)
            FatalError ("Could not set reference distance for listener.");
    
        //¥ Attach the sound localization component to each of the sound
        //¥ channels that we'll be playing through.
        for (i = 0; i < soundNumSounds; i++)
        {
        SoundComponentLink  myLink;
            
            //¥ Create the source
            theErr = SSpSource_New(&gSource[i]);
            if (theErr)
                FatalError("Could not create a sound sprocket source.");
            
            //¥ Install the filter
            myLink.description.componentType = kSoundEffectsType;
            myLink.description.componentSubType = kSSpLocalizationSubType;
            myLink.description.componentManufacturer = 0;
            myLink.description.componentFlags = 0; 
            myLink.description.componentFlagsMask = 0;
            myLink.mixerID = nil; 
            myLink.linkID = nil;
            
            theErr = SndSetInfo(gChannels[i], siPreMixerSoundComponent, &myLink);
             if (theErr) 
                FatalError("Could not install the sound sprocket filter into the channel.");
        }
        
        gSoundSprocket = true;
        SetListenerLocation(320, 240);
    }
}
 
//¥ --------------------    SoundHandlerReset
 
void
SoundHandlerReset(void)
{
UInt32  i;
 
    if (! gSoundHandlerInit)
        return;
 
    //¥ Deallocate the sound channels
    for (i = 0; i < soundNumSounds; i++)
    {
        if (gChannels[i])
        {
            SndDisposeChannel(gChannels[i], true);
            gChannels[i] = nil;
        }
    }
 
    //¥ Unload the actual sound effects
    for (i = 0; i < soundNumSounds; i++)
    {
        if (gSounds[i])
            DisposeHandleZ(&gSounds[i]);
    }
 
}
 
//¥ --------------------    SoundHandlerPlay
 
void
SoundHandlerPlay(GameSounds theSound, short x, short y)
{
SndCommand  theCommand;
OSStatus    theErr = noErr;
 
    //¥ Make sure that sound effects are turned on
    if (! gSoundEffects)
        return;
 
    //¥ Make sure we are initialized
    if (! gSoundHandlerInit)
        return;
 
    //¥ Make sure the requested sound is within the range of valid effects
    if (theSound < 0 || theSound >= soundNumSounds)
        return;
 
    //¥ Stop playback of any sound currently playing on this channel
    theCommand.cmd = quietCmd;
    SndDoImmediate(gChannels[theSound], &theCommand);
 
    //¥ Flush out any queued sounds waiting to play on this channel
    theCommand.cmd = flushCmd;
    SndDoImmediate(gChannels[theSound], &theCommand);
 
    //¥ If we're using SoundSprocket then set up the location parameters
    //¥ for the localization filter
    if (gSoundSprocket)
    {
    TQ3CameraPlacement  location;
    SSpLocalizationData localization;
    
        //¥ We want all the sounds to be localized, but when we explode we want to hear it
        //¥ all around us.
        if (soundPlayerHit != theSound)
            SSpSource_SetMode(gSource[theSound], kSSpSourceMode_Localized);
        else
            SSpSource_SetMode(gSource[theSound], kSSpSourceMode_Ambient);
 
        //¥ Allow the user to dynamically set the CPU usage range for SSp
        SSpSource_SetCPULoad(gSource[theSound], gCPUCurrentLoad);
 
        //¥ Make sure that the listener is listening at this sound
        SetListenerLocation(320 - x, 240 - y);
 
        //¥ Position the sound in space.
        location.cameraLocation.x = 320 - x;
        location.cameraLocation.y = 240 - y;
        location.cameraLocation.z = 0;
        
        //¥ Orient the sound so that it is down directed down towards the listener
        location.pointOfInterest.x = 0;
        location.pointOfInterest.y = -1;
        location.pointOfInterest.z = -1;
    
        location.upVector.x = 0;
        location.upVector.y = 1;
        location.upVector.z = 0;
    
        theErr = SSpSource_SetCameraPlacement (gSource[theSound], &location);
        if (theErr)
            FatalError ("Failed to set the source location");
            
        theErr = SSpSource_CalcLocalization (gSource[theSound], gListener, &localization);
        if (theErr)
            FatalError ("Failed to calculate the localization");
        
        // We don't do doppler, since we only localize the sound at the instant it is played.
        localization.currentLocation.sourceVelocity = 0;
        localization.currentLocation.listenerVelocity = 0;
        
        // We set the reference distance to a reasonable number that seems to get good results
        // We also provide a minimum distance or else we run into really LOUD, really CLIPPED sounds
        localization.referenceDistance = 20.0;
        if (localization.currentLocation.distance < 5.0)
            localization.currentLocation.distance = 5.0;
 
        theErr = SndSetInfo (gChannels[theSound], siSSpLocalization, &localization);
        if (theErr)
            FatalError("Failed to localize the channel");
    }
 
    //¥ Play the selected sound immediately
    theErr = SndPlay(gChannels[theSound], (SndListHandle) gSounds[theSound], true);
    if (theErr)
        FatalError("Failed to start sound in SndPlay().");
}
 
//¥ --------------------    SetListenerLocation
 
static void
SetListenerLocation(const SInt32 inPOI_X, const SInt32 inPOI_Y)
{
OSStatus            theErr = noErr;
TQ3CameraPlacement  location;
 
    if (! gSoundSprocket)
        return;
 
    //¥ Set the listener in the bottom middle of the screen
    location.cameraLocation.x = 320;
    location.cameraLocation.y = 240;
    location.cameraLocation.z = -10;
 
    //¥ Focus the listener's attention on the exact middle of the screen
    location.pointOfInterest.x = inPOI_X;
    location.pointOfInterest.y = inPOI_Y;
    location.pointOfInterest.z = 0;
    
    location.upVector.x = 0;
    location.upVector.y = 1;
    location.upVector.z = 0;
    
    theErr = SSpListener_SetCameraPlacement (gListener, &location);
    if (theErr)
        FatalError ("Failed to set the listener location");
}