SAM - Software Automatic Mouth on MicroPython
SAM (Software Automatic Mouth) is a classic 1982 speech synthesis engine, ported to MicroPython for the Raspberry Pi Pico. Give your robots a voice with just a single GPIO pin and a speaker!
29 March 20268 minute read
By Kevin McAleer
Share this article on
Table of Contents
SAM - Software Automatic Mouth on MicroPython
SAM (Software Automatic Mouth) is a classic 1982 speech synthesis engine, ported to MicroPython for the Raspberry Pi Pico. Give your robots a voice with just a single GPIO pin and a speaker!
Video
For every project I create, I often make a corresponding YouTube video. Sometimes, there might be more than one video for a single project. You can find these videos in this section.
Explore more through this this dedicated video.
Ahoy there makers,
This week I’m bringing a voice from 1982 back to life on modern hardware. Picture the scene: January 24th, 1984, the Flint Center for Performing Arts in Cupertino, California. Steve Jobs takes the Apple Macintosh out of the bag and lets the computer introduce itself using MacinTalk. That same year, a program called SAM — Software Automatic Mouth — was already making waves on the Commodore 64. Written by Mark Barton, SAM was one of the first widely available text-to-speech tools on home computers. It has since been reverse engineered, ported to C, then JavaScript, then Python, and now… MicroPython on the Raspberry Pi Pico.
The whole thing runs with a single speaker on a single GPIO pin. No DAC, no I2S module, no expensive hardware. Just a Pico and some PIO magic.
What is SAM?
SAM (Software Automatic Mouth) was originally written in 6502 assembly for the Commodore 64 back in 1982 by Mark Barton. It was one of the first consumer text-to-speech engines, appearing around the same time as Apple’s MacinTalk — the software used for the famous 1984 Macintosh launch. Both used formant synthesis, but they were independent projects.
SAM converts plain English text into speech audio using three stages:
-
The Reciter — Takes English text and converts it into phonemes (the fundamental units of sound in spoken language). It has over 200 context-sensitive rules to handle the many quirks of English pronunciation, looking at surrounding letters to determine what sound each letter should make.
-
The Phoneme Processor — Takes the phoneme codes and converts them into frame data. It applies transformation rules to handle diphthongs (sounds that glide between two vowel positions), inserts breath sounds, and manages stress and rhythm. This is where the speech starts to sound more natural.
-
The Renderer — Actually generates the audio samples using formant synthesis, simulating the resonant frequencies of the human vocal tract. Three overlapping waveforms (formants) combine to produce vowel sounds, while consonants use pre-sampled, bit-packed data.
The entire engine fits in just 22 kilobytes of RAM.
The Speed Problem (and a 40-Year-Old Solution)
The renderer involves a lot of maths — multiplications, lookups, and wave arithmetic running thousands of times per audio frame. MicroPython, for all its brilliance, is not fast at maths.
When I first got SAM running in pure MicroPython on the Pico, generating “Hello World” took 1,900 milliseconds — nearly two seconds. Adding MicroPython’s @native decorator (which compiles functions to native ARM machine code rather than bytecode) brought that down to about 1,200 milliseconds. Better, but still over a second for two words.
The real breakthrough came from writing the hot render loop as a native C module — a 296-line C file that implements the exact same render logic, compiled as an .mpy file that MicroPython can load dynamically. This brought render time down to around 17 milliseconds. Over 100 times faster.
The key trick? A multiplication lookup table. Instead of computing multiplications at runtime, you pre-compute every possible result and store it in a table. SAM packs a 4-bit sign value and a 4-bit amplitude value into a single byte index, giving 256 possible combinations stored in a 256-byte lookup table. One byte in, one byte out.
Here’s the beautiful part: this is the exact same trick that Mark Barton used in 1982 to work around the Commodore 64’s lack of a hardware multiply instruction. We embedded the same table directly in our C module. The same solution, 40 years apart, on completely different hardware. Good engineering solutions don’t have an expiry date.
Bill of Materials
This is one of the simplest circuits you’ll build. You only need:
| Component | Purpose | Approximate Cost |
|---|---|---|
| Raspberry Pi Pico | Runs SAM | ~£4 |
| Small speaker or piezo buzzer | Audio output | ~£1 |
| 1K resistor (optional) | Smooths out pops and clicks | ~£0.01 |
For enhanced audio clarity, you can optionally add:
| Component | Purpose |
|---|---|
| 10K resistor + 10nF capacitor | LC low-pass filter to remove PWM carrier |
| PAM8403 or LM386 audio amplifier module | Louder, clearer output |
| 14mm speaker | Better sound quality with amplifier |
But honestly, the minimal one-resistor setup is perfectly understandable for robot speech.
Wiring It Up
The circuit couldn’t be simpler:
- Connect GPIO 0 to the positive terminal of the speaker (optionally through a 1K resistor)
- Connect the speaker’s negative terminal to GND
That’s it. SAM generates audio as pulse width modulation on the GPIO pin.
How PIO Makes It Sound Great
This is where the Raspberry Pi Pico really shines. When playing audio, timing is everything — each sample needs to arrive at exactly the right moment. If the timing drifts or stutters, you hear clicks, pops, and distortion.
In MicroPython, the CPU is busy running your code, managing memory, and doing garbage collection. You can’t guarantee it’ll be free at the exact microsecond you need to push out the next audio sample.
The RP2040 has eight Programmable IO (PIO) state machines — tiny independent processors that run separately from the main CPU with their own clock and instruction set. They execute with cycle-accurate timing. No jitter, no interruptions.
SAM’s audio driver uses a tiny 8-instruction PIO assembly program to generate PWM output:
- Each audio sample is an 8-bit value
- The PIO reads values from its FIFO buffer and converts them into PWM cycles with 256 steps
- At a 22kHz sample rate, the PIO clock runs at around 17MHz
The clever part is DMA (Direct Memory Access). The DMA feeds audio samples from the buffer straight into the PIO’s FIFO. So the CPU generates the speech, fills a buffer, hands it to the DMA, and is then completely free to do other work. The PIO and DMA handle playback autonomously — zero CPU involvement during audio output.
On an ESP32, SAM has to fall back to a timer interrupt firing thousands of times per second to push each sample out. It works, but ties up the CPU and the timing is never quite as precise. On the Pico, PIO handles this perfectly every time.
Installing the Code
- Clone or download the SAM repository from GitHub
- Open Thonny (or use
mpremotefrom the command line) and copy the entiresamfolder to the root of your Raspberry Pi Pico’s filesystem - Also copy across the
sam_render.mpyfile - That’s it — you’re ready to go
Using SAM — It’s Three Lines of Code
from sam import Sam
sam = Sam(pin=0)
sam.say("Hello World")
That’s genuinely all you need. To see what’s happening under the hood:
sam.info()
This shows you something like:
Sample Rate: 22kHz
Renderer: Native C (active)
Audio Driver: PIO
Speed: 72
Pitch: 64
Mouth: 128
Throat: 128
Changing the Voice
You can adjust pitch, mouth, and throat to create different character voices:
sam.pitch = 64
sam.mouth = 128
sam.throat = 128
sam.say("I am a robot")
sam.pitch = 200
sam.mouth = 100
sam.throat = 100
sam.say("I am a tiny robot")
Here’s a quick guide to what each parameter does:
| Parameter | Range | Effect |
|---|---|---|
pitch |
0–255 | Higher values = higher pitched voice |
speed |
0–255 | Higher values = slower speech |
mouth |
0–255 | Controls the mouth “shape” — affects vowel formants |
throat |
0–255 | Controls the throat “shape” — affects vocal timbre |
Each combination sounds slightly different — experiment to find the perfect voice for your project.
Project Ideas
Now that your Pico can talk, here are some ideas to get you started:
- Announce your WiFi IP address — No screen? No problem. Have your Pico read out its IP address after connecting to the network
- Speaking clock — Announce the time at intervals or on button press
- Make your robot talk — Give your robot a personality with speech responses
- Button-triggered speech — Add a button to trigger phrases or status updates
- Talking plant monitor — Let your plants tell you when they need watering
- Alert system — Speak sensor readings or warnings aloud
- Retro computer voice — Recreate that classic 1980s computer voice for your projects
Troubleshooting
- No sound at all — Check that the speaker is connected to GPIO 0 (not GP0 on a different board) and GND. Make sure the speaker polarity is correct (red to GPIO, black to GND).
- Import error when loading SAM — Make sure the entire
samfolder is copied to the root of your Pico’s filesystem, not just individual files. The native C.mpymodule must match your MicroPython version. - Audio sounds garbled or distorted — Try adding the 1K resistor between GPIO 0 and the speaker if you haven’t already. Also check that nothing else is using GPIO 0.
Links
- SAM GitHub Repository: https://www.github.com/kevinmcaleer/sam
- Original SAM history: https://en.wikipedia.org/wiki/Software_Automatic_Mouth
- Raspberry Pi Pico documentation: https://www.raspberrypi.com/documentation/microcontrollers/
A voice from 1982 speaking through a chip from 2021 — and you can build the whole thing for around the price of a cup of coffee. If you try this out, I’d love to see what you’ve done with it. Drop a comment below or tag me on social media.
See you next time. Bye for now.
This page is awesome - Show some love!
Comments