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.
Playing Notes
You can play notes one at a time by using theSndDoCommand
orSndDoImmediate
function to issuefreqDurationCmd
sound commands. A sound plays for a specified duration at a specified frequency. You can play sounds defined by any of the three sound data formats. If you play wave-table data or sampled-sound data, then a voice must previously have been installed in the channel. (See "Installing Voices Into Channels" on page 2-43 for instructions on installing wave tables and sampled sounds as voices.)You can also play notes by issuing the
freqCmd
command, which is identical to thefreqDurationCmd
command, except that no duration is specified when you issuefreqCmd
.
The structure of a
- Note
- A
freqDurationCmd
command might in certain cases continue playing until another command is available in the sound channel. Therefore, to play a single note for a specified duration, you should issuefreqDurationCmd
followed immediately byquietCmd
. See "Stopping Sound Channels" on page 2-28 for further details onquietCmd
.freqDurationCmd
command is slightly different from that of most other sound commands. Theparam1
field contains the duration of the sound, specified in half-milliseconds. A value of 2000 represents a duration of 1 second. The maximum duration is 32,767, or about 16 seconds, in Sound Manager versions 2.0 and earlier; the maximum duration in Sound Manager version 3.0 and later is 65,536, or about 32 seconds. Theparam2
field specifies the frequency of the sound. The frequency is specified as a MIDI note value (that is, a value defined by the established MIDI standard). Listing 2-15 uses thefreqDurationCmd
command in a way that ensures the sound stops after the specified duration.Listing 2-15 Using the
freqDurationCmd
command
PROCEDURE MyPlayFrequencyOnce (mySndChan: SndChannelPtr; myMIDIValue: Integer; milliseconds: Integer); CONST kNoWait = TRUE; {add now to full queue?} VAR mySndCmd: SndCommand; {a sound command} myErr: OSErr; BEGIN {Start the sound playing.} WITH mySndCmd DO BEGIN cmd := freqDurationCmd; {play for period of time} param1 := milliseconds * 2; {half-milliseconds} param2 := myMIDIValue; {MIDI value to play} END; myErr := SndDoCommand(mySndChan, mySndCmd, NOT kNoWait); IF myErr <> noErr THEN DoError(myErr) ELSE BEGIN {ensure that sound stops} WITH mySndCmd DO BEGIN cmd := quietCmd; {stop playing sound} param1 := 0; {unused with quietCmd} param2 := 0; {unused with quietCmd} END; myErr := SndDoCommand(mySndChan, mySndCmd, NOT kNoWait); IF myErr <> noErr THEN DoError(myErr); END; END;Table 2-2 shows the decimal values that can be sent with afreqDurationCmd
orfreqCmd
command. Middle C is represented by a value of 60 and is defined by a special Sound Manager constant.
CONST kMiddleC = 60; {MIDI note value for middle C}Other specifiable frequencies correspond to MIDI note values.You can play square-wave and wave-table data at these frequencies only. If you are playing a sampled sound, however, you can modify the
sampleRate
field of the sound header to play a sound at an arbitrary frequency. To do so, use the following formula:new sample rate = (new frequency / original frequency) * original sample rate
where the new and original frequencies are measured in hertz. To convert a MIDI value to hertz for use in this formula, note that middle C is defined as 261.625 Hz and that the ratio between the frequencies of consecutive MIDI values equals the twelfth root of 2, defined by the constant
twelfthRootTwo
.
CONST twelfthRootTwo = 1.05946309434;You can rest a channel for a specified duration by issuing a
- IMPORTANT
- When calculating with numbers of type
Fixed
, pay attention to possible overflows. The maximum value of a number of typeFixed
is 65,535.0. As a result, some sample rates and pitches cannot be specified. Sound Manager version 3.0 fixes these overflow problems.restCmd
command. The duration, specified in half-milliseconds, is passed in theparam1
field of the sound command.Installing Voices Into Channels
You can play frequencies defined by any of the three sound data types. By playing a frequency defined by wave-table or sampled-sound data, you can achieve a different sound than by playing that same frequency using square-wave data. For example, you might wish to play the sound of a dog's barking at a variety of frequencies. To do that, however, you need to install a voice of the barking into the sound channel to which you want to sendfreqCmd
orfreqDurationCmd
commands.You can install a wave table into a channel as a voice by issuing the
waveTableCmd
command. Theparam1
field of the sound command specifies the length of the wave table, and theparam2
field is a pointer to the wave-table data itself. Note that the Sound Manager resamples the wave table so that it is exactly 512 bytes long.You can install a sampled sound into a channel as a voice by issuing the
soundCmd
command. You can either issue this command from your application or put it into an'snd '
resource. If your application sends this command,param2
is a pointer to the sampled sound locked in memory. IfsoundCmd
is contained within an'snd '
resource, the high bit of the command must be set. To use a sampled-sound'snd '
as a voice, first obtain a pointer to the sampled sound header locked in memory. Then pass this pointer inparam2
of asoundCmd
command. After using the sound, your application is expected to unlock this resource and allow it to be purged.Listing 2-16 demonstrates how you can use the
soundCmd
command to install a sampled sound in memory as a voice in a channel.Listing 2-16 Installing a sampled sound as a voice in a channel
FUNCTION MyInstallSampledVoice (mySndHandle: Handle; mySndChan: SndChannelPtr): OSErr; VAR mySndCmd: SndCommand; {a sound command} mySndHeader: SoundHeaderPtr; {sound header from resource} BEGIN {get pointer to sound header} mySndHeader := MyGetSoundHeader(mySndHandle); WITH mySndCmd DO BEGIN cmd := soundCmd; {install sampled voice} param1 := 0; {ignored with soundCmd} param2 := LongInt(mySndHeader); {store sound header location} END; IF mySndHeader = NIL THEN {check for defective handle} MyInstallSampledVoice := badFormat ELSE {install sound as voice} MyInstallSampledVoice := SndDoImmediate(mySndChan, mySndCmd); END;Listing 2-16 relies on theMyGetSoundHeader
function to obtain a pointer to the sound header within the sound handle. That function is defined in "Obtaining a Pointer to a Sound Header" on page 2-57 and returnsNIL
if the sound handle does not include a sound header. Note that theMyGetSoundHeader
function locks the sound handle in memory so that the pointer to the sound header remains valid. When you are done with the sound channel in which you have installed the sampled sound, you should unlock the sound handle and make it purgeable so that it does not waste memory.Looping a Sound Indefinitely
If you install a sampled sound as a voice in a channel and then play the sound using afreqCmd
orfreqDurationCmd
command that lasts longer than the sound, the sound will ordinarily stop before the end of the time specified by thefreqCmd
orfreqDurationCmd
command. Sometimes, however, this might not be what you'd like to have happen. For example, you might have recorded the sound of a violin playing and then stored that sound in a resource so that you could play the sound of a violin at a number of different frequencies. Although you could record the sound so that it is long enough to continue playing through the longestfreqCmd
orfreqDurationCmd
command that your application might require, this might not be practical. Fortunately, the Sound Manager provides a mechanism that allows you to repeat sections of sampled sound after the sound has finished playing once completely.When you use the
freqDurationCmd
command with a sampled sound as the voice,freqDurationCmd
starts at the beginning of the sampled sound. If necessary to achieve the desired duration of sound, the command replays that part of the sound that is between the loop points specified in the sampled sound header. Note that any sound preceding or following the loop points will not be replayed. There must be an ending point for the loop specified in the header in order forfreqDurationCmd
to work properly.Listing 2-17 Looping an entire sampled sound
PROCEDURE MyDoLoopEntireSound (sndHandle: Handle); VAR mySndHeader: SoundHeaderPtr; {sound header from resource} myTotalBytes: LongInt; {bytes of data to loop} BEGIN mySndHeader := MyGetSoundHeader(sndHandle); IF mySndHeader <> NIL THEN BEGIN {compute bytes of sound data} CASE mySndHeader^.encode OF stdSH: {standard sound header} WITH mySndHeader^ DO myTotalBytes := mySndHeader^.length; extSH: {extended sound header} WITH ExtSoundHeaderPtr(mySndHeader)^ DO myTotalBytes := numChannels * numFrames * (sampleSize DIV 8); cmpSH: {compressed sound header} WITH CmpSoundHeaderPtr(mySndHeader)^ DO myTotalBytes := numChannels * numFrames * (sampleSize DIV 8); END; WITH mySndHeader^ DO BEGIN {set loop points} loopStart := 0; {start with first byte} loopEnd := myTotalBytes - 1; {end with last byte} END; END; END;Listing 2-17 uses theMyGetSoundHeader
function defined in "Obtaining a Pointer to a Sound Header" on page 2-57. Note that the formula for computing the length of a sound depends on the type of sound header. Also, while the formula is the same for both an extended and a compressed sound header, you must write code that differentiates between the two types of sound headers because thesampleSize
field is not stored in the same location in both sound headers.