{"id":19155,"date":"2026-09-19T16:19:08","date_gmt":"2026-09-19T14:19:08","guid":{"rendered":"https:\/\/www.beseit.net\/?p=19155"},"modified":"2026-09-19T16:20:24","modified_gmt":"2026-09-19T14:20:24","slug":"servidor-local-tts-server-i-piper-ona-a-deepin-corregit","status":"publish","type":"post","link":"https:\/\/www.beseit.net\/?p=19155","title":{"rendered":"servidor local tts-server i Piper (ona) a Deepin corregit"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">UOS AI<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">iquel@Miquel-PC:~$ docker exec piper bash -c \u00abecho \\\u00bbHola, s\u00f3c l&#8217;Ona, la meva veu s&#8217;ha desenvolupat a la UPC de Barcelona.\\\u00bb | piper &#8211;model \/config\/ca_ES-upc_ona-medium.onnx &#8211;output_file -\u00bb | aplay -f S16_LE -r 22050 -c 1 Playing WAVE &#8216;stdin&#8217; : Signed 16 bit Little Endian, Rate 22050 Hz, Mono Miquel@Miquel-PC:~$ aquest issatge es reprodueix sense problemes amb el servidor local tts-server i Piper (ona) a Deepin. podries mostra el flux del texte fins arribar al altaveus?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><img decoding=\"async\" src=\"qrc:\/web\/dist\/assets\/deep-thinking.webp\">Deep think completado (tard\u00f3 3s)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Aqu\u00ed tens el flux complet del text fins als altaveus, pas a pas:<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Flux del text \u2192 so<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">plaintext<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1TEXT (stdin)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2 \u2502<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">3 \u25bc<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">4\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">5\u2502 docker exec piper bash -c \u00ab&#8230;\u00bb \u2502<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">6\u2502 \u2502 \u2502<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">7\u2502 \u25bc \u2502<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">8\u2502 echo \u00abHola, s\u00f3c l&#8217;Ona&#8230;\u00bb \u2500\u2500\u25ba stdin \u2502<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">9\u2502 \u2502 \u2502<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">10\u2502 \u25bc \u2502<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">11\u2502 piper &#8211;model ca_ES-upc_ona-medium.onnx \u2502<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">12\u2502 1. Normalitza el text (xifres, abreviatures&#8230;) \u2502<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">13\u2502 2. Fonetitza (text \u2192 fonemes) \u2502<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">14\u2502 3. S\u00edntesi neuronal (fonemes \u2192 mel-espectrograma) \u2502<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">15\u2502 4. Vocoder (mel-espectrograma \u2192 mostres PCM) \u2502<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">16\u2502 \u2502 \u2502<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">17\u2502 \u25bc \u2502<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">18\u2502 Sortida: PCM cru (S16_LE, 22050 Hz, mono) \u2500\u2500\u25ba stdout \u2502<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">19\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">20 \u2502<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">21 \u2502 pipe (|) \u2014 dins de l&#8217;host, fora del container<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">22 \u25bc<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">23\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">24\u2502 aplay -f S16_LE -r 22050 -c 1 \u2502<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">25\u2502 \u2502 \u2502<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">26\u2502 \u25bc \u2502<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">27\u2502 ALSA (Advanced Linux Sound Architecture) \u2502<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">28\u2502 \u2502 \u2502<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">29\u2502 \u25bc \u2502<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">30\u2502 Controlador del so (driver) \u2502<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">31\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">32 \u2502<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">33 \u25bc<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">34DAC (convertidor digital\u2192anal\u00f2gic) de la targeta de so<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">35 \u2502<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">36 \u25bc<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">37Amplificador \u2192 Altaveus \ud83d\udd0a<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">38<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">38 l\u00edneas de c\u00f3digo colapsadasExpandir<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Detalls clau de cada etapa<\/h2>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong><code>docker exec<\/code><\/strong> \u2014 Executa el proc\u00e9s dins del contenidor <code>piper<\/code>, que cont\u00e9 el model i l&#8217;eina.<\/li>\n\n\n\n<li><strong><code>echo ... | piper<\/code><\/strong> \u2014 El text entra per <em>stdin<\/em>. Piper fa la s\u00edntesi amb el model neuronal ONNX <code>ca_ES-upc_ona-medium<\/code>:\n<ul class=\"wp-block-list\">\n<li>El model converteix el text en una seq\u00fc\u00e8ncia de fonemes (representaci\u00f3 interm\u00e8dia).<\/li>\n\n\n\n<li>La xarxa neuronal genera l&#8217;espectrograma mel (representaci\u00f3 freq\u00fcencial de la veu).<\/li>\n\n\n\n<li>El <strong>vocoder<\/strong> converteix aquest espectrograma en mostres d&#8217;\u00e0udio PCM.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong><code>--output_file -<\/code><\/strong> \u2014 L&#8217;\u00e0udio es treu per <em>stdout<\/em> en format <strong>PCM cru<\/strong> (sense cap\u00e7alera WAV), a 22050 Hz, mono, 16 bits little-endian.<\/li>\n\n\n\n<li><strong><code>| aplay<\/code><\/strong> \u2014 La canonada passa aquest flux de dades a <code>aplay<\/code> de l&#8217;host:\n<ul class=\"wp-block-list\">\n<li><code>-f S16_LE<\/code> \u2192 interpreta les mostres com a enter amb signe de 16 bits little-endian.<\/li>\n\n\n\n<li><code>-r 22050<\/code> \u2192 freq\u00fc\u00e8ncia de mostreig.<\/li>\n\n\n\n<li><code>-c 1<\/code> \u2192 un canal (mono).<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>ALSA \u2192 DAC \u2192 Altaveus<\/strong> \u2014 <code>aplay<\/code> entrega les mostres al subsistema de so del kernel; la targeta de so converteix les mostres digitals en senyal anal\u00f2gic, que l&#8217;amplificador fa arribar als altaveus.<\/li>\n<\/ol>\n\n\n\n<h2 class=\"wp-block-heading\">Observaci\u00f3<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">El fet que funcioni aix\u00ed demostra que el contenidor genera <strong>\u00e0udio PCM cru a stdout<\/strong> i que el pipe el porta directament a l&#8217;ALSA de Deepin sense cap intermediari (sense PulseAudio\/PipeWire pel mig), cosa que minimitza la lat\u00e8ncia. Si volguessis enregistrar-ho en un fitxer WAV, n&#8217;hi hauria prou amb afegir-hi una cap\u00e7alera:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">bash<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1docker exec piper bash -c \u00abecho \\\u00bb&#8230;\\\u00bb | piper &#8211;model \/config\/ca_ES-upc_ona-medium.onnx &#8211;output_file -\u00bb | sox -t raw -r 22050 -e signed -b 16 -c 1 &#8211; output.wav<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2 l\u00edneas de c\u00f3digo colapsadasExpandir<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Vols que t&#8217;expliqui cap etapa amb m\u00e9s profunditat?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Enrutamiento Inteligente<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">TEXT (stdin) qu\u00e8 vol dir?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><img decoding=\"async\" src=\"qrc:\/web\/dist\/assets\/deep-thinking.webp\">Deep think completado (tard\u00f3 2s)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong><code>TEXT (stdin)<\/code><\/strong> vol dir que el text arriba al programa <strong>piper<\/strong> per l&#8217;<strong>entrada est\u00e0ndard<\/strong> (<em>standard input<\/em>), no pas com a argument ni com a fitxer.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Qu\u00e8 \u00e9s stdin?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Cada proc\u00e9s a Linux t\u00e9 tres canals de comunicaci\u00f3 per defecte:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Canal<\/th><th>Nom<\/th><th>Funci\u00f3<\/th><\/tr><\/thead><tbody><tr><td><strong>stdin<\/strong> (0)<\/td><td>Entrada est\u00e0ndard<\/td><td>D&#8217;on el programa <strong>llegeix<\/strong> dades<\/td><\/tr><tr><td>stdout (1)<\/td><td>Sortida est\u00e0ndard<\/td><td>On el programa <strong>escriu<\/strong> resultats<\/td><\/tr><tr><td>stderr (2)<\/td><td>Error est\u00e0ndard<\/td><td>On escriu missatges d&#8217;error<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Quan executes un programa des del terminal i no li dius d&#8217;on treure les dades, llegeix de <strong>stdin<\/strong> (que normalment \u00e9s el teclat).<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Al teu cas concret<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">bash<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1echo \u00abHola, s\u00f3c l&#8217;Ona&#8230;\u00bb | piper &#8211;model &#8230; &#8211;output_file &#8211;<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2 l\u00edneas de c\u00f3digo colapsadasExpandir<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Aqu\u00ed passa aix\u00f2:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">plaintext<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1echo \u00abHola&#8230;\u00bb pipe (|) piper<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2 \u2502 \u2502<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">3 escriu el text \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u25ba llegeix de stdin<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">4<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">4 l\u00edneas de c\u00f3digo colapsadasExpandir<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><code>echo<\/code> <strong>escriu<\/strong> el text al seu stdout.<\/li>\n\n\n\n<li>La <strong>canonada<\/strong> (<code>|<\/code>) connecta el stdout de <code>echo<\/code> amb el <strong>stdin<\/strong> de <code>piper<\/code>.<\/li>\n\n\n\n<li><code>piper<\/code> <strong>llegeix<\/strong> el text del seu stdin (com si vingu\u00e9s del teclat, per\u00f2 ve del pipe) i el processa.<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">\u00c9s per aix\u00f2 que el diagrama deia <strong>TEXT (stdin)<\/strong>: el text entra a piper per aquest canal est\u00e0ndard. Com que no especifiques cap fitxer d&#8217;entrada (<code>--input_file<\/code>), piper agafa les dades de stdin.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Per qu\u00e8 s&#8217;escriu aix\u00ed?<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong><code>|<\/code> (pipe)<\/strong> \u2192 encadena processos: la sortida d&#8217;un \u00e9s l&#8217;entrada del seg\u00fcent.<\/li>\n\n\n\n<li><strong><code>--output_file -<\/code><\/strong> \u2192 el guionet <code>-<\/code> \u00e9s una convenci\u00f3 que significa \u00abel mateix stdin\/stdout en lloc d&#8217;un fitxer\u00bb, \u00e9s a dir, <em>escriu l&#8217;\u00e0udio al stdout<\/em> perqu\u00e8 despr\u00e9s <code>aplay<\/code> el pugui rebre.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Resumint: <strong>stdin<\/strong> \u00e9s simplement \u00abel lloc per on piper rep el text d&#8217;entrada\u00bb \u2014 en aquest cas, a trav\u00e9s del pipe des de <code>echo<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Enrutamiento Inteligente<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">tic el problema que l&#8217;entrada nom\u00e8s es llegeix si el missat t\u00e9 un minin de caracters, per example &#8216;teulada&#8217; es perdria. qui pot ser el responsable?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><img decoding=\"async\" src=\"qrc:\/web\/dist\/assets\/deep-thinking.webp\">Deep think completado (tard\u00f3 30s)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Molt bona pregunta. La resposta curta: <strong>el sospit\u00f3s principal \u00e9s <code>aplay<\/code><\/strong> (o m\u00e9s exactament, com es gestiona el final del flux quan es llegeix d&#8217;un <em>pipe<\/em>), <strong>no pas Piper<\/strong>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Per qu\u00e8?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Amb un text curt com <code>teulada<\/code>, Piper genera l&#8217;\u00e0udio correctament, per\u00f2 l&#8217;\u00e0udio \u00e9s tan breu que tot el contingut pot quedar dins el <strong>buffer de la targeta de so<\/strong> sense arribar a sonar. La seq\u00fc\u00e8ncia \u00e9s:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">plaintext<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1piper genera poques dades \u2500\u2500\u25ba pipe \u2500\u2500\u25ba aplay escriu al buffer de ALSA<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2 \u2502<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">3 EOF (el contenidor acaba) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">4 \u25bc<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">5 aplay tanca el dispositiu de so<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">6 (snd_pcm_close) i descarta el que queda<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">7<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">7 l\u00edneas de c\u00f3digo colapsadasExpandir<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Quan <code>aplay<\/code> rep <strong>EOF<\/strong> pel stdin (perqu\u00e8 el contenidor ha acabat i el pipe es tanca), <strong>tanca el dispositiu de so immediatament<\/strong>, sovint sense esperar que el buffer de la targeta es buidi del tot. Amb missatges llargs, nom\u00e9s es talla un tros final petit i no ho notes; amb <code>teulada<\/code>, l&#8217;\u00e0udio sencer pot estar al buffer i es perd.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Com confirmar-ho (diagn\u00f2stic r\u00e0pid)<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Prova de guardar la sortida en un fitxer i reproduir-lo despr\u00e9s:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">bash<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1docker exec piper bash -c \u00abecho &#8216;teulada&#8217; | piper &#8211;model \/config\/ca_ES-upc_ona-medium.onnx &#8211;output_file -\u00bb &gt; \/tmp\/teulada.wav<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2aplay \/tmp\/teulada.wav<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">3<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">3 l\u00edneas de c\u00f3digo colapsadasExpandir<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Si <strong>sona b\u00e9<\/strong> des del fitxer \u2192 Piper funciona; el problema \u00e9s el <strong>pipe + aplay<\/strong>.<\/li>\n\n\n\n<li>Si el fitxer \u00e9s <strong>buit o gaireb\u00e9 buit<\/strong> \u2192 llavors seria Piper (menys probable, per\u00f2 ho descartem).<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Solucions pr\u00e0ctiques<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Opci\u00f3 1 \u2014 Afegir un coix\u00ed de silenci<\/strong> al final, perqu\u00e8 aplay tingui m\u00e9s dades i nom\u00e9s es talli el silenci:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">bash<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1docker exec piper bash -c \u00abecho &#8216;teulada&#8217; | piper &#8211;model \/config\/ca_ES-upc_ona-medium.onnx &#8211;output_file -\u00bb \\<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2 | sox -t wav &#8211; -t wav &#8211; pad 0 0.5 | aplay<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">3<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">3 l\u00edneas de c\u00f3digo colapsadasExpandir<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Opci\u00f3 2 \u2014 Reproduir amb el servidor de so<\/strong> (PipeWire\/PulseAudio), que s\u00ed que drena el buffer correctament abans de tancar:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">bash<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1docker exec piper bash -c \u00abecho &#8216;teulada&#8217; | piper &#8211;model &#8230; &#8211;output_file -\u00bb | pw-play &#8211;<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2# o b\u00e9:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">3docker exec piper bash -c \u00abecho &#8216;teulada&#8217; | piper &#8211;model &#8230; &#8211;output_file -\u00bb | paplay &#8211;<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">4<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">4 l\u00edneas de c\u00f3digo colapsadasExpandir<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Opci\u00f3 3 \u2014 Fitxer temporal<\/strong> (la m\u00e9s robusta):<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">bash<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1docker exec piper bash -c \u00abecho &#8216;teulada&#8217; | piper &#8211;model &#8230; &#8211;output_file -\u00bb &gt; \/tmp\/veu.wav<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2aplay \/tmp\/veu.wav<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">3<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">3 l\u00edneas de c\u00f3digo colapsadasExpandir<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Opci\u00f3 4 \u2014 Si vols mantenir el pipe amb aplay<\/strong>, for\u00e7a un drenatge expl\u00edcit amb un petit retard final:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">bash<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1docker exec piper bash -c \u00abecho &#8216;teulada&#8217; | piper &#8211;model &#8230; &#8211;output_file -; sleep 1\u00bb | aplay &#8230;<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2 l\u00edneas de c\u00f3digo colapsadasExpandir<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">(el <code>sleep<\/code> dins el contenidor fa que el pipe no es tanqui tan de pressa).<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p class=\"wp-block-paragraph\">Prova primer el <strong>diagn\u00f2stic amb fitxer<\/strong> per confirmar-ho. Si vols, puc ajudar-te a muntar un script que gestioni autom\u00e0ticament el cas dels textos curts. Qu\u00e8 prefereixes?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Enrutamiento Inteligente<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Miquel@Miquel-PC:~$ docker exec piper bash -c \u00abecho &#8216;teulada&#8217; | piper &#8211;model \/config\/ca_ES-upc_ona-medium.onnx &#8211;output_file -\u00bb &gt; \/tmp\/teulada.wav aplay \/tmp\/teulada.wav Playing raw data &#8216;\/tmp\/teulada.wav&#8217; : Unsigned 8 bit, Rate 8000 Hz, Mono Miquel@Miquel-PC:~$<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><img decoding=\"async\" src=\"qrc:\/web\/dist\/assets\/deep-thinking.webp\">Deep think completado (tard\u00f3 27s)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Molt revelador! Aquest missatge d&#8217;aplay \u00e9s la clau del misteri:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">plaintext<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1Playing raw data &#8216;\/tmp\/teulada.wav&#8217; : Unsigned 8 bit, Rate 8000 Hz, Mono<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2 l\u00edneas de c\u00f3digo colapsadasExpandir<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Qu\u00e8 vol dir aquest missatge?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">aplay diu <strong>\u00abraw data\u00bb<\/strong> amb format per defecte (U8, 8000 Hz, mono) \u2014 aix\u00f2 \u00e9s el que fa quan <strong>no troba cap cap\u00e7alera WAV<\/strong> al fitxer. \u00c9s a dir, <strong>el fitxer <code>\/tmp\/teulada.wav<\/code> \u00e9s buit o gaireb\u00e9 buit<\/strong> (menys de 44 bytes).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Comparem amb el primer comandament, on deia:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">plaintext<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1Playing WAVE &#8216;stdin&#8217; : Signed 16 bit Little Endian, Rate 22050 Hz, Mono<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2 l\u00edneas de c\u00f3digo colapsadasExpandir<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">All\u00e0 aplay <strong>s\u00ed<\/strong> va detectar la cap\u00e7alera WAV. La difer\u00e8ncia no \u00e9s aplay: <strong>\u00e9s que Piper no ha generat l&#8217;\u00e0udio per a \u00abteulada\u00bb<\/strong>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">El responsable: la segmentaci\u00f3 de frases de Piper<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Piper nom\u00e9s sintetitza <strong>frases completes<\/strong>. Divideix el text en frases buscant puntuaci\u00f3 de final de frase (<code>.<\/code>, <code>!<\/code>, <code>?<\/code>):<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>\"Hola, s\u00f3c l'Ona... Barcelona.\"<\/code> \u2192 acaba amb punt \u2192 <strong>frase completa \u2192 sintetitza<\/strong> \u2705<\/li>\n\n\n\n<li><code>\"teulada\"<\/code> \u2192 <strong>no acaba amb cap puntuaci\u00f3<\/strong> \u2192 l&#8217;\u00faltim fragment es considera incomplet i <strong>es descarta<\/strong> a l&#8217;EOF \u274c<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">\u00c9s un comportament conegut de Piper: <strong>si el text no acaba amb <code>.<\/code>, <code>!<\/code> o <code>?<\/code>, no produeix cap sortida<\/strong>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Comprovaci\u00f3 r\u00e0pida<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">bash<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1ls -l \/tmp\/teulada.wav # espera 0 bytes o &lt; 44<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2 l\u00edneas de c\u00f3digo colapsadasExpandir<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">bash<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1docker exec piper bash -c \u00abecho &#8216;teulada.&#8217; | piper &#8211;model \/config\/ca_ES-upc_ona-medium.onnx &#8211;output_file -\u00bb &gt; \/tmp\/teulada2.wav<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2aplay \/tmp\/teulada2.wav # ara hauria de dir \u00abPlaying WAVE&#8230;\u00bb i sonar<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">3<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">3 l\u00edneas de c\u00f3digo colapsadasExpandir<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Soluci\u00f3<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Assegura que el text <strong>sempre acabi amb puntuaci\u00f3<\/strong> abans de passar-lo a Piper. Per exemple:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">bash<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1docker exec piper bash -c \u00abecho \\\u00bbteulada.\\\u00bb | piper &#8211;model \/config\/ca_ES-upc_ona-medium.onnx &#8211;output_file -\u00bb | aplay -f S16_LE -r 22050 -c 1<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2 l\u00edneas de c\u00f3digo colapsadasExpandir<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Si ho fas des d&#8217;un script amb text variable, pots normalitzar-ho aix\u00ed:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">bash<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1TEXT=\u00bbteulada\u00bb<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2# Afegeix punt si el text no acaba en puntuaci\u00f3<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">3[[ \u00ab$TEXT\u00bb =~ [.!?]$ ]] || TEXT=\u00bb$TEXT.\u00bb<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">4<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">5docker exec piper bash -c \u00abecho \\\u00bb$TEXT\\\u00bb | piper &#8211;model \/config\/ca_ES-upc_ona-medium.onnx &#8211;output_file -\u00bb | aplay -f S16_LE -r 22050 -c 1<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">6<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">6 l\u00edneas de c\u00f3digo colapsadasExpandir<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Prova el test de <code>teulada.<\/code> amb punt i digue&#8217;m si ja sona \u2014 si \u00e9s aix\u00ed, el problema era exactament aquest.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Enrutamiento Inteligente<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Miquel@Miquel-PC:~$ ls -l \/tmp\/teulada.wav # espera 0 bytes o &lt; 44 -rw-rw-r&#8211; 1 Miquel Miquel 0 de set. 19 11:14 \/tmp\/teulada.wav Miquel@Miquel-PC:~$ docker exec piper bash -c \u00abecho &#8216;teulada.&#8217; | piper &#8211;model \/config\/ca_ES-upc_ona-medium.onnx &#8211;output_file -\u00bb &gt; \/tmp\/teulada2.wav aplay \/tmp\/teulada2.wav # ara hauria de dir \u00abPlaying WAVE&#8230;\u00bb i sonar Playing raw data &#8216;\/tmp\/teulada2.wav&#8217; : Unsigned 8 bit, Rate 8000 Hz, Mono Miquel@Miquel-PC:~$ no ha sonat encara que els altaveus han fet un sorollet?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><img decoding=\"async\" src=\"qrc:\/web\/dist\/assets\/deep-thinking.webp\">Deep think completado (tard\u00f3 41s)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Aquest resultat descarta la hip\u00f2tesi de la puntuaci\u00f3: fins i tot amb <code>teulada.<\/code> no hi ha sortida. El <strong>\u00absorollet\u00bb<\/strong> que has sentit \u00e9s simplement el <strong>clic de la targeta de so en obrir-se i tancar-se<\/strong> sense rebre dades \u2014 aplay no va reproduir res perqu\u00e8 el fitxer era buit (o quasi).<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">On som ara<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Prova<\/th><th>Resultat<\/th><th>Conclusi\u00f3<\/th><\/tr><\/thead><tbody><tr><td><code>teulada<\/code> (sense punt)<\/td><td>0 bytes<\/td><td>Piper no genera res<\/td><\/tr><tr><td><code>teulada.<\/code> (amb punt)<\/td><td>\u00abraw data\u00bb \u2192 buit<\/td><td><strong>La puntuaci\u00f3 no \u00e9s la causa<\/strong><\/td><\/tr><tr><td>Missatge llarg original<\/td><td>\u00abPlaying WAVE\u00bb \u2705<\/td><td>Piper funciona amb text llarg<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">El sospit\u00f3s ha canviat: <strong>Piper no genera cap sortida per a textos curts<\/strong> (o b\u00e9 docker exec perd la sortida petita). Cal separar aquestes dues possibilitats.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Diagn\u00f2stic 1: Quant surt exactament per stdout?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">bash<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1docker exec piper bash -c \u00abecho &#8216;teulada.&#8217; | piper &#8211;model \/config\/ca_ES-upc_ona-medium.onnx &#8211;output_file -\u00bb | wc -c<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2 l\u00edneas de c\u00f3digo colapsadasExpandir<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Si d\u00f3na <strong>0<\/strong> \u2192 Piper no sintetitza res (el problema \u00e9s dins el contenidor).<\/li>\n\n\n\n<li>Si d\u00f3na <strong>> 0<\/strong> \u2192 Piper s\u00ed que genera \u00e0udio, per\u00f2 el redireccionament\/docker exec el perd (problema de flux).<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Diagn\u00f2stic 2: Prova dins del contenidor (evita el stdout de docker)<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">bash<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1docker exec piper bash -c \u00abecho &#8216;teulada.&#8217; | piper &#8211;model \/config\/ca_ES-upc_ona-medium.onnx &#8211;output_file \/tmp\/test.wav &amp;&amp; wc -c \/tmp\/test.wav\u00bb<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2 l\u00edneas de c\u00f3digo colapsadasExpandir<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Si el fitxer dins el contenidor t\u00e9 <strong>> 0 bytes<\/strong> \u2192 Piper funciona; el problema \u00e9s el <strong>stdout de docker exec<\/strong> (buffering\/truncament per sortides petites).<\/li>\n\n\n\n<li>Si \u00e9s <strong>0 bytes<\/strong> \u2192 Piper mateix descarta els textos curts.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Diagn\u00f2stic 3: Troba el llindar<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">bash<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1for T in \u00abteulada\u00bb \u00abHola\u00bb \u00abHola Ona\u00bb \u00abHola Ona, com estas\u00bb \u00abAquesta es una frase normal\u00bb \u00abAquesta es una frase una mica mes llarga per provar el sistema\u00bb; do<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2 printf \u00ab%-45s \u2192 \u00bb \u00ab$T\u00bb<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">3 docker exec piper bash -c \u00abecho \\\u00bb$T\\\u00bb | piper &#8211;model \/config\/ca_ES-upc_ona-medium.onnx &#8211;output_file -\u00bb | wc -c<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">4done<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">5<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">5 l\u00edneas de c\u00f3digo colapsadasExpandir<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Aix\u00f2 et dir\u00e0 a partir de quants car\u00e0cters comen\u00e7a a generar \u00e0udio (si \u00e9s que ho fa).<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Diagn\u00f2stic 4: Mira si piper escriu algun error a stderr<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">bash<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1docker exec piper bash -c \u00abecho &#8216;teulada.&#8217; | piper &#8211;model \/config\/ca_ES-upc_ona-medium.onnx &#8211;output_file -\u00bb 2&gt;&amp;1 | head -50<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2 l\u00edneas de c\u00f3digo colapsadasExpandir<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p class=\"wp-block-paragraph\">Executa el <strong>diagn\u00f2stic 1 i 2<\/strong> i digue&#8217;m els resultats \u2014 amb aix\u00f2 ja sabrem si el culpable \u00e9s Piper o el canal de sortida de docker exec. Segons el resultat, la soluci\u00f3 ser\u00e0 ben diferent:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Piper no genera res<\/strong> \u2192 caldr\u00e0 ajustar com es passa el text (potser la versi\u00f3 de piper del contenidor t\u00e9 un m\u00ednim de car\u00e0cters, o requereix un par\u00e0metre com <code>--sentence-silence<\/code> o for\u00e7ar l&#8217;\u00faltima frase).<\/li>\n\n\n\n<li><strong>docker exec perd la sortida<\/strong> \u2192 la soluci\u00f3 ser\u00e0 escriure a un fitxer dins el contenidor i fer <code>docker cp<\/code>, o afegir <code>sleep<\/code> al final perqu\u00e8 docker exec flushegi.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Enrutamiento Inteligente<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Miquel@Miquel-PC:~$ docker exec piper bash -c \u00abecho &#8216;teulada.&#8217; | piper &#8211;model \/config\/ca_ES-upc_ona-medium.onnx &#8211;output_file -\u00bb | wc -c 0 Miquel@Miquel-PC:~$ docker exec piper bash -c \u00abecho &#8216;teulada.&#8217; | piper &#8211;model \/config\/ca_ES-upc_ona-medium.onnx &#8211;output_file \/tmp\/test.wav &amp;&amp; wc -c \/tmp\/test.wav\u00bb 28204 \/tmp\/test.wav Miquel@Miquel-PC:~$<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><img decoding=\"async\" src=\"qrc:\/web\/dist\/assets\/deep-thinking.webp\">Deep think completado (tard\u00f3 50s)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Resultat molt important! Aix\u00f2 canvia completament el diagn\u00f2stic:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Prova<\/th><th>Resultat<\/th><\/tr><\/thead><tbody><tr><td><code>--output_file -<\/code> (stdout)<\/td><td><strong>0 bytes<\/strong><\/td><\/tr><tr><td><code>--output_file \/tmp\/test.wav<\/code> (fitxer)<\/td><td><strong>28.204 bytes<\/strong> \u2705<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Piper S\u00cd que sintetitza \u00abteulada.\u00bb perfectament<\/strong> (28 KB \u2248 0,6 s d&#8217;\u00e0udio). El problema no \u00e9s la s\u00edntesi: \u00e9s que <strong>el canal de sortida per stdout (<code>--output_file -<\/code>) a trav\u00e9s de <code>docker exec<\/code> es perd<\/strong>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Qu\u00e8 passa exactament?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Quan el text \u00e9s curt, la s\u00edntesi \u00e9s tan r\u00e0pida que el contenidor acaba <strong>abans que docker exec hagi flushejat\/entregat la sortida de stdout<\/strong>, i el flux es tanca amb 0 bytes. Amb el text llarg, hi ha prou temps\/dades perqu\u00e8 la sortida arribi b\u00e9 \u2014 per aix\u00f2 el primer comandament va funcionar.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">No \u00e9s culpa de Piper (sintetitza b\u00e9), sin\u00f3 de <strong>la combinaci\u00f3 <code>stdout + docker exec<\/code> per a processos que acaben molt de pressa<\/strong>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Comprovacions per confirmar-ho<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>1. El text llarg encara surt per stdout?<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">bash<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1docker exec piper bash -c \u00abecho \\\u00bbHola, s\u00f3c l&#8217;Ona, la meva veu s&#8217;ha desenvolupat a la UPC de Barcelona.\\\u00bb | piper &#8211;model \/config\/ca_ES-upc_ona-medium.onnx &#8211;output_file -\u00bb | wc -c<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2 l\u00edneas de c\u00f3digo colapsadasExpandir<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Si d\u00f3na &gt;0, confirma que \u00e9s una q\u00fcesti\u00f3 de temps\/mida de la sortida.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>2. Hi ha un fitxer anomenat <code>-<\/code> dins el contenidor?<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">bash<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1docker exec piper bash -c \u00abls -la\u00bb<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2 l\u00edneas de c\u00f3digo colapsadasExpandir<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">(Per descartar que piper interpreti <code>-<\/code> com a nom de fitxer.)<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Soluci\u00f3 robusta (recomanada)<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Ja que sabem que escriure a fitxer funciona sempre, la soluci\u00f3 fiable \u00e9s <strong>generar el WAV dins del contenidor i copiar-lo fora<\/strong>:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">bash<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1docker exec piper bash -c \u00abecho &#8216;teulada.&#8217; | piper &#8211;model \/config\/ca_ES-upc_ona-medium.onnx &#8211;output_file \/tmp\/veu.wav\u00bb<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2docker cp piper:\/tmp\/veu.wav \/tmp\/veu.wav<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">3aplay \/tmp\/veu.wav<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">4<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">4 l\u00edneas de c\u00f3digo colapsadasExpandir<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I per al teu servidor TTS, un script com aquest:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">bash<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1#!\/bin\/bash<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2# tts.sh \u00abtext a dir\u00bb<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">3TEXT=\u00bb${1:?\u00das: tts.sh &#8216;text&#8217;}\u00bb<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">4[[ \u00ab$TEXT\u00bb =~ [.!?]$ ]] || TEXT=\u00bb$TEXT.\u00bb<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">5<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">6docker exec piper bash -c \u00abecho \\\u00bb$TEXT\\\u00bb | piper &#8211;model \/config\/ca_ES-upc_ona-medium.onnx &#8211;output_file \/tmp\/veu.wav\u00bb<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">7docker cp piper:\/tmp\/veu.wav \/tmp\/veu.wav<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">8aplay \/tmp\/veu.wav<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">9<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">9 l\u00edneas de c\u00f3digo colapsadasExpandir<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Alternativa si vols mantenir el pipe<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Espera un moment despr\u00e9s de la s\u00edntesi dins el contenidor perqu\u00e8 docker exec flushegi la sortida:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">bash<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1docker exec piper bash -c \u00abecho &#8216;teulada.&#8217; | piper &#8211;model \/config\/ca_ES-upc_ona-medium.onnx &#8211;output_file -; sleep 1\u00bb | aplay -f S16_LE -r 22050 -c 1<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2 l\u00edneas de c\u00f3digo colapsadasExpandir<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">El <code>sleep 1<\/code> fa que el contenidor no surti immediatament i la sortida de stdout arribi sencera.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p class=\"wp-block-paragraph\">Prova primer la <strong>comprovaci\u00f3 1<\/strong> (text llarg per wc) per confirmar la teoria, i despr\u00e9s la soluci\u00f3 amb <code>docker cp<\/code> que \u00e9s la que et resoldr\u00e0 el problema de manera definitiva. Qu\u00e8 surt?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Enrutamiento Inteligente<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Miquel@Miquel-PC:~$ aplay \/tmp\/veu.wav Playing WAVE &#8216;\/tmp\/veu.wav&#8217; : Signed 16 bit Little Endian, Rate 22050 Hz, Mono Miquel@Miquel-PC:~$ #!\/bin\/bash # tts.sh \u00abtext a dir\u00bb TEXT=\u00bb${1:?\u00das: tts.sh &#8216;text&#8217;}\u00bb [[ \u00ab$TEXT\u00bb =~ [.!?]$ ]] || TEXT=\u00bb$TEXT.\u00bb docker exec piper bash -c \u00abecho \\\u00bb$TEXT\\\u00bb | piper &#8211;model \/config\/ca_ES-upc_ona-medium.onnx &#8211;output_file \/tmp\/veu.wav\u00bb docker cp piper:\/tmp\/veu.wav \/tmp\/veu.wav aplay \/tmp\/veu.wav bash: 1: \u00das: tts.sh text bash: !?]$ ]] || TEXT=\u00bb$TEXT.\u00bb: event not found Traceback (most recent call last): File \u00ab\/lsiopy\/bin\/piper\u00bb, line 6, in &lt;module&gt; sys.exit(main()) ~~~~^^ File \u00ab\/lsiopy\/lib\/python3.14\/site-packages\/piper\/__main__.py\u00bb, line 251, in main with wav_file: ^^^^^^^^ File \u00ab\/usr\/lib\/python3.14\/wave.py\u00bb, line 477, in __exit__ self.close() ~~~~~~~~~~^^ File \u00ab\/usr\/lib\/python3.14\/wave.py\u00bb, line 595, in close self._ensure_header_written(0) ~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^ File \u00ab\/usr\/lib\/python3.14\/wave.py\u00bb, line 613, in _ensure_header_written raise Error(&#8216;# channels not specified&#8217;) wave.Error: # channels not specified Successfully copied 1.54kB to \/tmp\/veu.wav Playing raw data &#8216;\/tmp\/veu.wav&#8217; : Unsigned 8 bit, Rate 8000 Hz, Mono Miquel@Miquel-PC:~$ el primer bash ha dit &#8216;teulada&#8217; el segon nom\u00e9s un sorollet<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><img decoding=\"async\" src=\"qrc:\/web\/dist\/assets\/deep-thinking.webp\">Deep think completado (tard\u00f3 15s)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Perfecte! El que has fet \u00e9s <strong>enganxar el script l\u00ednia a l\u00ednia al terminal interactiu<\/strong>, i aix\u00f2 ha provocat dos errors cl\u00e0ssics:<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Qu\u00e8 ha passat<\/h2>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong><code>bash: !?]$ ]]...: event not found<\/code><\/strong> \u2192 El terminal interactiu de bash interpreta <code>!?<\/code> com a <em>expansi\u00f3 d&#8217;historial<\/em> (buscar l&#8217;\u00faltima comanda que contingui aquest text). Per aix\u00f2 la l\u00ednia del regex ha esclatat. <strong>En un fitxer de script aix\u00f2 no passa<\/strong> (l&#8217;expansi\u00f3 d&#8217;historial nom\u00e9s actua al terminal).<\/li>\n\n\n\n<li><strong><code>wave.Error: # channels not specified<\/code><\/strong> \u2192 Com que l&#8217;assignaci\u00f3 de <code>$TEXT<\/code> va fallar (no hi havia argument), Piper va rebre <strong>text buit<\/strong> i va petar en intentar tancar el WAV. El <code>docker cp<\/code> va copiar un fitxer antic\/parcial (1,54 kB) i aplay el va reproduir com a dades brutes \u2192 el \u00absorollet\u00bb.<\/li>\n<\/ol>\n\n\n\n<h2 class=\"wp-block-heading\">La soluci\u00f3: crea el script com a fitxer<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Copia i enganxa aix\u00f2 (sencer) al terminal:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">bash<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1cat &gt; ~\/tts.sh &lt;&lt;&#8216;EOF&#8217;<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2#!\/bin\/bash<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">3# tts.sh \u00abtext a dir\u00bb<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">4TEXT=\u00bb$1&#8243;<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">5if [ -z \u00ab$TEXT\u00bb ]; then<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">6 echo \u00ab\u00das: tts.sh &#8216;text a dir'\u00bb &gt;&amp;2<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">7 exit 1<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">8fi<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">9<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">10# Afegeix un punt final si el text no en t\u00e9<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">11case \u00ab$TEXT\u00bb in<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">12 *[.!?]) ;;<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">13 *) TEXT=\u00bb$TEXT.\u00bb ;;<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">14esac<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">15<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">16docker exec piper bash -c \u00abecho \\\u00bb$TEXT\\\u00bb | piper &#8211;model \/config\/ca_ES-upc_ona-medium.onnx &#8211;output_file \/tmp\/veu.wav\u00bb<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">17docker cp piper:\/tmp\/veu.wav \/tmp\/veu.wav<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">18aplay \/tmp\/veu.wav<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">19EOF<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">20chmod +x ~\/tts.sh<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">21<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">21 l\u00edneas de c\u00f3digo colapsadasExpandir<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ara prova-ho:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">bash<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1~\/tts.sh \u00abteulada\u00bb<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2~\/tts.sh \u00abHola, s\u00f3c l&#8217;Ona, la meva veu s&#8217;ha desenvolupat a la UPC de Barcelona.\u00bb<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">3<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">3 l\u00edneas de c\u00f3digo colapsadasExpandir<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Per qu\u00e8 aquesta versi\u00f3 funciona<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong><code>case<\/code> en comptes de regex<\/strong> \u2192 evita el problema del <code>!<\/code> (l&#8217;expansi\u00f3 d&#8217;historial no s&#8217;activa dins d&#8217;un fitxer).<\/li>\n\n\n\n<li><strong>El text es passa com a argument<\/strong> (<code>$1<\/code>), no enganxat al mig.<\/li>\n\n\n\n<li><strong>La s\u00edntesi va a un fitxer<\/strong> (<code>\/tmp\/veu.wav<\/code>), que \u00e9s el m\u00e8tode que hem verificat que sempre funciona (28 KB per a \u00abteulada.\u00bb), i despr\u00e9s <code>docker cp<\/code> el porta a l&#8217;host perqu\u00e8 <code>aplay<\/code> el reprodueixi.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Si vols confirmar la teoria del stdout perdut, tamb\u00e9 pots provar aix\u00f2 (hauria de donar &gt;0):<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">bash<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1docker exec piper bash -c \u00abecho \\\u00bbHola, s\u00f3c l&#8217;Ona, la meva veu s&#8217;ha desenvolupat a la UPC de Barcelona.\\\u00bb | piper &#8211;model \/config\/ca_ES-upc_ona-medium.onnx &#8211;output_file -\u00bb | wc -c<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2 l\u00edneas de c\u00f3digo colapsadasExpandir<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Prova el script i digue&#8217;m si ja parla l&#8217;Ona correctament! \ud83d\udde3\ufe0f<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Enrutamiento Inteligente<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Miquel@Miquel-PC:~$ ~\/tts.sh \u00abteulada\u00bb ~\/tts.sh \u00abHola, s\u00f3c l&#8217;Ona, la meva veu s&#8217;ha desenvolupat a la UPC de Barcelona.\u00bb Successfully copied 30.2kB to \/tmp\/veu.wav Playing WAVE &#8216;\/tmp\/veu.wav&#8217; : Signed 16 bit Little Endian, Rate 22050 Hz, Mono Successfully copied 214kB to \/tmp\/veu.wav Playing WAVE &#8216;\/tmp\/veu.wav&#8217; : Signed 16 bit Little Endian, Rate 22050 Hz, Mono Miquel@Miquel-PC:~$ perfecte. els dos van b\u00e9<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><img decoding=\"async\" src=\"qrc:\/web\/dist\/assets\/deep-thinking.webp\">Deep think completado (tard\u00f3 2s)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Molt b\u00e9! \ud83c\udf89 L&#8217;Ona ja parla sense problemes, tant per a paraules curtes (<code>teulada<\/code> \u2192 30,2 kB) com per a frases llargues (214 kB).<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Resum del problema i la soluci\u00f3<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Causa arrel:<\/strong> el canal de sortida per stdout (<code>--output_file -<\/code>) a trav\u00e9s de <code>docker exec<\/code> perdia les dades quan la s\u00edntesi acabava massa de pressa (textos curts). No era culpa ni de Piper ni de aplay.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Soluci\u00f3 final:<\/strong> generar el WAV dins del contenidor, copiar-lo amb <code>docker cp<\/code> i reproduir-lo amb <code>aplay<\/code>. Aix\u00ed sempre funciona, sigui quin sigui el llarg del text.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">El teu script <code>~\/tts.sh<\/code> queda com a base s\u00f2lida per al teu servidor TTS.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Millores opcionals (si vols)<\/h2>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Evitar el <code>docker cp<\/code><\/strong> \u2014 Si recrees el contenidor, pots muntar un volum compartit i escriure-hi directament:<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">bash<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1docker run &#8230; -v \/tmp\/audio:\/audio &#8230; piper<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2# despr\u00e9s, al script:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">3docker exec piper bash -c \u00abecho \\\u00bb$TEXT\\\u00bb | piper &#8211;model &#8230; &#8211;output_file \/audio\/veu.wav\u00bb<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">4aplay \/tmp\/audio\/veu.wav<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">5<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">5 l\u00edneas de c\u00f3digo colapsadasExpandir<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Tornar al pipe pur<\/strong> (si vols estalviar el pas per fitxer), amb el <code>sleep<\/code> perqu\u00e8 docker exec flushegi:<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">bash<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1docker exec piper bash -c \u00abecho \\\u00bb$TEXT\\\u00bb | piper &#8211;model &#8230; &#8211;output_file -; sleep 1&#8243; | aplay -f S16_LE -r 22050 -c 1<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2 l\u00edneas de c\u00f3digo colapsadasExpandir<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Integrar-ho amb el teu tts-server<\/strong>: el script ja accepta el text com a argument, aix\u00ed que pots cridar-lo des del teu servei quan rebi una petici\u00f3.<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">Necessites res m\u00e9s, com ara adaptar el script perqu\u00e8 llegeixi el text d&#8217;un fitxer o d&#8217;un par\u00e0metre de la l\u00ednia de comandes d&#8217;una altra manera?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Enrutamiento Inteligente<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">dacord ho integrem a tts_server<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><img decoding=\"async\" src=\"qrc:\/web\/dist\/assets\/deep-thinking.webp\">Deep think completado (tard\u00f3 2s)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">D&#8217;acord! Abans de proposar-te la integraci\u00f3, deixa&#8217;m recordar si tenim context sobre el teu tts-server.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><img decoding=\"async\" src=\"qrc:\/web\/dist\/assets\/deep-thinking.webp\">Deep think completado (tard\u00f3 2s)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">No tinc cap informaci\u00f3 pr\u00e8via sobre el teu <code>tts-server<\/code>, aix\u00ed que necessito que em diguis com est\u00e0 fet per integrar-hi el script de la manera correcta.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Preguntes r\u00e0pides<\/h2>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Qu\u00e8 \u00e9s <code>tts-server<\/code>?<\/strong> \u2014 Un script Python (Flask\/FastAPI)? Node.js? Un binari? Un servei systemd?<\/li>\n\n\n\n<li><strong>On \u00e9s el codi?<\/strong> \u2014 Ruta del fitxer o directori (p. ex. <code>~\/tts-server\/<\/code>, <code>\/opt\/tts-server\/<\/code>&#8230;)<\/li>\n\n\n\n<li><strong>Quina interf\u00edcie exposa?<\/strong> \u2014 HTTP (endpoint tipus <code>POST \/tts<\/code>)? L\u00ednia de comandes? Socket?<\/li>\n\n\n\n<li><strong>Com rep el text i qu\u00e8 ha de retornar?<\/strong> \u2014 JSON amb text? Ha de tornar l&#8217;\u00e0udio com a resposta HTTP o nom\u00e9s reproduir-lo?<\/li>\n<\/ol>\n\n\n\n<h2 class=\"wp-block-heading\">Mentrestant, una integraci\u00f3 gen\u00e8rica (Python + HTTP)<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Perqu\u00e8 et facis una idea, aix\u00ed seria un servidor m\u00ednim que fa servir el teu pipeline (script \u2192 Piper \u2192 aplay) i retorna l&#8217;\u00e0udio:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">python<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1from flask import Flask, request, Response, jsonify<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2import subprocess, tempfile, os<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">3<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">4app = Flask(__name__)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">5<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">6def sintetitza(text):<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">7 \u00ab\u00bb\u00bbGenera WAV amb el contenidor piper i el retorna com a bytes.\u00bb\u00bb\u00bb<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">8 # 1. Genera el WAV dins del contenidor<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">9 subprocess.run(<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">10 [\u00abdocker\u00bb, \u00abexec\u00bb, \u00abpiper\u00bb, \u00abbash\u00bb, \u00ab-c\u00bb,<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">11 f\u00bbecho \\\u00bb{text}\\\u00bb | piper &#8211;model \/config\/ca_ES-upc_ona-medium.onnx &#8211;output_file \/tmp\/veu.wav\u00bb],<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">12 check=True)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">13 # 2. Copia&#8217;l fora<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">14 subprocess.run([\u00abdocker\u00bb, \u00abcp\u00bb, \u00abpiper:\/tmp\/veu.wav\u00bb, \u00ab\/tmp\/veu.wav\u00bb], check=True)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">15 # 3. Llegeix el fitxer<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">16 with open(\u00ab\/tmp\/veu.wav\u00bb, \u00abrb\u00bb) as f:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">17 return f.read()<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">18<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">19@app.post(\u00ab\/tts\u00bb)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">20def tts():<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">21 data = request.get_json(force=True)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">22 text = data.get(\u00abtext\u00bb, \u00ab\u00bb)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">23 if not text:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">24 return jsonify({\u00aberror\u00bb: \u00abfalta el camp &#8216;text'\u00bb}), 400<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">25 audio = sintetitza(text)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">26 return Response(audio, mimetype=\u00bbaudio\/wav\u00bb)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">27<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">27 l\u00edneas de c\u00f3digo colapsadasExpandir<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Amb aix\u00f2, una petici\u00f3 <code>POST \/tts<\/code> amb <code>{\"text\": \"teulada\"}<\/code> retornaria el WAV directament, i tu decideixes si el client el reprodueix o el servidor ho fa amb <code>aplay<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Expliqueu-me com \u00e9s el teu <code>tts-server<\/code> i t&#8217;ho adapto exactament. \ud83d\ude42<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Enrutamiento Inteligente<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"\/tmp\/UosAiImage-1789812210593.jpg\" alt=\"\"\/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">demana&#8217;m la informaci\u00f3 que vulguis<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><img decoding=\"async\" src=\"qrc:\/web\/dist\/assets\/deep-thinking.webp\">Deep think completado (tard\u00f3 5s)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Gr\u00e0cies per la captura! Ja veig que \u00e9s un projecte Python amb <code>venv<\/code>, uns scripts de prova (<code>prova_piper.py<\/code>, <code>prova_piper_dbg.py<\/code>) i una subcarpeta <code>tts-server\/<\/code>. Per integrar-hi el pipeline correctament, necessito que em responguis aix\u00f2:<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Preguntes<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>1. Ruta absoluta del projecte<\/strong><br>Quina \u00e9s la ruta completa del directori? (p. ex. <code>\/home\/Miquel\/tts-server\/<\/code>) \u2014 Aix\u00ed puc llegir els fitxers directament i veure el codi real.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>2. On \u00e9s el codi principal del servidor?<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>El servidor en si \u00e9s dins la subcarpeta <code>tts-server\/<\/code>? (quins fitxers hi ha?)<\/li>\n\n\n\n<li>O <code>prova_piper.py<\/code> \/ <code>prova_piper_dbg.py<\/code> s\u00f3n els scripts que fas servir?<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>3. Quin framework utilitza?<\/strong><br>Flask? FastAPI? <code>http.server<\/code> de Python? Sockets?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>4. Quina interf\u00edcie exposa?<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Port i endpoints (p. ex. <code>POST \/tts<\/code> al port 5000)<\/li>\n\n\n\n<li>Qu\u00e8 rep (JSON amb <code>text<\/code>?) i qu\u00e8 retorna (\u00e0udio WAV? nom\u00e9s OK? text?)<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>5. Com l&#8217;arrenques?<\/strong><br><code>python server.py<\/code>? Un servei systemd? Docker?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>6. El <code>venv\/<\/code> \u00e9s el que utilitza el servidor?<\/strong><br>I quin Python hi ha dins (<code>venv\/bin\/python --version<\/code>)?<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p class=\"wp-block-paragraph\">Un cop em diguis la <strong>ruta absoluta<\/strong>, puc llegir <code>prova_piper.py<\/code>, <code>server.log<\/code> i el contingut de <code>tts-server\/<\/code> per adaptar-ho exactament al teu codi, sense suposar res.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Enrutamiento Inteligente<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">ubicaci\u00f3: \/home\/Miquel\/tts-server<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Execution ProcessProcessed for 24s<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Perfecte, ara llegeixo els fitxers per veure el codi real.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><img decoding=\"async\" src=\"qrc:\/web\/dist\/assets\/deep-thinking.webp\">Deep think completado (tard\u00f3 8s)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ja tinc una visi\u00f3 clara! Llegeixo els fitxers de prova i el log: el teu <code>tts-server<\/code> \u00e9s en realitat un servidor <strong>speech-to-speech<\/strong> (STT amb Parakeet + VAD + TTS amb Piper) que escolta al port <strong>10200<\/strong> i parla per socket amb missatges JSON (<code>synthesize<\/code>, <code>audio-start<\/code>, <code>audio-chunk<\/code>, <code>audio-stop<\/code>).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Els fitxers <code>server.py<\/code>\/<code>app.py<\/code> dins la subcarpeta no existeixen (m&#8217;ha dit \u00abPar\u00e0metre no v\u00e0lid\u00bb). Deixa&#8217;m localitzar el codi real del servidor.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><img decoding=\"async\" src=\"qrc:\/web\/dist\/assets\/deep-thinking.webp\">Deep think completado (tard\u00f3 3s)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">La subcarpeta <code>tts-server\/<\/code> i <code>piper-config\/<\/code> existeixen, per\u00f2 no puc llistar-ne el contingut directament des d&#8217;aqu\u00ed (nom\u00e9s en veig les metadades). Necessito que em mostris l&#8217;arbre de fitxers per localitzar el codi del servidor.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Executa aix\u00f2 al terminal i enganxa&#8217;m la sortida<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">bash<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1find \/home\/Miquel\/tts-server -maxdepth 4 -type f | sort<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2 l\u00edneas de c\u00f3digo colapsadasExpandir<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Amb la llista de fitxers podr\u00e9 llegir el codi exacte on es crida Piper dins del servidor (el que ara deu fer servir <code>--output_file -<\/code> i pateix el problema dels textos curts) i adaptar-hi la soluci\u00f3 robusta del fitxer + <code>docker cp<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Enrutamiento Inteligente<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Miquel@Miquel-PC:~$ find \/home\/Miquel\/tts-server -maxdepth 4 -type f | sort \/home\/Miquel\/tts-server\/piper-config\/ca_ES-upc_ona-medium.onnx \/home\/Miquel\/tts-server\/piper-config\/ca_ES-upc_ona-medium.onnx.json \/home\/Miquel\/tts-server\/piper-config\/voices.json \/home\/Miquel\/tts-server\/prova_piper_dbg.py \/home\/Miquel\/tts-server\/prova_piper.py \/home\/Miquel\/tts-server\/server.log \/home\/Miquel\/tts-server\/tts-server\/prova_piper_dbg.py \/home\/Miquel\/tts-server\/tts-server\/prova_piper.py \/home\/Miquel\/tts-server\/tts-server\/server.log \/home\/Miquel\/tts-server\/tts-server\/venv\/bin\/accelerate \/home\/Miquel\/tts-server\/tts-server\/venv\/bin\/accelerate-config \/home\/Miquel\/tts-server\/tts-server\/venv\/bin\/accelerate-estimate-memory \/home\/Miquel\/tts-server\/tts-server\/venv\/bin\/accelerate-launch \/home\/Miquel\/tts-server\/tts-server\/venv\/bin\/accelerate-merge-weights \/home\/Miquel\/tts-server\/tts-server\/venv\/bin\/activate \/home\/Miquel\/tts-server\/tts-server\/venv\/bin\/activate.csh \/home\/Miquel\/tts-server\/tts-server\/venv\/bin\/activate.fish \/home\/Miquel\/tts-server\/tts-server\/venv\/bin\/Activate.ps1 \/home\/Miquel\/tts-server\/tts-server\/venv\/bin\/cffi-gen-src \/home\/Miquel\/tts-server\/tts-server\/venv\/bin\/distro \/home\/Miquel\/tts-server\/tts-server\/venv\/bin\/f2py \/home\/Miquel\/tts-server\/tts-server\/venv\/bin\/fastapi \/home\/Miquel\/tts-server\/tts-server\/venv\/bin\/faster-qwen3-tts \/home\/Miquel\/tts-server\/tts-server\/venv\/bin\/gradio \/home\/Miquel\/tts-server\/tts-server\/venv\/bin\/hf \/home\/Miquel\/tts-server\/tts-server\/venv\/bin\/hf-gradio \/home\/Miquel\/tts-server\/tts-server\/venv\/bin\/httpx \/home\/Miquel\/tts-server\/tts-server\/venv\/bin\/huggingface-cli \/home\/Miquel\/tts-server\/tts-server\/venv\/bin\/idna \/home\/Miquel\/tts-server\/tts-server\/venv\/bin\/isympy \/home\/Miquel\/tts-server\/tts-server\/venv\/bin\/markdown-it \/home\/Miquel\/tts-server\/tts-server\/venv\/bin\/nano-parakeet \/home\/Miquel\/tts-server\/tts-server\/venv\/bin\/nltk \/home\/Miquel\/tts-server\/tts-server\/venv\/bin\/normalizer \/home\/Miquel\/tts-server\/tts-server\/venv\/bin\/numba \/home\/Miquel\/tts-server\/tts-server\/venv\/bin\/numpy-config \/home\/Miquel\/tts-server\/tts-server\/venv\/bin\/onnxruntime_test \/home\/Miquel\/tts-server\/tts-server\/venv\/bin\/openai \/home\/Miquel\/tts-server\/tts-server\/venv\/bin\/pip \/home\/Miquel\/tts-server\/tts-server\/venv\/bin\/pip3 \/home\/Miquel\/tts-server\/tts-server\/venv\/bin\/pip3.12 \/home\/Miquel\/tts-server\/tts-server\/venv\/bin\/proton \/home\/Miquel\/tts-server\/tts-server\/venv\/bin\/proton-viewer \/home\/Miquel\/tts-server\/tts-server\/venv\/bin\/pygmentize \/home\/Miquel\/tts-server\/tts-server\/venv\/bin\/qwen-tts-demo \/home\/Miquel\/tts-server\/tts-server\/venv\/bin\/speech-to-speech \/home\/Miquel\/tts-server\/tts-server\/venv\/bin\/tiny-agents \/home\/Miquel\/tts-server\/tts-server\/venv\/bin\/torchfrtrace \/home\/Miquel\/tts-server\/tts-server\/venv\/bin\/torchrun \/home\/Miquel\/tts-server\/tts-server\/venv\/bin\/tqdm \/home\/Miquel\/tts-server\/tts-server\/venv\/bin\/transformers \/home\/Miquel\/tts-server\/tts-server\/venv\/bin\/typer \/home\/Miquel\/tts-server\/tts-server\/venv\/bin\/upload_theme \/home\/Miquel\/tts-server\/tts-server\/venv\/bin\/uvicorn \/home\/Miquel\/tts-server\/tts-server\/venv\/bin\/websockets \/home\/Miquel\/tts-server\/tts-server\/venv\/pyvenv.cfg \/home\/Miquel\/tts-server\/venv\/bin\/accelerate \/home\/Miquel\/tts-server\/venv\/bin\/accelerate-config \/home\/Miquel\/tts-server\/venv\/bin\/accelerate-estimate-memory \/home\/Miquel\/tts-server\/venv\/bin\/accelerate-launch \/home\/Miquel\/tts-server\/venv\/bin\/accelerate-merge-weights \/home\/Miquel\/tts-server\/venv\/bin\/activate \/home\/Miquel\/tts-server\/venv\/bin\/activate.csh \/home\/Miquel\/tts-server\/venv\/bin\/activate.fish \/home\/Miquel\/tts-server\/venv\/bin\/Activate.ps1 \/home\/Miquel\/tts-server\/venv\/bin\/cffi-gen-src \/home\/Miquel\/tts-server\/venv\/bin\/distro \/home\/Miquel\/tts-server\/venv\/bin\/f2py \/home\/Miquel\/tts-server\/venv\/bin\/fastapi \/home\/Miquel\/tts-server\/venv\/bin\/faster-qwen3-tts \/home\/Miquel\/tts-server\/venv\/bin\/gradio \/home\/Miquel\/tts-server\/venv\/bin\/hf \/home\/Miquel\/tts-server\/venv\/bin\/hf-gradio \/home\/Miquel\/tts-server\/venv\/bin\/httpx \/home\/Miquel\/tts-server\/venv\/bin\/huggingface-cli \/home\/Miquel\/tts-server\/venv\/bin\/idna \/home\/Miquel\/tts-server\/venv\/bin\/isympy \/home\/Miquel\/tts-server\/venv\/bin\/markdown-it \/home\/Miquel\/tts-server\/venv\/bin\/nano-parakeet \/home\/Miquel\/tts-server\/venv\/bin\/nltk \/home\/Miquel\/tts-server\/venv\/bin\/normalizer \/home\/Miquel\/tts-server\/venv\/bin\/numba \/home\/Miquel\/tts-server\/venv\/bin\/numpy-config \/home\/Miquel\/tts-server\/venv\/bin\/onnxruntime_test \/home\/Miquel\/tts-server\/venv\/bin\/openai \/home\/Miquel\/tts-server\/venv\/bin\/pip \/home\/Miquel\/tts-server\/venv\/bin\/pip3 \/home\/Miquel\/tts-server\/venv\/bin\/pip3.12 \/home\/Miquel\/tts-server\/venv\/bin\/proton \/home\/Miquel\/tts-server\/venv\/bin\/proton-viewer \/home\/Miquel\/tts-server\/venv\/bin\/pygmentize \/home\/Miquel\/tts-server\/venv\/bin\/qwen-tts-demo \/home\/Miquel\/tts-server\/venv\/bin\/speech-to-speech \/home\/Miquel\/tts-server\/venv\/bin\/tiny-agents \/home\/Miquel\/tts-server\/venv\/bin\/torchfrtrace \/home\/Miquel\/tts-server\/venv\/bin\/torchrun \/home\/Miquel\/tts-server\/venv\/bin\/tqdm \/home\/Miquel\/tts-server\/venv\/bin\/transformers \/home\/Miquel\/tts-server\/venv\/bin\/typer \/home\/Miquel\/tts-server\/venv\/bin\/upload_theme \/home\/Miquel\/tts-server\/venv\/bin\/uvicorn \/home\/Miquel\/tts-server\/venv\/bin\/websockets \/home\/Miquel\/tts-server\/venv\/pyvenv.cfg Miquel@Miquel-PC:~$<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><img decoding=\"async\" src=\"qrc:\/web\/dist\/assets\/deep-thinking.webp\">Deep think completado (tard\u00f3 6s)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">El codi del servidor <strong>no \u00e9s un projecte local<\/strong>: \u00e9s un paquet Python instal\u00b7lat al venv (el binari <code>speech-to-speech<\/code> i el log ho confirmen \u2014 paquet <code>speech_to_speech<\/code>). El <code>find<\/code> amb <code>-maxdepth 4<\/code> no arriba fins a <code>site-packages<\/code>, per aix\u00f2 no el veus.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Executa aix\u00f2 per localitzar el codi on es crida Piper:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">bash<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1find \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech -type f -name \u00ab*.py\u00bb | sort<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2 l\u00edneas de c\u00f3digo colapsadasExpandir<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">bash<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1grep -rln \u00abpiper\\|piper-config\\|output_file\u00bb \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/ 2&gt;\/dev\/null<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2 l\u00edneas de c\u00f3digo colapsadasExpandir<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">La primera comanda et donar\u00e0 l&#8217;estructura del paquet; la segona, <strong>quins fitxers concrets fan servir Piper<\/strong> (all\u00e0 hi haur\u00e0 el codi que hem de modificar per arreglar els textos curts). Enganxa&#8217;m la sortida.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Enrutamiento Inteligente<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Miquel@Miquel-PC:~$ find \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech -type f -name \u00ab*.py\u00bb | sort \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/api\/__init__.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/api\/openai_realtime\/handlers\/audio.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/api\/openai_realtime\/handlers\/base.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/api\/openai_realtime\/handlers\/conversation.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/api\/openai_realtime\/handlers\/__init__.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/api\/openai_realtime\/handlers\/response.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/api\/openai_realtime\/handlers\/session.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/api\/openai_realtime\/__init__.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/api\/openai_realtime\/pipeline_unit.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/api\/openai_realtime\/runtime_config.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/api\/openai_realtime\/server.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/api\/openai_realtime\/service.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/api\/openai_realtime\/utils.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/api\/openai_realtime\/websocket_router.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/arguments_classes\/chat_tts_arguments.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/arguments_classes\/facebookmms_tts_arguments.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/arguments_classes\/faster_whisper_stt_arguments.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/arguments_classes\/__init__.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/arguments_classes\/kokoro_tts_arguments.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/arguments_classes\/language_model_arguments.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/arguments_classes\/language_model_base_arguments.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/arguments_classes\/mlx_audio_whisper_arguments.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/arguments_classes\/module_arguments.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/arguments_classes\/paraformer_stt_arguments.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/arguments_classes\/parakeet_tdt_arguments.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/arguments_classes\/pocket_tts_arguments.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/arguments_classes\/qwen3_tts_arguments.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/arguments_classes\/responses_api_language_model_arguments.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/arguments_classes\/socket_receiver_arguments.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/arguments_classes\/socket_sender_arguments.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/arguments_classes\/vad_arguments.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/arguments_classes\/websocket_streamer_arguments.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/arguments_classes\/whisper_stt_arguments.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/baseHandler.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/connections\/__init__.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/connections\/local_audio_streamer.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/connections\/socket_receiver.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/connections\/socket_sender.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/connections\/websocket_streamer.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/__init__.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/LLM\/chat.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/LLM\/compaction_prompt.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/LLM\/__init__.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/LLM\/language_model.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/LLM\/lm_output_processor.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/LLM\/responses_api_language_model.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/LLM\/tool_call\/function_call.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/LLM\/tool_call\/function_tool.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/LLM\/tool_call\/signature_from_schema.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/LLM\/tool_call\/tool_prompt.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/LLM\/utils.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/LLM\/voice_prompt.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/pipeline\/cancel_scope.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/pipeline\/control.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/pipeline\/events.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/pipeline\/handler_types.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/pipeline\/__init__.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/pipeline\/log_context.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/pipeline\/messages.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/pipeline\/queue_types.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/pipeline\/speculative_turns.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/s2s_pipeline.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/STT\/base_stt_handler.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/STT\/faster_whisper_handler.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/STT\/__init__.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/STT\/lightning_whisper_mlx_handler.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/STT\/mlx_audio_whisper_handler.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/STT\/paraformer_handler.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/STT\/parakeet_tdt_handler.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/STT\/smart_progressive_streaming.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/STT\/transcription_notifier.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/STT\/whisper_stt_handler.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/TTS\/chatTTS_handler.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/TTS\/facebookmms_handler.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/TTS\/__init__.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/TTS\/kokoro_handler.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/TTS\/pocket_tts_handler.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/TTS\/qwen3_tts_handler.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/utils\/__init__.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/utils\/mlx_lock.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/utils\/thread_manager.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/utils\/utils.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/VAD\/__init__.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/VAD\/vad_handler.py \/home\/Miquel\/tts-server\/venv\/lib\/python3.12\/site-packages\/speech_to_speech\/VAD\/vad_iterator.py Miquel@Miquel-PC:~$<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><img decoding=\"async\" src=\"qrc:\/web\/dist\/assets\/deep-thinking.webp\">Deep think completado (tard\u00f3 10s)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Interessant! Al paquet <code>speech_to_speech<\/code> <strong>no hi ha cap handler de Piper<\/strong> (nom\u00e9s chatTTS, facebookmms, kokoro, pocket_tts, qwen3). Per\u00f2 el teu client <code>prova_piper.py<\/code> fa servir la veu <code>ca_ES-upc_ona-medium<\/code> (Piper) i funciona. Deixa&#8217;m llegir els fitxers clau per veure com es connecta Piper i on es genera l&#8217;\u00e0udio.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><img decoding=\"async\" src=\"qrc:\/web\/dist\/assets\/deep-thinking.webp\">Deep think completado (tard\u00f3 8s)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">El paquet <code>speech_to_speech<\/code> instal\u00b7lat \u00e9s el pipeline complet (VAD \u2192 STT \u2192 LLM \u2192 TTS amb chatTTS\/facebookMMS\/pocket\/kokoro\/qwen3), per\u00f2 <strong>no cont\u00e9 cap handler de Piper<\/strong> ni el protocol JSON que fa servir el teu <code>prova_piper.py<\/code> (missatges <code>synthesize<\/code>, <code>audio-chunk<\/code>, port 10200).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Aix\u00f2 vol dir que el teu servidor a 10200 \u00e9s un <strong>codi propi<\/strong> que no he trobat encara. Necessito localitzar-lo.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Executa aquestes comandes i enganxa&#8217;m la sortida<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>1. Trobar el codi que implementa el protocol (synthesize \/ audio-chunk \/ 10200):<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">bash<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1grep -rln \u00abaudio-chunk\\|synthesize\\|10200\u00bb \/home\/Miquel\/tts-server 2&gt;\/dev\/null | grep -v \u00ab\/venv\/\u00bb<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2 l\u00edneas de c\u00f3digo colapsadasExpandir<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Si no surt res, amplia a tot el teu home:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">bash<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1grep -rln \u00abaudio-chunk\u00bb \/home\/Miquel 2&gt;\/dev\/null | grep -v \u00ab\/venv\/\u00bb | head -20<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2 l\u00edneas de c\u00f3digo colapsadasExpandir<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>2. Veure qu\u00e8 est\u00e0 executant-se ara mateix (el servidor en marxa):<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">bash<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1ps aux | grep -v grep | grep -E \u00abpython|speech\u00bb<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2 l\u00edneas de c\u00f3digo colapsadasExpandir<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>3. Com l&#8217;arrenques? (historial de comandes):<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">bash<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1history | grep -iE \u00abtts|speech|10200|piper\u00bb | tail -20<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2 l\u00edneas de c\u00f3digo colapsadasExpandir<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Amb aix\u00f2 sabr\u00e9 on \u00e9s el codi del servidor que hem de modificar (on es crida Piper amb <code>--output_file -<\/code>) per canviar-lo a la soluci\u00f3 robusta del fitxer + <code>docker cp<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Enrutamiento Inteligente<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Miquel@Miquel-PC:~$ grep -rln \u00abaudio-chunk\\|synthesize\\|10200\u00bb \/home\/Miquel\/tts-server 2&gt;\/dev\/null | grep -v \u00ab\/venv\/\u00bb<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><img decoding=\"async\" src=\"qrc:\/web\/dist\/assets\/deep-thinking.webp\">Deep think completado (tard\u00f3 2s)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">La cerca dins de <code>\/home\/Miquel\/tts-server<\/code> no ha trobat res, aix\u00ed que el codi del servidor de 10200 <strong>\u00e9s en un altre lloc<\/strong>. Necessito les altres dues comandes per localitzar-lo:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>1. Cerca m\u00e9s \u00e0mplia a tot el teu home:<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">bash<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1grep -rln \u00abaudio-chunk\u00bb \/home\/Miquel 2&gt;\/dev\/null | grep -v \u00ab\/venv\/\u00bb | head -20<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2 l\u00edneas de c\u00f3digo colapsadasExpandir<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>2. Qu\u00e8 s&#8217;est\u00e0 executant ara mateix?<\/strong> (aix\u00f2 \u00e9s el m\u00e9s important \u2014 et dir\u00e0 des d&#8217;on s&#8217;ha llan\u00e7at el servidor):<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">bash<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1ps aux | grep -v grep | grep -E \u00abpython|speech\u00bb<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2 l\u00edneas de c\u00f3digo colapsadasExpandir<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>3. Historial d&#8217;arrencada:<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">bash<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1history | grep -iE \u00abtts|speech|10200|piper\u00bb | tail -20<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2 l\u00edneas de c\u00f3digo colapsadasExpandir<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">La comanda <strong>2<\/strong> \u00e9s clau: mostrar\u00e0 la l\u00ednia de comandes completa del proc\u00e9s que escolta al port 10200, i amb aix\u00f2 sabr\u00e9 exactament quin script i des de quina ruta s&#8217;executa.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Enrutamiento Inteligente<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">tampoc trova res.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><img decoding=\"async\" src=\"qrc:\/web\/dist\/assets\/deep-thinking.webp\">Deep think completado (tard\u00f3 3s)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Cap problema. Necessito les comandes de processos per veure qu\u00e8 escolta al port 10200. Executa aquestes:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>1. Quin proc\u00e9s escolta al port 10200?<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">bash<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1ss -tlnp | grep 10200<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2 l\u00edneas de c\u00f3digo colapsadasExpandir<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>2. Processos Python\/speech en marxa:<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">bash<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1ps aux | grep -v grep | grep -E \u00abpython|speech|piper\u00bb<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2 l\u00edneas de c\u00f3digo colapsadasExpandir<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>3. Si no hi ha res en marxa<\/strong>, busca el codi en llocs habituals fora del home:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">bash<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1ls -la \/opt \/srv 2&gt;\/dev\/null | head -40<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2find \/opt \/srv \/usr\/local\/bin -maxdepth 3 -type f \\( -name \u00ab*tts*\u00bb -o -name \u00ab*speech*\u00bb \\) 2&gt;\/dev\/null<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">3<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">3 l\u00edneas de c\u00f3digo colapsadasExpandir<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">La comanda <strong>1<\/strong> \u00e9s la m\u00e9s directa: et dir\u00e0 el PID i la l\u00ednia de comandes exacta del servidor que at\u00e9n les peticions de <code>prova_piper.py<\/code>. Si el servidor no est\u00e0 en marxa ara mateix, digues-m&#8217;ho i busquem el codi per un altre cam\u00ed (p. ex. <code>history<\/code> o <code>nohup.out<\/code>).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Enrutamiento Inteligente<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Miquel@Miquel-PC:~$ ss -tlnp | grep 10200 LISTEN 0 4096 0.0.0.0:10200 0.0.0.0:* LISTEN 0 4096 [::]:10200 [::]:* Miquel@Miquel-PC:~$ ps aux | grep -v grep | grep -E \u00abpython|speech|piper\u00bb root 1777 0.0 0.0 220 76 ? S 10:57 0:00 s6-supervise svc-piper 911 1934 0.0 0.8 288484 64444 ? Ssl 10:57 0:01 python3 -m wyoming_piper &#8211;uri tcp:\/\/0.0.0.0:10200 &#8211;length-scale 1.0 &#8211;noise-scale 0.667 &#8211;noise-w 0.333 &#8211;speaker 0 &#8211;voice ca_ES-upc_ona-medium &#8211;data-dir \/config &#8211;download-dir \/config &#8211;update-voices Miquel 5861 38.4 3.9 1186907532 316636 ? Rl 11:04 27:18 \/usr\/lib\/qt6\/libexec\/QtWebEngineProcess &#8211;type=renderer &#8211;webengine-schemes=qrc:LV &#8211;no-sandbox &#8211;disable-speech-api &#8211;enable-logging &#8211;log-level=2 &#8211;disable-databases &#8211;disable-blink-features=EyeDropperAPI,WebOTP &#8211;lang=es &#8211;num-raster-threads=2 &#8211;enable-main-frame-before-activation &#8211;renderer-client-id=3 &#8211;time-ticks-at-unix-epoch=-1789808253112640 &#8211;launch-time-ticks=439341684 &#8211;shared-files=v8_context_snapshot_data:100 &#8211;field-trial-handle=0,i,8848351394108136581,9392166901195399885,262144 &#8211;enable-features=NetworkServiceInProcess2,TracingServiceInProcess &#8211;disable-features=BackgroundFetch,EyeDropper,InstalledApp,WebOTP,WebPayments,WebUSB &#8211;variations-seed-version Miquel@Miquel-PC:~$<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><img decoding=\"async\" src=\"qrc:\/web\/dist\/assets\/deep-thinking.webp\">Deep think completado (tard\u00f3 57s)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ara ho veig tot clar! \ud83c\udfaf El teu \u00abtts-server\u00bb \u00e9s en realitat <strong>wyoming-piper<\/strong>: el servidor de TTS de Home Assistant (protocol Wyoming) executant-se dins del contenidor <code>piper<\/code>:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">plaintext<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1PID 1934: python3 -m wyoming_piper &#8211;uri tcp:\/\/0.0.0.0:10200 &#8230; &#8211;voice ca_ES-upc_ona-medium<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2 l\u00edneas de c\u00f3digo colapsadasExpandir<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I el teu <code>prova_piper.py<\/code> parla el <strong>protocol Wyoming<\/strong> (missatges <code>synthesize<\/code>, <code>audio-start<\/code>, <code>audio-chunk<\/code>, <code>audio-stop<\/code>).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Aix\u00f2 canvia la perspectiva de la integraci\u00f3: el servidor wyoming-piper <strong>no passa per <code>docker exec --output_file -<\/code><\/strong> (fa servir Piper internament), aix\u00ed que el problema de \u00abteulada\u00bb aqu\u00ed \u00e9s probablement un altre: <strong>la puntuaci\u00f3 final de frase<\/strong>. Recorda que mai vam aclarir del tot si Piper requereix el punt final \u2014 ho hem de verificar.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Prova 1: Piper requereix el punt final? (dins el contenidor)<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">bash<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1docker exec piper bash -c \u00abecho &#8216;teulada&#8217; | piper &#8211;model \/config\/ca_ES-upc_ona-medium.onnx &#8211;output_file \/tmp\/t1.wav &amp;&amp; wc -c \/tmp\/t1.wav\u00bb<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2docker exec piper bash -c \u00abecho &#8216;teulada.&#8217; | piper &#8211;model \/config\/ca_ES-upc_ona-medium.onnx &#8211;output_file \/tmp\/t2.wav &amp;&amp; wc -c \/tmp\/t2.wav\u00bb<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">3<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">3 l\u00edneas de c\u00f3digo colapsadasExpandir<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Prova 2: El servidor wyoming (port 10200) respon per a textos curts?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">bash<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1python3 &#8211; &lt;&lt;&#8216;EOF&#8217;<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2import socket, json<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">3<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">4def test(texto):<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">5 s = socket.create_connection((\u00ab127.0.0.1\u00bb, 10200), timeout=10)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">6 f = s.makefile(\u00abrwb\u00bb)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">7 evt = json.dumps({\u00abtype\u00bb: \u00absynthesize\u00bb, \u00abdata\u00bb: {\u00abtext\u00bb: texto, \u00abvoice\u00bb: \u00abca_ES-upc_ona-medium\u00bb}})<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">8 f.write((evt + \u00ab\\n\\n\u00bb).encode()); f.flush()<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">9 total = 0<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">10 while True:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">11 h = f.readline()<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">12 if not h: break<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">13 m = json.loads(h)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">14 if m.get(\u00abtype\u00bb) == \u00abaudio-chunk\u00bb:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">15 total += m[\u00abdata\u00bb][\u00abbytes\u00bb]; f.read(m[\u00abdata\u00bb][\u00abbytes\u00bb])<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">16 elif m.get(\u00abtype\u00bb) == \u00abaudio-stop\u00bb: break<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">17 s.close()<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">18 print(f\u00bb{texto!r}: {total} bytes d&#8217;\u00e0udio\u00bb)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">19<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">20test(\u00abteulada\u00bb)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">21test(\u00abteulada.\u00bb)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">22EOF<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">23<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">23 l\u00edneas de c\u00f3digo colapsadasExpandir<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Qu\u00e8 esperem<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Si <strong>Prova 1<\/strong>: <code>t1<\/code> = 0 i <code>t2<\/code> > 0 \u2192 <strong>Piper exigeix punt final<\/strong>.<\/li>\n\n\n\n<li>Si <strong>Prova 2<\/strong>: \u00abteulada\u00bb = 0 i \u00abteulada.\u00bb > 0 \u2192 <strong>el servidor falla per falta de puntuaci\u00f3<\/strong>, i la integraci\u00f3 consistir\u00e0 a fer que wyoming-piper afegeixi el punt autom\u00e0ticament (per exemple amb un wrapper al binari <code>piper<\/code> dins del contenidor).<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Executa les dues proves i enganxa&#8217;m els resultats.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Enrutamiento Inteligente<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Miquel@Miquel-PC:~$ docker exec piper bash -c \u00abecho &#8216;teulada&#8217; | piper &#8211;model \/config\/ca_ES-upc_ona-medium.onnx &#8211;output_file \/tmp\/t1.wav &amp;&amp; wc -c \/tmp\/t1.wav\u00bb docker exec piper bash -c \u00abecho &#8216;teulada.&#8217; | piper &#8211;model \/config\/ca_ES-upc_ona-medium.onnx &#8211;output_file \/tmp\/t2.wav &amp;&amp; wc -c \/tmp\/t2.wav\u00bb 42540 \/tmp\/t1.wav 28204 \/tmp\/t2.wav Miquel@Miquel-PC:~$<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><img decoding=\"async\" src=\"qrc:\/web\/dist\/assets\/deep-thinking.webp\">Deep think completado (tard\u00f3 3s)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Resultat molt aclaridor! Ambdues opcions generen \u00e0udio:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Text<\/th><th>Fitxer<\/th><th>Bytes<\/th><\/tr><\/thead><tbody><tr><td><code>teulada<\/code> (sense punt)<\/td><td>t1.wav<\/td><td><strong>42.540<\/strong> \u2705<\/td><\/tr><tr><td><code>teulada.<\/code> (amb punt)<\/td><td>t2.wav<\/td><td><strong>28.204<\/strong> \u2705<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Piper no requereix punt final<\/strong> \u2014 la hip\u00f2tesi de la puntuaci\u00f3 queda descartada. Els 0 bytes del principi eren nom\u00e9s el problema del stdout de <code>docker exec<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ara falta la <strong>Prova 2<\/strong> (la que et vaig donar): comprovar si el servidor wyoming-piper (port 10200) respon amb \u00e0udio per a \u00abteulada\u00bb. Executa-la:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">bash<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1python3 &#8211; &lt;&lt;&#8216;EOF&#8217;<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2import socket, json<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">3<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">4def test(texto):<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">5 s = socket.create_connection((\u00ab127.0.0.1\u00bb, 10200), timeout=10)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">6 f = s.makefile(\u00abrwb\u00bb)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">7 evt = json.dumps({\u00abtype\u00bb: \u00absynthesize\u00bb, \u00abdata\u00bb: {\u00abtext\u00bb: texto, \u00abvoice\u00bb: \u00abca_ES-upc_ona-medium\u00bb}})<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">8 f.write((evt + \u00ab\\n\\n\u00bb).encode()); f.flush()<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">9 total = 0<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">10 while True:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">11 h = f.readline()<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">12 if not h: break<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">13 m = json.loads(h)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">14 if m.get(\u00abtype\u00bb) == \u00abaudio-chunk\u00bb:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">15 total += m[\u00abdata\u00bb][\u00abbytes\u00bb]; f.read(m[\u00abdata\u00bb][\u00abbytes\u00bb])<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">16 elif m.get(\u00abtype\u00bb) == \u00abaudio-stop\u00bb: break<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">17 s.close()<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">18 print(f\u00bb{texto!r}: {total} bytes d&#8217;\u00e0udio\u00bb)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">19<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">20test(\u00abteulada\u00bb)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">21test(\u00abteulada.\u00bb)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">22EOF<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">23<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">23 l\u00edneas de c\u00f3digo colapsadasExpandir<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Aix\u00f2 ens dir\u00e0 si el servidor ja funciona per a textos curts (i llavors la integraci\u00f3 \u00e9s al teu client\/script) o si cal arreglar wyoming-piper.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Enrutamiento Inteligente<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Miquel@Miquel-PC:~$ python3 &#8211; &lt;&lt;&#8216;EOF&#8217; import socket, json def test(texto): s = socket.create_connection((\u00ab127.0.0.1\u00bb, 10200), timeout=10) f = s.makefile(\u00abrwb\u00bb) evt = json.dumps({\u00abtype\u00bb: \u00absynthesize\u00bb, \u00abdata\u00bb: {\u00abtext\u00bb: texto, \u00abvoice\u00bb: \u00abca_ES-upc_ona-medium\u00bb}}) f.write((evt + \u00ab\\n\\n\u00bb).encode()); f.flush() total = 0 while True: h = f.readline() if not h: break m = json.loads(h) if m.get(\u00abtype\u00bb) == \u00abaudio-chunk\u00bb: total += m[\u00abdata\u00bb][\u00abbytes\u00bb]; f.read(m[\u00abdata\u00bb][\u00abbytes\u00bb]) elif m.get(\u00abtype\u00bb) == \u00abaudio-stop\u00bb: break s.close() print(f\u00bb{texto!r}: {total} bytes d&#8217;\u00e0udio\u00bb) test(\u00abteulada\u00bb) test(\u00abteulada.\u00bb) EOF Traceback (most recent call last): File \u00ab&lt;stdin&gt;\u00bb, line 19, in &lt;module&gt; File \u00ab&lt;stdin&gt;\u00bb, line 12, in test File \u00ab\/usr\/lib\/python3.12\/json\/__init__.py\u00bb, line 346, in loads return _default_decoder.decode(s) ^^^^^^^^^^^^^^^^^^^^^^^^^^ File \u00ab\/usr\/lib\/python3.12\/json\/decoder.py\u00bb, line 341, in decode raise JSONDecodeError(\u00abExtra data\u00bb, s, end) json.decoder.JSONDecodeError: Extra data: line 1 column 62 (char 61) Miquel@Miquel-PC:~$<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><img decoding=\"async\" src=\"qrc:\/web\/dist\/assets\/deep-thinking.webp\">Deep think completado (tard\u00f3 33s)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">El test ha fallat amb un error de JSON: <strong>\u00abExtra data: line 1 column 62\u00bb<\/strong>. Aix\u00f2 vol dir que la primera l\u00ednia que torna el servidor <strong>no \u00e9s un sol objecte JSON<\/strong> \u2014 hi ha m\u00e9s dades enganxades, o el format real de la resposta \u00e9s diferent del que assum\u00edem. Necessito veure la resposta <strong>crua<\/strong> del servidor per entendre el protocol real.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Executa aquesta depuraci\u00f3<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">bash<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1python3 &#8211; &lt;&lt;&#8216;EOF&#8217;<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2import socket, json<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">3<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">4s = socket.create_connection((\u00ab127.0.0.1\u00bb, 10200), timeout=10)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">5f = s.makefile(\u00abrwb\u00bb)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">6evt = json.dumps({\u00abtype\u00bb: \u00absynthesize\u00bb, \u00abdata\u00bb: {\u00abtext\u00bb: \u00abteulada\u00bb, \u00abvoice\u00bb: \u00abca_ES-upc_ona-medium\u00bb}})<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">7print(\u00abENVIO:\u00bb, evt)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">8f.write((evt + \u00ab\\n\\n\u00bb).encode()); f.flush()<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">9<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">10s.settimeout(3)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">11try:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">12 raw = s.recv(200)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">13 print(\u00abREBUT cru:\u00bb, repr(raw))<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">14except socket.timeout:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">15 print(\u00ab(timeout: el servidor no respon)\u00bb)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">16s.close()<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">17EOF<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">18<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">18 l\u00edneas de c\u00f3digo colapsadasExpandir<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">El <code>repr()<\/code> et mostrar\u00e0 els bytes exactes que envia el servidor (incloent salts de l\u00ednia i car\u00e0cters de control). Amb aix\u00f2 veur\u00e9 si:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>El servidor espera un <strong>handshake previ<\/strong> (p. ex. un event <code>describe<\/code> primer, com fa el protocol Wyoming est\u00e0ndard)<\/li>\n\n\n\n<li>O la resposta t\u00e9 un format diferent del que fa servir el teu <code>prova_piper.py<\/code><\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Recorda que <code>prova_piper.py<\/code> <strong>no l&#8217;hem vist funcionar mai en aquesta conversa<\/strong> \u2014 potser el protocol del servidor real \u00e9s diferent del que assumeix el teu client.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Enrutamiento Inteligente<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Miquel@Miquel-PC:~$ python3 &#8211; &lt;&lt;&#8216;EOF&#8217; import socket, json s = socket.create_connection((\u00ab127.0.0.1\u00bb, 10200), timeout=10) f = s.makefile(\u00abrwb\u00bb) evt = json.dumps({\u00abtype\u00bb: \u00absynthesize\u00bb, \u00abdata\u00bb: {\u00abtext\u00bb: \u00abteulada\u00bb, \u00abvoice\u00bb: \u00abca_ES-upc_ona-medium\u00bb}}) print(\u00abENVIO:\u00bb, evt) f.write((evt + \u00ab\\n\\n\u00bb).encode()); f.flush() s.settimeout(3) try: raw = s.recv(200) print(\u00abREBUT cru:\u00bb, repr(raw)) except socket.timeout: print(\u00ab(timeout: el servidor no respon)\u00bb) s.close() EOF ENVIO: {\u00abtype\u00bb: \u00absynthesize\u00bb, \u00abdata\u00bb: {\u00abtext\u00bb: \u00abteulada\u00bb, \u00abvoice\u00bb: \u00abca_ES-upc_ona-medium\u00bb}} REBUT cru: b'{\u00abtype\u00bb: \u00abaudio-start\u00bb, \u00abversion\u00bb: \u00ab1.10.2\u00bb, \u00abdata_length\u00bb: 61}\\n{\u00abrate\u00bb: 22050, \u00abwidth\u00bb: 2, \u00abchannels\u00bb: 1, \u00abtimestamp\u00bb: null}{\u00abtype\u00bb: \u00abaudio-chunk\u00bb, \u00abversion\u00bb: \u00ab1.10.2\u00bb, \u00abdata_length\u00bb: 61, \u00abpayload_le&#8217; Miquel@Miquel-PC:~$<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><img decoding=\"async\" src=\"qrc:\/web\/dist\/assets\/deep-thinking.webp\">Deep think completado (tard\u00f3 83s)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ara s\u00ed que veig el protocol real! La resposta crua del servidor:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">plaintext<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1{\u00abtype\u00bb: \u00abaudio-start\u00bb, \u00abversion\u00bb: \u00ab1.10.2\u00bb, \u00abdata_length\u00bb: 61}\\n{\u00abrate\u00bb: 22050, &#8230;}{\u00abtype\u00bb: \u00abaudio-chunk\u00bb, \u00abversion\u00bb: \u00ab1.10.2\u00bb, \u00abdata_length\u00bb: 61, &#8230;<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2 l\u00edneas de c\u00f3digo colapsadasExpandir<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Dues conclusions importants<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>1. Bona not\u00edcia: el servidor wyoming-piper S\u00cd que genera \u00e0udio per a \u00abteulada\u00bb!<\/strong> \ud83c\udf89 Ha respost amb <code>audio-start<\/code> i <code>audio-chunk<\/code> \u2014 el problema dels textos curts era nom\u00e9s del cam\u00ed <code>docker exec --output_file -<\/code>, no del servidor.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>2. El teu <code>prova_piper.py<\/code> parla un protocol incorrecte.<\/strong> El protocol Wyoming real separa <strong>cap\u00e7alera<\/strong> (amb <code>data_length<\/code>) i <strong>payload<\/strong> (els bytes seg\u00fcents). El teu client assumia que la l\u00ednia JSON contenia les dades directament (<code>data: {bytes: N}<\/code>) \u2014 per aix\u00f2 petaria en llegir la resposta.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">La integraci\u00f3: un client Wyoming correcte<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Guarda aquest script (reempla\u00e7a el protocol erroni):<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">bash<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1cat &gt; \/home\/Miquel\/tts-server\/tts_wyoming.py &lt;&lt;&#8216;EOF&#8217;<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2#!\/usr\/bin\/env python3<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">3&#8243;\u00bb\u00bbClient Wyoming correcte per a wyoming-piper (TTS) &#8211; port 10200.\u00bb\u00bb\u00bb<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">4import json, socket, subprocess, sys, wave<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">5<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">6HOST, PORT = \u00ab127.0.0.1\u00bb, 10200<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">7VOICE = \u00abca_ES-upc_ona-medium\u00bb<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">8OUT = \u00ab\/tmp\/veu_wyoming.wav\u00bb<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">9<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">10def send_event(f, evt_type, data=None):<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">11 payload = json.dumps(data or {}).encode()<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">12 header = json.dumps({\u00abtype\u00bb: evt_type, \u00abdata_length\u00bb: len(payload)}).encode() + b\u00bb\\n\u00bb<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">13 f.write(header + payload)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">14 f.flush()<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">15<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">16def read_event(f):<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">17 line = f.readline()<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">18 if not line:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">19 return None, None<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">20 meta = json.loads(line)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">21 n = meta.get(\u00abdata_length\u00bb, 0)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">22 payload = f.read(n) if n &gt; 0 else b\u00bb\u00bb<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">23 return meta[\u00abtype\u00bb], payload<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">24<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">25def main():<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">26 text = sys.argv[1] if len(sys.argv) &gt; 1 else \u00abHola\u00bb<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">27 s = socket.create_connection((HOST, PORT), timeout=30)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">28 f = s.makefile(\u00abrwb\u00bb)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">29 send_event(f, \u00absynthesize\u00bb, {\u00abtext\u00bb: text, \u00abvoice\u00bb: VOICE})<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">30 chunks, rate, width, channels = [], 22050, 2, 1<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">31 while True:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">32 t, payload = read_event(f)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">33 if t is None:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">34 print(\u00abConnexi\u00f3 tancada pel servidor\u00bb); break<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">35 if t == \u00abaudio-start\u00bb:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">36 info = json.loads(payload)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">37 rate = info.get(\u00abrate\u00bb, rate)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">38 width = info.get(\u00abwidth\u00bb, width)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">39 channels = info.get(\u00abchannels\u00bb, channels)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">40 elif t == \u00abaudio-chunk\u00bb:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">41 chunks.append(payload)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">42 elif t == \u00abaudio-stop\u00bb:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">43 break<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">44 s.close()<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">45 audio = b\u00bb\u00bb.join(chunks)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">46 if not audio:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">47 print(\u00abERROR: no s&#8217;ha rebut \u00e0udio\u00bb); sys.exit(1)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">48 with wave.open(OUT, \u00abwb\u00bb) as w:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">49 w.setnchannels(channels); w.setsampwidth(width); w.setframerate(rate)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">50 w.writeframes(audio)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">51 print(f\u00bbOK: {OUT} ({len(audio)} bytes, {rate} Hz)\u00bb)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">52 subprocess.run([\u00abaplay\u00bb, OUT])<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">53<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">54if __name__ == \u00ab__main__\u00bb:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">55 main()<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">56EOF<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">57<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">57 l\u00edneas de c\u00f3digo colapsadasExpandir<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Prova-ho<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">bash<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1python3 \/home\/Miquel\/tts-server\/tts_wyoming.py \u00abteulada\u00bb<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2python3 \/home\/Miquel\/tts-server\/tts_wyoming.py \u00abHola, s\u00f3c l&#8217;Ona, la meva veu s&#8217;ha desenvolupat a la UPC de Barcelona.\u00bb<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">3<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">3 l\u00edneas de c\u00f3digo colapsadasExpandir<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Aquest client parla el protocol real de wyoming-piper: envia <code>synthesize<\/code> amb cap\u00e7alera correcta (<code>data_length<\/code>), llegeix <code>audio-start<\/code> \u2192 <code>audio-chunk<\/code> \u2192 <code>audio-stop<\/code>, guarda el WAV i el reprodueix amb <code>aplay<\/code>. <strong>El servidor ja fa la seva feina; el que faltava era el client correcte.<\/strong> Prova-ho i digue&#8217;m si l&#8217;Ona parla tant els textos curts com els llargs!<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Enrutamiento Inteligente<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"\/tmp\/UosAiImage-1789813628558.jpg\" alt=\"\"\/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">aix\u00ed es pot esborrar prova_piper.py?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><img decoding=\"async\" src=\"qrc:\/web\/dist\/assets\/deep-thinking.webp\">Deep think completado (tard\u00f3 6s)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">S\u00ed! Pots esborrar-los tranquil\u00b7lament \u2014 <code>tts_wyoming.py<\/code> els substitueix:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong><code>prova_piper.py<\/code><\/strong> \u2014 protocol Wyoming incorrecte (assumia <code>data: {bytes: N}<\/code> dins la cap\u00e7alera) \u2192 obsolet.<\/li>\n\n\n\n<li><strong><code>prova_piper_dbg.py<\/code><\/strong> \u2014 mateix protocol incorrecte, nom\u00e9s amb logs \u2192 tamb\u00e9 obsolet.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Per seguretat, en comptes d&#8217;esborrar-los, els moc a una carpeta de c\u00f2pia de seguretat (aix\u00ed si algun dia els necessites, hi s\u00f3n):<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><img decoding=\"async\" src=\"qrc:\/web\/dist\/assets\/deep-thinking.webp\">Deep think completado (tard\u00f3 1s)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Fet! \u2705 Els dos scripts antics han quedat moguts a:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">plaintext<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1\/home\/Miquel\/tts-server\/backup_scripts\/<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2\u251c\u2500\u2500 prova_piper.py<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">3\u2514\u2500\u2500 prova_piper_dbg.py<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">4<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">4 l\u00edneas de c\u00f3digo colapsadasExpandir<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Estructura final de <code>tts-server\/<\/code><\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Fitxer\/carpeta<\/th><th>Estat<\/th><\/tr><\/thead><tbody><tr><td><strong><code>tts_wyoming.py<\/code><\/strong><\/td><td>\u2705 El client correcte (protocol Wyoming real)<\/td><\/tr><tr><td><code>piper-config\/<\/code><\/td><td>Models de Piper<\/td><\/tr><tr><td><code>tts-server\/<\/code><\/td><td>Subcarpeta amb c\u00f2pies<\/td><\/tr><tr><td><code>venv\/<\/code><\/td><td>Entorn Python<\/td><\/tr><tr><td><code>server.log<\/code><\/td><td>Logs<\/td><\/tr><tr><td><code>backup_scripts\/<\/code><\/td><td>Antics scripts (obsolets)<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Si ho vols netejar del tot, ara pots esborrar la carpeta <code>backup_scripts<\/code> amb <code>rm -r<\/code> \u2014 o deixar-la com a c\u00f2pia de seguretat.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Recorda que l&#8217;\u00fanic que necessites fer servir d&#8217;ara endavant \u00e9s:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">bash<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1python3 \/home\/Miquel\/tts-server\/tts_wyoming.py \u00abteulada\u00bb<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2 l\u00edneas de c\u00f3digo colapsadasExpandir<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I si algun dia vols fer-ho servir com a ordre r\u00e0pida, pots crear un enlla\u00e7 o un <code>alias<\/code>:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">bash<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1echo &#8216;alias ona=\u00bbpython3 \/home\/Miquel\/tts-server\/tts_wyoming.py\u00bb&#8216; &gt;&gt; ~\/.bashrc<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2source ~\/.bashrc<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">3ona \u00abHola, s\u00f3c l&#8217;Ona!\u00bb<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">4<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">4 l\u00edneas de c\u00f3digo colapsadasExpandir<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Vols que t&#8217;ajudi amb alguna altra cosa?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Enrutamiento Inteligente<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">fem una prova amb un text cur, com &#8216;teulada&#8217;<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><img decoding=\"async\" src=\"qrc:\/web\/dist\/assets\/deep-thinking.webp\">Deep think completado (tard\u00f3 4s)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Endavant! Executa aix\u00f2 al teu terminal:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">bash<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1python3 \/home\/Miquel\/tts-server\/tts_wyoming.py \u00abteulada\u00bb<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2 l\u00edneas de c\u00f3digo colapsadasExpandir<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Qu\u00e8 has d&#8217;esperar<\/h2>\n\n\n\n<ol class=\"wp-block-list\">\n<li>El script connecta a wyoming-piper (port 10200) i envia <code>synthesize<\/code> amb el text <code>teulada<\/code>.<\/li>\n\n\n\n<li>El servidor sintetitza (recorda que ja vam veure que respon amb <code>audio-start<\/code> + <code>audio-chunk<\/code> per a textos curts).<\/li>\n\n\n\n<li>Veur\u00e0s un missatge com: plaintext 1OK: \/tmp\/veu_wyoming.wav (XXXXX bytes, 22050 Hz)22 l\u00edneas de c\u00f3digo colapsadasExpandir<\/li>\n\n\n\n<li>Llavors <code>aplay<\/code> reproduir\u00e0 el WAV i hauries de sentir <strong>\u00abteulada\u00bb<\/strong> \ud83d\udd0a<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">Si surt el <code>OK<\/code> i sona, ja tenim la integraci\u00f3 completa funcionant amb textos curts. Digue&#8217;m qu\u00e8 passa!<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Enrutamiento Inteligente<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Miquel@Miquel-PC:~$ python3 \/home\/Miquel\/tts-server\/tts_wyoming.py \u00abteulada\u00bb Traceback (most recent call last): File \u00ab\/home\/Miquel\/tts-server\/tts_wyoming.py\u00bb, line 54, in &lt;module&gt; main() File \u00ab\/home\/Miquel\/tts-server\/tts_wyoming.py\u00bb, line 31, in main t, payload = read_event(f) ^^^^^^^^^^^^^ File \u00ab\/home\/Miquel\/tts-server\/tts_wyoming.py\u00bb, line 19, in read_event meta = json.loads(line) ^^^^^^^^^^^^^^^^ File \u00ab\/usr\/lib\/python3.12\/json\/__init__.py\u00bb, line 341, in loads s = s.decode(detect_encoding(s), &#8216;surrogatepass&#8217;) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File \u00ab\/usr\/lib\/python3.12\/encodings\/utf_16_le.py\u00bb, line 16, in decode return codecs.utf_16_le_decode(input, errors, True) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ UnicodeDecodeError: &#8216;utf-16-le&#8217; codec can&#8217;t decode byte 0x0a in position 444: truncated data decoding with &#8216;utf-16-le&#8217; codec failed Miquel@Miquel-PC:~$<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><img decoding=\"async\" src=\"qrc:\/web\/dist\/assets\/deep-thinking.webp\">Deep think completado (tard\u00f3 47s)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">L&#8217;error ens dona una pista clau: <code>json.loads<\/code> ha rebut una l\u00ednia de m\u00e9s de 444 bytes plena de binaris (per aix\u00f2 detecta UTF-16). Aix\u00f2 vol dir que <strong>la lectura s&#8217;ha desincronitzat<\/strong>: hem llegit \u00e0udio cru com si fos una cap\u00e7alera. La meva suposici\u00f3 sobre el camp <code>data_length<\/code> dels <code>audio-chunk<\/code> deu ser incorrecta.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Necessito veure la seq\u00fc\u00e8ncia completa d&#8217;esdeveniments. Executa aquesta depuraci\u00f3 que mostra cada cap\u00e7alera i el seu payload:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">bash<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1python3 &#8211; &lt;&lt;&#8216;EOF&#8217;<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2import socket, json<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">3<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">4s = socket.create_connection((\u00ab127.0.0.1\u00bb, 10200), timeout=10)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">5f = s.makefile(\u00abrwb\u00bb)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">6<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">7# Envia synthesize amb el protocol correcte<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">8payload = json.dumps({\u00abtext\u00bb: \u00abteulada\u00bb, \u00abvoice\u00bb: \u00abca_ES-upc_ona-medium\u00bb}).encode()<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">9header = json.dumps({\u00abtype\u00bb: \u00absynthesize\u00bb, \u00abdata_length\u00bb: len(payload)}).encode() + b\u00bb\\n\u00bb<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">10f.write(header + payload); f.flush()<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">11<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">12for i in range(8):<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">13 line = f.readline()<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">14 if not line:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">15 print(f\u00bb&#8212; event {i}: fi de connexi\u00f3\u00bb); break<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">16 print(f\u00bb&#8212; event {i} &#8212;\u00ab)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">17 print(\u00abheader:\u00bb, repr(line[:120]))<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">18 try:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">19 meta = json.loads(line)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">20 n = meta.get(\u00abdata_length\u00bb, 0)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">21 print(\u00abtype:\u00bb, meta.get(\u00abtype\u00bb), \u00ab| data_length:\u00bb, n, \u00ab| claus:\u00bb, list(meta.keys()))<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">22 body = f.read(n)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">23 print(\u00abpayload:\u00bb, repr(body[:60]), \u00ab&#8230; len:\u00bb, len(body))<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">24 except Exception as e:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">25 print(\u00abERROR:\u00bb, e)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">26 break<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">27s.close()<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">28EOF<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">29<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">29 l\u00edneas de c\u00f3digo colapsadasExpandir<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Aix\u00f2 em mostrar\u00e0:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Quants bytes declara cada <code>audio-chunk<\/code> (<code>data_length<\/code>)<\/li>\n\n\n\n<li>Si hi ha un camp addicional (com <code>payload_length<\/code>) que \u00e9s el que realment indica la mida de l&#8217;\u00e0udio<\/li>\n\n\n\n<li>On exactament es desincronitza la lectura<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Amb aix\u00f2 corregeixo el client perqu\u00e8 llegeixi el protocol exacte de wyoming-piper.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Enrutamiento Inteligente<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Miquel@Miquel-PC:~$ python3 &#8211; &lt;&lt;&#8216;EOF&#8217; import socket, json s = socket.create_connection((\u00ab127.0.0.1\u00bb, 10200), timeout=10) f = s.makefile(\u00abrwb\u00bb) # Envia synthesize amb el protocol correcte payload = json.dumps({\u00abtext\u00bb: \u00abteulada\u00bb, \u00abvoice\u00bb: \u00abca_ES-upc_ona-medium\u00bb}).encode() header = json.dumps({\u00abtype\u00bb: \u00absynthesize\u00bb, \u00abdata_length\u00bb: len(payload)}).encode() + b\u00bb\\n\u00bb f.write(header + payload); f.flush() for i in range(8): line = f.readline() if not line: print(f\u00bb&#8212; event {i}: fi de connexi\u00f3\u00bb); break print(f\u00bb&#8212; event {i} &#8212;\u00ab) print(\u00abheader:\u00bb, repr(line[:120])) try: meta = json.loads(line) n = meta.get(\u00abdata_length\u00bb, 0) print(\u00abtype:\u00bb, meta.get(\u00abtype\u00bb), \u00ab| data_length:\u00bb, n, \u00ab| claus:\u00bb, list(meta.keys())) body = f.read(n) print(\u00abpayload:\u00bb, repr(body[:60]), \u00ab&#8230; len:\u00bb, len(body)) except Exception as e: print(\u00abERROR:\u00bb, e) break s.close() EOF &#8212; event 0 &#8212; header: b'{\u00abtype\u00bb: \u00abaudio-start\u00bb, \u00abversion\u00bb: \u00ab1.10.2\u00bb, \u00abdata_length\u00bb: 61}\\n&#8217; type: audio-start | data_length: 61 | claus: [&#8216;type&#8217;, &#8216;version&#8217;, &#8216;data_length&#8217;] payload: b'{\u00abrate\u00bb: 22050, \u00abwidth\u00bb: 2, \u00abchannels\u00bb: 1, \u00abtimestamp\u00bb: null&#8217; &#8230; len: 61 &#8212; event 1 &#8212; header: b'{\u00abtype\u00bb: \u00abaudio-chunk\u00bb, \u00abversion\u00bb: \u00ab1.10.2\u00bb, \u00abdata_length\u00bb: 61, \u00abpayload_length\u00bb: 2048}\\n&#8217; type: audio-chunk | data_length: 61 | claus: [&#8216;type&#8217;, &#8216;version&#8217;, &#8216;data_length&#8217;, &#8216;payload_length&#8217;] payload: b'{\u00abrate\u00bb: 22050, \u00abwidth\u00bb: 2, \u00abchannels\u00bb: 1, \u00abtimestamp\u00bb: null&#8217; &#8230; len: 61 &#8212; event 2 &#8212; header: b&#8217;@\\x01y\\x00\\xca\\xff\\xe5\\xfer\\xfe\\xdb\\xfed\\xfe&lt;\\xff\\x89\\xfe\\xaf\\xfe\\xfb\\xfe\\xb6\\xff\\xa9\\xffA\\x00\\x80\\x00&#8243;\\x01v\\x01N\\x01\\x04\\x02K\\x01,\\x02\\xcc\\x01R\\x01\\xa1\\x01\\x9d\\x01\\xc4\\x01&amp;\\x02z\\x02\\x0c\\x02\\xb9\\x02M\\x02\\x14\\x02|\\x01\\xfb\\x01\\xb5\\x02\\x0f\\x01\\xe3\\x02&amp;\\x02\\x07\\x03\\x16\\x03;\\x02\\xeb\\x01y\\x00b\\x03\\xf8\\x00\\xfa\\x01\\x7f\\x02\\x8a\\x01\\xf0\\x02\\x0e\\x02\\xc9\\x03\\x80\\x03t\\x03\\x92\\x03q\\x01\/\\x03\\x9d\\x01\\x86\\x02\\xa9\\x02V\\x03&#8242; ERROR: &#8216;utf-8&#8217; codec can&#8217;t decode byte 0xca in position 4: invalid continuation byte Miquel@Miquel-PC:~$<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><img decoding=\"async\" src=\"qrc:\/web\/dist\/assets\/deep-thinking.webp\">Deep think completado (tard\u00f3 32s)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ja ho veig! La depuraci\u00f3 ha revelat el format real dels <code>audio-chunk<\/code> de wyoming-piper:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">plaintext<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1{\u00abtype\u00bb: \u00abaudio-chunk\u00bb, \u00abversion\u00bb: \u00ab1.10.2\u00bb, \u00abdata_length\u00bb: 61, \u00abpayload_length\u00bb: 2048}<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2\u251c\u2500\u2500 data_length (61) \u2192 JSON de metadades (rate\/width\/channels\/timestamp)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">3\u2514\u2500\u2500 payload_length (2048) \u2192 L&#8217;\u00c0UDIO REAL<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">4<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">4 l\u00edneas de c\u00f3digo colapsadasExpandir<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">El meu client nom\u00e9s llegia <code>data_length<\/code> (61 bytes de metadades) i ignorava els <strong>2048 bytes d&#8217;\u00e0udio<\/strong> que venen despr\u00e9s \u2014 per aix\u00f2 la lectura es desincronitzava i llegia \u00e0udio cru com a cap\u00e7alera.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Corregeix el client<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">bash<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1cat &gt; \/home\/Miquel\/tts-server\/tts_wyoming.py &lt;&lt;&#8216;EOF&#8217;<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2#!\/usr\/bin\/env python3<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">3&#8243;\u00bb\u00bbClient Wyoming correcte per a wyoming-piper (TTS) &#8211; port 10200.\u00bb\u00bb\u00bb<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">4import json, socket, subprocess, sys, wave<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">5<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">6HOST, PORT = \u00ab127.0.0.1\u00bb, 10200<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">7VOICE = \u00abca_ES-upc_ona-medium\u00bb<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">8OUT = \u00ab\/tmp\/veu_wyoming.wav\u00bb<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">9<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">10def send_event(f, evt_type, data=None):<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">11 payload = json.dumps(data or {}).encode()<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">12 header = json.dumps({\u00abtype\u00bb: evt_type, \u00abdata_length\u00bb: len(payload)}).encode() + b\u00bb\\n\u00bb<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">13 f.write(header + payload)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">14 f.flush()<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">15<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">16def read_event(f):<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">17 line = f.readline()<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">18 if not line:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">19 return None, None, None<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">20 meta = json.loads(line)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">21 n = meta.get(\u00abdata_length\u00bb, 0)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">22 data = f.read(n) if n &gt; 0 else b\u00bb\u00bb<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">23 pn = meta.get(\u00abpayload_length\u00bb, 0) # camp extra amb l&#8217;\u00e0udio real<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">24 payload = f.read(pn) if pn &gt; 0 else b\u00bb\u00bb<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">25 return meta[\u00abtype\u00bb], data, payload<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">26<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">27def main():<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">28 text = sys.argv[1] if len(sys.argv) &gt; 1 else \u00abHola\u00bb<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">29 s = socket.create_connection((HOST, PORT), timeout=30)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">30 f = s.makefile(\u00abrwb\u00bb)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">31 send_event(f, \u00absynthesize\u00bb, {\u00abtext\u00bb: text, \u00abvoice\u00bb: VOICE})<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">32 chunks, rate, width, channels = [], 22050, 2, 1<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">33 while True:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">34 t, data, payload = read_event(f)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">35 if t is None:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">36 print(\u00abConnexi\u00f3 tancada pel servidor\u00bb); break<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">37 if t == \u00abaudio-start\u00bb:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">38 info = json.loads(data)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">39 rate = info.get(\u00abrate\u00bb, rate)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">40 width = info.get(\u00abwidth\u00bb, width)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">41 channels = info.get(\u00abchannels\u00bb, channels)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">42 elif t == \u00abaudio-chunk\u00bb:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">43 chunks.append(payload) # nom\u00e9s l&#8217;\u00e0udio real<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">44 elif t == \u00abaudio-stop\u00bb:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">45 break<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">46 s.close()<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">47 audio = b\u00bb\u00bb.join(chunks)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">48 if not audio:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">49 print(\u00abERROR: no s&#8217;ha rebut \u00e0udio\u00bb); sys.exit(1)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">50 with wave.open(OUT, \u00abwb\u00bb) as w:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">51 w.setnchannels(channels); w.setsampwidth(width); w.setframerate(rate)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">52 w.writeframes(audio)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">53 print(f\u00bbOK: {OUT} ({len(audio)} bytes, {rate} Hz)\u00bb)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">54 subprocess.run([\u00abaplay\u00bb, OUT])<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">55<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">56if __name__ == \u00ab__main__\u00bb:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">57 main()<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">58EOF<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">59<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">59 l\u00edneas de c\u00f3digo colapsadasExpandir<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Prova-ho<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">bash<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1python3 \/home\/Miquel\/tts-server\/tts_wyoming.py \u00abteulada\u00bb<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2 l\u00edneas de c\u00f3digo colapsadasExpandir<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ara hauria de llegir les metadades (61 bytes), acumular l&#8217;\u00e0udio real (<code>payload_length<\/code>), i reproduir \u00abteulada\u00bb correctament. Digue&#8217;m com va! \ud83d\udd0a<\/p>\n","protected":false},"excerpt":{"rendered":"<p>UOS AI iquel@Miquel-PC:~$ docker exec piper bash -c \u00abecho \\\u00bbHola, s\u00f3c l&#8217;Ona, la meva veu s&#8217;ha desenvolupat a la UPC de Barcelona.\\\u00bb | piper &#8211;model \/config\/ca_ES-upc_ona-medium.onnx &#8211;output_file -\u00bb | aplay -f S16_LE -r 22050 -c 1 Playing WAVE &#8216;stdin&#8217; : &hellip; <a href=\"https:\/\/www.beseit.net\/?p=19155\">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-19155","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\/19155","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=19155"}],"version-history":[{"count":1,"href":"https:\/\/www.beseit.net\/index.php?rest_route=\/wp\/v2\/posts\/19155\/revisions"}],"predecessor-version":[{"id":19156,"href":"https:\/\/www.beseit.net\/index.php?rest_route=\/wp\/v2\/posts\/19155\/revisions\/19156"}],"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=19155"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.beseit.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=19155"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.beseit.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=19155"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}