Apple II Technical Notes _____________________________________________________________________________ Developer Technical Support Apple IIGS #37: Free-Form Synthesizer Tips Revised by: Jim Mensch November 1988 Written by: Jim Mensch May 1988 This Technical Note is intended to help a person who is unfamiliar with the Apple IIGS Sound Tool Set use the Free-Form Synthesizer effectively. _____________________________________________________________________________ The primary function of the Free-Form Synthesizer is to allow an application program to start one or more complex digitized or computed waveforms playing on the Apple IIGS without further intervention from the application. The waveform is a series of bytes, each representing the amplitude of your outgoing sound at a particular moment in time (defined by the sampling frequency you set). After a call to FFStartSound, the Sound Tool Set takes care of all chores involved in loading the DOC RAM, setting up registers, and actually playing your sound. Once playing, your sound will continue until either the Sound Tool Set encounters a NIL pointer in the waveform list, or until you call FFStopSound. FFStartSound Parameters FFStartSound has only two parameters: the first a Word containing channel, generator, and mode information, and the second a Pointer to a parameter block. |15 |14 |13 |12 |11 |10| 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |___________| |__________| |___________| |___________| | | | | | | | | DOC channel number ($0-$1) | Reserved must be set to 0 | top 3 bits should be set to 0| | | Free-Form Synthesizer = $01 Generator number ($0-$E) Note Synthesizer = $02 Reserved = $03-$07 Application defined = $08-$0F Figure 1 - Channel-Generator-Mode Word The Channel-Generator-Mode Word is broken down into 4 nibbles. The low-order nibble specifies the particular synthesizer you are using. (Because this Note is only about the Free-Form Synthesizer, we will be using only a 1 in this nibble.) The adjacent nibble must be set to 0 for now. The next nibble specifies which generator to use. The IIGS has 15 generators from which to choose, and as the application designer, it is up to you to decide which one to use. It might be appropriate, however, to call FFGeneratorStatus first to ensure that the generator currently is available. (It could be in use already by a desk accessory or previously started sound.) The high-order nibble specifies which channel to use. The IIGS supports two separate sound channels for output. If you are using a stereo adapter, you could start up many sounds and route them to either channel 0 or channel 1 to get a full stereo effect. (The channel is ignored if you are not using a special piece of multi-channel hardware.) The parameter block contains parameters describing the sound and how it should be played. Here is a sample Pascal definition of that parameter block: FFParmBlock = record waveStart:Ptr; waveSize:Integer; freqOffset:Integer; DOCBuffer:Integer; { High order byte significant } bufferSize:Integer; { Low order byte significant } nextWave:^FFParmBlock; volSetting:Integer; end; The first parameter is a 4-byte address telling the Free-Form Synthesizer where in memory it can locate your sample data. The next parameter is a word specifying the number of 256-byte pages of sound you wish to play. The waveform data should be a series of bytes, each representing one sample. Wave tables must be exact multiples of 256 bytes. Note: A zero value in the waveform can cause a sound to stop, so be sure to check your data to ensure that this does not happen. The frequency offset parameter specifies the sampling frequency that the Free- Form Synthesizer should use during playback. This number can be computed by the following formula: freqOffset = ((32*Sample rate in Hertz)/1645) The frequency offset parameter is the most often misunderstood parameter, so I will explain a little about sampling rates. The sampling rate is how many samples (bytes) per second to play. If you have a digitized wave that represents 2 seconds of sound, and it takes up 44K of memory, then it was sampled at 22 kHz (which, by the way, is good for full sound reproduction). The sampling rate must be at least twice that of the maximum fundamental frequency you want to sample. However, for good sound reproduction, you may want to sample at least eight times the fundamental frequency in order to capture the higher harmonics of musical instruments and the human voice. The DOC starting address and buffer size tell the Free-Form Synthesizer which portion of the 64K sound RAM to use as a buffer during playback. The wave is taken from your waveform in chunks and placed in sound RAM for playback. Each time the buffer nears empty, it will need to be reloaded with more sound. The size of the buffer specified determines how often the Free-Form Synthesizer must interrupt the 65816 to reload the buffer. The buffer size must be a power of two because of the way the sound General Logic Unit (GLU) specifies addresses. (The value for this parameter must also be a power of two.) A good length to use would be at least 1/10 second of sound. For example, if you were using a sampling rate of 16 kHz (16,000 samples per second), you would want a buffer at least 2,048 bytes long, or about 8 pages. It does not hurt to round this number up. You manage the DOC RAM, so you should decide what memory to use. It is usually a good idea to have multiple buffers if you have a chain of waves. (I like leaving page zero free, as the Note Synthesizer uses the data in the first 256 bytes, and accidentally placing a zero in that page could cause it to fail.) The next wave pointer is a 4-byte pointer to the next parameter block. With this parameter you can string together many waveforms for more continuous sound, or you can make your sounds infinitely recursive by pointing back to the original wave form. The volume setting is a word which represents the relative playback volume. It can range from 0 to 255. Other Tips When you shut down the Sound Tool Set, it will stop all pending sounds, so be sure to leave ample time between starting and ending a sound. If you have a series of wave forms strung together, you can change their parameters on the fly. Changes take effect as soon as the waveform is started. (You could use this to find the correct sampling frequency of a wave, by having the next wave pointer point back to the start of your parameter block. This would cause the sound to play indefinitely. You then could change the freqOffset value, and the sound would change each time it is restarted.) Here is a sample code segment (in APW Assembler format) that creates a 1-kHz wave in memory sampled at 16 kHz and plays it: FFSound DATA theSound ds $2000 ; FFSound wave... MyFFRecord dc A4'theSound' ; address of wave dc i'$20' ; size of wave in pages.. Rate dc i'311' ; 16-kHz sample rate dc i'1' ; DOC starting address dc i'$0800' ; DOC buffer size dc a4'0' ; no next wave Vol1 dc i'$007F' ; kinda medium.. ; 1-kHz triangle wave sampled at 16 kHz one full segment oneAngle dc i1'$40,$50,$60,$70,$80,$90,$A0,$B0' dc i1'$C0,$B0,$A0,$90,$80,$70,$60,$50' End TestFF Start Using FFSound MakeWave ANop ldx #$0000 MW0010 txa ; get index and #$000F ; use just low nibble as index tay ; into triangle wave table lda oneAngle,y ; sta theSound,X ; and store it into sound buf inx inx cpx #$2000 ; we Done? blt MW0010 ; nope better finish PushWord #$0001 PushLong #MyFFRecord _FFStartSound rts end Further Reference o Apple IIGS Toolbox Reference, Volume 2