Cover image for SAM - Software Automatic Mouth on MicroPython

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 2026
8 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!


 29 March 2026   |     8 minute read   |   By Kevin McAleer   |   Share this article on

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:

  1. 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.

  2. 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.

  3. 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:

  1. Connect GPIO 0 to the positive terminal of the speaker (optionally through a 1K resistor)
  2. 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

  1. Clone or download the SAM repository from GitHub
  2. Open Thonny (or use mpremote from the command line) and copy the entire sam folder to the root of your Raspberry Pi Pico’s filesystem
  3. Also copy across the sam_render.mpy file
  4. 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 sam folder is copied to the root of your Pico’s filesystem, not just individual files. The native C .mpy module 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.


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.



Code

View Code Repository on GitHub - https://www.github.com/kevinmcaleer/sam

This page is awesome - Show some love!


What are you looking for?
Watch Videos Get Ideas Learn Something Read a Review Read the Blog Search
... Z Z z