do re bB
the BASICs of batari music

background
Music on the Atari 2600 is a little odd. To put it simply, the way an Atari makes sounds means it can't play the "normal" notes that make up Western music.

The good news is that batari BASIC makes it pretty easy to experiment with the sound capabilities of the 2600. It doesn't have built in sound commands, per se, but it's pretty easy to write the logic to set the 6 special TIA variables that control what's coming out of your TV speakers while your bB program is under way.

(Incidentally, everything in this page (except for the code samples) applies to people coding in ASM as well.)

The 2600 can play two tones at once. It has several different "voices" ("distortions") that either voice can be set to (some good for music, some good for sound or drum effects), and each voice is set to one of 32 values that control the pitch.

This page can't make a musician out of you...it will help if you know the very basics of music: what notes are, and how letters are used to describe notes and all that stuff. Also, this page will mostly discuss music, not ideas for Sound Effects, which have to share the same two voices.

theory
Why does an Atari play "different" notes than everything else? I'm no sound scientist, and not much of a musician, but here's the general idea as I understand it: the "pitch" of a tone (how high or low it sounds) depends on its "frequency". The higher the frequency, the higher the note sounds. If the frequency is twice as fast, the note is one octave higher (from middle C to high C, say.) The math for going from one to another can be complex -- see this page for an explanation. But the Atari takes a shortcut...it takes a base frequency and then divides it by between 1 and 32. So they don't quite "match up" to "real" notes, because it uses a different set of differences of the frequencies.

practice
There are 6 variables, 3 for each of the 2 voices. The ones for the first voice end in "0", the ones for the other voice end in "1":
AUDV0/AUDV1: volume. Set between 0 (for "off") and 15 (maximum)
AUDF0/AUDF1: frequency. Set between 0 ("higest") and 31 ("lowest")
AUDC0/AUDC1: channel. The "type" of voice, 0 to 15.

Here's how Glenn Saunders describes the various channels:
DECIMAL
DISTORTION
VALUE		WHAT IT SOUNDS LIKE
----------	---------------------
00 & 11 	TOTALLY SILENT
01		Buzzy tones
02		Carries distortion 1 downward into a rumble.
03		Flangy wavering tones, like a UFO
04 & 05		Pure tones
06 & 10		Inbetween pure tone and buzzy tones (Adventure death uses this)
			Maybe filters off the highs here
07 & 09		Reedy tones, much brighter, down to Enduro car rumble
08		White noise/explosions/lightning, jet/spacecraft engine
12 & 13		Pure tones, goes much lower in pitch than 04 & 05.
14		Electronic tones, mostly lows, extends to rumble.
15		Electronic tones, mostly highs, extends to rumble.
So as "Batari" puts it himself:
setting the values, for instance, by AUDV0=10:AUDC0=12:AUDF0=4 will produce a tone, and it will stay on until you set AUDV0=0. typically, a frame counter is set up that keeps sounds on for a specified number of frames (which occur 60 times a second.)

pitch picking
So, assuming you have a certain set of notes you'd like to hear on your Atari, what values should you push into AUDF#?

There are two ways to go about this. If you want to match the actual pitch (for example, if you were trying to use an Atari as an instrument to be played along with some other instruments), you might use Eckhard Stolberg's Atari 2600 VCS Sound Frequency And Waveform Guide. (local mirror) It tells you what frequencies to use for what notes on what channels and how far off your tuning will be.

However, most human ears (the ones without "perfect pitch") don't care what the actual frequencies are, just as long as the relative notes fit each other. Thomas Jentzsch made a rough but powerful tool called "Tune2600". Originally it was a DOS-ish command line tool but I've given it a new front end and put it on the web: webTune2600. It uses a Piano-like UI and, thanks to B. Watson, a guitar-based one as well.

Enter the notes you'd like to hear into the UI and then hit "Calculate Matching 2600 Values". You'll see a table like the following...
Note Optimal TIA Cents Off Dist
(AUDC_)
Pitch
(AUDF_)
c2 32.6 32.7 6.9% 6 30
d2 36.4 36.2 -9.8% 6 27
e2 40.7 40.6 -5.6% 6 24
g2 48.1 48.3 8.5% 6 20
One final thing: Europe and the USA use different standards for their TV pictures. The North American standard is "NTSC", and the European standard is "PAL". For music purposes, the important thing to know is that NTSC updates the screen (and therefore, bB does everything outside the kernal) at 60 frames a second, and PAL runs at 50 FPS. This means their tuning can be different. (Under "Options", Tune2600 can use either standard.)

got the beat
Once we know what distortion and frequency to use (and we pick a volume to play...I usually use 8 for AUDV0 or AUDV1, in the middle of the volume range) then we just need to know how long to play each note.

I'm goint to use the NTSC standard here, 60 FPS vs. PAL's 50. (PAL will tend to be a bit slower and lower if the programmer doesn't especialy code to compensate.)

60 FPS means that if we hold a tone for 60 frames (e.g.. calling bB's drawkernal routine) a note will last one second. One popular modern tempo for music is "120 BPM" or Beats Per Minute, a nice fast pace. So, lets do the simple math to see how many frames a beat will last:
X frames = 1 beat * 1 minute  * 60 seconds * 60 frames
                    120 beats    1 minute    1 second
In this case, X is 30. In other words, the number of frames for a single beat is (3600 / # of BPM). (300 / BMP for PAL.)

Of course, two notes in a row without a break between them will sound like one big note. Therefore, at least for repeated notes you may want to put in a period of silence between...say, 28 frames of AUDV0 = 8, then 5 frames of AUDV = 2 for silence.

all together now
Now we should be able to make a simple music engine in batari BASIC...here's a simple engine I came up with. The parts that we'll have to customize for the new music are in red.
 dim musicPointer=a
 dim musicTimer=b
 dim tempaudv=t

 rem VOLUME DOWN AND SET CHANNELS
 AUDV0=0
 AUDC0=DISTORTION

 rem INITIALIZE POINTERS AND TIMERS
 musicPointer=$FF
 musicTimer=0

startLoop
 rem TIME TO UPDATE NOTE?
 if musicTimer = 0 then gosub changeMusicNote
 musicTimer = musicTimer - 1

 drawscreen
 goto startLoop

changeMusicNote
  musicPointer = musicPointer + 1
  AUDF0 = musicData[musicPointer]

  tempaudv = 8
  if musicData[musicPointer] = $FF then tempaudv = 0
  AUDV0 = tempaudv

  musicPointer = musicPointer + 1
  musicTimer = musicData[musicPointer]
  rem value is (2 * #_OF_NOTES) - 1
  if musicPointer > (2 * #_OF_NOTES) - 1 then musicPointer = #-1
 return

 data musicData
 LIST OF FREQUENCY, LENGTH
end
(With this engine, putting "-1" for a note's frequency means "silence", setting AUDV0 to 0.)

So, lets pick a simple tune...previously we used webTune2600 to get c, d, e, and g, which are the notes we need for "Mary Had A Little Lamb". Here's the tune with the lyrics and notes:
Mary  had a  little lamb, 
E  D  C   D  E   E  E 
little lamb, little lamb
D  D   D     E   G  G
Mary  had a  little lamb, 
E  D C   D E   E  E
It's fleece was white as snow
E    D      D   E     D  C
Replacing notes with the AUDF0 values:
Mary  had a  little lamb, 
24 27 30  27 24  24 24
little lamb, little lamb
27 27  27    24  20 20
Mary  had a  little lamb, 
24 27 30  27 24  24 24
It's fleece was white as snow
24   27     27  24    27 30
Every syllable in the song takes about a beat, except for some of the "lamb"s and "snow" which take 2 or 4. So the lengths are:
Mary  had a  little lamb, 
30 30 30  30 30 30  60 
little lamb, little lamb
30 30  60    30  30 60
Mary  had a  little lamb, 
30 30 30  30 30 30  30 
It's fleece was white as snow
30   30     30  30    30 120
Merging those into the note frequency, note length format the engine uses:
24,30,27,30,30,30,27,30,24,30,24,30,24,60
27,30,27,30,27,60,24,30,20,30,20,60
24,30,27,30,30,30,27,30,24,30,24,30,24,30
24,30,27,30,27,30,24,30,27,30,30,120

One final issue: when we have two notes of the same frequency in a row, they sound like one big note, so lets fix that by taking 2 frames off of each note that's the same pitch as the next note and making them a rest (rests are indicated by "-1" for the value.)

24,30,27,30,30,30,27,30,24,28,-1,2,24,28,-1,2,24,60
27,28,-1,2,27,28,-1,2,27,60,24,30,20,28,-1,2,20,60
24,30,27,30,30,30,27,30,24,28,-1,2,24,28,-1,2,24,28,-1,2
24,30,27,28,-1,2,27,30,24,30,27,30,30,120

Now all we need is to count the notes and rests...35, or 70 pieces of data, one less than that is 69 (heh heh) and that's the value we plug into the loop. So here's the entire program:
 dim musicPointer=a
 dim musicTimer=b
 dim tempaudv=t

 rem VOLUME DOWN AND SET CHANNELS
 AUDV0=0
 AUDC0=6

 rem INITIALIZE POINTERS AND TIMERS
 musicPointer=$FF
 musicTimer=0

startLoop

 rem TIME TO UPDATE NOTE?
 if musicTimer = 0 then gosub changeMusicNote
 musicTimer = musicTimer - 1

 drawscreen
 goto startLoop

changeMusicNote
  musicPointer = musicPointer + 1
  AUDF0 = musicData[musicPointer]

  tempaudv = 8
  if musicData[musicPointer] = $FF then tempaudv = 0
  AUDV0 = tempaudv

  musicPointer = musicPointer + 1
  musicTimer = musicData[musicPointer]
  rem value is (2 * #_OF_NOTES) - 1
  if musicPointer > 69 then musicPointer = #-1

 return

 data musicData
 24,30,27,30,30,30,27,30,24,28,-1,2,24,28,-1,2,24,60
 27,28,-1,2,27,28,-1,2,27,60,24,30,20,28,-1,2,20,60
 24,30,27,30,30,30,27,30,24,28,-1,2,24,28,-1,2,24,28,-1,2
 24,30,27,28,-1,2,27,30,24,30,27,30,30,120
end
And thus Mary has her little lamb! Exciting, huh? It's more bass-y than I expected, but I like it. If you're too lazy to compile and run that, here's the binary image of it.

Of course, you can use this same basic engine idea for both voices... A while back I ported the music I made for the game "JoustPong"...it uses one voice for a simple bassline and the other voice for a bit of drum. Here's the source code for that, and the binary image

next steps
Well, now you know all the basics of 2600 music. The example engine I gave you is flexible, but limited: it doesn't change distortion values or volume. (You would have to give up ROM space or use another RAM counter, or both, to be able to change those on the fly.) Also, there might be ways of making it more efficient for certain tunes or other applications. Another idea would be to decrease the note volume each frame, that makes a nice, somewhat less "computer music" sound.

There are other neat tricks too, like playing the same notes at the same time with different distortion values, or faking two meoldies at once by switching between them rapidly. Paul Slocum's sequencer kit has some neat stuff, including the 2600 Sound And Music Programming Guide (local mirror). He's more focused on "real musicians" but he's done some great stuff...check out the rest of his site!

back to the batari BASIC page