...
Just my blog

Blog about everything, mostly about tech stuff I made. Here is the list of stuff I'm using at my blog. Feel free to ask me about implementations.

Soft I recommend
Py lib I recommend

I'm using these libraries so you can ask me about them.

Raspberry 5 test-to-speech using Piper

How to make your Raspberry 5 talking with python lib Piper?

I wanted to make my Raspberry tell me about power outages and air alerts near my location.

The setup is pretty simple and described ar the repo:

I didn't want to waste my time making yet another Python venv and that's why I've chosen a simple binary mode.

Setup

Now about the setup:

  • Raspberry 5.
  • USB sound card with 3.5 jack.
  • USB-powered speaker with a 3.5 jack.

Sound

CMD to change the volume programmatically:

amixer set Master 80% >> /dev/null

 Sound devices and controls

You may have a problem with playing generated WAV files and making voice directly. So you need to choose devices for a different output.

To list devices run:

# List devices
aplay -L
# I only have USB card:
hw:CARD=Audio,DEV=0
     USB Audio, USB Audio
    Direct hardware device without any conversions
plughw:CARD=Audio,DEV=0
     USB Audio, USB Audio
    Hardware device with all software conversions
sysdefault:CARD=Audio
     USB Audio, USB Audio
    Default Audio Device
front:CARD=Audio,DEV=0
     USB Audio, USB Audio
    Front output / input
iec958:CARD=Audio,DEV=0
     USB Audio, USB Audio
    IEC958 (S/PDIF) Digital Audio Output
dmix:CARD=Audio,DEV=0
     USB Audio, USB Audio
    Direct sample mixing device
usbstream:CARD=Audio
     USB Audio
    USB Stream Output
 
The problem:
aplay --device="dmix:CARD=Audio,DEV=0" /home/USER/Music/notifications/generated/UPS_no_power.wav
Playing WAVE '/home/USER/Music/notifications/generated/UPS_no_power.wav' : Signed 16 bit Little Endian, Rate 22050 Hz, Mono
aplay: set_params:1358: Channels count non available
 

Play

So I'm trying all the devices I have with different options:
  • play synthetic voice
  • play prerecorded voice
  • play a regular WAV sound from the internet
Now try to play the previously generated file.
I need 5 seconds of sleep to actually run into another room and check the voice because my RPi is already in use as a DNS\DHCP server.
 
# Playing:
sleep 5 && aplay --device="plughw:CARD=Audio,DEV=0" /home/USER/Music/notifications/generated/UPS_no_power.wav 
sleep 5 && aplay --device="sysdefault:CARD=Audio" /home/USER/Music/notifications/generated/UPS_no_power.wav
# Not playing
aplay --device="hw:CARD=Audio,DEV=0" /home/USER/Music/notifications/generated/UPS_no_power.wav
aplay --device="front:CARD=Audio,DEV=0" /home/USER/Music/notifications/generated/UPS_no_power.wav
aplay --device="iec958:CARD=Audio,DEV=0" /home/USER/Music/notifications/generated/UPS_no_power.wav
aplay --device="dmix:CARD=Audio,DEV=0" /home/USER/Music/notifications/generated/UPS_no_power.wav
aplay --device="usbstream:CARD=Audio" /home/USER/Music/notifications/generated/UPS_no_power.wav
# Test ddownloaded WAV and prerecorded
sleep 10 && aplay --device="plughw:CARD=Audio,DEV=0" /home/USER/Music/notifications/generated/UPS_no_power.wav && aplay --device="plughw:CARD=Audio,DEV=0" /home/USER/Music/notifications/stop.wav

# Play the voice right away
sleep 4 && amixer set Master 60% >> /dev/null && echo "ей, увага, повітряна тривога у київській області." | /home/USER/text_to_speech/piper/piper --model /home/USER/text_to_speech/ukr/uk_UA-ukrainian_tts-medium.onnx --speaker 0 --output-raw | aplay -r 22050 -f S16_LE -t raw - && amixer set Master 10% >> /dev/null

# Eng
echo "Hey, Warning there is an air alert in Kyiv oblast" | /home/USER/text_to_speech/piper/piper --model /home/USER/text_to_speech/eng/en_GB-northern_english_male-medium.onnx --output-raw | aplay -r 22050 -f S16_LE -t raw -
 

Playing synthetic voice right away:

echo "Робота від батареї" | /home/USER/text_to_speech/piper/piper --model /home/USER/text_to_speech/ukr/uk_UA-ukrainian_tts-medium.onnx --speaker 1 --output_file UPS_no_power.wav
 

Recording the voice to a WAV file:

echo "Увага, робота від батареї, рівень заряду п'ятдесят відсотків" | /home/USER/text_to_speech/piper/piper --model /home/USER/text_to_speech/ukr/uk_UA-ukrainian_tts-medium.onnx --speaker 1 --output_file UPS_50_percent.wav
 

Volume levels

I use different approaches, for example:
Initially, the max volume is set to not greater than 50%, but even that level can be annoying, for example during nighttime.
But what if I want to be aware of air alert, but not scared of the voice at full blast?
  1. Set new master volume
  2. Make an announcement
  3. Set previous or lower master volume back
sleep 4 && amixer set Master 50% >> /dev/null && echo "Hey, Hey, Warning there is an air alert in Kyiv oblast" | /home/USER/text_to_speech/piper/piper --model /home/USER/text_to_speech/eng/en_GB-northern_english_male-medium.onnx --output-raw | aplay -r 22050 -f S16_LE -t raw - && amixer set Master 10% >> /dev/null
 

Bonus

During Rapsberry5 to Home Assistant integration, I made a script for playing sound notifications.
The use case is simple:
  • locally run the script with the required parameters
  • remotely you need to run a remote SSH command
 
 
#!/bin/bash
# Play notification sound at givel volume with given file

PATH_TO_NOTIFICATIONS="/home/USER/Music/notifications"
PATH_TO_DING="/home/USER/Music/notifications/stop.wav"

verbose=$3
sound_volume=$1
file_to_play=$2

play_sound_notification(){
  # No verbose by default, and verbose if any arg!
  if [ -z "$verbose" ]; then
    verbose=false
  else
    verbose=true
    echo "DEBUG: mode at any arg: $verbose"
  fi

  # Volume check if integer:
  if [[ "$sound_volume" =~ ^[0-9]+$ || "$sound_volume" =~ ^[-][0-9]+$  ]]; then
    # Volume check if GTE 101 or LT 1:
    if [[ $sound_volume -ge 101 ]] || [[ $sound_volume -lt 0 ]]; then
      echo "ERROR: Volume is incorrect not 1-100: $volume"
      return 0
    fi
    # All correct:
    if $verbose ; then echo "DEBUG: volume is OK: $volume"; fi
  else
    echo "ERROR: Volume is incorrect, not an integer: $volume"
    return 0
  fi

  # Check if file exsists:
  file_full_path="$PATH_TO_NOTIFICATIONS/$file_to_play"
  if [ -f $file_full_path ]; then
      if $verbose ; then echo echo "DEBUG: file exists: $file_full_path"; fi
  else
    echo "File: $file_to_play"
    echo "ERROR: The file does not exist at path with this name: $file_full_path"
    return 0
  fi

  # Set volume:
  if $verbose; then
    echo "Set volume at level: $sound_volume"
    echo "============================"
    amixer set Master $sound_volume%
    echo "============================"
    echo "Play initial ding to wake up USB Sound device: $PATH_TO_DING"
    aplay --device="dmix:CARD=Audio,DEV=0" -q --nonblock --duration=1 $PATH_TO_DING
    echo "Now play requested notification: $file_full_path"
    aplay --device="dmix:CARD=Audio,DEV=0" -q $file_full_path
    echo "Set volume back at level 10%"
    echo "============================"
    amixer set Master 10%
    echo "============================"
  else
    amixer set Master $sound_volume% >> /dev/null
    aplay --device="dmix:CARD=Audio,DEV=0" -q --nonblock --duration=1 $PATH_TO_DING
    aplay --device="dmix:CARD=Audio,DEV=0" -q $file_full_path
    amixer set Master 10% >> /dev/null
  fi
  # Play notification:

  # End and exit
  return 0
}

play_sound_notification $sound_volume $file_to_play $verbose
 
How to run from another host, for example, Home Assistant VM:
  • you need to set SSH keys between HA and RPi
ssh -o UserKnownHostsFile=/config/.ssh/known_hosts -i /config/.ssh/id_rsa USER@IP_ADDR '/home/USER/play_sound_notification.sh {{volume}} {{file}}'
 
Do not forget to map those variables to your paths:
  • PATH_TO_NOTIFICATIONS - folder where your WAV files located
  • PATH_TO_DING - path to a short (1-2 sec) and quiet notification sound to wake up the speaker

In my case, my device is cutting a few seconds of any sound it plays. So I've added one "silent" notification sound before any real notification. 

It must be a sound, I've tried to fool the device with a silent file and it doesn't help.

 
Later I'll add another post with full instructions on How to make sound notifications from Home Assistant to remote Raspberry.