Last active
January 4, 2025 00:41
-
-
Save alexkuz/f24f93245ff80458c9b6ec93c644c40b to your computer and use it in GitHub Desktop.
Install Piper TTS as speech dispatcher module
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/sh | |
set -e | |
BASE_PIPER_VOICES_URL=${BASE_PIPER_VOICES_URL:-https://huggingface.co/rhasspy/piper-voices/resolve/main} | |
INSTALL_DIR=${INSTALL_DIR:-~/.local/share/speech-dispatcher-piper} | |
CONFIG_DIR=${CONFIG_DIR:-~/.config/speech-dispatcher/modules} | |
CONFIG_PATH=$CONFIG_DIR/piper-generic.conf | |
PLATFORM=${PLATFORM:-$(uname -m)} | |
PIPER_BIN_GZ_URL=${PIPER_BIN_GZ_URL:-https://github.com/rhasspy/piper/releases/latest/download/piper_linux_$PLATFORM.tar.gz} | |
PIPER_GENERIC_CONF=$(cat <<EOF | |
DefaultVoice "en_GB-alan-low" | |
GenericCmdDependency "sox" | |
GenericCmdDependency "jq" | |
GenericExecuteSynth \ | |
"cd $INSTALL_DIR && \ | |
./check_piper_voice.sh \$VOICE && \ | |
printf %s \'\$DATA\' \ | |
| ./piper/piper --model \'voices/\$VOICE.onnx\' --output_raw \ | |
| sox -v \$VOLUME -r \$(jq .audio.sample_rate < \'voices/\$VOICE.onnx.json\') -c 1 \ | |
-b 16 -e signed-integer -t raw - -t wav - tempo \$RATE pitch \$PITCH norm \ | |
| \$PLAY_COMMAND" | |
GenericRateAdd 1 | |
GenericPitchAdd 1 | |
GenericVolumeAdd 1 | |
GenericRateMultiply 1 | |
GenericPitchMultiply 750 | |
GenericVolumeMultiply 1 | |
EOF | |
) | |
# checks if piper voice is present and download it if not | |
CHECK_PIPER_VOICE_SH=$(cat <<EOF | |
#!/bin/sh | |
VOICE=\$1 | |
VOICE_MODEL=\$VOICE.onnx | |
VOICE_JSON=\$VOICE.onnx.json | |
BASE_PIPER_VOICES_URL=$BASE_PIPER_VOICES_URL | |
if ! [ -f "voices/\$VOICE_MODEL" ] || ! [ -f "voices/\$VOICE_JSON" ]; then | |
LANG_FULL=\$(echo "\$VOICE" | cut -d '-' -f 1) | |
LANG_SHORT=\$(echo "\$LANG_FULL" | cut -d '_' -f 1) | |
NAME=\$(echo "\$VOICE" | cut -d '-' -f 2) | |
QUALITY=\$(echo "\$VOICE" | cut -d '-' -f 3) | |
if ! [ -f "voices/\$VOICE_JSON" ]; then | |
curl -s -L -C - -o "voices/\$VOICE_JSON.download" "\$BASE_PIPER_VOICES_URL/\$LANG_SHORT/\$LANG_FULL/\$NAME/\$QUALITY/\$VOICE_JSON?download=true" | |
mv "voices/\$VOICE_JSON.download" "voices/\$VOICE_JSON" | |
fi | |
if ! [ -f "voices/\$VOICE_MODEL" ]; then | |
curl -s -L -C - -o "voices/\$VOICE_MODEL.download" "\$BASE_PIPER_VOICES_URL/\$LANG_SHORT/\$LANG_FULL/\$NAME/\$QUALITY/\$VOICE_MODEL?download=true" | |
mv "voices/\$VOICE_MODEL.download" "voices/\$VOICE_MODEL" | |
fi | |
fi | |
EOF | |
) | |
echo "\033[1;36mInstalling piper dependencies: \033[0mjq, sox" | |
apt -qq install jq sox | |
mkdir -p "$INSTALL_DIR/voices" | |
if ! [ -d "$CONFIG_DIR" ]; then | |
echo "\033[1;36mConfig directory not found, \033[0mexecuting spd-conf" | |
spd-conf -n | |
fi | |
# if config exists, backup old version and create new one | |
if [ -f "$CONFIG_PATH" ]; then | |
echo "\033[1;36mConfig file found, \033[0mbacking up old version" | |
i=1 | |
while [ -f "$CONFIG_PATH.$i.bak" ]; do | |
i=$((i+1)) | |
done | |
mv "$CONFIG_PATH" "$CONFIG_PATH.$i.bak" | |
fi | |
touch "$CONFIG_PATH" | |
echo "\033[1;36mDownload voice list and create piper config\033[0m" | |
VOICE_JSON=$(mktemp) | |
curl -s -L -o "$VOICE_JSON" "$BASE_PIPER_VOICES_URL/voices.json" | |
VOICES=$(jq -r 'map(.key) | @sh' < "$VOICE_JSON") | |
rm "$VOICE_JSON" | |
for VOICE in $VOICES; do | |
VOICE=${VOICE#\'} && VOICE=${VOICE%\'} | |
LANG_FULL=$(echo "$VOICE" | cut -d '-' -f 1) | |
# if voice contains "female", use FEMALE1 as voice name, otherwise use MALE1 | |
VOICE_GENDER=MALE1 | |
case "$VOICE" in | |
*female*) | |
VOICE_GENDER=FEMALE1 | |
;; | |
esac | |
echo "AddVoice \"$LANG_FULL\" \"$VOICE_GENDER\" \"$VOICE\"" >> "$CONFIG_PATH" | |
done | |
echo "$PIPER_GENERIC_CONF" >> "$CONFIG_PATH" | |
echo "\033[1;36mDownload piper binaries\033[0m" | |
PIPER_BIN_GZ=$(mktemp) | |
curl -s -L -o "$PIPER_BIN_GZ" "$PIPER_BIN_GZ_URL" | |
rm -rf "$INSTALL_DIR/piper" | |
tar -xzf "$PIPER_BIN_GZ" -C "$INSTALL_DIR" | |
rm "$PIPER_BIN_GZ" | |
echo "\033[1;36mCreate check_piper_voice.sh\033[0m" | |
echo "$CHECK_PIPER_VOICE_SH" > "$INSTALL_DIR/check_piper_voice.sh" | |
chmod +x "$INSTALL_DIR/check_piper_voice.sh" | |
# ask if user wants to test piper | |
printf "\033[1;36mDo you want to test piper? [Y/n]\033[0m " | |
read -r YN </dev/tty | |
YN=${YN:-y} | |
case $YN in | |
[Yy]* ) | |
echo "\033[1;36mPronouncing the following sentence:\033[0m \"Piper is now installed\"" | |
spd-say -w -o piper-generic -y en_GB-alan-low "Piper is now installed" | |
;; | |
* ) ;; | |
esac | |
echo "\033[1;32mInstallation complete\033[0m" |
I appreciate the code, working on a different project, but it helped me with piper and sox.
Without adding the module to speechd.conf, it won't work.
Also, a major bug that still exists in spd-conf
: it crashes when there is no config (why, oh why?). All we have to do is mkdir -p ~/.config/speech-dispatcher && touch ~/.config/speech-dispatcher/speechd.conf
so it doesn't crash.
SPD_CONF_DIR=${SPD_CONF_DIR:-~/.config/speech-dispatcher}
CONFIG_DIR=${CONFIG_DIR:-$SPD_CONF_DIR/modules}
# ...
if ! [ -d "$CONFIG_DIR" ]; then
echo "Config directory not found, executing spd-conf"
# WORKAROUND BUG in spd-conf when speechd.conf does not exist
mkdir -p $SPD_CONF_DIR && touch $SPD_CONF_DIR/speechd.conf
spd-conf -n
fi
# ...
# Add module to speechd.conf
echo 'AddModule "piper" "sd_generic" "piper-generic.conf"' | tee -a $SPD_CONF_DIR/speechd.conf
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
In the following snippet from above:
I have a question - You have used
VOLUME
,RATE
andPITCH
variables. Are these standard variables from speech dispatcher?