Spoken status announcements for Klipper — without stalling the printer.
Your printer tells you what it is doing out loud: "Starting bed mesh calibration", "Filament runout", "Print completed". 33 clips included, a four-level verbosity setting so it is not constantly talking at you, an optional volume slider in KlipperScreen and Mainsail, and tooling to generate your own clips in a matching voice.
MIT licensed. Python 3 standard library only, no pip installs on the printer.
That is where everyone starts, and it has three problems that are not obvious until you hit them. All three are solved here, and all three are worth knowing about even if you write your own.
This is the big one. RUN_SHELL_COMMAND does not return until the command
finishes, and the gcode queue waits. Call aplay directly and the printer
stops for the entire length of the clip.
With four-second announcements and three of them in PRINT_START, that is
twelve seconds of a machine standing still. Mid-print it is worse: a stall with
a hot nozzle sitting on the part leaves a blob in the wall. That is not
hypothetical — it is what prompted this package.
Everything here goes through scripts/play_sound.sh, which nohups the player
and exits. Measured on a Raspberry Pi 4: 0.060 s against a 0.011 s
bare-gcode baseline, versus the multi-second block it replaces.
If you take one thing from this repo, take this one. It applies to any shell command you might fire mid-print.
Cheap HDMI and USB audio devices power down between clips. The next stream starts playing into a sink that has not finished waking, and the beginning is simply gone. You get "…leted" instead of "Print completed".
The fix is a lead-in buffer: about a second of digital silence at the head of every file, which the device consumes while it wakes.
This is the caveat that costs people the most time, because the symptom looks
like a bad recording rather than a hardware behaviour. All clips here carry
1.2 s of leading silence, and tools/process_sounds.py adds it to anything
you generate.
It also means your clips will be longer than the words in them, and that is correct. Do not trim it out to "tidy them up".
Since playback is detached, that second costs the printer nothing — it delays the sound, not the machine.
100% on the ALSA mixer is 0.00 dB — no attenuation, not a boost. If the clips are quiet, the volume control cannot fix it; loudness has to come out of the files.
tools/process_sounds.py handles that: high-pass, RMS targeting, and a
look-ahead limiter. See docs/GENERATING.md.
And the volume scale is dB, not loudness — amixer's "50%" is about −25 dB, so a raw slider has two-thirds dead travel. The macro applies a square-root curve to fix it.
cd ~/printer_data/config
git clone https://github.com/Simon-CR/klipper-voice-cues.git voice_cues
sudo apt install -y alsa-utilsYou also need gcode_shell_command — from
kiauh (Advanced → G-Code Shell Command) or
klippain-shelltools.
Then:
-
Find your audio device
aplay -L | grep -i -A1 'hdmi\|usb\|headphone' aplay -D sysdefault:CARD=vc4hdmi0 voice_cues/sounds/print_started.wav
Iterate until you hear it. If nothing works at all, see docs/HARDWARE.md.
-
Edit
voice_cues/config.sh— setSOUND_DIRandALSA_DEVICE. -
Edit the two paths at the top of
voice_cues/voice_cues.cfg, plus the two[gcode_shell_command]paths just below them. They ship pointing at/home/pi/printer_data/config/voice_cues/. -
Add to
printer.cfg:[include voice_cues/voice_cues.cfg]You also need a
[save_variables]section if you do not have one — the audio level and volume persist there:[save_variables] filename: ~/printer_data/config/variables.cfg -
FIRMWARE_RESTART, then runVOICE_TEST.
Add one line wherever something worth announcing happens:
[gcode_macro PRINT_END]
gcode:
...
PLAY_AUDIO FILE=print_completedFor things that need you, mark the class:
PLAY_AUDIO FILE=filament_not_detected CLASS=important TONE=errorCLASS=important survives the IMPORTANT level and becomes a tone under TONES.
Everything else is routine and goes quiet as soon as the user turns the
verbosity down.
docs/INTEGRATION.md has copy-paste blocks for the usual
macros — PRINT_START, PRINT_END, PAUSE, RESUME, CANCEL_PRINT,
runout handlers, filament load and unload.
| Level | Routine | Important |
|---|---|---|
TALKATIVE |
speaks | speaks |
IMPORTANT |
silent | speaks |
TONES |
silent | short tone |
MUTE |
silent | silent |
AUDIO_LEVEL LEVEL=IMPORTANT
Or the buttons AUDIO_TALKATIVE / AUDIO_IMPORTANT / AUDIO_TONES /
AUDIO_MUTE, which appear in KlipperScreen's macro panel. The setting
persists.
Anything unmarked is routine, so a new announcement is quiet-by-default and has to earn its way up. That is the safe direction: a printer that talks constantly is one you stop listening to, and then the runout announcement is worth nothing.
SET_SOUND_VOLUME VOLUME=70
For a slider in KlipperScreen (Pins panel) and Mainsail (Miscellaneous),
uncomment the [output_pin sound_volume] block in voice_cues.cfg and give it
a free pin on your board. Details and pin-choosing advice are in that file's
comments.
33 files, 44.1 kHz mono 16-bit PCM.
Print — print_started print_completed print_paused print_resumed
print_canceled operation_aborted soak_complete
Filament — loading_filament unloading_filament filament_loaded
filament_detected filament_not_detected filament_unload_failed
wrong_filament not_enough_filament
Calibration — starting_bed_mesh bed_mesh_complete doing_z_tilt
z_tilt_complete touch_z_probing touch_z_complete calibration_success
homing_failed probe_failed mesh_failed
Other — nozzle_change_complete warning critical_error
Tones — beep_info beep_success beep_warning beep_error
beep_critical
Making your own, including matching the voice of the existing set: docs/GENERATING.md.
samples/ has the same line in twelve voices — six male, six female, US and GB — processed through the identical pipeline, so what you hear is what your printer would say. Listen first, then download only the model you want.
Swapping is a two-line edit in tools/make_sounds.py and one command. The
high-tier models are the ones worth having: generation happens on a
workstation, so the larger download costs you nothing ongoing.
| Symptom | Cause |
|---|---|
| Printer pauses when a clip plays | A shell command is calling aplay directly. Route it through play_sound.sh. |
| First word cut off | Lead-in buffer too short for your device — increase LEAD_MS in tools/process_sounds.py and reprocess. See §2 above. |
| Nothing at all | aplay -L for the right device name; test by hand; check /tmp/klipper_audio.log. |
| Too quiet at 100% | The mixer cannot exceed 0 dB. Reprocess louder — see docs/GENERATING.md. |
| Slider does nothing below ~70% | You removed the square-root curve in _APPLY_VOLUME. The scale is dB. |
Unknown command: RUN_SHELL_COMMAND |
gcode_shell_command is not installed. |
| Clips overlap or drop | flock missing. apt install util-linux. |
Built for a Voron Trident running Klipper + Moonraker with a BTT HDMI display. Speech generated with piper.
Contributions welcome — especially clips in other languages and voices, and
confirmations of which ALSA_DEVICE value works on other hardware.