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
- whether a machine can produce stereophonic sounds
- what version of the Sound Manager is available
- whether a machine can play multiple channels of sound, and whether it can take advantage of the enhanced Sound Manager's play-from-disk capabilities
- whether a sound playing from disk is active or paused
- how many channels of sound are currently open
- whether the system beep has been disabled
Gestalt
function and Sound Manager routines to determine these types of information.Obtaining Information About Available Sound Features
You can use theGestalt
function to obtain information about a number of hardware- and software-related sound features. For instance, you can useGestalt
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 callGestalt
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 useGestalt
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 thegestaltSoundAttr
selector.
CONST gestaltSoundAttr = 'snd '; {sound attributes}IfGestalt
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 byGestalt
:
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 bitgestaltStereoCapability
isTRUE
, the built-in hardware can play stereo sounds. The bitgestaltStereoMixing
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 theGestalt
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 thegestaltHasSoundInputDevice
bit. To determine whether a built-in sound input device is available, you can test thegestaltBuiltInSoundInput
bit. ThegestaltSoundIOMgrPresent
bit indicates whether the sound input routines are available. Because thegestaltHasSoundInputDevice
bit is not set if the routines are not available, only sound input device drivers should need to use thegestaltSoundIOMgrPresent
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, theGestalt
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 byGestalt
.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;TheMyHasEnhancedSoundManager
function defined in Listing 2-10 relies on theMyTrapAvailable
function, which is an application-defined routine provided in Inside Macintosh: Operating System Utilities. If the_SoundDispatch
trap is not available, theSndSoundManagerVersion
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 theGestalt
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, usingGestalt
, 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 theGestalt
function with thegestaltHardwareAttr
selector and thegestaltHasASC
bit. Listing 2-11 combines these two tests into a single routine that returnsTRUE
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 functionMyCanPlayMultiChannels
first tries to get the desired information by calling theGestalt
function with thegestaltSoundAttr
selector. IfGestalt
returns successfully and thegestaltMultiChannels
bit is set in theresponse
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, thenMyCanPlayMultiChannels
calls theGestalt
function with thegestaltHardwareAttr
selector. If the computer contains the Apple Sound Chip, then again multichannel play capability is present.
You could write a similar function to test for the ability to initiate a play from disk. Listing 2-12 shows an example.
- 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.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 theSndChannelStatus
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 useSndChannelStatus
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 callingSndChannelStatus
. 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;ThescStartTime
,scEndTime
, andscCurrentTime
fields are 0 unless the Sound Manager is currently playing from disk through the specified channel. If a play from disk is occurring, thescStartTime
andscEndTime
fields reflect the starting and ending points of the play, defined in seconds; thescCurrentTime
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 thescStartTime
andscEndTime
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 thescStartTime
field from that in thescCurrentTime
field. However, because the Sound Manager updates the value of thescCurrentTime
field only periodically, you should not rely on the accuracy of its value.The
scChannelBusy
andscChannelPaused
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 bothscChannelBusy
andscChannelPaused
areFALSE
, 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}ThescCPULoad
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 thescChannelPaused
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 thescChannelPaused
field. ThescChannelPaused
field isTRUE
only if the Sound Manager is executing a disk-based playback on the channel and that playback is paused by theSndPauseFilePlay
function. This problem is fixed in Sound Manager versions 3.0 and later.Obtaining Information About All Sound Channels
You can use theSndManagerStatus
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 theSndManagerStatus
function is a pointer to a Sound Manager status record, which you must allocate before callingSndManagerStatus
. 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;ThesmNumChannels
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 theSndNewChannel
function and not yet disposed.The Sound Manager uses information that it returns in the
smMaxCPULoad
andsmCurCPULoad
fields to help it determine whether it can allocate a new channel when your application calls theSndNewChannel
function. The Sound Manager setssmMaxCPULoad
to a default value of 100 at startup time, and thesmCurCPULoad
field reflects the approximate percentage of CPU processing power currently taken by allocated sound channels.
Listing 2-14 illustrates the use of
- WARNING
- Your application should not reply on the values returned in the
smMaxCPULoad
andsmCurCPULoad
fields. To determine if it is safe to allocate a channel, simply try to allocate it with theSndNewChannel
function. That function returns the appropriate result code if allocating the channel would put too much of a strain on CPU processing.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
andSndSetSysBeepState
--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 theSysBeep
procedure. No sound is created and the menu bar does not flash. Also, no resources are loaded into memory.
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.
- 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.