{"id":19016,"date":"2026-09-12T13:03:41","date_gmt":"2026-09-12T11:03:41","guid":{"rendered":"https:\/\/www.beseit.net\/?p=19016"},"modified":"2026-09-12T17:18:59","modified_gmt":"2026-09-12T15:18:59","slug":"integracio-de-speech-dispatcher-amb-piper-tts-veu-ona-upc-a-deepin","status":"publish","type":"post","link":"https:\/\/www.beseit.net\/?p=19016","title":{"rendered":"Integraci\u00f3 de Speech Dispatcher amb Piper TTS (veu Ona, UPC) a Deepin"},"content":{"rendered":"\n<h1 class=\"wp-block-heading\"><em>Data: 12 de setembre de 2026 \u2014 Estat actual del projecte<\/em><\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83c\udfaf Objectiu<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Aconseguir que <strong>Firefox pugui llegir p\u00e0gines web en catal\u00e0<\/strong> amb la veu <strong>Ona (UPC)<\/strong> mitjan\u00e7ant la cadena:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">Firefox \u2192 Speech Dispatcher \u2192 m\u00f2dul gen\u00e8ric (sd_generic) \u2192 script pont \u2192 Piper (Docker) \u2192 Veu Ona<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">El repte: Speech Dispatcher no detecta cap veu per defecte (0 veus), perqu\u00e8 Piper no \u00e9s un motor TTS natiu de Speech Dispatcher.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83c\udfd7\ufe0f Arquitectura de la soluci\u00f3<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Component<\/th><th>Paper<\/th><\/tr><\/thead><tbody><tr><td><strong>Firefox<\/strong><\/td><td>Client que demana s\u00edntesi de veu a Speech Dispatcher<\/td><\/tr><tr><td><strong>Speech Dispatcher<\/strong><\/td><td>Daemon intermediari (0.11.5)<\/td><\/tr><tr><td><strong><code>sd_generic<\/code><\/strong><\/td><td>M\u00f2dul gen\u00e8ric de Speech Dispatcher que executa ordres externes<\/td><\/tr><tr><td><strong><code>piper-spd<\/code><\/strong><\/td><td>Script pont: rep text, truca a Docker, retorna WAV<\/td><\/tr><tr><td><strong>Piper (Docker)<\/strong><\/td><td>Motor TTS al port 10200 (protocol Wyoming)<\/td><\/tr><tr><td><strong>Veu <code>ca_ES-upc_ona-medium<\/code><\/strong><\/td><td>Veu catalana d&#8217;Ona, desenvolupada a la UPC<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83d\udcc2 Fitxers creats<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">1. Script pont <code>\/usr\/local\/bin\/piper-spd<\/code><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Rep el text de Speech Dispatcher, el passa al contenidor Piper i genera el fitxer WAV:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">#!\/bin\/bash\n# piper-spd: pont entre Speech Dispatcher i Piper TTS (veus UPC)\nMODEL=\"\/config\/ca_ES-upc_ona-medium.onnx\"\nOUTPUT=\"\"\nTEXT=\"\"\nVOICE=\"${VOICE:-ona}\"\n\nwhile [ $# -gt 0 ]; do\n  case \"$1\" in\n    -o) OUTPUT=\"$2\"; shift 2;;\n    -l|--list)\n      echo \"ona ca_ES medium\"\n      echo \"pau ca_ES medium\"\n      exit 0;;\n    -*) shift;;\n    *) TEXT=\"$TEXT $1\"; shift;;\n  esac\ndone\n\n[ -z \"$OUTPUT\" ] &amp;&amp; { echo \"Error: falta -o FITXER\" >&amp;2; exit 1; }\n\ncase \"$VOICE\" in\n  pau) MODEL=\"\/config\/ca_ES-upc_pau-medium.onnx\";;\n  *)   MODEL=\"\/config\/ca_ES-upc_ona-medium.onnx\";;\nesac\n\nif ! docker ps --format '{{.Names}}' | grep -q '^piper$'; then\n  echo \"Error: el contenidor piper no est\u00e0 en marxa\" >&amp;2\n  exit 1\nfi\n\ndocker exec piper bash -c \"printf '%s' \\\"$TEXT\\\" | piper --model $MODEL --output_file \/tmp\/piper_spd.wav\" 2>\/dev\/null\nif ! docker cp piper:\/tmp\/piper_spd.wav \"$OUTPUT\" 2>\/dev\/null; then\n  echo \"Error: no s'ha pogut generar l'\u00e0udio\" >&amp;2\n  exit 1\nfi\ndocker exec piper rm -f \/tmp\/piper_spd.wav 2>\/dev\/null\nexit 0<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">2. Configuraci\u00f3 del m\u00f2dul <code>\/etc\/speech-dispatcher\/modules\/piper-generic.conf<\/code><\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">GenericExecuteSynth \"VOICE=$VOICE \/usr\/local\/bin\/piper-spd -o $FILE $TEXT\"\nGenericSsmlMode \"off\"\nGenericLanguage \"ca\" \"ca\"\n\nAddVoice \"ca\" \"FEMALE1\" \"ona\"\ndefaultVoice \"ona\"<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">3. Registre a <code>\/etc\/speech-dispatcher\/speechd.conf<\/code><\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">AddModule \"piper\" \"sd_generic\" \"\/etc\/speech-dispatcher\/modules\/piper-generic.conf\"<\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83e\uddd7 Obstacles superats (i com es van resoldre)<\/h2>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>M\u00f2dul <code>sd_generic<\/code> no localitzable<\/strong> \u2192 Resulta que es troba a <code>\/usr\/lib\/speech-dispatcher-modules\/<\/code> (sense subdirectori intermedi).<\/li>\n\n\n\n<li><strong>Speech Dispatcher es negava a arrencar<\/strong> \u2192 El directori de depuraci\u00f3 <code>\/tmp\/speechd-debug<\/code> bloquejava l&#8217;inici; cal esborrar-lo abans d&#8217;arrencar.<\/li>\n\n\n\n<li><strong>Error <code>--voice is required for the piper backend<\/code><\/strong> (Docker) \u2192 Calia especificar <code>PIPER_VOICE=ca_ES-upc_ona-medium<\/code> en crear el contenidor.<\/li>\n\n\n\n<li><strong><code>aplay<\/code> dins del contenidor no funcionava<\/strong> \u2192 La s\u00edntesi es fa al contenidor i la reproducci\u00f3 a l&#8217;amfitri\u00f3.<\/li>\n\n\n\n<li><strong>Error <em>\u00abThe module does not have any voice configured\u00bb<\/em><\/strong> \u2192 Faltava la secci\u00f3 <strong><code>AddVoice<\/code><\/strong> a la configuraci\u00f3 del m\u00f2dul gen\u00e8ric. Aquest era el pas definitiu! \u2705<\/li>\n<\/ol>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\u2705 Estat actual<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Docker<\/strong> instal\u00b7lat i operatiu a Deepin<\/li>\n\n\n\n<li><strong>Piper TTS<\/strong> funcionant al port 10200 amb la veu <strong>Ona<\/strong><\/li>\n\n\n\n<li><strong>Speech Dispatcher<\/strong> arrenca amb el m\u00f2dul piper registrat<\/li>\n\n\n\n<li><strong>Script pont<\/strong> validat (genera WAV de ~90 kB correctament)<\/li>\n\n\n\n<li><strong>Veus definides<\/strong> al m\u00f2dul (<code>AddVoice \"ca\" \"FEMALE1\" \"ona\"<\/code>)<\/li>\n\n\n\n<li><strong>Prova final d&#8217;\u00e0udio<\/strong> pendent en un equip amb sortida de so<\/li>\n\n\n\n<li><strong>Prova a Firefox<\/strong> pendent (mode lectura \u2192 \u00abLlegir en veu alta\u00bb)<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83d\udd27 Proves de verificaci\u00f3<\/h2>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># 1. M\u00f2duls de sortida (ha d'apar\u00e8ixer \"piper\")\nspd-say -O\n\n# 2. Veus disponibles\nspd-say -o piper -L\n\n# 3. S\u00edntesi amb l'Ona\nspd-say -o piper \"Hola, soc l'Ona, la meva veu s'ha desenvolupat a la UPC de Barcelona\"<\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83d\udd04 Guia de reproducci\u00f3 (en un altre punt de treball)<\/h2>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Instal\u00b7lar Docker i engegar el contenidor Piper:<\/strong> podem primer eliminar piper<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">docker rm -f piper<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">docker run -d --name piper -p 10200:10200 -e PIPER_VOICE=ca_ES-upc_ona-medium --restart unless-stopped linuxserver\/piper\n<\/pre>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Instal\u00b7lar Speech Dispatcher:<\/strong><code>sudo apt install speech-dispatcher -y<\/code><\/li>\n\n\n\n<li><strong>Crear els 2 fitxers i modificar linia<\/strong> de \/etc\/speech-dispatcher\/speechd.conf.<\/li>\n<\/ol>\n\n\n\n<pre class=\"wp-block-code\"><code>#\tel fitxers estan descrits m\u00e9s amunt\n#       Acci\u00f3\t       Fitxer\n1\tCrear script\tsudo nano \/usr\/local\/bin\/piper-spd\n2\tCrear config del m\u00f2dul\tsudo nano \/etc\/speech-dispatcher\/modules\/piper.conf\n3\tAfegir l\u00ednia AddModule \"piper\" \"sd_generic\" \"piper-spd\"\t\/etc\/speech-dispatcher\/speechd.conf \u2b05\ufe0f (no al piper.conf)\n\n<\/code><\/pre>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Reiniciar:<\/strong> <\/li>\n<\/ol>\n\n\n\n<pre class=\"wp-block-code\"><code>rm -rf \/tmp\/speechd-debug\npkill -9 -f speech-dispatcher\nsleep 1\nspeech-dispatcher\n<\/code><\/pre>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Provar<\/strong> amb les ordres de verificaci\u00f3.<\/li>\n<\/ol>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<pre class=\"wp-block-code\"><code>spd-say -o piper -y ca_ES-upc_ona-medium \"Hola, s\u00f3c l'Ona, la veu del Piper.\"\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83d\ude80 Properes passes<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Baixar la veu <code>pau<\/code><\/strong> (masculina, UPC) i afegir <code>AddVoice \"ca\" \"MALE1\" \"pau\"<\/code> al m\u00f2dul.<\/li>\n\n\n\n<li><strong>Prova final<\/strong> a un equip amb \u00e0udio i validaci\u00f3 a Firefox (mode lectura).<\/li>\n\n\n\n<li><strong>Automatitzaci\u00f3<\/strong> de l&#8217;arrencada de Speech Dispatcher a l&#8217;inici de sessi\u00f3.<\/li>\n\n\n\n<li><strong>Portabilitat al Synology<\/strong> (192.168.1.49) replicant el contenidor Piper.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83c\udf93 Conclusions<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>La integraci\u00f3 de <strong>Piper amb Speech Dispatcher<\/strong> \u00e9s possible mitjan\u00e7ant el <strong>m\u00f2dul gen\u00e8ric<\/strong> (<code>sd_generic<\/code>) amb un <strong>script pont<\/strong>, sense necessitat de compilar cap extensi\u00f3 nativa.<\/li>\n\n\n\n<li>El punt cr\u00edtic va ser la <strong>definici\u00f3 de veus (<code>AddVoice<\/code>)<\/strong> \u2014 sense ella, el m\u00f2dul no pot inicialitzar-se.<\/li>\n\n\n\n<li>El resultat permetr\u00e0 que <strong>qualsevol aplicaci\u00f3<\/strong> que parli amb Speech Dispatcher (Firefox, ORCA, etc.) faci servir la veu <strong>Ona en catal\u00e0<\/strong>.<\/li>\n\n\n\n<li>L&#8217;\u00fas de <strong>Docker<\/strong> garanteix la portabilitat entre equips i plataformes (incl\u00f2s Synology).<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p class=\"wp-block-paragraph\"><em>Cr\u00e8dits: projecte realitzat amb assist\u00e8ncia de UOS AI sobre Deepin Desktop Environment.<\/em><\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p class=\"wp-block-paragraph\">Espero que et sigui \u00fatil al blog! Si quan el reprodueixis al punt de treball amb so veus algun detall a afinar (qualitat, volum, etc.), ho ajustem. Bona sort amb la reproducci\u00f3! \ud83c\udf99\ufe0f\ud83d\ude0a<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Data: 12 de setembre de 2026 \u2014 Estat actual del projecte \ud83c\udfaf Objectiu Aconseguir que Firefox pugui llegir p\u00e0gines web en catal\u00e0 amb la veu Ona (UPC) mitjan\u00e7ant la cadena: El repte: Speech Dispatcher no detecta cap veu per defecte &hellip; <a href=\"https:\/\/www.beseit.net\/?p=19016\">Continua llegint <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":3167,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[207,1],"tags":[],"class_list":["post-19016","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-deepin-linux","category-bloc-de-notes"],"_links":{"self":[{"href":"https:\/\/www.beseit.net\/index.php?rest_route=\/wp\/v2\/posts\/19016","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.beseit.net\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.beseit.net\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.beseit.net\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.beseit.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=19016"}],"version-history":[{"count":16,"href":"https:\/\/www.beseit.net\/index.php?rest_route=\/wp\/v2\/posts\/19016\/revisions"}],"predecessor-version":[{"id":19040,"href":"https:\/\/www.beseit.net\/index.php?rest_route=\/wp\/v2\/posts\/19016\/revisions\/19040"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.beseit.net\/index.php?rest_route=\/wp\/v2\/media\/3167"}],"wp:attachment":[{"href":"https:\/\/www.beseit.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=19016"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.beseit.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=19016"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.beseit.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=19016"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}