As I listened to my Atari play a new song that I had entered from a magazine listing, I could hear that some of the notes were not quite right. The music extended into the third octave above middle C, and though the tune was recognizable, some of the notes were off pitch enough to make listening to the tune unpleasant. I decided that it was time for me to investigate 16-bit music. What I discovered was not only that the accuracy of the notes could be improved dramatically, but also that the effective range could be more than doubled.
Before we discuss 16-bit music, let's take a look at what is happening when we use the SOUND statement, or in other words, eight-bit sound, in Atari BASIC. The following registers in the POKEY chip are used for sound generation:
AUDF1 (53760) - Audio Frequency Register 1
AUDC1 (53761) - Audio Control Register 1
AUDF2 (53762) - Audio Frequency Register 2
AUDC2 (53763) - Audio Control Register 2
AUDF3 (53764) - Audio Frequency Register 3
AUDC3 (53765) - Audio Control Register 3
AUDF4 (53766) - Audio Frequency Register 4
AUDC4 (53767) - Audio Control Register 4
AUDCTL (53768) - Audio Mode Control Register
The audio control registers are used to set volume (low order four bits) and sound content (high order bits). Thus there are 16 different volume settings and a variety of sounds available. For this discussion we are concerned only with pure tones, corresponding to SOUND x,x,10,x.
the audio frequency registers are used to control the divide by N circuits. These circuits use the contents of the frequency registers to divide a "clock" frequency to produce different output frequencies. Since they are one-byte registers, they are referred to as eight-bit dividers. The output frequency is determined by the formula F0 = F/(2 × (AUDF + 1)), where F is the clock frequency and AUDF the value in the audio frequency register. With a normal clock rate of 64 kilohertz (or more exactly 63,921 cycles per second), the frequency range is about 125 hertz to 32 kilohertz.
The effective range for music is limited to about four octaves. This is because the tuning accuracy of notes being produced becomes progressively worse as the frequency gets higher. Figure 1 illustrates this very clearly. It shows how far out of tune, measured in "cents," each note in the four octave range is. (A cent is 1/100 of a half-step. A sound which is 50 cents sharp or flat is exactly halfway between two notes.) Notes which are less than ten cents out of tune are usually acceptable, though two notes played together could sound bad if their combined inaccuracy is too large. For example, if you play a note which is eight cents flat followed by a higher note which is eight cents sharp, the second note will probably sound out of tune.
Tuning inaccuracy results from having a limited number of values to use as dividers. With an eight-bit divider, only 256 unique frequencies can be produced. The A note in the fourth octave should be 440 cycles per second. To produce this note on the Atari, the number 72 is used as a divider. The resulting frequency is 437.8 hertz, whith is 8.6 cents flat. If instead we use a 71 as a divider, the output frequency is 443.9 hertz. This note is 15.3 cents sharp and is obviously a poorer choice than the note using 72. The choices become more restricted as the notes get higher. For the A note in the sixth octave, for example, 17 produces a note which is 15.3 cents sharp, while 18 produces a note 78.4 cents flat (closer to G# than A).
Luckily, the Atari provides a solution to this problem: 16-bit dividers. With a 16-bit divider 65,536 different output frequencies are possible. For example, to reproduce tha A in octave 6, we could use either 502 (1.8 cents flat) or 501 (1.6 cents sharp) and not able to hear any difference. Figure 2 shows how dramatically the range and accuracy are improved.
More accurate tuning does not come without a price. Sixteen-bit dividers are obtained by combining frequency registers: AUDF1 with AUDF2, or AUDF3 with AUDF4. This gives us a choice of one 16-bit and two eight-bit voices, or two 16-bit voices. We also cannot use the SOUND statement, even for the eight-bit voices, as it will confuse our settings for the 16-bit sound. As it turns out, this is not much of a problem since machine language routines to play the music are simple and have the added advantage of being faster than separate SOUND statements.
Now let's look at how 16-bit sound is set up. The audio mode control register has four bits for this purpose:
Bit 6—Clock channel 1 with 1.79 megahertz instead of 64 kilohertz Bit 5—Clock channel 3 with 1.79 megahertz Bit 4—Combine channels 1 and 2 Bit 3—Combine channels 3 and 4
The other bits in AUDCTL have no bearing on this discussion, so we will ignore them. If you are curious, see Chapters 2 and 3 the Hardware Manual.
The 1.79 megahertz (1.78979 megahertz, to be exact) clock rate is required to obtain the full range of output frequencies. The formula for determining output frequency is a little different: F0 = F/(2 × (AUDF + 7)). In this case, AUDF is the two-byte frequency register value. The second register of the pair is the low order byte, either AUDF2 or AUDF4. For example, to use 1049 as a divider with registers 1 and 2, we would POKE 4 in AUDF3 and 25 in AUDF1.
Now take a look at the BASIC 16-bit sound subroutines. The first plays one 16-bit and two eight-bit voices, and the second plays two 16-bit voices. Notice the SOUND 0,0,0,0 at the beginning of each routine. This statement must be included to initialize POKEY for sound. The POKE 53768,X initializes AUDCTL for 16-bit sound, either one or two voices. Remember that any SOUND statement executed later will reset this register to zero.
To use these subroutines, simply copy one or the other into your program and do a GOSUB 20100 once at the beginning of the program. Then, to play music, do the appropriate machine language call, X = USR(ADR(HF1$),N1,V1,N2,V2,N3,V3) OR X = USR(ADR(HF2$),N1,V1,N2,V2). Nx is the note to be played and Vx is the volume. N1 is the 16-bit voice in the three-voice routine. You don't need to pass parameters for unused voices. For example, if you want only the 16-bit voice in the three-voice routine, you can use X = USR(ADR(HF1$),N1,V1), but to use only an eight-bit voice you would have to use X = USR(ADR(HF1$),0,0,N2,V2).
The note tables give you the most accurate values for four octaves of eight-bit and nine octaves of 16-bit notes. In a practical sense, the first octave of 16-bit notes is not usable because there are some loud harmonics which tend to mask the actual note being played. You can get some good sounds if you hook up to a stereo amplifier, however. Notice that the eight-bit value for F# in the third octave is 172 rather than 173 as shown in the BASIC Reference Manual. 173 produces a note which is more than 12 cents flat, while the note from 172 is only 2.4 cents flat.
|C||27357||OCTAVE 1||C||848||30||OCTAVE 6|
|C||13675||OCTAVE 2||C||421||14||OCTAVE 7|
|C||6834||243||OCTAVE 3||C||207||OCTAVE 8|
|C||3414||121||OCTAVE 4||C||100||OCTAVE 9|
|C||1703||60||OCTAVE 5||C||46||OCTAVE 10|
20000 REM 16-BIT SOUND ROUTINE 1 20010 REM 20020 REM 1 16-BIT & 2 8-BIT VOICES 20030 REM 20040 REM X=USR(ADR(HF1$),N1,V1,N2,V2,N3,V3) 20050 REM 20100 SOUND 0,0,0,0:X=64+16:POKE 53768,X 20110 DIM HF1$(56):RESTORE 20140 20120 FOR I=1 TO 56:READ X:HF1$(I,I)=CHR$(X):NEXT I 20130 RETURN 20140 DATA 104,170,104,141,2,210,104,141,0,210,104,104,41,15,9,160,141,3,210 20150 DATA 224,2,240,32,104,104,141,4,210,104,104,41,15,9,160,141,5,210 20160 DATA 224,4,240,14,104,104,141,6,210,104,104,41,15,9,160,141,7,210,96
20000 REM 16-BIT SOUND ROUTINE 2 20010 REM 20020 REM 2 16-BIT VOICES 20030 REM 20040 REM X=USR(ADR(HF2$),N1,V1,N2,V2) 20050 REM 20100 SOUND 0,0,0,0:X=(64+16)+(32+8):POKE 53768,X 20110 DIM HF2$(41):RESTORE 20140 20120 FOR I=1 TO 41:READ X:HF2$(I,I)=CHR$(X):NEXT I 20130 RETURN 20140 DATA 104,170,104,141,2,210,104,141,0,210,104,104,41,15,9,160,141,3,210 20150 DATA 224,2,240,17 20160 DATA 104,141,6,210,104,141,4,210,104,104,41,15,9,160,141,7,210,96
Return to Table of Contents | Previous Section | Next Section