Make ‘Computer Music’ with a very small program!

By Monty, 11th May 2015

BB13This is a fun little program to generate ‘computer music’, specifically ‘bytebeats’ which sound like the chip music featured in the early computer games. As is well-known, sound can be digitised and stored in computers. Conversely, numbers can be converted to sound. But those numbers don’t have to come from a file, they can be generated on the fly, for example by the following program:

#!/usr/bin/python3
# bytebeats0.py

t = 0
while True:
    print(chr(int(
        t
        ) % 256 ), end='')
    t += 1

Pipe this into aplay like so: bytebeats0.py | aplay or into sox:  bytebeats0.py | sox -r 8000 -b 8 -c 1 -t raw -e unsigned-integer – -d

(I’m not a sox expert, there’s probably a better way…).

Although the value of t in the loop will grow quite large, we feed t % 256 into the chr() function to keep it in range (of 8-bits). Some people have been experimenting with variations on the simple t formula, by performing bit operations on t – shifting, masking, etc. So for example, instead of t, try other formulae such as

  • (t*((15&t>>11)%12)&55-(t>>5|t>>12)|t*(t>>10)*32)-1
  • t*3&(t>>10)|t*12&(t>>10)|t*10&((t>>8)*55)&128
  • t*4&(t>>10)|t*4&(t*6>>8)&t|64
  • t*(t+(t>>9|t>>13))%40&120

This next program uses PyAudio to convert the numbers to an audio stream.

#!/usr/bin/python3
import pyaudio

#   Initialise PyAudio
PyAudio = pyaudio.PyAudio		# error msgs are usually safe to ignore
pa = PyAudio()
audio = pa.open(format=pa.get_format_from_width(1),channels=1,rate=8000,output=True)

t = 0
while True:
    audio.write(chr(t%256))                     # convert to 8-bit
    t += 1

You might get some warnings from PyAudio, like:

ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.rear
ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.center_lfe
ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.side
ALSA lib pcm_route.c:947:(find_matching_chmap) Found no matching channel map
Cannot connect to server socket err = No such file or directory
Cannot connect to server request channel
jack server is not running or cannot be started

With any luck, you can safely ignore them.

Finally, let’s add some graphing, and a primitive capability to manage a list of bytebeat functions. It plots a graph of the beginning of the audio sequence. Here are some brief notes about it:

  1. Import math functions only if you want to try the sine wave
  2. Instead of plugging the sound function directly into the output statement, I store it in a list of strings, so that I can use them to label the graph
  3. The stop & start parameters are to make it easier to select functions to play, e.g. a range such as all (start=0, stop = len(funcs)) or the last one
  4. You need  a lot of audio samples for the piece to last long enough; plotting them all would make a very crowded graph
  5. Sound values are computed in 2 sections 1: graph & audio values, 2: audio values only
  6. We get values from the function using eval(f)%256, the modulo to keep values in 8-bit range
  7. Use Pyplot to set up the graph, and plot it. You might need to remove the block parameter…
  8. Compute the rest of the values
  9. Write them (as chr()) to the audio stream
  10. The final input() ensures the final graph doesn’t disappear when the program ends; you might be able to dispense with it.
#!/usr/bin/python3
#	ByteBeats.py

import matplotlib.pyplot as pl
import pyaudio
from math import sin, pi

#   Initialise PyAudio
PyAudio = pyaudio.PyAudio		# error msgs are usually safe to ignore
pa = PyAudio()
audio = pa.open(format=pa.get_format_from_width(1),channels=1,rate=8000,output=True)

funcs = [
	't*2',
	't*5&t>>7|t*3&t>>10|t>>4',
	'127.5*sin(pi*t/50)+127.5',
	't*(t+(t>>9|t>>13))%40&120',
	'(t&t%255)-(t*3&t>>13&t>>6)',
	't*((t>>3|t>>13)&t>>6|t>>8)',
	't*4&(t>>10)|t*4&(t*6>>8)&t|64',
	'(t>>6|t|t>>(16))*10+((t>>11)&7)',
	't*(((t>>12)|(t>>8))&(63&(t>>4)))',
	't*(4|t*t&4<<t)*(t&(t>>9|t>>13)+1)',
	't*3&(t>>10)|t*12&(t>>10)|t*10&((t>>8)*55)&128',
        '(((t*5&t>>6)^(t>>4|t>>2&t%255|t*3&t>>8)-10)/4)',
	'(t*((15&t>>11)%12)&55-(t>>5|t>>12)|t*(t>>10)*32)-1'
          ]
stop = len(funcs)                       # number of functions
start = stop - 1                        # only playing the last one
start = 0                               # playing all
pls = 10000                             # number of samples to plot
aus = 100000                            # number of audio samples
#   Note that in this loop, we calculate the samples in 2 parts -
#   first those we want to plot, then the rest - so the plot is shown sooner

for i in range(start,stop):             # for a range of functions:
    f = funcs[i]                            # select a function
    x = [x for x in range(aus)]             # compute x range
    y = [int(eval(f)%256) for t in x[:pls]]   # only what's needed for plot

    pl.figure(figsize=(15,10))                  # set the plot size
    pl.title(f)
    pl.plot(x[:pls],y)
    pl.show(block=False)

    y[pls:] = [int(eval(f)%256) for t in x[pls:]]   # now the rest...
    for v in y:                                 # for all y
        # replace with print(...,end='') if no pyaudio
        audio.write(chr(v))                     # convert to 8-bit

input("Press Enter")

Installation Notes

You’ll need to install matplotlib, pyaudio, and aplay (or sox). If you’re on Linux it should be fairly easy, just be sure to get the python3- versions. Synaptic is good for installing when you don’t know the exact name, e.g. for matplotlib you should see ‘python3-matplotlib’, for pyaudio it’s python3-pyaudio, and aplay is in the alsa-utils. Or you can use apt-get:

sudo apt-get install python3-matplotlib python3-pyaudio alsa-utils

Mac or Windows I don’t know. Synaptic is a GUI front-end to apt-get, so not I think available on OS-X. I tend to use synaptic first, if possible, or apt-get (command line) or pip (the PyPA recommended tool for installing Python packages) if the instructions say so. I don’t know if there’s a compelling reason to prefer any one of them.

You might want to check out these matplotlib installation notes.

PyAudio is available for Mac. Aplay is Linux only; here’s a thread about aplay equivalents on Mac which happens also to be about bytebeat music one-liners!

If you’re only interested in hearing the music and don’t care about the graphs you can of course cut out the pyplot bits. And you really only need one of aplay or equivalent/pyaudio for that.

If you’re into fractals check out my FractalArt.Gallery. There’s a musical Mandelbrot that works with pyaudio.

More information about bytebeats:

Discover bytebeat. A new genre of algorithmic music has been developed by demoscene coder viznut, a.k.a. PwP. Sharing genes with chiptunes and facilitated by bitwise operators, bytebeats are decidedly non-traditional music created by short, programmatic formulas. Read about computationally minimal art, the aesthetic that spawned bytebeat. Try your hand at composing (somehelpful examples). Read an explanation of how the formulas work. A few more pieces. (from Today’s formulaic music)

One Comment

  1. […] draw amazing pictures like the Mandelbrot Set and other fractals, or Harmonographs, or that make computer music. But while I was learning Python, and looking for examples on the web to study, I found that most […]

What do you think?

Leave a Reply

%d bloggers like this: