home *** CD-ROM | disk | FTP | other *** search
/ Apple Developer Connection 1998 Fall: Game Toolkit / Disc.iso / Samples / SprocketExamples / DroneZone / DroneZone Sources / DZFake3DSound.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-07-14  |  8.2 KB  |  349 lines  |  [TEXT/CWIE]

  1. /*
  2.  *    File:        DZFake3DSound.c
  3.  *    Author:        Dan Venolia
  4.  *
  5.  *    Contents:    Diverts off the calls to SndSetInfo that are destined for the
  6.  *                3D sound localization component.  A useful debugging tool.
  7.  *
  8.  *    Copyright © 1996 Apple Computer, Inc.
  9.  */
  10.  
  11. #include <assert.h>
  12. #include <math.h>
  13. #include <Resources.h>
  14.  
  15. #include <Sound.h>
  16.  
  17. #include "SoundSprocket.h"
  18.  
  19. #include "DZFake3DSound.h"
  20.  
  21. #if FAKE_3D_SOUND
  22.  
  23. #undef SndSetInfo
  24. #undef SndGetInfo
  25.  
  26.  
  27. static SpeakerKind            gSpeakerKind        = kSpeakerKindStereo;
  28. static float                gSpeakerAngle        = 3.14159*0.16667;        // 30°
  29.  
  30.  
  31. static OSErr FakeGet3DCPULoadLimit(
  32.     SndChannelPtr            inSndChannel,
  33.     UInt32*                    outCPULoadLimit);
  34.  
  35. static OSErr FakeSet3DSetup(
  36.     SndChannelPtr            inSndChannel,
  37.     const Snd3DSetup*        in3DSetup);
  38.  
  39. static OSErr FakeGet3DSetup(
  40.     SndChannelPtr            inSndChannel,
  41.     Snd3DSetup*                out3DSetup);
  42.  
  43. static OSErr FakeSet3DInfo(
  44.     SndChannelPtr            inSndChannel,
  45.     const Snd3DInfo*        in3DInfo);
  46.  
  47.  
  48. /* =============================================================================
  49.  *        FakeSndSetInfo
  50.  *
  51.  *    Overrides the behavior of SndSetInfo for selected messages.
  52.  * ========================================================================== */
  53. OSErr FakeSndSetInfo(
  54.     SndChannelPtr            inSndChannel,
  55.     OSType                    inSelector,
  56.     void*                    inData)
  57. {
  58.     OSErr                    err;
  59.     
  60.     switch (inSelector)
  61.     {
  62.     
  63. #ifdef FAKE_3D_INSTALL_ONLY
  64.         case si3DInstall:
  65.         {
  66.         
  67. #ifdef REAL_3D_INSTALL
  68.             SoundComponentLink  link;
  69.  
  70.             link.description.componentType = kSoundEffectsType;
  71.             link.description.componentSubType = 'snd3';
  72.             link.description.componentManufacturer = 'appl';  // set to zero for default/any
  73.             link.description.componentFlags = 0;        
  74.             link.description.componentFlagsMask = 0;    
  75.             link.mixerID = nil;
  76.             err = SndSetInfo(inSndChannel, siPreMixerSoundComponent, &link);
  77. #else
  78.  
  79.             SndListHandle sndHdl = (SndListHandle)GetResource(soundListRsrc, 1000);
  80.             if (sndHdl)
  81.             {
  82.                 HLock((Handle)sndHdl);
  83.                 err = SndPlay(inSndChannel, sndHdl, false);
  84.                 ReleaseResource((Handle)sndHdl);
  85.                 HUnlock((Handle)sndHdl);
  86.             }
  87.             else  err = ResError();
  88. #endif
  89.  
  90.         }
  91.         break;
  92. #else
  93.         case si3DInstall:
  94.             err = noErr;
  95.         break;
  96.         
  97.         case si3DSetup:
  98.             err = FakeSet3DSetup(inSndChannel, (const Snd3DSetup*) inData);
  99.         break;
  100.         
  101.         case si3DInfo:
  102.             err = FakeSet3DInfo(inSndChannel, (const Snd3DInfo*) inData);
  103.         break;
  104. #endif
  105.         
  106.         default:
  107.             err = SndSetInfo(inSndChannel, inSelector, inData);
  108.     }
  109.     
  110.     return err;
  111. }
  112.  
  113.  
  114. /* =============================================================================
  115.  *        FakeSndGetInfo
  116.  *
  117.  *    Overrides the behavior of SndGetInfo for selected messages.
  118.  * ========================================================================== */
  119. OSErr FakeSndGetInfo(
  120.     SndChannelPtr            inSndChannel,
  121.     OSType                    inSelector,
  122.     void*                    outData)
  123. {
  124.     OSErr                    err;
  125.     
  126.     switch (inSelector)
  127.     {
  128. #ifndef FAKE_3D_INSTALL_ONLY
  129.         case si3DCPULoadLimit:
  130.             err = FakeGet3DCPULoadLimit(inSndChannel, (UInt32*) outData);
  131.         break;
  132.         
  133.         case si3DSetup:
  134.             err = FakeGet3DSetup(inSndChannel, (Snd3DSetup*) outData);
  135.         break;
  136. #endif
  137.         
  138.         default:
  139.             err = SndGetInfo(inSndChannel, inSelector, outData);
  140.     }
  141.     
  142.     return err;
  143. }
  144.  
  145.  
  146. /* =============================================================================
  147.  *        FakeGet3DCPULoadLimit(internal)
  148.  *
  149.  *    Returns the CPU load limit.
  150.  * ========================================================================== */
  151. OSErr FakeGet3DCPULoadLimit(
  152.     SndChannelPtr            inSndChannel,
  153.     UInt32*                    outCPULoadLimit)
  154. {
  155.     assert(outCPULoadLimit != NULL);
  156.     
  157.     *outCPULoadLimit = 6;  //• or whatever
  158.     
  159.     return noErr;
  160. }
  161.  
  162.  
  163. /* =============================================================================
  164.  *        FakeSet3DSetup (internal)
  165.  *
  166.  *    Salts away the info in globals.
  167.  * ========================================================================== */
  168. OSErr FakeSet3DSetup(
  169.     SndChannelPtr            inSndChannel,
  170.     const Snd3DSetup*        in3DSetup)
  171. {
  172.     assert(in3DSetup != NULL);
  173.     assert(in3DSetup->version == 1);
  174.     
  175.     gSpeakerKind    = in3DSetup->speakerKind;
  176.     gSpeakerAngle    = in3DSetup->speakerAngle;
  177.     
  178.     return noErr;
  179. }
  180.  
  181.  
  182. /* =============================================================================
  183.  *        FakeSet3DSetup (internal)
  184.  *
  185.  *    Returns the global storage for the speaker setup.
  186.  * ========================================================================== */
  187.  OSErr FakeGet3DSetup(
  188.     SndChannelPtr            inSndChannel,
  189.     Snd3DSetup*                out3DSetup)
  190. {
  191.     assert(out3DSetup != NULL);
  192.     
  193.     out3DSetup->version            = 1;
  194.     out3DSetup->speakerKind        = gSpeakerKind;
  195.     out3DSetup->speakerAngle    = gSpeakerAngle;
  196.     out3DSetup->reserved0        = 0;
  197.     out3DSetup->reserved1        = 0;
  198.     
  199.     return noErr;
  200. }
  201.  
  202.  
  203. /* =============================================================================
  204.  *        FakeSet3DInfo (internal)
  205.  *
  206.  *    Sends volumeCmd and rateMultiplierCmd to the sound channel for a cheesy
  207.  *    version of localized sound.  Note: this is incompatible with an app that
  208.  *    uses the volumeCmd or rateMultiplierCmd itself since we stomp them.
  209.  * ========================================================================== */
  210. OSErr FakeSet3DInfo(
  211.     SndChannelPtr            inSndChannel,
  212.     const Snd3DInfo*        in3DInfo)
  213. {
  214.     #define kMinVolume        0.0                // Lowest volume setting
  215.     #define kMaxVolume        4.0                // Highest volume setting -- must be less than 256!
  216.     #define kSpeedOfSound    344.0            // Speed of sound
  217.     
  218.     OSErr                    err;
  219.     float                    amplitude;
  220.     float                    speakerAngle;
  221.     float                    norm;
  222.     float                    leftAmplitude;
  223.     float                    rightAmplitude;
  224.     float                    coneAttenuation;
  225.     float                    rateMultiplier;
  226.     SndCommand                sndCommand;
  227.     
  228.     // Validate parameters
  229.     assert(inSndChannel != NULL);
  230.     assert(in3DInfo != NULL);
  231.     
  232.     if (in3DInfo->sourceMode == kSourceModeLocalized)
  233.     {
  234.         // Find the distance attenuation
  235.         amplitude = (in3DInfo->referenceDistance / in3DInfo->currentLocation.distance);
  236.         
  237.         // Do the angular attenuation
  238.         coneAttenuation = in3DInfo->coneAttenuation;
  239.         if (coneAttenuation != 0.0)
  240.         {
  241.             // Check whether we're inside the cone
  242.             if (in3DInfo->currentLocation.projectionAngle >
  243.                 in3DInfo->coneAngleCos)
  244.             {
  245.                 // Inside the cone -- blend the attenuation
  246.                 coneAttenuation *=
  247.                         (1.0-in3DInfo->currentLocation.projectionAngle) /
  248.                         (1.0-in3DInfo->coneAngleCos);
  249.             }
  250.             
  251.             amplitude *= powf(10.0, 0.05*coneAttenuation);
  252.         }
  253.         
  254.         // Compute the left and right speaker amplitudes
  255.         if (gSpeakerKind == kSpeakerKindMono)
  256.         {
  257.             leftAmplitude  = amplitude;
  258.             rightAmplitude = amplitude;
  259.         }
  260.         else
  261.         {
  262.             // Find the speaker spread
  263.             if (gSpeakerKind == kSpeakerKindStereo)
  264.             {
  265.                 speakerAngle = gSpeakerAngle;
  266.             }
  267.             else
  268.             {
  269.                 // Headphones -- 180°
  270.                 speakerAngle = _PI;
  271.             }
  272.             
  273.             // Compute the panned amplitudes
  274.             norm = in3DInfo->currentLocation.latitude/(0.5*speakerAngle);
  275.             
  276.             if (norm <= -1.0)
  277.             {
  278.                 // To the left
  279.                 leftAmplitude  = amplitude;
  280.                 rightAmplitude = 0.0;
  281.             }
  282.             else if (norm >= 1.0)
  283.             {
  284.                 // To the right
  285.                 leftAmplitude  = 0.0;
  286.                 rightAmplitude = amplitude;
  287.             }
  288.             else
  289.             {
  290.                 // In the center
  291.                 leftAmplitude  = amplitude * cosf((norm + 1.0) * (0.25*_PI));
  292.                 rightAmplitude = amplitude * cosf((1.0 - norm) * (0.25*_PI));
  293.             }
  294.         }
  295.         
  296.         // Pin the amplitude within the acceptable range
  297.         if (leftAmplitude < kMinVolume) leftAmplitude = kMinVolume;
  298.         if (leftAmplitude > kMaxVolume) leftAmplitude = kMaxVolume;
  299.         
  300.         if (rightAmplitude < kMinVolume) rightAmplitude = kMinVolume;
  301.         if (rightAmplitude > kMaxVolume) rightAmplitude = kMaxVolume;
  302.         
  303.         // Compute the Doppler shift
  304.         rateMultiplier =    (kSpeedOfSound + in3DInfo->currentLocation.listenerVelocity) /
  305.                             (kSpeedOfSound - in3DInfo->currentLocation.sourceVelocity);
  306.         
  307.         //• TODO: fail gracefully when over mach 1
  308.         //• TODO: pin to a reasonable range (?)
  309.     }
  310.     else
  311.     {
  312.         // Just play the sound
  313.         leftAmplitude  = 1.0;
  314.         rightAmplitude = 1.0;
  315.         
  316.         rateMultiplier = 1.0;
  317.     }
  318.     
  319.     // Change the left and right amplitudes
  320.     sndCommand.cmd = volumeCmd;
  321.     sndCommand.param1 = 0;
  322.     sndCommand.param2 = ((long) (rightAmplitude * 0x0100)) << 16 |
  323.                         ((long) (leftAmplitude  * 0x0100));
  324.     err = SndDoImmediate(inSndChannel, &sndCommand);
  325.     
  326.     if (err != noErr)
  327.     {
  328.         return err;
  329.     }
  330.     
  331.     // Change the rate multiplier
  332.     sndCommand.cmd = rateMultiplierCmd;
  333.     sndCommand.param1 = 0;
  334.     sndCommand.param2 = (long)(rateMultiplier*65536.0 + 0.5);
  335.     err = SndDoImmediate(inSndChannel, &sndCommand);
  336.     
  337.     if (err != noErr)
  338.     {
  339.         return err;
  340.     }
  341.     
  342.     return noErr;
  343. }
  344.  
  345.  
  346. #endif /* FAKE_3D_SOUND */
  347.  
  348.  
  349.