Previous Book Contents Book Index Next

Inside Macintosh: Sound /
Chapter 2 - Sound Manager / Using the Sound Manager


Legacy Documentclose button

Important: Inside Macintosh: Sound is deprecated as of Mac OS X v10.5. For new audio development in Mac OS X, use Core Audio. See the Audio page in the ADC Reference Library.

Obtaining Sound-Related Information

Developments in the sound hardware available on Macintosh computers and in the Sound Manager routines that allow you to drive that hardware have made it imperative that your application pay close attention to the sound-related features of the operating environment. For example, some Macintosh computers do not have the sound input hardware necessary to allow sound recording. Similarly, some other Macintosh computers are not able to record sounds and play sounds simultaneously. Before taking advantage of a sound-related feature that is not available on all Macintosh computers, you should check to make sure that the target machine provides the features you need.

To make appropriate decisions about the sound you want to produce, you might need to know some or all of the following types of information:

The following sections describe how to use the Gestalt function and Sound Manager routines to determine these types of information.

Obtaining Information About Available Sound Features

You can use the Gestalt function to obtain information about a number of hardware- and software-related sound features. For instance, you can use Gestalt to determine whether a machine can produce stereophonic sounds and whether it can mix both left and right channels of sound on the internal speaker. Many applications don't need to call Gestalt to get this kind of information if they rely on the Sound Manager's ability to produce reasonable sounding output on whatever audio hardware is available. Other applications, however, do need to use Gestalt to get this information if they depend on specific hardware or software features that are not available on all Macintosh computers.

To get sound-related information from Gestalt, pass it the gestaltSoundAttr selector.

CONST
   gestaltSoundAttr        = 'snd ';   {sound attributes}
If Gestalt returns successfully, it passes back to your application a 32-bit value that represents a bit pattern. The following constants define the bits currently set or cleared by Gestalt:

CONST
   gestaltStereoCapability    = 0;     {built-in hw can play stereo sounds}
   gestaltStereoMixing        = 1;     {built-in hw mixes stereo to mono}
   gestaltSoundIOMgrPresent   = 3;     {sound input routines available}
   gestaltBuiltInSoundInput   = 4;     {built-in input hw available}
   gestaltHasSoundInputDevice = 5;     {sound input device available}
   gestaltPlayAndRecord       = 6;     {built-in hw can play while recording}
   gestalt16BitSoundIO        = 7;     {built-in hw can handle 16-bit data}
   gestaltStereoInput         = 8;     {built-in hw can record stereo sounds}
   gestaltLineLevelInput      = 9;     {built-in input hw needs line level}
   gestaltSndPlayDoubleBuffer = 10;    {play from disk routines available}
   gestaltMultiChannels       = 11;    {multiple channels of sound supported}
   gestalt16BitAudioSupport   = 12;    {16-bit audio data supported}
If the bit gestaltStereoCapability is TRUE, the built-in hardware can play stereo sounds. The bit gestaltStereoMixing indicates that the sound hardware of the machine mixes both left and right channels of stereo sound into a single audio signal for the internal speaker. Listing 2-9 demonstrates the use of the Gestalt function to determine if a machine can play stereo sounds.

Listing 2-9 Determining if stereo capability is available

FUNCTION MyHasStereo: Boolean;
VAR
   myFeature:     LongInt;
   myErr:         OSErr;
BEGIN
   myErr := Gestalt(gestaltSoundAttr, myFeature);
   IF myErr = noErr THEN      {test stereo capability bit}
      MyHasStereo := BTst(myFeature, gestaltStereoCapability)
   ELSE
      MyHasStereo := FALSE;   {no sound features available}
END;
As shown in the chapter "Introduction to Sound on the Macintosh," you can determine whether your application can record by testing the gestaltHasSoundInputDevice bit. To determine whether a built-in sound input device is available, you can test the gestaltBuiltInSoundInput bit. The gestaltSoundIOMgrPresent bit indicates whether the sound input routines are available. Because the gestaltHasSoundInputDevice bit is not set if the routines are not available, only sound input device drivers should need to use the gestaltSoundIOMgrPresent bit.

For a complete description of the response bits set by Gestalt, see "Gestalt Selector and Response Bits" beginning on page 2-90.

Obtaining Version Information

The Sound Manager provides functions that allow you to determine the version numbers both of the Sound Manager itself and of the MACE compression and expansion routines. Generally, you should avoid trying to determine which features or routines are present by reading a version number. Usually, the Gestalt function (discussed in the previous section) provides a better way to find out if some set of features, such as sound input capability, is available. In some cases, however, you can use these version routines to overcome current limitations of the information returned by Gestalt.

Both of these functions return a value of type NumVersion that contains the same information as the first 4 bytes of a resource of type 'vers'. The first and second bytes contain the major and minor version numbers, respectively; the third and fourth bytes contain the release level and the stage of the release level. For most purposes, the major and minor release version numbers are sufficient to identify the version. (See the chapter "Finder Interface" of Inside Macintosh: Macintosh Toolbox Essentials for a complete discussion of the format of 'vers' resources.)

You can use the SndSoundManagerVersion function to determine which version of the Sound Manager is present. Listing 2-10 shows how to determine if the enhanced Sound Manager is available.

Listing 2-10 Determining if the enhanced Sound Manager is present

FUNCTION MyHasEnhancedSoundManager: Boolean;
VAR
   myVersion:     NumVersion;
BEGIN
   IF MyTrapAvailable(_SoundDispatch) THEN
   BEGIN
      myVersion := SndSoundManagerVersion;
      MyHasEnhancedSoundManager := myVersion.majorRev >= 2;
   END
   ELSE
      MyHasEnhancedSoundManager := FALSE
END;
The MyHasEnhancedSoundManager function defined in Listing 2-10 relies on the MyTrapAvailable function, which is an application-defined routine provided in Inside Macintosh: Operating System Utilities. If the _SoundDispatch trap is not available, the SndSoundManagerVersion function is not available either, in which case the enhanced Sound Manager is certainly not available.

You can use the MACEVersion function to determine the version number of the available MACE routines (for example, Comp3to1).

Testing for Multichannel Sound and Play-From-Disk Capabilities

The ability to play multiple channels of sound simultaneously and the ability to initiate plays from disk were first introduced with the enhanced Sound Manager. Even with the enhanced Sound Manager, however, these capabilities are present only on computers equipped with suitable sound output hardware (such as an Apple Sound Chip). Sound Manager version 3.0 defines 2 additional bits in the Gestalt response parameter that allow you to test directly for these two capabilities.

CONST
   gestaltSndPlayDoubleBuffer    = 10; {play from disk routines available}
   gestaltMultiChannels          = 11; {multiple channels of sound supported}
Ideally, it should be sufficient to test directly, using Gestalt, for either multichannel sound capability or play-from-disk capability. If your application happens to be running under the enhanced Sound Manager, however, the two new response bits are not defined. In that case, you'll need to test also whether the Apple Sound Chip is available, because multichannel sound and play from disk are supported by the enhanced Sound Manager only if the Apple Sound Chip is available. To test for the presence of the Apple Sound Chip, you can use the Gestalt function with the gestaltHardwareAttr selector and the gestaltHasASC bit. Listing 2-11 combines these two tests into a single routine that returns TRUE if the computer supports multichannel sound.

Listing 2-11 Testing for multichannel play capability

FUNCTION MyCanPlayMultiChannels: Boolean;
VAR
   myResponse:    LongInt;
   myResult:      Boolean;
   myErr:         OSErr;
   myVersion:     NumVersion;
BEGIN
   myResult := FALSE;
   myVersion := SndSoundManagerVersion;
   myErr := Gestalt(gestaltSoundAttr, myResponse);
   IF myVersion.majorRev >= 3 THEN
      IF (myErr = noErr) AND (BTst(myResponse, gestaltMultiChannels)) THEN
         myResult := TRUE
   ELSE
      BEGIN
         myErr := Gestalt(gestaltHardwareAttr, myResponse);
         IF (myErr = noErr) AND (BTst(myResponse, gestaltHasASC)) THEN
            myResult := TRUE
      END;
   MyCanPlayMultiChannels := myResult;
END;
The function MyCanPlayMultiChannels first tries to get the desired information by calling the Gestalt function with the gestaltSoundAttr selector. If Gestalt returns successfully and the gestaltMultiChannels bit is set in the response parameter, then multichannel play capability is present. Notice that the multichannel bit is checked only if the version of the Sound Manager is 3.0 or greater. If the version is not at least 3.0, then MyCanPlayMultiChannels calls the Gestalt function with the gestaltHardwareAttr selector. If the computer contains the Apple Sound Chip, then again multichannel play capability is present.

Note
The gestaltHasASC bit is set only on machines that contain an Apple Sound Chip. You should test for the presence of the Apple Sound Chip only in the circumstances described above.
You could write a similar function to test for the ability to initiate a play from disk. Listing 2-12 shows an example.

Listing 2-12 Testing for play-from-disk capability

FUNCTION HasPlayFromDisk: Boolean;
VAR
   myResponse:    LongInt;
   myResult:      Boolean;
   myErr:         OSErr;
   myVersion:     NumVersion;
BEGIN
   myResult := FALSE;
   myVersion := SndSoundManagerVersion;
   myErr := Gestalt(gestaltSoundAttr, myResponse);
   IF myVersion.majorRev >= 3 THEN
      IF (myErr = noErr) AND 
                        (BTst(myResponse, gestaltSndPlayDoubleBuffer)) THEN
         myResult := TRUE
   ELSE
      BEGIN
         myErr := Gestalt(gestaltHardwareAttr, myResponse);
         IF (myErr = noErr) AND (BTst(myResponse, gestaltHasASC)) THEN
            myResult := TRUE
      END;
   HasPlayFromDisk := myResult;
END;

Obtaining Information About a Single Sound Channel

You can use the SndChannelStatus function to obtain information about a single sound channel and about the status of a disk-based playback on that channel, if one exists. For example, you can use SndChannelStatus to determine if a channel is being used for play from disk, how many seconds of the sound have been played, and how many seconds remain to be played.

One of the parameters required by the SndChannelStatus function is a pointer to a sound channel status record, which you must allocate before calling SndChannelStatus. A sound channel status record has this structure:

TYPE SCStatus =
RECORD
   scStartTime:            Fixed;      {starting time for play from disk}
   scEndTime:              Fixed;      {ending time for play from disk}
   scCurrentTime:          Fixed;      {current time for play from disk}
   scChannelBusy:          Boolean;    {TRUE if channel is processing cmds}
   scChannelDisposed:      Boolean;    {reserved}
   scChannelPaused:        Boolean;    {TRUE if channel is paused}
   scUnused:               Boolean;    {unused}
   scChannelAttributes:    LongInt;    {attributes of this channel}
   scCPULoad:              LongInt;    {CPU load for this channel}
END;
The scStartTime, scEndTime, and scCurrentTime fields are 0 unless the Sound Manager is currently playing from disk through the specified channel. If a play from disk is occurring, the scStartTime and scEndTime fields reflect the starting and ending points of the play, defined in seconds; the scCurrentTime field indicates the number of seconds between the beginning of the sound on disk and the part of the sound currently being played. The Sound Manager sets the values of the scStartTime and scEndTime fields based on the values you set in an audio selection record. (See page 2-100 for a description of the audio selection record.)

Note that because the Sound Manager might be playing only a selection of a sound, the scCurrentTime field does not reflect the number of seconds of sound play that have elapsed. To compute the number of seconds of sound play elapsed, you can subtract the value in the scStartTime field from that in the scCurrentTime field. However, because the Sound Manager updates the value of the scCurrentTime field only periodically, you should not rely on the accuracy of its value.

The scChannelBusy and scChannelPaused fields reflect whether a channel is processing commands and whether a channel is paused, respectively. After issuing a series of sound commands, you can use these fields to determine if the channel has finished processing all of the commands. If both scChannelBusy and scChannelPaused are FALSE, the Sound Manager has processed all of the channel's commands.

You can mask out certain values in the scChannelAttributes field to determine how a channel has been initialized.

CONST
   initPanMask    = $0003;    {mask for right/left pan values}
   initSRateMask  = $0030;    {mask for sample rate values}
   initStereoMask = $00C0;    {mask for mono/stereo values}
The scCPULoad field previously reflected the percentage of CPU processing power used by the sound channel. However, this field is obsolete, and you should not rely on its value.

Listing 2-13 illustrates the use of the SndChannelStatus function. It defines a function that takes a sound channel pointer as a parameter and determines whether a disk-based playback on that channel is paused.

Listing 2-13 Determining whether a sound channel is paused

FUNCTION MyChannelIsPaused (chan: SndChannelPtr): Boolean;
VAR
   myErr:         OSErr;
   mySCStatus:    SCStatus;
BEGIN
   MyChannelIsPaused := FALSE;
   myErr := SndChannelStatus(chan, Sizeof(SCStatus), @mySCStatus);
   IF myErr = noErr THEN
      MyChannelIsPaused := mySCStatus.scChannelPaused;
END;
The function defined in Listing 2-13 simply reads the scChannelPaused field to see if the playback is currently paused.

Note
In Sound Manager versions earlier than 3.0, pausing a sound channel by issuing a pauseCmd command does not change the scChannelPaused field. The scChannelPaused field is TRUE only if the Sound Manager is executing a disk-based playback on the channel and that playback is paused by the SndPauseFilePlay function. This problem is fixed in Sound Manager versions 3.0 and later.

Obtaining Information About All Sound Channels

You can use the SndManagerStatus function to determine information about all the sound channels that are currently allocated by all applications. For example, you can use this function to determine how many channels are currently allocated. One of the parameters required by the SndManagerStatus function is a pointer to a Sound Manager status record, which you must allocate before calling SndManagerStatus. A Sound Manager status record has this structure:

TYPE SMStatus =
PACKED RECORD
   smMaxCPULoad:     Integer;    {maximum load on all channels}
   smNumChannels:    Integer;    {number of allocated channels}
   smCurCPULoad:     Integer;    {current load on all channels}
END;
The smNumChannels field contains the number of sound channels currently allocated. This does not mean that the channels are actually being used, only that they have been created with the SndNewChannel function and not yet disposed.

The Sound Manager uses information that it returns in the smMaxCPULoad and smCurCPULoad fields to help it determine whether it can allocate a new channel when your application calls the SndNewChannel function. The Sound Manager sets smMaxCPULoad to a default value of 100 at startup time, and the smCurCPULoad field reflects the approximate percentage of CPU processing power currently taken by allocated sound channels.

WARNING
Your application should not reply on the values returned in the smMaxCPULoad and smCurCPULoad fields. To determine if it is safe to allocate a channel, simply try to allocate it with the SndNewChannel function. That function returns the appropriate result code if allocating the channel would put too much of a strain on CPU processing.
Listing 2-14 illustrates the use of SndManagerStatus. It defines a function that returns the number of sound channels currently allocated by all applications.

Listing 2-14 Determining the number of allocated sound channels

FUNCTION MyGetNumChannels: Integer;
VAR
   myErr:         OSErr;
   mySMStatus:    SMStatus;
BEGIN
   MyGetNumChannels := 0;
   myErr := SndManagerStatus (Sizeof(SMStatus), @mySMStatus);
   IF myErr = noErr THEN
      MyGetNumChannels := mySMStatus.smNumChannels;
END;

Determining and Changing the Status of the System Alert Sound

The enhanced Sound Manager includes two routines--SndGetSysBeepState and SndSetSysBeepState--that allow you to determine and alter the status of the system alert sound. You might wish to disable the system alert sound if you are playing sound and need to ensure that the sound you are playing is not interrupted. Currently, two states are defined:

CONST
   sysBeepDisable       = $0000;    {system alert sound disabled}
   sysBeepEnable        = $0001;    {system alert sound enabled}
You can determine the status of the system alert sound like this:

SndGetSysBeepState(currentState);
And you can disable the system alert sound like this:

myErr := SndSetSysBeepState(sysBeepDisable);
When the system alert sound is disabled, the Sound Manager effectively ignores all calls to the SysBeep procedure. No sound is created and the menu bar does not flash. Also, no resources are loaded into memory.

Note
Even when the system alert sound is enabled, it's possible that the system alert sound will not play; for example, the speaker volume might be set to 0, or playing the requested system alert sound might require too much CPU time. In such a case, the menu bar flashes.
By default, the system alert sound is enabled. If you disable the system alert sound so that your application can play a sound without being interrupted, be sure to enable the sound when your application receives a suspend event or when the user quits your application.


Previous Book Contents Book Index Next

© Apple Computer, Inc.
2 JUL 1996