Sound
Sampling, sample rate, bit depth and file size, and finding a note from its samples.
Do this lesson in the simulatorSound is a wave: air pressure rising and falling, smoothly and continuously. A computer can only store numbers, so to record sound it measures the wave at regular moments and stores each measurement as a binary number. That is sampling. In this lesson you sample a note, measure how much data it takes, and use the samples to work out which note it was and play it on BugBot's buzzer.
Sampling a wave
A note of 440 hertz is a wave that repeats 440 times a second. Here is one sampled 4,000 times a second, shown for its first few samples:
# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()
import math
rate = 4000 # samples per second
freq = 440 # the note, in hertz
samples = []
for i in range(40): # a hundredth of a second
t = i / rate
samples.append(math.sin(2 * math.pi * freq * t))
for s in samples[:20]:
bar = int((s + 1) * 20)
print(f"{s:6.2f} " + " " * bar + "*")
Each row is one sample, and the stars trace the shape of the wave. The sample rate is how many samples are taken each second, measured in hertz. More samples per second follow the wave more closely.
Bit depth
Each sample is stored as a whole number with a fixed number of bits: the bit depth. With 8 bits there are 256 levels, so each measurement is rounded to the nearest level:
# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()
import math
def sample(freq, rate, seconds, depth):
levels = 2 ** depth
out = []
for i in range(int(rate * seconds)):
wave = math.sin(2 * math.pi * freq * i / rate) # -1 to 1
out.append(round((wave + 1) / 2 * (levels - 1))) # 0 to levels - 1
return out
print("2-bit:", sample(440, 4000, 0.004, 2))
print("8-bit:", sample(440, 4000, 0.004, 8))
With 2 bits there are only four levels, 0 to 3, and the wave becomes a rough staircase. With 8 bits the steps are much finer. A higher bit depth records the sound more accurately.
File size
The size of a sound file in bits is:
sample rate × bit depth × duration in seconds
For stereo, multiply by the 2 channels as well.
# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()
def sound_bits(rate, depth, seconds, channels=1):
return rate * depth * seconds * channels
print("robot beep, 8 kHz, 8-bit, 1 s:", sound_bits(8000, 8, 1) / 8, "bytes")
print("CD quality, 44.1 kHz, 16-bit, stereo, 3 minutes:", sound_bits(44100, 16, 180, 2) / 8 / 1000 / 1000, "MB")
A three-minute song at CD quality is about 32 MB, which is why music is compressed.
From samples back to a note
The samples hold the wave, so the note can be recovered from them. Count how often the wave crosses the middle going upwards: that is once per cycle, so crossings per second is the frequency.
# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()
import math
rate, seconds = 8000, 0.5
samples = [math.sin(2 * math.pi * 523 * i / rate) for i in range(int(rate * seconds))]
rises = 0
for i in range(1, len(samples)):
if samples[i - 1] < 0 <= samples[i]:
rises = rises + 1
freq = round(rises / seconds)
print("the samples hold a note of about", freq, "Hz")
tone(freq, 0.5)
Quality and size
Increasing the sample rate or the bit depth makes the recording closer to the original sound, and makes the file bigger. Every recording is a choice between the two. A telephone uses 8,000 samples a second, enough for speech; music uses 44,100 or more.
Task: find the note
Sample a note of 659 Hz for half a second at a sample rate of 8,000 Hz. Print samples: <n> and 8-bit size: <n> bits, worked out from the rate and duration. Then count the upward crossings, print note: <freq> Hz, and play that frequency on the buzzer for half a second.
# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()
import math
rate = 8000
seconds = 0.5
Challenges
- Sample the same note at 1,000 samples a second. Does the crossing count still find it? Why not?
- Mix two notes by adding their samples. What does the crossing method report now?
- How many minutes of 8 kHz, 8-bit sound fit in 1 MB?