articles

This weekend I thought it would be cool to learn about digital audio. Specifically, I wanted to look inside of audio files and know how they work on the level of bytes. Also, I'm going to do my exploring without looking at other source, like Audacity, to make things more interesting.

It turns out that digital audio files are split up into frames that are typically either 8-bits or 16-bits. So a wave file is essentially list of 8-bit or 16-bit numbers that represent the pressure on the microphone. The frame rate dictates how many of these frames go by per second.

In thirty minutes, I was successfully able to come up with code for creating a .wav file at any frequency:
import wave
import math
import array

#Create a 16 bit wave
width = 2 #16 bits is 2 bytes
maxval = 65535
sample_rate=44100
channels = 1
samples = array.array('h') #Signed short

# Create a sine wav, of frequency fFreq
nSamples = sample_rate*2 #two seconds long
fFreq = 440
w = fFreq*2*math.pi / sample_rate
amplitude = 65535/2 #Loudest amplitude
samples.extend( (amplitude * math.sin(w*x)) for x in xrange(nSamples) )

f = wave.open('c:\\a440.wav', 'wb')
f.setnchannels(channels)
f.setframerate(sample_rate)
f.setsampwidth(width)
f.writeframes(samples.tostring())
f.close()
Some of my ideas: The array type works perfectly for storing the samples - it is far more efficient than a Python list. Note also that I am using a "generator expression", which works similarly to a list comprehension but does not need to actually create the list. In the future I plan on using generators for creating the signals.

It is weird that 16-bit sound is in the "signed short" format, instead of being unsigned.

On a less serious note, I then saw the beep method winsound module, and was amused:
import winsound
# so retro!
winsound.Beep(440,10)
winsound.Beep(600,10)
#bonus stage!
for i in range(40,400,30):
    winsound.Beep(i,90)
There's no way to control the volume, though, so this can get annoying.

Next I'll be writing some sweet audio effects.