`Volumes I, II, and III
`
`A
`VV
`
`Addison-Wesley Publishing Company, Inc.
`Reading, Massachusetts Menlo Park, California New York
`Don Mills, Ontario Wokingham, England Amsterdam Bonn
`Sidney Singapore Tokyo Madrid San Juan Paris
`Seoul Milan Mexico City Taipei
`
`Page 00001
`
`
`
`Apple, the Apple logo, LaserWn'ter, and Lisa are registered trademarks of A
`pple Computer, Inc.
`Macintosh, the Macintosh logo, MacWrite, MacPaint, MacDraw,
`and MacWorks are trademarks of
`Apple Computer, Inc.
`
`Simultaneously published in the United States and Canada.
`
`Written by Caroline Rose with B
`Steve Chemicoff, Chris B
`Sandy Tompkins
`
`Katie Withey, Mark Metzler,
`and Brian Howard, assisted by
`to Cary Clark and Scott Knaster.
`This book was produced using the Apple Macintosh computer and the LaserWn'ter printer.
`ISBN 0-201-17737-4
`
`l2 l3 l4 l5 l6 l7 l8—MU—959493929l
`Twelfth printing, October 1991
`
`Page 00002
`
`
`
`1 You should already be familiar with:
`
`I events, as discussed in chapter 8 of Volume I
`
`I the Memory Manager
`
`I the use of devices and device drivers, as described in chapter 6
`
`1 ABOUT THE SOUND DRIVER
`
`‘: The Sound Driver is a standard Macintosh device driver in ROM that's used to synthesize sound.
`You can generate sound characterized by any lcind of waveform by using the three different
`sound synthesizers in the Sound Driver:
`
`I The four-tone" synthesizer is used to make simple harmonic tones, with up to four
`"voices" producing sound simultaneously; it requires about 50% of the microprocessor's
`attention during any given time interval.
`
`I The square-wave synthesizer is used to produce less harmonic sounds such as beeps,
`and requires about 2% of the processors time.
`'
`
`L
`
`I The free-form synthesizer is used to make complex music and speech; it requires about
`20% of the processor's time.
`
`The Macintosh XL is equipped only with a square-wave synthesizer, all infonnation in this
`chapter about four-tone and free-form sound applies only to the Macintosh 128K and 512K.
`
`figure 1 depicts the waveform of a typical sound wave, and the terms used to describe it. The
`magnitude is the vertical distance between any given point on the wave and the horizontal line
`‘ . ut which the wave oscillates; you can think of the magnitude as the volume level. The
`*1 a plitude is the maximum magnitude of a periodic wave. The wavelength is the horizontal
`xtent of one complete cycle of the wave. Magnitude and wavelength can be measured in any
`mu '1: of distance. The period is the time elapsed during one complete cycle of a wave. The
`:: quency is the reciprocal of the period, or the number of cycles per second—also called hertz
`S
`2). The phase is some fraction of a wave cycle (measured from a fixed point on the wave).
`
`ere are many different types of waveforms, three of which are depicted in Figure 2. Sine
`aves are generated by objects that oscillate periodically at a single frequency (such as a tuning
`yrk). Square waves are generated by objects that toggle instantly between two states at a single
`~n
`- - uency (such as an electronic "beep"). Free-form waves are the most common of all, and are
`-- - rated by objects that vibrate at rapidly changing frequencies with rapidly changing
`"' Z gnitudes (such as your vocal cords).
`
`
`
`.l3AlJ(Ipunogg
`
`About the Sound Driver II-223
`
`Page 00003
`
`
`
`Inside Macintosh
`
`period T (sec)
`,v———— wavelength :——_
`
`frequency .f (Hz) := 1
`T
`
`amplitude
`
`magnitude
`
`'=————— one cycle——-—"
`
`Figure 1. Waveform
`
`%“o%u“a“a“a”
`
`sine wave
`
`square wave
`
`free-form wave
`
`Figure 2. Types of Waveforms
`
`Figure 3 shows analog and digital representations of a waveform. The Sound Driver represents
`waveforms digitally, so all waveforms must be converted from their analog representation to a
`digital representation. The rows of numbers at the bottom ofthe figure are digital representations
`of the waveform. The numbers in the upper row are the magnitudes relative to the horizontal
`zero-magnitude line. The numbers in the lower row all represent the same relative magnitudes,
`but have been normalized to positive numbers; you'll use numbers like these when calling the
`Sound Driver.
`
`II-224 About the Sound Driver
`
`Page 00004
`
`
`
`
`
`amp!itude C
`
`EEEEEEEEEEEEEEEE
`----------------
`IIIIIIIIIIIIIIII
`IIIIIIIIIIIIIIII
`IIIIIIIIIIIIIIII
`IIIIIIIIIIIIIIII
`IIIIIIIIIIIIIIII
`IIIIIIIIIIIIIIII
`0 3 5 6 7 6 5 3 O '3'5'6'7'6'5'3 0
`710121314131Z10 7 4 21012 4 7
`
`analog representation
`
`digital representations
`
`Figure 3. Analog and Digital Representations of a Waveform
`
`SOUND DRIVER SYNTHESIZERS
`
`A description of the sound to be generated by a synthesizer is contained in a data structure called a
`synthesizer buffer. A synthesizer buffer contains the duration, pitch, phase, and waveform of
`the sound the synthesizer will generate. The exact structure of a synthesizer buffer differs for
`each type of synthesizer being used. The first word in every synthesizer buffer is an integer that
`identifies the synthesizer, and must be one of the following predefined constants:
`
`CONST swMode
`ftMode
`ffldode
`
`{square-wave synthesizer}
`-1;
`1; {four-tone synthesizer}
`0;
`{free-form synthesizer}
`
`Sguare-Wave Synthesizer
`
`The square-wave synthesizer is used to make sounds such as beeps. A square-wave synthesizer
`buffer has the following structure:
`
`TYPE SWSynthRec = RECORD
`INTEGER;
`mode:
`triplets: Tones
`END;
`
`{always swnode}
`{sounds}
`
`SWSynthPtr = "SWSynthRec;
`
`Tones
`Tone
`
`ARR.AY[0 . . 5000] OF Tone;
`RECORD
`
`count:
`amplitude:
`duration:
`END,’
`
`INTEGER;
`INTEGER;
`INTEGER
`
`{frequency}
`Iamplitude, 0-255}
`[duration in ticks}
`
`Sound Driver Synthesizers II-225
`
`
`
`ratpqpunos3
`
`Page 00005
`
`
`
`Inside Macintosh
`
`Each tone triplet contains the count, amplitude, and duration of a different sound. You can store
`as many triplets in a synthesizer buffer as there's room for.
`
`The count integer can range in value from 0 to 65535. The actual frequency the count
`corresponds to is given by the relationship:
`
`frequency (Hz) = 783360 / count
`
`A partial list of count values and corresponding frequencies for notes is given in the summary at
`the end of this chapter.
`
`The type Tones is declared with 5001 elements to allow you to pass up to 5000 sounds (the last
`element must contain 0). To be space—efficient, your application shouldn't declare a variable of
`type Tones; instead, you can do something like this:
`
`VAR myPtr: Ptr;
`my!-landle: Handle ;
`mySWPtr : SWSynthPtr;
`
`{allocate space for the buffer}
`myflanclle :- NewHandle(buffSize);
`{lock the buffer}
`HLock(myHandle) .'
`{dereference the handle}
`myPtr := mg/Handle‘;
`{coerce type to SWsynthPtr}
`mySWPtr
`:= sWSynthPtr(myPtr),-
`{identify the synthesizer}
`mySWPtr".mode := swMode;
`{fill the buffer with values }
`mySWPtr".triplets[0] .count
`{ describing the sound}
`.
`.
`.
`Startsound(myPtr,buffSize,POINTER(-1) );
`{produce the sound}
`I-lUnlock(myHandle)
`{unlock the buffer}
`
`:= 2;
`
`where buffSize contains the number of bytes in the synthesizer buffer. This example
`dereferences handles instead of using pointers directly, to minimize the number of nonrelocatable
`objects in the heap.
`
`Assembly-language note: The global variable CurPitch contains the current value of
`the count field.
`
`The amplitude can range from 0 to 255. The duration specifies the number of ticks that the sound
`will be generated.
`
`The list of tones ends with a triplet in which all fields are set to 0. When the square—wave
`synthesizer is used, the sound specified by each triplet is generated once, and then the synthesizer
`stops.
`
`Four-Tone Synthesizer
`
`The four-tone synthesizer is used to produce harmonic sounds such as music. It can
`simultaneously generate four different sounds, each with its own frequency, phase, and
`waveform.
`
`II-226 Sound Driver Synthesizers
`
`Page 00006
`
`
`
`FTSynthPtr = "FTSynthRec;
`
`The sndRec field points to a four-tone record, which describes the four tones:
`
`TYPE FTSoundRec = RECORD
`
`'
`
`INTEGER;
`duration:
`Fixed;
`soundlRate:
`LONGINT;
`soundlPhase:
`Fixed;
`sound2Rate:
`sound2Phase: LONGINT;
`sound3Rate:
`Fixed;
`sound3Phase: LONGINT;
`sound4Rate:
`Fixed;
`sound4Phase: LONGINT;
`soundlwave : WavePt 1:;
`sound2Wave : WavePt r;
`sound3Wave : WavePt r;
`sound4Wave : WavePtr
`END;
`
`(duration in ticks}
`{tone 1 cycle rate}
`{tone 1 byte offset}
`{tone
`cycle rate}
`{tone
`byte offset}
`{tone
`cycle rate}
`{tone
`byte offset}
`{tone
`cycle rate}
`{tone
`byte offset}
`{tone
`waveform}
`{tone
`waveform}
`{tone
`waveform}
`{tone
`waveform}
`
`nl>(IJh)l-‘uh-nb(40(a)l\)l\)
`
`F'1‘SndRecPtr = "FTSoundRec :
`
`Wave
`WavePtr
`
`= PACKED ARRAY[0. .255] OF Byte;
`= “Wave;
`
`Assembly-language note: The address of the four-tone record currently in use is
`stored in the global variable SoundPI:r.
`
`The duration integer indicates the number of ticks that the sound will be generated. Each phase
`long integer indicates the byte within the waveform description at which the synthesizer should
`begin producing sound (the first byte is byte number 0). Each rate value determines the speed at
`which the synthesizer cycles through the waveform, from 0 to 255.
`
`The four-tone synthesizer creates sound by starting at the byte in the waveform description
`specified by the phase, and skipping ahead the number of bytes specified by the rate field every
`44.93 microseconds; when the time specified by the duration has elapsed, the synthesizer stops.
`The rate field determines how the waveform will be "sampled", as shown in Figure 4. For
`nonperiodic waveforms, this is best illustrated by example: If the rate field is 1, each byte value
`in the waveform will be used, each producing sound for 44.93 microseconds. If the rate field is
`0.1, each byte will be used 10 times, each therefore producing sound for a total of 449.3
`microseconds. If the rate field is 5, only every fifth byte in the waveform will be sampled, each
`producing sound for 44.93 microseconds.
`
`
`
`.!GAlJ([punog3
`
`Sound Driver Synthesizers II-227
`
`Page 00007
`
`
`
`Inside Macintosh
`
`If the wavefomi contains one wavelength, the frequency that the rate corresponds to is given by:
`
`frequency (Hz) = 1000000 / (44.93 / (rate/256))
`
`You can use the Toolbox Utility routines FixMul and FixRatio to calculate this, as follows:
`
`frequenc
`
`:= FixMul(rate,FixRatio(22257,256))
`
`The maximum rate of 256 conesponds to approximately 22.3 kilohertz if the waveform contains
`one wavelength, and a rate of 0 produces no sound. A partial list of rate values and
`corresponding frequencies for notes is given in the summary at the end of this chapter.
`
`Free-Form Synthesizer
`
`The free-form synthesizer is used to synthesize complex music and speech. The sound to be
`produced is represented as a waveform whose complexity and length are limited only by available
`memory.
`
`A free—form synthesizer buffer has the following structure:
`
`TYPE FFSynthRec = RECORD
`INTEGER;
`mode:
`Fixed;
`count:
`waveBytes: Freewave
`END;
`
`{always ffMode}
`{"sampling" factor}
`{waveform description}
`
`FFSynthPtr
`
`"FFSynthRec;
`
`Freewave
`
`= PACKED ARRAY[0..30000] OF Byte;
`
`The type FreeWave is declared with 30001 elements to allow you to pass a very long waveform.
`To be space-efficient, your application shouldn't declare a variable of type FreeWave; instead,
`you can do something like this:
`
`VAR nwPtr: Ptr;
`myfiandlez Handle;
`myFFPtr: FFSynthPtr;
`
`myHandle :— NewHand1e(buffsize);
`HLock(myHandle):
`myPtr := myfiandle“;
`myFFPtr
`:= FFSynthPtr(myPtr);
`myFFPtr‘.mode := ffMode;
`myFFPtr“.count
`:= FixRatio(1,1);
`myFFPtr“.waveBytes[0]
`:= 0;
`
`{allocate space for the buffer}
`{lock the buffer}
`{dereference the handle}
`{coerce type to FFSynthPtr}
`{identify the synthesizer)
`{fill the buffer with values }
`{ describing the sound}
`
`StartSound(myPtr,buffsize,POINTER(-1));
`HUnlock(myHand1e)
`
`{produce the sound}
`{unlock the buffer}
`
`where buffSize contains the number of bytes in the synthesizer buffer. This example
`dereferences handles instead of using pointers directly, to minimize the number of nonrelocatable
`objects in the heap.
`
`II-228 Sound Driver Synthesizers
`
`Page 00008
`
`
`
`119:3
`
`original wave
`
`flgfi
`
`rate field = 1
`
`M r
`
`ate field = 2
`
`
`
`.lO‘\iJ(]punng3
`
`rate field = .5
`
`Figure 4. Effect of the Rate Field
`
`Sound Driver Synthesizers II-229
`
`Page 00009
`
`
`
`Inside Macintosh
`
`The free—forrn synthesizer creates sound by starting at the first byte in the waveform and skipping
`ahead the number of bytes specified by count every 44.93 microseconds. The count field
`determines how the waveform will be "sampled"; it's analogous to the rate field of the four-tone
`synthesizer (see Figure 4 above). When the end of the waveform is reached, the synthesizer will
`stop.
`
`For periodic waveforms, you can determine the frequency of the wave cycle by using the
`following relationship:
`
`frequency (Hz) = 1000000 / (44.93 4* (wavelength/count))
`
`You can calculate this with Toolbox Utility routines as follows:
`
`frequency := E‘ixMu'l(count,FixRatio(22257,wavelength))
`
`The wavelength is given in bytes. For example, the frequency of a wave with a 100—byte
`wavelength played at a count value of 2 would be approximately 445 Hz.
`
`USING THE SOUND DRIVER
`
`The Sound Driver is opened automatically when the system starts up. Its driver name is
`'.Sound', and its driver reference number is -4. To close or open the Sound Driver, you can use
`the Device Manager Close and Open functions. Because the driver is in ROM, there's really no
`reason to close it.
`
`To use one of the three types of synthesizers to generate sound, you can do the following: Use
`the Memory Manager function NewHandle to allocate heap space for a synthesizer buffer; then
`lock the buffer, fill it with values describing the sound, and make a StartSound call to the Sound
`Driver. StartSound can be called either synchronously or asynchronously (with an optional
`completion routine). When called synchronously, control returns to your application after the
`sound is completed. When called asynchronously, control returns to your application
`immediately, and your application is free to perform other tasks while the sound is produced.
`
`To produce continuous, unbroken sounds, it's sometimes advantageous to preallocate space for
`all the synthesizer buffers you require before you make the first StartSound call. Then, while one
`asynchronous Startsound call is being completed, you can calculate the waveform values for the
`next call.
`
`To avoid the click that may occur between Startsound calls when using the four-tone synthesizer,
`set the duration field to a large value and just change the value of one of the rate fields to start a
`new sound. To avoid the clicks that may occur during four-tone and free-forrn sound generation,
`fill the waveform description with multiples of 740 bytes.
`
`Warning: The Sound Driver uses interrupts to produce sound. If other device drivers are
`in use, they may turn off interrupts, making sound production unreliable. For instance, if
`the Disk Driver is accessing a disk during sound generation, a "crackling" sound may be
`produced.
`
`To determine when the sound initiated by a Startsound call has been completed, you can poll the
`SoundDone function. You can cancel any current StartSound call and any pending asynchronous
`Startsound calls by calling StopSound. By calling GetSoundVol and SetSoundVol, you can get
`and set the current speaker volume level.
`
`II-230 Sound Driver Synthesizers
`
`Page 00010