home *** CD-ROM | disk | FTP | other *** search
/ MacFormat 1999 Spring / macformat-077.iso / Shareware Plus / Development / SpriteWorld 2.2 / SpriteWorld files / Utils / SWSounds.c < prev    next >
Encoding:
Text File  |  1999-02-08  |  16.7 KB  |  665 lines  |  [TEXT/CWIE]

  1. ///--------------------------------------------------------------------------------------
  2. // SWSounds.c
  3. //
  4. // Created 11/13/96
  5. ///--------------------------------------------------------------------------------------
  6.  
  7. #include "SWSounds.h"
  8.  
  9.  
  10. #define kNonZeroValue            1        // used to avoid a bug in the Sound Manager
  11.  
  12.  
  13. typedef struct ChanStruct
  14. {
  15.     SndChannelPtr    channelP;            // Pointer to the sound channel
  16.     short            curSoundID;            // The ID of the sound currently playing in this channel.
  17.     short            volume;                // current volume for this channel
  18.     short            stereoPos;            // stereo position of this channel
  19.     Boolean            isDone;                // Is this channel done playing the last sound?
  20.     Boolean            isAvailable;        // Used by SetChannelAvailability.
  21. } ChanStruct, *ChanStructPtr;
  22.  
  23.  
  24. short        gNumSounds;
  25. short        gNumChannels;                // Number of sound channels
  26. ChanStruct    *gChanArray;                // Channel array that's allocated at run-time
  27. Handle        *gSoundHArray;                // Array of handles to all our sounds
  28. Boolean        gStereoMode = true;            // Whether to use stereo or mono
  29. long        gOldSystemSoundVolume = -1;    // used by SaveSystemVolume and RestoreSystemVolume
  30.  
  31. SndCallBackUPP    gSoundCallBackUPP;        // Needed for PPC
  32.  
  33.  
  34. ///--------------------------------------------------------------------------------------
  35. // IsNewSoundManagerInstalled
  36. ///--------------------------------------------------------------------------------------
  37.  
  38. Boolean IsNewSoundManagerInstalled( void )
  39. {
  40.     NumVersion    theVersion;
  41.     void        *trap1;
  42.     void        *trap2;
  43.     Boolean        installed;
  44.  
  45.     installed = FALSE;
  46.     trap1 = (void *)GetToolTrapAddress(_SoundDispatch);    // SndSoundManagerVersion Trap
  47.     trap2 = (void *)GetToolTrapAddress(_Unimplemented);    // Unimplemented Trap
  48.     
  49.     if (trap1 != trap2)
  50.     {
  51.         theVersion = SndSoundManagerVersion();
  52.         if (theVersion.majorRev >= 3)
  53.         {
  54.             installed = TRUE;
  55.         }
  56.     }
  57.     
  58.     return installed;
  59. }
  60.  
  61.  
  62. ///--------------------------------------------------------------------------------------
  63. // SetSystemVolume
  64. ///--------------------------------------------------------------------------------------
  65.  
  66. void    SetSystemVolume(short volume)        
  67. {
  68.     if (volume < 0)
  69.         volume = 0;
  70.     else if (volume > 7)
  71.         volume = 7;
  72.     
  73.         // Convert from 0-7 to 0-256
  74.     volume = (256 * volume + 6) / 7;    // the + 6 avoids rounding errors
  75.     
  76.         // We set the left and right speaker volume to the same value.
  77.     SetDefaultOutputVolume((long)((long)volume << 16L | (volume & 0xFFFF)));
  78. }
  79.  
  80.  
  81. ///--------------------------------------------------------------------------------------
  82. // GetSystemVolume
  83. ///--------------------------------------------------------------------------------------
  84.  
  85. void    GetSystemVolume(short *volume)        
  86. {
  87.     short    leftVol, rightVol, combinedVol, result;
  88.     long    totalVol;
  89.     
  90.         // We set the left and right speaker volume to the same value.
  91.     GetDefaultOutputVolume(&totalVol);
  92.     
  93.     rightVol = totalVol >> 16L;
  94.     leftVol = totalVol & 0xFFFF;
  95.     
  96.         // Get the combined volume of the left and right speakers
  97.     combinedVol = (rightVol + leftVol) / 2;
  98.     
  99.         // Convert from 0-256 to 0-7
  100.     result = (combinedVol * 7) / 256;
  101.     if (result == 0 && combinedVol > 0)        // Any value above 0 has volume
  102.         result = 1;
  103.     
  104.     *volume = result;
  105. }
  106.  
  107.  
  108. ///--------------------------------------------------------------------------------------
  109. // SaveSystemVolume
  110. ///--------------------------------------------------------------------------------------
  111.  
  112. void    SaveSystemVolume( void )
  113. {
  114.     GetDefaultOutputVolume(&gOldSystemSoundVolume);
  115. }
  116.  
  117.  
  118. ///--------------------------------------------------------------------------------------
  119. // RestoreSystemVolume
  120. ///--------------------------------------------------------------------------------------
  121.  
  122. void    RestoreSystemVolume( void )
  123. {
  124.         // We set the left and right speaker volume to the same value.
  125.     if (gOldSystemSoundVolume != -1)
  126.         SetDefaultOutputVolume(gOldSystemSoundVolume);
  127. }
  128.  
  129.  
  130. ///--------------------------------------------------------------------------------------
  131. // CreateSoundChannels
  132. ///--------------------------------------------------------------------------------------
  133.  
  134. OSErr    CreateSoundChannels(short numChannels)
  135. {
  136.     Size            arraySize;
  137.     short            n;
  138.     OSErr            err = noErr;
  139.     
  140.     gSoundCallBackUPP = NewSndCallBackProc(SoundCallBack);
  141.  
  142.     gNumChannels = numChannels;
  143.     if (numChannels <= 0)
  144.         return -1;
  145.     
  146.         // Allocate memory for gChanArray
  147.     arraySize = (Size)numChannels * sizeof(ChanStruct);
  148.     gChanArray = (ChanStruct *)NewPtrClear(arraySize);
  149.     err = MemError();
  150.     
  151.     
  152.     if (err == noErr)
  153.     {
  154.             // Create the sound channels
  155.         for (n=0; n < numChannels; n++)
  156.         {
  157.             err = SndNewChannel(&gChanArray[n].channelP, sampledSynth, initMono, gSoundCallBackUPP);
  158.             gChanArray[n].channelP->userInfo = SetCurrentA5();
  159.             gChanArray[n].isDone = true;
  160.             gChanArray[n].curSoundID = -1;
  161.             gChanArray[n].volume = kFullVolume;
  162.             gChanArray[n].stereoPos = 0;
  163.             gChanArray[n].isAvailable = true;
  164.             
  165.             if (err)
  166.                 break;
  167.         }
  168.         
  169.         if (err != noErr) // Dispose what we created
  170.         {
  171.             while (n)
  172.             {
  173.                 SndDisposeChannel(gChanArray[--n].channelP, true);
  174.             }
  175.             
  176.             DisposePtr((Ptr)gChanArray);
  177.         }
  178.     }
  179.     
  180.     if (err != noErr)
  181.     {
  182.             // Get rid of our UPPs
  183.         DisposeRoutineDescriptor( gSoundCallBackUPP );
  184.     }
  185.     
  186.     return err;
  187. }
  188.  
  189.  
  190. ///--------------------------------------------------------------------------------------
  191. // DisposeSoundChannels
  192. ///--------------------------------------------------------------------------------------
  193.  
  194. void    DisposeSoundChannels( void )
  195. {
  196.     while (gNumChannels)
  197.     {
  198.         SndDisposeChannel(gChanArray[--gNumChannels].channelP, true);
  199.     }
  200.     
  201.     DisposePtr((Ptr)gChanArray);
  202.     
  203.         // Get rid of our UPPs
  204.     DisposeRoutineDescriptor( gSoundCallBackUPP );
  205. }
  206.  
  207.  
  208. ///--------------------------------------------------------------------------------------
  209. // LoadSounds
  210. ///--------------------------------------------------------------------------------------
  211.  
  212. OSErr    LoadSounds( short startResID, short numSounds )
  213. {
  214.     short        index;
  215.     Size        arraySize;
  216.     OSErr        err = noErr;
  217.     
  218.     gNumSounds = numSounds;
  219.     
  220.         // Allocate memory for gSoundHArray
  221.     arraySize = (Size)numSounds * sizeof(Handle);
  222.     gSoundHArray = (Handle *)NewPtrClear(arraySize);
  223.     err = MemError();
  224.     
  225.     if (err == noErr)
  226.     {
  227.             // Load the sounds
  228.         for (index = 0; index < numSounds && err != memFullErr; index++)
  229.         {
  230.             gSoundHArray[index] = GetResource('snd ', startResID + index);
  231.             
  232.             if (gSoundHArray[index] != NULL)
  233.             {
  234.                 HLock(gSoundHArray[index]);
  235.             }
  236.             else
  237.             {
  238.                     // If old err was a memory error, don't replace it with resNotFound
  239.                 if (err == noErr || err == resNotFound)
  240.                     err = ResError() ? ResError() : resNotFound;
  241.             }
  242.         }
  243.     }
  244.     
  245.     return err;
  246. }
  247.  
  248.  
  249. ///--------------------------------------------------------------------------------------
  250. // DisposeSounds
  251. ///--------------------------------------------------------------------------------------
  252.  
  253. void    DisposeSounds( void )
  254. {
  255.     short    index;
  256.     
  257.     for (index = 0; index < gNumSounds; index++)
  258.     {
  259.         ReleaseResource(gSoundHArray[index]);
  260.     }
  261.     
  262.     DisposePtr((Ptr)gSoundHArray);
  263.  
  264. }
  265.  
  266.  
  267. ///--------------------------------------------------------------------------------------
  268. // PlaySound - starts playing a sound in the appropriate channel
  269. ///--------------------------------------------------------------------------------------
  270.  
  271. void    PlaySound(
  272.     short soundID, 
  273.     short channelNum, 
  274.     PlayType playType)
  275. {
  276.     PlaySound2(soundID, channelNum, playType, 256, 0, k22khz, false);
  277. }
  278.  
  279.  
  280. ///--------------------------------------------------------------------------------------
  281. // PlaySound2 - starts playing a sound in the appropriate channel
  282. ///--------------------------------------------------------------------------------------
  283.  
  284. void    PlaySound2(
  285.     short soundID, 
  286.     short channelNum, 
  287.     PlayType playType,
  288.     short volume,
  289.     short stereoPosition,
  290.     UnsignedFixed rate,
  291.     Boolean doLoopingSound)
  292. {
  293.     #pragma        unused(rate, doLoopingSound)
  294.     
  295.     if (soundID < 0 || soundID >= gNumSounds || gSoundHArray[soundID] == NULL)
  296.         return;
  297.     
  298.     channelNum = FindChannel(soundID, channelNum, playType);
  299.     
  300.     SetChannelVolume(channelNum, volume);
  301.     SetStereoPosition(channelNum, stereoPosition);
  302.     PlayMySound(soundID, channelNum);
  303. }
  304.  
  305.  
  306. ///--------------------------------------------------------------------------------------
  307. // FindChannel - used by PlaySound2
  308. ///--------------------------------------------------------------------------------------
  309.  
  310. short    FindChannel(short soundID, short channelNum, PlayType playType)
  311. {
  312.     Boolean        allChannelsAreBusy;
  313.     short        emptyChannelNum;
  314.     short        n;
  315.     
  316.     if (channelNum < 0 || channelNum >= gNumChannels)
  317.         channelNum = 0;
  318.     
  319.  
  320.     if (playType == kPlaySoundInChannel)
  321.     {
  322.         return channelNum;
  323.     }
  324.     else if (playType == kReplaceSameSound)
  325.     {
  326.         for (n=0; n < gNumChannels; n++)    // Look for our sound in all channels
  327.         {
  328.             if (gChanArray[n].curSoundID == soundID)
  329.             {
  330.                 return n;
  331.             }
  332.         }
  333.     }
  334.     
  335.         // The following code is executed if playType is kFindEmptyChannel or if
  336.         // playType was kReplaceSameSound and the sound was not found in any channel.
  337.     
  338.     
  339.         // Determine if all channels are busy
  340.     allChannelsAreBusy = true;
  341.     for (n=0; n < gNumChannels; n++)
  342.     {
  343.         if (gChanArray[n].isDone && gChanArray[n].isAvailable)
  344.         {
  345.             allChannelsAreBusy = false;
  346.             emptyChannelNum = n;
  347.             break;
  348.         }
  349.     }
  350.     
  351.     
  352.     if (gChanArray[channelNum].isDone)
  353.     {
  354.         return channelNum;        // The requested channel is free
  355.     }
  356.     else if (allChannelsAreBusy)
  357.     {
  358.             // First search all other channels for our sound
  359.         for (n=0; n < gNumChannels; n++)
  360.         {
  361.             if (gChanArray[n].curSoundID == soundID)
  362.             {
  363.                 return n;
  364.             }
  365.         }
  366.         
  367.             // If none are found, return the requested channel
  368.         return channelNum;    
  369.     }
  370.     else
  371.     {
  372.         return emptyChannelNum;        // Return an empty channel
  373.     }
  374. }
  375.  
  376.  
  377. ///--------------------------------------------------------------------------------------
  378. // PlayMySound - called by PlaySound
  379. ///--------------------------------------------------------------------------------------
  380.  
  381. void    PlayMySound(short soundID, short channelNum)
  382. {
  383.     SndCommand    theCommand;
  384.     
  385.         // Stop any sound already playing in this channel
  386.     StopChannel(channelNum);
  387.  
  388.         // Play the sound
  389.     gChanArray[channelNum].isDone = false;
  390.     gChanArray[channelNum].curSoundID = soundID;
  391.     SndPlay(gChanArray[channelNum].channelP, (SndListHandle)gSoundHArray[soundID], true);
  392.  
  393.         // Install the callBack
  394.     theCommand.cmd = callBackCmd;
  395.     theCommand.param1 = kNonZeroValue;
  396.     theCommand.param2 = channelNum;
  397.     SndDoCommand(gChanArray[channelNum].channelP, &theCommand, false);
  398. }
  399.  
  400.  
  401. ///--------------------------------------------------------------------------------------
  402. // StopSound
  403. ///--------------------------------------------------------------------------------------
  404.  
  405. void    StopSound(short soundID)
  406. {
  407.     short n;
  408.     
  409.     if (soundID < 0 || soundID >= gNumSounds)
  410.         return;
  411.     
  412.     for (n=0; n < gNumChannels; n++)
  413.     {
  414.         if (gChanArray[n].curSoundID == soundID)
  415.         {
  416.             StopChannel(n);
  417.         }
  418.     }
  419. }
  420.  
  421.  
  422. ///--------------------------------------------------------------------------------------
  423. // StopChannel
  424. ///--------------------------------------------------------------------------------------
  425.  
  426. void    StopChannel(short channelNum)
  427. {
  428.     SndCommand    theCommand;
  429.     
  430.     if (channelNum < 0 || channelNum >= gNumChannels)
  431.         return;
  432.     
  433.         // Stop the sound already playing in this channel
  434.     if (!gChanArray[channelNum].isDone)
  435.     {
  436.         theCommand.cmd = quietCmd;
  437.         SndDoImmediate(gChanArray[channelNum].channelP, &theCommand);
  438.         theCommand.cmd = flushCmd;
  439.         SndDoImmediate(gChanArray[channelNum].channelP, &theCommand);
  440.         gChanArray[channelNum].isDone = true;
  441.         gChanArray[channelNum].curSoundID = -1;
  442.     }
  443. }
  444.  
  445.  
  446. ///--------------------------------------------------------------------------------------
  447. // SetChannelVolume
  448. ///--------------------------------------------------------------------------------------
  449.  
  450. void    SetChannelVolume(short channelNum, short newVolume)
  451. {
  452.     if (channelNum < 0 || channelNum >= gNumChannels)
  453.         return;
  454.     
  455.     if (gChanArray[channelNum].volume != newVolume)
  456.     {
  457.         SetVolumeAndStereoPosition(channelNum, newVolume, gChanArray[channelNum].stereoPos);
  458.     }
  459. }
  460.  
  461.  
  462. ///--------------------------------------------------------------------------------------
  463. // SetStereoPosition
  464. ///--------------------------------------------------------------------------------------
  465.  
  466. void    SetStereoPosition(short channelNum, short stereoPosition)
  467. {
  468.     if (channelNum < 0 || channelNum >= gNumChannels)
  469.         return;
  470.     
  471.     if (gChanArray[channelNum].stereoPos != stereoPosition)
  472.     {
  473.         SetVolumeAndStereoPosition(channelNum, gChanArray[channelNum].volume, stereoPosition);
  474.     }
  475. }
  476.  
  477.  
  478. ///--------------------------------------------------------------------------------------
  479. // SetVolumeAndStereoPosition - used internally.
  480. ///--------------------------------------------------------------------------------------
  481.  
  482. void    SetVolumeAndStereoPosition(short channelNum, short newVolume, short stereoPosition)
  483. {
  484.     short            leftVolume, rightVolume, stereoOffset;
  485.     SndCommand        theCommand;
  486.     
  487.     if (channelNum < 0 || channelNum >= gNumChannels)
  488.         return;
  489.     
  490.     if (newVolume < 0)
  491.         newVolume = 0;
  492.         
  493.     if (stereoPosition < -256)
  494.         stereoPosition = -256;
  495.     else if (stereoPosition > 256)
  496.         stereoPosition = 256;
  497.     
  498.     gChanArray[channelNum].volume = newVolume;
  499.     gChanArray[channelNum].stereoPos = stereoPosition;
  500.     
  501.     if (gStereoMode == true)
  502.     {
  503.             // Calculate stereoOffset based on stereoPosition and the current volume
  504.         stereoOffset = (newVolume * stereoPosition) / 256;
  505.         
  506.         leftVolume = newVolume - stereoOffset;
  507.         rightVolume = newVolume + stereoOffset;
  508.     }
  509.     else
  510.     {
  511.             // Mono sound
  512.         leftVolume = newVolume;
  513.         rightVolume = newVolume;
  514.     }
  515.  
  516.         // Issue the command
  517.     theCommand.param1 = 0;
  518.     theCommand.param2 = (long)((long)rightVolume << 16L | (leftVolume & 0xFFFF));
  519.     theCommand.cmd = volumeCmd;
  520.     SndDoImmediate(gChanArray[channelNum].channelP, &theCommand);
  521. }
  522.  
  523.  
  524. ///--------------------------------------------------------------------------------------
  525. // SetChannelAvailability
  526. ///--------------------------------------------------------------------------------------
  527.  
  528. void    SetChannelAvailability(short channelNum, Boolean isAvailable)
  529. {
  530.     if (channelNum < 0 || channelNum >= gNumChannels)
  531.         return;
  532.     
  533.     gChanArray[channelNum].isAvailable = isAvailable;
  534. }
  535.  
  536.  
  537. ///--------------------------------------------------------------------------------------
  538. // SetStereoMode
  539. ///--------------------------------------------------------------------------------------
  540.  
  541. void    SetStereoMode(Boolean mode)
  542. {
  543.     short    n;
  544.     
  545.     gStereoMode = mode;
  546.     
  547.         // Update all channels for the current mode
  548.     for (n = 0; n < gNumChannels; n++)
  549.     {
  550.         SetVolumeAndStereoPosition(n, gChanArray[n].volume, gChanArray[n].stereoPos);
  551.     }
  552. }
  553.  
  554.  
  555. ///--------------------------------------------------------------------------------------
  556. //   GetStereoPositionOfSprite
  557. ///--------------------------------------------------------------------------------------
  558.  
  559. short    GetStereoPositionOfSprite(SpritePtr srcSpriteP, Rect *destRectP)
  560. {
  561.     short    col, stereoPos;
  562.     
  563.     col = srcSpriteP->destFrameRect.left + SW_RECT_WIDTH(srcSpriteP->destFrameRect) / 2;
  564.     
  565.     stereoPos = GetStereoPositionOfColumn(col, destRectP);
  566.     
  567.     return stereoPos;
  568. }
  569.  
  570.  
  571. ///--------------------------------------------------------------------------------------
  572. //   GetStereoPositionOfColumn
  573. ///--------------------------------------------------------------------------------------
  574.  
  575. short    GetStereoPositionOfColumn(long col, Rect *backRectP)
  576. {
  577.     Rect    backRect = *backRectP;
  578.     long    screenMidCol;
  579.     short    offset, stereoPos;
  580.     
  581.         // Make sure the left side starts at 0
  582.     if (backRect.left != 0)
  583.     {
  584.         offset = backRect.left;
  585.         backRect.left -= offset;
  586.         backRect.right -= offset;
  587.         col -= offset;
  588.     }
  589.     
  590.     screenMidCol = backRect.right / 2;
  591.     
  592.         // Fix col if out of bounds
  593.     if (col < 0)
  594.         col = 0;
  595.     else if (col > backRect.right)
  596.         col = backRect.right;
  597.     
  598.         // Calculate it
  599.     col -= screenMidCol;
  600.     stereoPos = (col * 256) / screenMidCol;
  601.     
  602.     return stereoPos;
  603. }
  604.  
  605.  
  606. ///--------------------------------------------------------------------------------------
  607. // FindSound - searches all channels for soundID. Returns channel number, or -1.
  608. ///--------------------------------------------------------------------------------------
  609.  
  610. short    FindSound(short soundID)
  611. {
  612.     short        channel;
  613.     Boolean        foundSound;
  614.  
  615.     foundSound = false;
  616.     for (channel = 0; channel < gNumChannels; channel++)
  617.     {
  618.         if (gChanArray[channel].curSoundID == soundID)
  619.         {
  620.             foundSound = true;
  621.             break;
  622.         }
  623.     }
  624.     
  625.     if (foundSound)
  626.         return channel;
  627.     else
  628.         return -1;
  629. }
  630.  
  631.  
  632. ///--------------------------------------------------------------------------------------
  633. // SoundCallBack - called when a channel is done playing a sound
  634. ///--------------------------------------------------------------------------------------
  635.  
  636. pascal void SoundCallBack(SndChannelPtr theChannel, SndCommand *theCommand)
  637. {
  638.     short        channelNum;
  639.     long        saveA5;
  640.  
  641. #if SW_PPC
  642.     #pragma unused(saveA5, theChannel)
  643. #else
  644.     saveA5 = SetA5(theChannel->userInfo);
  645. #endif
  646.  
  647.         // We check for kNonZeroValue to avoid a bug in some versions of the Sound Manager.
  648.     if (theCommand->param1 == kNonZeroValue)
  649.     {
  650.         channelNum = theCommand->param2;
  651.         
  652.         if (channelNum >= 0 && channelNum < gNumChannels)
  653.         {
  654.             gChanArray[channelNum].isDone = true;
  655.             gChanArray[channelNum].curSoundID = -1;
  656.         }
  657.     }
  658.     
  659.             
  660. #if !SW_PPC
  661.     SetA5(saveA5);
  662. #endif
  663. }
  664.  
  665.