How do ones and zeros sound?

1-bit music is probably the most challenging electronic music genre, and definitely not the most mellifluous one. It originated long time ago when the early personal computers had a periodic timer and a connected piezo buzzer rather than a proper sound card with a speaker. We are now used to the electronic music with all the synthesizers, they are used in almost every song we hear. And we hear the songs only because the digital device converts a stream of digital signals into varying amplitude of a nicely constructed magnetic loudspeaker. Complex waveforms, precise pitches, unbelievable effects are all possible because of the high sample rate and sound channel depth.

1-bit music is nothing like that. You only have two levels of the output signal, and your playback device is incapable of producing much more - it’s a thin membrane connected to a piezo crystal, that changes its shape when the voltage is applied to it. Unlike hi-fi magnetic speakers, piezo buzzers are only good at, well, buzzing. To make things worse, the sound circuit only allows configuring a timer to a given frequency flipping the signal level and producing a monotonic square wave.

Sometimes 1-but audio devices have a buzzer wired up to some GPIO lines and the CPU can control the speaker directly, but there are always certain tricks that can make the square wave music sound more interesting. We are surrounded by dozens of dull 1-bit audio devices, from smoke detectors and toys to greeting cards playing melody when you open them. And it turns out there is the whole subculture around producing 1-bit music.

1-bit sound effects

The most obvious effect is to change the frequency of the signal a little bit. Sliding the pitch quickly up or down would create the effects similar to firing the arms in the old games. Oscillating the pitch up and down would create a vibtrato effect.

The other trick one can do is to change the duty cycle of the square wave. This somewhat affects the perceived volume of the signal and creates slightly different harmonics. People typically choose duty cycles of 50%, 25%, 12.5% etc, otherwise it will sound a bit “off tune”. Of course, duty cycle can also be modulated, like the pitch, using some low-frequency oscillator.

One can create different envelopes for both, pitch and duty cycle and that would define some new 1-bit “instruments”.

1-bit sound

If you are curious how it sounds - you may watch this YouTube video, demonstrating a 1-bit AVR synthesizer. Beware, it may sound too harsh and loud.

1-bit composers at work

You might wonder why I talk about this weird music genre in a programming blog. Composing 1-bit music often starts with choosing (or programming your own) sound engine. It is very different from recording a song in Garage Band. The sound engine (or sound routine) is typically a loop implemented in assembly for the particular platform, that scans the patterns of notes (outer loop) and plays each of them according to the current “instrument” settings (inner loop).

The most common platforms for 1-bit music are ZX Spectrum, HP calculators, Arduino boards, old PCs running DOS and similar retro or low-end computers. Squeezing maximum functionality from the sound routine requires lots of skills and understanding of the platform.

Here I wanted to experiment with “fake” 1-bit music, for those who might be interested in the topic but not yet ready to buy a computer from the 80s on Ebay. That’s why I built 1bitr.

1bitr is an ultimately minimal, hackable 1-bit music tracker that follows the UNIX way of doing one thing only. It has no UI, assuming that you use your text editor for coding both, music and the sound engine, if you like. It reads notes from stdin, meaning that you may use UNIX shell tools to combine patterns of notes or pre-process them. All it does next is parsing the columns of notes and additional data and passes it to the underlying sound engine. It comes with two very basic engines to start with, but you are free to build your own ones! It also does audio playback on Linux and macOS and supports exporting raw PCM data if you want to post-process your song in a DAW later (yes, 1-bit music does not have to be pure 1-bit, you can compose ambient 1-bit music, or enhance it with effects like filters or reverb).

Sound engines

The most simple sound engine that comes with 1bitr is “Zero” engine:

static void engine_0(int *row, void (*out)(unsigned char)) {
  static int tempo = SAMPLE_RATE / 8;
  int counter = row[0] / 2;
  unsigned char value = 0;
  if (row[1]) {
    tempo = row[1] * 100;
  }
  for (int timer = 0; timer < tempo; timer++) {
    if (--counter == 0) {
      value = !value;
      counter = row[0];
    }
    out(value);
  }
}

This engine expects two columns of data, the first column would be a note and the second is tempo. If the note is zero - it means silence, if the tempo is zero - it remains unchanged. The engine uses a single counter variable, that starts with a note half-period and counts down until zero. Then it flips the audio output and resets the counter to its initial value to count down the second half of the note. This continues for as long as the note row should play, which is defined by the tempo variable.

Here is a sample input that plays a C major scale:

; 0 (for Engine Zero, a single-channel PWM with adjustable tempo)
; Just a boring C-major scale played with Engine 0
C-4 70
D-4
E-4
F-4
G-4
A-4
B-4
C-5

Running in your shell ./1bitr < SCALE.txt should play some beeping sounds.

This engine has no effects, has a constant 50% duty cycle and is utterly dull and monophonic.

1-bit polyphony

Adding effects to a single channel is rather straightforward. Counter should count up to the full note period, audio signal is set to zero then the note starts (or the counter resets) and it should be set to one when the counter reaches the duty cycle threshold. Adjusting these two parameters (note period and duty cycle) with some hand-coded algorithms or envelopes would bring us a humble number of effects to make a richer sound.

But how to overcome the limits of a single audio channel? Well, there is an old trick. So old that is was used by Bach in a number of his works. If we quickly switch between the two interchanging notes - it will sound as if they are playing simultaneously. How quickly? As quickly, as possible. Since 1bitr runs on a modern CPU, the only restriction here is audio sample rate. At the sample rate of 44.1KHz we can toggle between the two audio channels 22050 times per second and it should not be noticeable to a human ear, which is capable of perceiving frequencies up to 20KHz.

This is what engine “One” does in 1bitr (check out the sources), but this is not the only way to achieve polyphony. One can use bitwise XOR to combine outputs of the two channels. Although it is much easier to implement, there is one significant limitation - if both channels play the same note they will become muted. But still, worth trying!

1-bit samples

Finally, what if you need drums? Drums are nothing like a buzzing oscillator. In 1-bit music there is a concept of “interrupting click drums”. It means: when a drum should be played – it plays a short burst of sound (imitating a drum) before the other audio channels do their playback. In other words, drums interrupt audio for a short period of time, and this pause remains unnoticeable to a human ear.

What kind of sound to play to make it drum-like? First option is white noise. If you randomly play 1 and 0 levels for a few milliseconds - it will sound like a lo-fi hi-hat drum. Kick drums can be simulated with a square wave oscillator, playing a low pitch note, sliding down for a short period of time.

But nothing stops you from using audio samples in 1-bit music. Of course, one can have pre-defined arrays of ones and zeros and stream them, but that is a waste of space, highly discouraged in 1-bit world. Alternative would be pulse width encoding. We know that in a 1-bit audio sample zero is always followed by one, which is followed by another zero at some point. So instead of having an array of ones and zeros we store an array of intervals between the first 0 and the following 1 and the following 0 and so on. In other words, [23, 5, 3] would mean play 0 for 23 cycles, play 1 for 5 cycles, play 0 for 3 cycles. This approach is used in engine “One”, as well as many other real-world 1-bit sound engines.

There’s no such thing as two

I still think that there are more 1-bit musicians than 1-bit music listeners in the world, but the concept is nevertheless, very unusual and exciting. I can imagine using 1-bit instruments as part of the other, more traditional sound tracks. I also miss the days of the tracker music, when composing music for the compouter was simple and fun. If you came up with a new sound engine or some new 1-bit music - please open an issue or a pull request for 1bitr project!

I shall mention that there are some “real” 1-bit trackers, such as Beepola or 1tracker, they have GUI and play music through the emulation of the real ZX spectrum sound engines. So if 1bitr is too limiting for your needs - feel free to try those.

I hope you’ve enjoyed this article. You can follow – and contribute to – on Github, Mastodon, Twitter or subscribe via rss.

Mar 14, 2021