An option for using piper
and onnx
voices in the browser is through Speech Dispatcher, which Chromium-based browsers (Chrome, Brave, Opera, Edge) and Firefox use for Web Speech API.
I have added the piper
module to Speech Dispatcher following the instructions here module request: piper #866.
Tested on Chromium Version 128.0.6586.0 (Developer Build) (64-bit) and Firefox Nightly 130.0a1. Chromium works. Firefox does not load the piper
voices.
In pertinent part.
Download the piper
executable from releases, extract the contents and save to ~/.local/opt/piper
.
Download a couple .onnx
files and save to ~/.local/share/piper/voices
.
Create a symbolic link to piper
executable in ~/.local/bin
, ln -s ~/.local/opt/piper/piper piper
.
Install python3-speechd
sudo apt install python3-speechd
spd-conf -u
Modify ~/.config/speech-dispatcher/speechd.conf
to add the piper
module
AddModule "piper" "sd_generic" "piper.conf"
or set piper
as the default module
DefaultModule espeak-ng
# piper
Create ~/.config/speech-dispatcher/modules/piper.conf
Debug 0
GenericExecuteSynth "printf %s \'$DATA\' | /home/xubuntu/.local/bin/piper --length_scale 1 --sentence_silence 0 --model ~/.local/share/piper/voices/$VOICE --output-raw | aplay -r 22050 -f S16_LE -t raw -"
# only use medium quality voices to respect the 22050 rate for aplay in the command above.
GenericCmdDependency "piper"
GenericCmdDependency "aplay"
GenericCmdDependency "printf"
GenericSoundIconFolder "/usr/share/sounds/sound-icons/"
GenericPunctNone ""
GenericPunctSome "--punct=\"()<>[]{}\""
GenericPunctMost "--punct=\"()[]{};:\""
GenericPunctAll "--punct"
#GenericStripPunctChars ""
GenericLanguage "en" "en_US" "utf-8"
AddVoice "en" "MALE1" "en_US-hfc_male-medium.onnx"
AddVoice "en" "FEMALE1" "en_US-hfc_female-medium.onnx"
DefaultVoice "en_US-hfc_male-medium.onnx"
#GenericRateForceInteger 1
#GenericRateAdd 1
#GenericRateMultiply 100
Restart speech-dispatcher
with speech-dispatcher restart
.
Terminate and restart chrome
, killall -9 chrome
.
Open DevTools, test in console
var voices = speechSynthesis.getVoices().filter(({name}) => name.includes("piper"));
var u = new SpeechSynthesisUtterance();
u.voice = voices[0];
u.text = "Test, test, test. Test to the point it breaks.";
speechSynthesis.speak(u);
console.log(JSON.stringify(voices.map(({default:_default, lang, localService, name, voiceURI}) => ({_default, lang, localService, name, voiceURI})), null, 2));
VM924:6 [
{
"_default": false,
"lang": "en",
"localService": true,
"name": "en_US-hfc_female-medium.onnx piper",
"voiceURI": "en_US-hfc_female-medium.onnx piper"
},
{
"_default": false,
"lang": "en",
"localService": true,
"name": "en_US-hfc_male-medium.onnx piper",
"voiceURI": "en_US-hfc_male-medium.onnx piper"
}
]