Async Sounds
Volume Number: 7
Issue Number: 3
Column Tag: Pascal Forum
Related Info: Sound Manager
Asynchronous Sounds
By Aaron Walsh, Chestnut Hill, MA
Note: Source code files accompanying article are located on MacTech CD-ROM or
source code disks.
[Aaron Walsh works in Information Processing Support (IPS) as a consultant
for Boston College. In addition to supporting the college’s computing community, Aaron
oversees the Advanced Technology Center (ATC).
The ATC provides faculty and staff with access to technology such as high
resolution scanners, large screen color monitors, film recording, and sound digitizing
for the vision impared. It also provides for the community help to evaluate some of the
industries most advanced personal workstations (ex. IBM RS6000, Apple Macintosh
IIFX).]
Introduction
This article will explain how to add sound to your application using the Sound
Manager. The Sound Manager replaces the Sound Driver, which was the original device
driver for the Macintosh Plus and the Macintosh SE. The Sound Manager is available in
System 6.0.x, providing a more flexible and convenient means of using sound on the
Macintosh. Complete Sound Manager documentation is available on AppleLink.
I will concentrate on the use of sound resources ( resources of type ‘snd ‘) and
the Sound Manager calls used to play these resources. Not only is this method simple,
but it is also extremely powerful in the sense that you can take ‘snd ‘ resources from
other applications and incorporate them into yours. Simply use ResEdit to copy the
‘snd ‘ resource to your application. Once you have the resource, you should be able to
play it to your heart’s content.
My program demonstrates the use of asynchronous sound. Playing sound
asynchronously allows your application to continue executing while the sound is being
generated. If the sound is not played asynchronously (that is, sychronously) the
application will wait until the entire sound has finished playing before executing the
next statement. This is fine if the sound is short, or you if you do indeed want the sound
played before continuing. However, this is next to worthless if you need the sound
generated con currently with graphics or other tasks (can you imagine Crystal Quest
seizing each time a Zap, Bong, or Gloink is made?!).
Please note that my code serves as a functional demonstration, not an iron clad
application. The only task performed while the sound plays is a flashing menu bar,
demonstrating asynchronous sound. Also, error checking is not terribly sophisticated.
I give you the necessary framework to build upon, not extra code to wade through. For
a fully functional application, complete with robust error checking and loaded with
comments, hop on AppleLink. In the sample code section of Macintosh Technical
Support is SoundApp. It was written by Jim Reekes, the same Apple employee who
wrote the Sound Manager documentation. Not only is this a thorough example of the
Sound Manager, but it also contains a good deal of information on known Sound Manager
bugs.
On with the show....
Structurally Sound
Before plunging headfirst into a long-winded Sound Manager discussion and code
examples, you may find it helpful (perhaps even necessary) to understand the basic
elements used in creating sound on the Macintosh. The process of creating sounds is
similar to the operation of an assembly line. The sound data is taken through various
stages of processing, massaged and tweaked along the way until it reaches its final
destination (usually the speaker, in some cases a MIDI device).
Sound resources ( resources of type ‘snd ‘, hereafter referred to as snd’s)
contain the data to be put on the assembly line. Snd’s can be created from scratch with
tools such as ResEdit, at the expense of your sanity. I recommend using a sound
digitizer, such as MacRecorder by Farallon. This allows you to record live and
pre recorded music directly into an snd resource. Very clean, very easy. You can also
edit the sound, adding special effects such as echo, bender, fade, etc., to produce custom
sounds and music.
Snd’s come in two formats. Format 1 was developed by Apple, specifically for
use with the Sound Manager. Format 2 was also designed by Apple, but for use with
HyperCard. The Sound Manager is capable of playing both formats, as my sample code
demonstrates. If you do not know what format your snd is, check the first word (in hex
format) of the resource with resource tool such as ResEdit. This word contains the snd
format (0001 = Format 1, 0002 = Format 2). MacRecorder is capable of creating
either format, although saving your sound directly into an snd resource will create
format 1.
An ‘snd ‘ resource contains sound commands, special instructions containing
information which alter the final sound. A sound command may be nothing more that
the description of a note to be played, or it may be an instruction used change the
characteristic of a sound already in progress (i.e. amplitude, timber, frequency, etc).
ormat) of the resource with resource tool such as ResEdit. This word contains the snd
format (0001 = Format 1, 0002 = Format 2). MacRecorder is capable of creating
either format, although saving your sound directly into an snd resource will create
format 1.
Each sound command is exactly eight bytes in length:
The first word is the command number, followed by six bytes that comprise the
sound command options. The pointer bit indicates that the snd contains both sound
commands and associated sound data (i.e. sampled sound or wave table data). A full
description of available sound commands is included in the Sound Manager
documentation.
Although an snd resource contains the needed sound commands to generate
synchronous sound, you can drop additional sound commands on the conveyor belt with
the SndDoCommand and SndDoImmediate calls. We will call SndDoCommand to add a
callback sound command to the process, which will facilitate the generation of
asynchronous sound. I’ll explain this in detail later.
The sound commands are held in memory in a structure called a channel. The
default sound channel is a queue capable of holding 128 sound commands, which are
passed one at time to a specific synthesizer. This sound channel provides interaction
between the application and the audio hardware. When you add a sound command as
described above, it is actually sent to the same sound channel in memory that holds the
snd’s sound commands.
The sound commands are then sent to a synthesizer. A synthesizer is much like a
device driver. It is responsible for interpreting the sound commands sent through the
sound channel and playing them on the hardware associated with it (usually the
speaker).
In short, you take a bunch of sound commands (from an snd resource) and cram
them into a sound channel. One at a time they are sent to a synthesizer where they are
interpreted and passed on to the speaker which makes the final sound [Figure 1]. Easy,
huh? How do you get all this to happen? I thought you might be wondering....
Figure 1.
Tools of the Trade
Now that you know the basic process that occurs when sound is played on the
Macintosh, lets explore the Toolbox calls that make it possible.
To produce synchronous sound you only need to use one call:
{1}
Function SndPlay (chan: SndChannelPtr; sndHdl: Handle; async:
Boolean) : OSErr;
If you pass NIL as the SndChannelPtr parameter, you will only be able to produce
synchronous sound (in this case the Boolean parameter is ignored, even if True is
passed). If you can at least pass the snd’s handle, you can produce sound:
{2}
Error := SndPlay(NIL, mySndHandle, FALSE);
In this case the sound must finish playing before execution resumes. A default
sound channel is created in the application’s heap, through which the sound commands
are passed to a synthesizer for processing. After the sound commands have been
processed and the sound has finished playing on the audio hardware, the memory used
for the default sound channel is freed.
To produce asynchronous sound, you must pass a channel pointer to SndPlay (NIL
won’t cut it) along with True as the Boolean parameter. Hmmmm....well, the first thing
you need to do is create a sound channel in memory:
{3}
Function SndNewChannel (VAR chan: SndChannelPtr; synth: Integer;
init: Longint; userRoutine: ProcPtr): OSErr;
Hold it! We only need a channel pointer, so why all the parameters? I thought
you might ask.
Turn the Channel, Please
To better understand the SndNewChannel call, we must have a more intimate
knowledge of the parameters it requires. Let’s begin with a more thorough explanation
of the synthesizer.
On the Macintosh, a synthesizer is actually a resource of type ‘snth’. It is code
that interprets the sound commands sent through the channel. Currently, the Sound
Manager offers four standard synthesizers, each with special capabilities that provide
different methods for expressing sound:
• The note synthesizer is capable of generating simple melodies and sounds. Only
one note at a time can be played.
• The wave-table synthesizer produces more complex sounds, based on the
description of a single wave cycle. Multipart music is possible using one
polyphonic channel or several monophonic channels.
• The MIDI synthesizer allows music to be played on an external synthesizer,
provided a Musical Instrument Data Interface (MIDI) adapter is connected to one
of the serial ports. Fully functional MIDI applications cannot be written using the
current Sound Manager due to bugs and limitations.
• The sampled-sound synthesizer will play back digitally pre recorded or
precomputed sounds. The sounds are passed to the synthesizer in sampled sound
buffers. These buffers can be played at different sampling rates to effect sound
pitch.
Since each ‘snth’ is a resource, a resource ID is used to distinguish between
them:
ID Synthesizer Target Macintosh
$0001 noteSynth general for any Macintosh
$0003 waveTableSynth general for any Macintosh
$0005 sampledSynth general for any Macintosh
$0006-$00FF reserved for Apple general for any Macintosh
$0100-$0799 free for Developer general for any Macintosh
$0801 noteSynth Mac w/Apple Sound Chip
$0803 waveTableSynth Mac w/Apple Sound Chip
$0805 sampledSynth Mac w/Apple Sound Chip
$0806-$08FF reserved for Apple Mac w/Apple Sound Chip
$0900-$0999 free for Developers Mac w/Apple Sound Chip
$1001 noteSynth Mac Plus and SE
$1003 waveTableSynth Mac Plus and SE
$1005 sampledSynth Mac Plus and SE
$1006-$10FF reserved for Apple Mac Plus and SE
$1100-$1199 free for Developers Mac Plus and SE
You can specify which synthesizer to link to your channel by supplying one of the
first three ID numbers ($0001 - $0005) as the second parameter to SndNewChannel.
However, if the snd resource already contains a synthesizer ID (those created with
MacRecorder do), you should pass zero (0) as the synth parameter. Only one
synthesizer may be linked to a channel at a time, although multiple channels may be
created and linked to the same synthesizer. This is because an active synthesizer
“controls” the sound hardware until the sound channel is disposed. Two synthesizers
vying for hardware control is sure to bring your Mac to its knees.
The Sound Manager will attempt to play your sound on the best equipment