Tuesday, June 2, 2020

Sampled sound 1980s style: from SN76489 square waves to samples

In the early 1980s, the Texas Instruments SN76489 sound chip was found in a variety of personal computers, consoles and arcade cabinets.

The legend, the chip itself

It is a fairly simple chip, consisting of 3 "tone channels", and one "noise channel", typically mapped to 3 note channels and one drum channel. The tone channels play square waves and the noise channel can emit either white noise or "periodic" noise. Despite this simplicity, human creativity -- as usual -- came to the rescue to bang out some good sounding tunes.

A couple of examples for you: Galaforce title music and Icarus title music (both BBC Micro).

Programmers even worked out how to coax sampled sound out of this beast back in the day! Sampled sound, from square waves?! The (very brief) sampled speech intro to Spy Hunter is a good example.

This blog post concerns how the mechanics of the sampled sound actually work, but first a quick demo of my own attempt to advance the art from sample discs produced back it the day. My own sample disc playback looks like this:

"sampstream" running in beebjit
Amusingly, the sample playback quality on a 1980 chip is obviously not too badly mangled because YouTube content ID recognizes emulator recordings of the commercial songs I chose as demos 😆 But you can click here or click here to play these sampstream demo discs in the jsbeeb emulator. It sounds similar to real hardware, although real hardware sounds less harsh and louder (you may need to crank your volume up).

"sampstream" features:
  • Near instant start and uninterrupted 400k of sample playback. It uses a custom asynchronous disc loader that auto-detects the Intel 8271 vs. Western Digital 1770 disc controller and drives it directly and asynchronously.
  • A real-time 50Hz oscilloscope view.
  • Noise bands that show disc read bandwidth vs. sample consumption bandwidth
sampstream does not feature best-in-class sample pre-processing and playback. It also does not use compression, despite samples being pretty compressible. So it's a tech demo that could be made to sound better and play for longer. The pcmenc project is the state-of-the-art I'm aware of for good quality playback. (A lot of what it does it fight against poor 4-bit and non-linear per-sample resolution, using clever techniques such as multi-channel balancing and mid-point rejigging.)

[sampstream, and the quicdisc library is it based, are open source and hosted on GitHub here]

So how does sample playback actually work? The sampstream example keeps things simple and uses only one tone channel. For a SN76489 tone channel, we control:
  • The channel period, i.e. the frequency of the square wave output -- 1024 possible values.
  • The channel volume -- 16 possible values.
  • A special register that outputs a byte of sampled sound. (Hahaha no.)
There's a few good references on the SN76489 online, including how to get sampled sound out of the thing. I recommend this resource over at SMS Power, as well as the MAME SN76489 driver source code to give you an idea of the flavors and variants.

However, some sources hand-wave regarding how it actually works, so let's cover that here by getting out our trusty oscilloscope and applying it to the SN76489 in the BBC Micro.

The usual basis of sampled sound playback is driving the SN76489 as a PCM device, i.e. a sequence of sample amplitude values. Everyone seems to agree this is initiated by setting a tone channel to a very high frequency, or very short period. There is disagreement on what period value to use, and why it works.

In the BBC Micro, the clock input to the sound chip is 2MHz and it has an internal divide-by-8, making it a 250kHz device. If we set a tone channel period to 1, it will invert that channel's output every clock tick (high, low, high, low...), forming a square wave. A full waveform cycle will need to have a high segment and a low segment, meaning that the output waveform should be a 125kHz square wave.

Investigating the output with an oscilloscope is easy because the SN76489 pinout is trivial:
Not too many pins!

The pin of interest is pin 7, "SND OUT". What does that look like with all channels silent, except for one tone channel, set to period 1 and maximum volume?

SND OUT from the SN76489 in blue

In this scope capture, the blue line is the SND OUT output from the SN76489. As can be seen it is a square wave as expected. It's at a 125kHz frequency, 720mV from peak to peak with its upper peak around 3.3v. Right away we notice a surprise: we might have expected an audio signal to center around 0v, but this signal is centered around 3v or so! The chip works like this:
  • Each audio channel contributes two alternating voltages (peak and trough) to the SND OUT total voltage.
  • Neither of the alternating voltages are negative.
  • For silent channels, the two alternating voltages will in fact be the same (about 0.8v).
  • For max volume channels, the two alternating voltages will be 0v and about 0.8v.
  • For this particular SN variant, it is "inverted" compared to what you might expect: a silent channel will output a constant voltage of 0.8v or so, not 0v. This inversion is interesting but does not affect ability to play sampled sound.
The yellow line above is the output a couple of components later in the audio chain: an LM386N-1 audio amplifier, after traversing an LM324N op-amp. We can see that already, the 125kHz frequency has been partially filtered out by the LM chips. This is why we use such a high frequency as a PCM carrier: it won't affect the audible output. The audio chain will filter it out; the speaker won't be able to respond fast enough to any remnants; and at any rate, the human ultimately at the end of the audio chain cannot hear it.

The PCM stream is modulated simply by rapidly altering the volume of the 125kHz wave, resulting in a voltage output looking like this (for our 1 tone channel case):

The above graph shows a few samples at max volume at the start, some interim, and a few at silent volume (the flat top line) at the end. The red trend line shows what the actual PCM waveform will look like with the 125kHz carrier filtered out. The key for sample playback is likely that the mid-point voltage differs for each volume. Note how its amplitude is half that of the carrier wave. This is likely one reason sampled sound playback is relatively quiet.

So now we've seen how a PCM waveform can emerge from just a 125kHz square wave and volume changes. There's some extra ciruitry between the LM386N-1 and the speaker, to keep the speaker happy. It normalizes the oscillation around the positive voltage point to be an oscillation around 0v.

It's important to recap how the SN76489 voltage output does not behave. It does not oscillate evenly around a fixed 0v point, which would look like this:

The above would not be conducive to sampled sound playback as the average signal after eliminating the 125kHz carrier would be a constant 0v!

Although we're done with explaining sampled sound output, it's also worth looking at a few other oscilloscope traces to understand SN76489 behavior. Here's a look at the other extreme, the longest period this particular model can output for a 121Hz sound:

SN76489 output in green, LM386N-1 in blue

Some references state that the SN76489 cannot hold a steady square wave output, particularly for longer periods. However, this is clearly not true -- at least for this variant. The square wave output is very clean and it is likely the LM324N or LM386N-1 that is introducing interesting voltage decays into the output waveforms.

Finally, here's another SN76489 vs. LM386N-1 output for an 8kHz square wave:

The output is close to a sine wave, not a square wave! This interesting distortion at 8kHz may be because some sources reference an 8kHz low-pass filter as a part of the LM324N setup.

1 comment:

tricky said...

Thanks for a great little article and the insights behind the curtain.
I love that you have followed the signal through the pipeline and found some interesting artefacts. But can they be exploited!
The streaming library sounds great too.