Script PowerShell: Test de veu amb entrada personalitzada

Script:

# Crear l'objecte de síntesi de veu
$speak = New-Object -ComObject SAPI.SpVoice

# Mostrar les veus disponibles
Write-Host "Veus disponibles:"
$voices = $speak.GetVoices()
for ($i = 0; $i -lt $voices.Count; $i++) {
    Write-Host "$i - " $voices.Item($i).GetDescription()
}

# Seleccionar veu
$index = Read-Host "Introdueix el número de la veu que vols utilitzar"
$speak.Voice = $voices.Item($index)

# Entrada de text
$entrada = Read-Host "Introdueix el text que vols escoltar"

# Reproduir el text
$speak.Speak($entrada)

🛠️ Com executar-lo

  1. Obre PowerShell com a administrador.
  2. Enganxa el codi anterior.
  3. Se’t mostrarà la llista de veus disponibles.
  4. Introdueix el número corresponent a la veu que vols provar.
  5. Escriu el text que vols que es llegeixi.
  • Aquest script accedeix a les veus SAPI, com Microsoft David Desktop, Zira Desktop, etc.
  • Les veus Neural del Narrador (com JennyNeural, HelenaNeural) no són accessibles amb aquest mètode directe. Si vols, et puc preparar una versió amb WinRT per accedir a les veus OneCore.

Amb les veus del narrador:

Vols que et generi també la versió avançada amb WinRT per accedir a les veus del Narrador?

# Requereix Windows 10/11 i PowerShell 5.x o superior

Add-Type -AssemblyName System.Runtime.WindowsRuntime
$SynthType = [Windows.Media.SpeechSynthesis.SpeechSynthesizer]
$Synth = New-Object $SynthType

# Llistar veus disponibles
$voices = $Synth.AllVoices
Write-Host "Veus OneCore disponibles:"
for ($i = 0; $i -lt $voices.Count; $i++) {
    $voice = $voices[$i]
    Write-Host "$i - $($voice.DisplayName) [$($voice.Language)]"
}

# Seleccionar veu
$index = Read-Host "Introdueix el número de la veu que vols utilitzar"
$Synth.Voice = $voices[$index]

# Entrada de text
$text = Read-Host "Introdueix el text que vols escoltar"

# Sintetitzar i reproduir
$stream = $Synth.SynthesizeTextToStreamAsync($text).GetAwaiter().GetResult()
$player = New-Object System.Media.SoundPlayer
$player.Stream = $stream.AsStream()
$player.PlaySync()

Com executar-lo

  1. Obre PowerShell com a administrador.
  2. Enganxa el codi anterior.
  3. Veuràs la llista de veus OneCore disponibles (amb nom i idioma).
  4. Introdueix el número de la veu que vols provar.
  5. Escriu el text que vols escoltar.

Edge & Google TTS Web App

Edge & Google TTS Web App

Aquest projecte proporciona una aplicació web lleugera per convertir text en veu en diferents idiomes, mitjançant tecnologies de Microsoft (Edge TTS) i Google (gTTS).


📁 Estructura del projecte

/edge_tts_clean/
│
├── app.py (o serve_gtts.py)      ← Servidor Flask que processa les peticions web
├── generar_gtts.py               ← Conversió de text a veu amb Google TTS (gTTS)
│
├── /audio/                       ← Conté els fitxers d’àudio generats (tts.mp3)
│
├── /venv/                        ← Entorn virtual Python (Flask, edge_tts, gtts, etc.)
│
├── index.php                     ← Interfície web: formulari per introduir text i seleccionar veu
│                                 (executa generar_gtts.py o generar_tts.py segons la veu escollida)
└── ...


(venv_web) root@Synology_sec:/volume1/web/edge_tts_clean# dir
total 48
drwxrwxr-x+ 1 urqtejmi http    176 Jul 12 16:33 .
dr-xr-xr-x+ 1 root     root   1780 Jul 12 12:32 ..
dr-xr-xr-x+ 1 urqtejmi users     0 Jul 12 12:37 app
dr-xr-xr-x+ 1 urqtejmi users   420 Jul 12 13:56 audio
-r-xr-xr-x+ 1 urqtejmi users   603 Jul 12 12:36 estructura.php
-r-xr-xr-x+ 1 urqtejmi users   531 Jul 12 16:34 generar_gtts.py
-r-xr-xr-x+ 1 root     root  15984 Jul 12 13:37 hola.mp3
-r-xr-xr-x+ 1 urqtejmi users  3344 Jul 12 17:07 index.php
-r-xr-xr-x+ 1 urqtejmi users  3723 Jul 12 15:48 serve_tts.py
-r-xr-xr-x  1 urqtejmi users   420 Jul 12 13:35 setup.sh
-rwxrwxr-x+ 1 http     http  10224 Jul 12 17:10 tts.mp3
dr-xr-xr-x+ 1 root     root     56 Jul 12 13:31 venv
dr-xr-xr-x+ 1 urqtejmi users     0 Jul 12 12:37 web
(venv_web) root@Synology_sec:/volume1/web/edge_tts_clean#

✨ Característiques

  • Conversió de text a veu en català, castellà, anglès i francès
  • Suport per a veus de Microsoft Edge i Google
  • Interfície web simple (Flask) amb selector de veu
  • Opcionalment integrable amb PHP (index.php)

⚙️ Requisits

  • Python 3.9
  • Entorn virtual creat amb python -m venv venv

Paquets necessaris:

pip install flask edge-tts gtts

🚀 Execució

  1. Activa l’entorn virtual:
source venv/bin/activate
  1. Inicia el servidor:
python serve_gtts.py
  1. Accedeix a l’aplicació web:
http://<IP-del-servidor>:8000

🌍 Veus disponibles

Català

  • gtts-ca → Google TTS (Català)
  • ca-ES-JoanaNeural → Microsoft Edge TTS

Castellà

  • es-ES-ElviraNeural
  • es-ES-AlvaroNeural

Anglès

  • en-US-JennyNeural
  • en-GB-RyanNeural

Francès

  • fr-FR-DeniseNeural
  • fr-FR-HenriNeural

📄 Fitxers clau

  • serve_gtts.py: Aplicació Flask que gestiona la interfície i genera l’àudio.
  • generar_tts.py: Utilitza edge-tts per generar veu.
  • generar_gtts.py: Utilitza gtts per generar veu.
  • index.php: Versió alternativa amb PHP per entorns web que ho requereixin.

🚫 Notes de seguretat

  • Valida les entrades si s’exposa públicament.
  • Revisa permisos d’escriptura a la carpeta audio/.

📍 Desenvolupat a

  • Synology NAS (amb Python i entorns virtuals)
  • Accés LAN via IP local (p. ex. http://192.168.0.49:8000)

🎓 Llicència

Projecte privat per a finalitats educatives o de prova. No distribuïble com a servei comercial sense llicències de veus.


✏️ Contacte

Per a preguntes o ampliacions: [Afegeix les dades de contacte si cal]

Publicat dins de Veus

Text to Speech (amb Python)

Estr/00-sapi_tts/
├── exec_script_py.php     ← Crida el Python
├── generar_tts.py ← Script Python que genera l'àudio 
|                    des de Synology
├── index.php              ← Interfície web
└── tts.mp3                ← Fitxer generatuctura

index.php

<?php
// Llista de veus
$voices = [
    "Microsoft Helena Desktop",
    "Microsoft Hazel Desktop",
    "Microsoft Zira Desktop",
    "Microsoft Hortense Desktop",
    "Vocalizer Expressive Jordi Harpo 22kHz"
];

// Carrega valors antics per mantenir-los al formulari
$lastText = isset($_GET['text']) ? htmlspecialchars($_GET['text']) : '';
$lastVoice = isset($_GET['voice']) ? $_GET['voice'] : '';
?>
<!DOCTYPE html>
<html lang="ca">
<head>
    <meta charset="UTF-8">
    <title>Generador TTS</title>
</head>
<body>
    <h1>Text-to-Speech (amb Python)</h1>
    <form action="exec_script_py.php" method="POST">

        <label for="text">Introdueix el text:</label><br>
        <textarea name="text" id="text" rows="4" cols="50"><?= $lastText ?></textarea><br><br>

        <label for="voice">Selecciona una veu:</label><br>
        <select name="voice" id="voice">
            <?php foreach ($voices as $voice): ?>
                <option value="<?= htmlspecialchars($voice) ?>" <?= ($voice == $lastVoice ? 'selected' : '') ?>>
                    <?= htmlspecialchars($voice) ?>
                </option>
            <?php endforeach; ?>
        </select><br><br>

        <input type="submit" value="Generar àudio">
    </form>

    <?php if (isset($_GET['error'])): ?>
        <p style="color:red;">❌ Error: <?= htmlspecialchars($_GET['error']) ?></p>
    <?php endif; ?>

    <?php if (file_exists("tts.mp3")): ?>
        <h2>Resultat:</h2>
        <audio controls>
            <source src="tts.mp3?<?= time() ?>" type="audio/mpeg">
            El teu navegador no suporta àudio.
        </audio>
    <?php endif; ?>
</body>
</html>

generar_tts.py

import sys
import os

# 🔧 Elimina rutes locals que poden interferir
sys.path = [p for p in sys.path if ".local" not in p and "urqtejmi" not in p]

print("🔍 sys.path netejat:")
for p in sys.path:
    print("  -", p)

# 📦 Comprovació de la importació
try:
    import gtts
    print("📦 gtts importat des de:", getattr(gtts, '__file__', '❌ No definit'))
    from gtts import gTTS
    print("✅ gTTS importat correctament")
except Exception as e:
    print("❌ Error important gTTS:", e)
    raise

# 📄 Preparació del fitxer de sortida
base_dir = os.path.dirname(os.path.abspath(__file__))
output_path = os.path.join(base_dir, "tts.mp3")

# 📝 Recollir text i veu
text = sys.argv[1] if len(sys.argv) > 1 else "Text per defecte"
voice = sys.argv[2] if len(sys.argv) > 2 else "default"

print(f"▶ Text: {text}")
print(f"▶ Veu seleccionada: {voice} (no utilitzada per gTTS)")
print(f"▶ Fitxer a guardar: {output_path}")

# 🔊 Generar MP3
tts = gTTS(text=text, lang='ca')
tts.save(output_path)

exec_script_py.php

<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $textRaw = $_POST['text'] ?? '';
    $voiceRaw = $_POST['voice'] ?? '';

    $text = escapeshellarg($textRaw);
    $voice = escapeshellarg($voiceRaw);

    // ✅ Ruta al Python de l'entorn virtual
    $python = '/volume2/web/00-sapi_tts/venv/bin/python';

    // ✅ Comanda per executar el script Python
    $command = "$python generar_tts.py $text $voice 2>&1";

    // 🔁 Executa el script i captura la sortida
    $output = shell_exec($command);

    echo "<pre>📤 Comanda executada:\n$command\n\n📄 Sortida:\n$output</pre>";

    // ❌ Si no s'ha generat l'àudio, redirigeix amb error
    if (!file_exists("tts.mp3")) {
        $error = "No s'ha pogut generar l'àudio. Sortida: $output";
        header("Location: index.php?error=" . urlencode($error) . "&text=" . urlencode($textRaw) . "&voice=" . urlencode($voiceRaw));
        exit();
    }

    // ✅ Si tot ha anat bé, redirigeix amb els valors conservats
    header("Location: index.php?text=" . urlencode($textRaw) . "&voice=" . urlencode($voiceRaw));
    exit();
}
?>

🛠️ Historial de passos per solucionar l’error amb gtts

1. Diagnòstic inicial

  • El teu script Python donava aquest error:
  ImportError: cannot import name 'gTTS' from 'gtts' (unknown location)
  • El mòdul gtts semblava instal·lat, però gtts.__file__ retornava None.

2. Depuració del sistema

  • Vam comprovar que hi havia múltiples versions de gtts instal·lades:
  • Una a /usr/lib/python3.8/site-packages
  • Una altra a ~/.local/lib/python3.8/site-packages ❌ (interferia)
  • Vam eliminar la versió local conflictiva:
  rm -rf ~/.local/lib/python3.8/site-packages/gtts
  rm -rf ~/.local/lib/python3.8/site-packages/gTTS-*.dist-info

3. Reinstal·lació neta

  • Vam reinstal·lar gtts globalment:
  sudo /bin/python3 -m pip install --no-cache-dir gtts
  • Tot i això, l’error persistia perquè el servidor web seguia usant el Python del sistema, amb rutes contaminades.

4. Creació d’un entorn virtual

  • Vam crear un entorn virtual aïllat:
  python3 -m virtualenv venv
  source venv/bin/activate
  pip install gtts
  • Això va instal·lar gtts netament a:
  /volume2/web/00-sapi_tts/venv/lib/python3.8/site-packages

5. Modificació del script PHP

  • Vam modificar exec_script_py.php per cridar el Python del virtualenv:
  $python = '/volume2/web/00-sapi_tts/venv/bin/python';
  $command = "$python generar_tts.py $text $voice 2>&1";
  • Això va assegurar que el servidor web executés el Python correcte, amb el mòdul gtts funcional.

6. Èxit final

  • El fitxer tts.mp3 es va generar correctament.
  • El reproductor d’àudio es va mostrar a la pàgina.
  • El text “bona nit” es va convertir en veu 🎉

🔜 Què queda pendent?

  • La veu no sona com la del “Jordi” de Vocalizer: això és perquè gtts fa servir la veu de Google Translate, no veus locals com Vocalizer.
  • Si vols fer servir veus com la del Jordi, caldrà integrar motors com:
  • eSpeak NG
  • Festival
  • Vocalizer TTS (si tens llicència)
  • Microsoft Azure, Google Cloud TTS, etc.

Vols que demà explorem com fer servir una veu real com la del Jordi amb un motor TTS local o en el núvol? O vols que et prepari una comparativa de motors TTS amb veus catalanes?

Publicat dins de Veus

🔊 Generador de veu amb Edge TTS i Google TTS🔊 tts a 192.168.0.49/00-sapi_tts/ & tts a 192.168.1.49/00-sapi_tts/

/edge_tts
├── exec_script_py.php         ← Script PHP que crida Python des de la web
├── generar_tts.py             ← Genera l'àudio amb Edge TTS
├── generar_gtts.py            ← Versió que utilitza Google TTS
├── generar_veu.py             ← Alternativa o prova per generar veu
├── diagnostic_edge_tts.py     ← Diagnòstic o comprovació del servei TTS
├── index.php                  ← Interfície web principal del projecte
├── tts.mp3                    ← Fitxer d’àudio generat (resultat final)
├── 
├── env_tts/                   ← Entorn virtual Python dedicat a TTS
├── venv/                      ← Possible entorn virtual duplicat
├── venv_web/                  ← Potser creat per a la web; revisa si s’usa
└── edge_tts/                  ← Pot contenir recursos o mòduls complementaris
 

Entorn

la carpeta té crear un entorn virtual amb una versió específica de Python especifica:

index.php

<?php
// Llista de veus
$voices = [
    "Microsoft Helena Desktop",
    "Microsoft Hazel Desktop",
    "Microsoft Zira Desktop",
    "Microsoft Hortense Desktop",
    "Vocalizer Expressive Jordi Harpo 22kHz"
];

// Carrega valors antics per mantenir-los al formulari
$lastText = isset($_GET['text']) ? htmlspecialchars($_GET['text']) : '';
$lastVoice = isset($_GET['voice']) ? $_GET['voice'] : '';
?>
<!DOCTYPE html>
<html lang="ca">
<head>
    <meta charset="UTF-8">
    <title>Generador TTS</title>
</head>
<body>
    <h1>Text-to-Speech (amb Python)</h1>
    <form action="exec_script_py.php" method="post">
        <label for="text">Introdueix el text:</label><br>
        <textarea name="text" id="text" rows="4" cols="50"><?= $lastText ?></textarea><br><br>

        <label for="voice">Selecciona una veu:</label><br>
        <select name="voice" id="voice">
            <?php foreach ($voices as $voice): ?>
                <option value="<?= htmlspecialchars($voice) ?>" <?= ($voice == $lastVoice ? 'selected' : '') ?>>
                    <?= htmlspecialchars($voice) ?>
                </option>
            <?php endforeach; ?>
        </select><br><br>

        <input type="submit" value="Generar àudio">
    </form>

    <?php if (isset($_GET['error'])): ?>
        <p style="color:red;">❌ Error: <?= htmlspecialchars($_GET['error']) ?></p>
    <?php endif; ?>

    <?php if (file_exists("tts.mp3")): ?>
        <h2>Resultat:</h2>
        <audio controls>
            <source src="tts.mp3?<?= time() ?>" type="audio/mpeg">
            El teu navegador no suporta àudio.
        </audio>
    <?php endif; ?>
</body>
</html>

generar_tts.py


import sys
import os
from gtts import gTTS

# Agafa el directori actual del fitxer Python
base_dir = os.path.dirname(os.path.abspath(__file__))
output_path = os.path.join(base_dir, "tts.mp3")

# Recollir text i veu
text = sys.argv[1] if len(sys.argv) > 1 else "Text per defecte"
voice = sys.argv[2] if len(sys.argv) > 2 else "default"

print(f"▶ Text: {text}")
print(f"▶ Veu seleccionada: {voice} (no utilitzada per gTTS)")
print(f"▶ Fitxer a guardar: {output_path}")

# Generar MP3
tts = gTTS(text=text, lang='ca')
tts.save(output_path)

exec_script_py.php

<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $textRaw = $_POST['text'] ?? '';
    $voiceRaw = $_POST['voice'] ?? '';

    // Guarda per reenviar després
    $text = escapeshellarg($textRaw);
    $voice = escapeshellarg($voiceRaw);

    // DEBUG: mostra quin python s'està usant
    $pythonPath = shell_exec("where python3");
    echo "<pre>Python detectat:\n$pythonPath</pre>";



    // Crida python del synology? o pytho windows 11 amb la comanda real
    $command = "/bin/python3 generar_tts.py $text $voice 2>&1";
    $output = shell_exec($command);

    echo "<pre>Sortida execució:\n$command\n\n$output</pre>"; // Ajuda per depuració

    // Comprova si s'ha creat l'àudio
    if (!file_exists("tts.mp3")) {
        $error = "No s'ha pogut generar l'àudio. Sortida: $output";
        header("Location: index.php?error=" . urlencode($error) . "&text=" . urlencode($textRaw) . "&voice=" . urlencode($voiceRaw));
        exit();
    }

    // Redirigeix a index amb el text i veu conservats
    header("Location: index.php?text=" . urlencode($textRaw) . "&voice=" . urlencode($voiceRaw));
    exit();
}
?>

Publicat dins de Veus

projecte python

C:\
├── python\
│   ├── tts-edge.py       ← Generació amb veus EDGE (Aria, Herena…)
│   ├── tts-to-wav.py     ← Generació amb veus SAPI5 (Zira, Jordi…)
│   └── list-voices.py    ← Llista combinada de veus per al menú
├── AppServ\
│   └── www\
│       └── tts\
│           ├── index.php ← Interfície web principal
│           ├── tts.wav   ← Fitxer d’àudio generat
│           └── log.txt   ← Registre de veus i execucions




tts-edge.py
import sys
import asyncio
from edge_tts import Communicate

async def main():
    if len(sys.argv) < 3:
        print("❌ Cal passar el text i la veu com a paràmetres.")
        return

    text = sys.argv[1]
    voice = sys.argv[2]
    output = "C:/AppServ/www/tts/tts.wav"

    try:
        communicate = Communicate(text=text, voice=voice)
        await communicate.save(output)
        print(f"✅ Fitxer generat: {output}")
        with open("C:/AppServ/www/tts/log.txt", "a", encoding="utf-8") as log:
            log.write(f"[EDGE] {voice} → {output}\n")
    except Exception as e:
        print(f"❌ Error: {e}")

asyncio.run(main())

tts-to-wav.py
import sys
import os
import comtypes.client

def main():
    if len(sys.argv) < 3:
        print("❌ Cal passar el text i la veu com a paràmetres.")
        return

    text = sys.argv[1]
    voice_name = sys.argv[2]
    output = "C:/AppServ/www/tts/tts.wav"

    try:
        sapi = comtypes.client.CreateObject("SAPI.SpVoice")
        stream = comtypes.client.CreateObject("SAPI.SpFileStream")
        from comtypes.gen import SpeechLib

        for token in sapi.GetVoices():
            if voice_name in token.GetDescription():
                sapi.Voice = token
                break
        else:
            print(f"❌ Veu no trobada: {voice_name}")
            return

        stream.Open(output, SpeechLib.SSFMCreateForWrite)
        sapi.AudioOutputStream = stream
        sapi.Speak(text)
        stream.Close()
        print(f"✅ Fitxer generat: {output}")
        with open("C:/AppServ/www/tts/log.txt", "a", encoding="utf-8") as log:
            log.write(f"[SAPI5] {voice_name} → {output}\n")
    except Exception as e:
        print(f"❌ Error: {e}")

if __name__ == "__main__":
    main()

list-voices.py
import comtypes.client
import subprocess
import json

def get_sapi5_voices():
    sapi = comtypes.client.CreateObject("SAPI.SpVoice")
    voices = []
    for token in sapi.GetVoices():
        name = token.GetDescription()
        voices.append(f"SAPI5|{name}")
    return voices

def get_edge_voices():
    try:
        subprocess.run(["edge-tts", "--list-voices", "--write-json", "voices.json"], capture_output=True, text=True)
        with open("voices.json", "r", encoding="utf-8") as f:
            data = json.load(f)
        return [f"EDGE|{v['ShortName']}" for v in data if v["Locale"].startswith("ca") or v["Locale"].startswith("en")]
    except Exception as e:
        print(f"❌ Error obtenint veus EDGE: {e}")
        return []

if __name__ == "__main__":
    all_voices = get_sapi5_voices() + get_edge_voices()
    for voice in all_voices:
        print(voice)

index.php
Ja el tens actualitzat per cridar els scripts Python i mostrar el reproductor, selector de veus i registre.
erfecte, Miquel. Ho deixem aquí, doncs, amb tot el projecte ben estructurat i funcionant amb:

✔️ Veus SAPI5 i EDGE

✔️ Generació d’àudio amb Python

✔️ Interfície web amb menú dinàmic

✔️ Registre de veus i execucions (log.txt)

Tens una base sòlida i extensible. Quan vulguis continuar — ja sigui per afegir suport a .mp3, fer-ho portable, o penjar-ho a un servidor — només cal que em diguis “tornem-hi”.

Mentrestant, et mereixes una pausa i un bon brindis. Salut! 🥂

Vols que et generi un petit banner gràfic per al teu projecte amb el nom “Projecte Veus amb Copilot”? Podria quedar bé a la capçalera de la web.

Publicat dins de Veus

Projecte Veus amb Copilot

📁 Estructura del projecte

C:\
├── tts\
│   ├── tts-to-wav.ps1       ← Script PowerShell per generar l’àudio
│   └── list-voices.ps1      ← Script PowerShell per llistar veus
└── AppServ\
    └── www\
        └── tts\
            ├── index.php    ← Interfície web
            └── tts.wav      ← Fitxer d’àudio generat

📄 Què és un fitxer .ps1?

Un fitxer .ps1 és un script de PowerShell, el llenguatge de línia de comandes i automatització de Windows. S’utilitza per executar ordres, scripts i automatitzar tasques del sistema.

📘 Documentació dels components

1. list-voices.ps1 — Llista de veus SAPI5


Add-Type -AssemblyName System.Speech
$synth = New-Object System.Speech.Synthesis.SpeechSynthesizer

$synth.GetInstalledVoices() | ForEach-Object {
    $info = $_.VoiceInfo
    $label = "SAPI5|$($info.Name)"
    Write-Output $label
}

🔹 Funció: Detecta totes les veus SAPI5 instal·lades i les imprimeix amb el prefix SAPI5|.


2. tts-to-wav.ps1 — Generació d’àudio

param (
    [string]$text = "Hola món",
    [string]$voiceName,
    [string]$outputPath = "C:\tts\tts.wav"
)

try {
    if ($voiceName -like "SAPI5|*") {
        $realVoice = $voiceName.Substring(6)
        Add-Type -AssemblyName System.Speech
        $synth = New-Object System.Speech.Synthesis.SpeechSynthesizer
        $synth.SelectVoice($realVoice)
        $synth.SetOutputToWaveFile($outputPath)
        $synth.Speak($text)
    } else {
        Write-Error "Format de veu desconegut: $voiceName"
        exit 1
    }
}
catch {
    Write-Error "Error inesperat: $_"
    exit 1
}

🔹 Funció: Rep un text i una veu, i genera un fitxer .wav amb la veu seleccionada.

3. index.php — Interfície web

<?php
// Obtenim la llista de veus
$voiceListCmd = "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe -ExecutionPolicy Bypass -File C:\\tts\\list-voices.ps1";
exec($voiceListCmd, $voices, $voiceStatus);

// Depuració: mostra les veus detectades
echo "<pre><strong>Veus detectades:</strong>\n";
print_r($voices);
echo "</pre>";

// Si falla, usem una llista per defecte
if ($voiceStatus !== 0 || empty($voices)) {
    $voices = [
        "SAPI5|Microsoft Helena Desktop",
        "SAPI5|Microsoft Hazel Desktop",
        "SAPI5|Microsoft Zira Desktop"
    ];
}
?>

<!DOCTYPE html>
<html lang="ca">
<head>
    <meta charset="UTF-8">
    <title>Conversió TTS</title>
</head>
<body>
    <h2>🔊 Conversió de text a veu (SAPI5)</h2>
    
  
    <form method="get">
        <label for="text">Text a llegir:</label><br>
        <textarea name="text" rows="4" cols="60"><?= htmlspecialchars($_GET['text'] ?? '') ?></textarea><br><br>

        <label for="voice">Selecciona una veu:</label><br>
        <select name="voice">
            <?php foreach ($voices as $v): ?>
                <option value="<?= htmlspecialchars($v) ?>" <?= ($v === ($_GET['voice'] ?? '')) ? 'selected' : '' ?>>
                    <?= htmlspecialchars($v) ?>
                </option>
            <?php endforeach; ?>
        </select><br><br>

        <button type="submit">▶️ Generar àudio</button>
    </form>

    <?php
    $text = $_GET['text'] ?? '';
    $voice = $_GET['voice'] ?? '';
    $audioFile = '/tts/tts.wav';

    if ($text && $voice) {
        $escapedText = escapeshellarg($text);
        $escapedVoice = escapeshellarg($voice);
        $escapedOutput = escapeshellarg(__DIR__ . DIRECTORY_SEPARATOR . 'tts.wav');

        $cmd = "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe -ExecutionPolicy Bypass -File C:\\tts\\tts-to-wav.ps1 -text $escapedText -voiceName $escapedVoice -outputPath $escapedOutput";

        // 🔍 DEBUG: Mostra la comanda que s'executarà
        echo "<pre><strong>Comanda PowerShell:</strong>\n$cmd\n</pre>";

        exec($cmd, $outputLines, $status);

        if ($status === 0 && file_exists(__DIR__ . '/tts.wav')) {
            echo "<h3>✅ Àudio generat:</h3>";
            echo "<audio controls autoplay style='margin-top:10px;'>
                    <source src=\"$audioFile\" type=\"audio/wav\">
                    El teu navegador no suporta àudio.
                  </audio>";
        } else {
            echo "<p style='color:red;'>❌ Error generant l’àudio.</p>";
            echo "<pre><strong>Sortida PowerShell:</strong>\n";
            print_r($outputLines);
            echo "\nCodi de sortida: $status</pre>";
        }
    }
    ?>
</body>
</html>
Publicat dins de Veus

python i veus

Requisits:

  • Tener instal·lat Python.
  • Instal·lar la llibreria pyttsx3:
pip install pyttsx3

pip install pyttsx3

PS C:\WINDOWS\system32> python -V
Python 3.13.5
PS C:\WINDOWS\system32>
import pyttsx3

def llistar_veus():
    engine = pyttsx3.init()
    voices = engine.getProperty('voices')
    
    for idx, voice in enumerate(voices):
        print(f"Veu {idx + 1}:")
        print(f"  ID: {voice.id}")
        print(f"  Nom: {voice.name}")
        print(f"  Llengua: {voice.languages}")
        print(f"  Gènere: {voice.gender}")
        print()

if __name__ == "__main__":
    llistar_veus()

Aquest script es pot llançar des de powerShell

python c:\python\llista_veus.py

python s:\python\llista_veus.py

si volem afegir les OneCore


python c:\python\llista_veus_onecore.py

python s:\python\llista_veus_onecore.py


python c:\python\llista_veus_onecore.py

python s:\python\llista_veus_onecore.py

import pyttsx3
import winreg

def llistar_veus_sapi():
    print("🎙️ Veus SAPI instal·lades:\n")
    engine = pyttsx3.init()
    voices = engine.getProperty('voices')
    for idx, voice in enumerate(voices):
        print(f"Veu {idx + 1}:")
        print(f"  ID: {voice.id}")
        print(f"  Nom: {voice.name}")
        print(f"  Llengua: {voice.languages}")
        print(f"  Gènere: {voice.gender}")
        print()

def obtenir_nom_veu_onecore(subkey):
    for value_name in ["", "Name", "DisplayName", "Attributes"]:
        try:
            val = winreg.QueryValueEx(subkey, value_name)[0]
            if isinstance(val, str) and val.strip():
                return val.strip()
        except FileNotFoundError:
            continue
    return "Desconegut"

def llistar_veus_onecore():
    base_key = r"SOFTWARE\Microsoft\Speech_OneCore\Voices\Tokens"
    try:
        with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, base_key) as key:
            print("🎙️ Veus OneCore disponibles:\n")
            for i in range(winreg.QueryInfoKey(key)[0]):
                subkey_name = winreg.EnumKey(key, i)
                with winreg.OpenKey(key, subkey_name) as subkey:
                    nom = obtenir_nom_veu_onecore(subkey)
                    print(f"ID: {subkey_name}")
                    print(f"Nom: {nom}\n")
    except FileNotFoundError:
        print("No s'ha trobat la clau de registre per a les veus OneCore. Assegura't que estàs a Windows 11.")

if __name__ == "__main__":
    llistar_veus_sapi()
    llistar_veus_onecore()


python c:\python\veus_sapi_onecore.py

python s:\python\veus_sapi_onecore.py

import pyttsx3
import winreg

def llistar_veus_sapi():
    print("🎙️ Veus SAPI instal·lades:\n")
    engine = pyttsx3.init()
    voices = engine.getProperty('voices')
    for idx, voice in enumerate(voices):
        print(f"Veu {idx + 1}:")
        print(f"  ID: {voice.id}")
        print(f"  Nom: {voice.name}")
        print(f"  Llengua: {voice.languages}")
        print(f"  Gènere: {voice.gender}")
        print()

def obtenir_nom_veu_onecore(subkey):
    for value_name in ["", "Name", "DisplayName", "Attributes"]:
        try:
            val = winreg.QueryValueEx(subkey, value_name)[0]
            if isinstance(val, str) and val.strip():
                return val.strip()
        except FileNotFoundError:
            continue
    return "Desconegut"

def llistar_veus_onecore():
    base_key = r"SOFTWARE\Microsoft\Speech_OneCore\Voices\Tokens"
    try:
        with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, base_key) as key:
            print("🎙️ Veus OneCore disponibles:\n")
            for i in range(winreg.QueryInfoKey(key)[0]):
                subkey_name = winreg.EnumKey(key, i)
                with winreg.OpenKey(key, subkey_name) as subkey:
                    nom = obtenir_nom_veu_onecore(subkey)
                    print(f"ID: {subkey_name}")
                    print(f"Nom: {nom}\n")
    except FileNotFoundError:
        print("No s'ha trobat la clau de registre per a les veus OneCore. Assegura't que estàs a Windows 11.")

if __name__ == "__main__":
    llistar_veus_sapi()
    llistar_veus_onecore()

Publicat dins de Veus

instal·la-veus-onecore.ps1

# Llengües que vols instal·lar
$Languages = @("ca-ES", "es-ES", "en-US", "en-GB", "fr-FR")

# Components de veu i reconeixement per a cada llengua
$CapabilitiesBase = @(
  "Language.Basic~~~{0}~0.0.1.0",
  "Language.Handwriting~~~{0}~0.0.1.0",
  "Language.Speech~~~{0}~0.0.1.0",
  "Language.TextToSpeech~~~{0}~0.0.1.0"
)

foreach ($lang in $Languages) {
    Write-Host "🌐 Processant idioma: $lang" -ForegroundColor Cyan
    foreach ($capTemplate in $CapabilitiesBase) {
        $cap = [string]::Format($capTemplate, $lang)
        $capState = Get-WindowsCapability -Online -Name $cap -ErrorAction SilentlyContinue

        if ($capState -and $capState.State -eq "Installed") {
            Write-Host "✅ Ja instal·lat: $cap" -ForegroundColor Green
        }
        else {
            Write-Host "📦 Instal·lant: $cap" -ForegroundColor Yellow
            try {
                Add-WindowsCapability -Online -Name $cap -ErrorAction Stop
                Write-Host "✅ Instal·lat correctament: $cap" -ForegroundColor Green
            } catch {
                Write-Host "❌ Error instal·lant $cap: $_" -ForegroundColor Red
            }
        }
    }
    Write-Host "----------------------------`n"
}

Write-Host "🔁 Quan acabi TOT, reinicia Windows manualment per activar les veus." -ForegroundColor Magenta

es-ES (Espanyol)

en-US (Anglès EUA)

en-GB (Anglès UK)

fr-FR (Francès)

ca-ES (Català)


📌 Com usar-lo

  1. Desa el contingut anterior com a fitxer:
    • Exemple: C:\tts\instal·la-veus-onecore.ps1
  2. Obre PowerShell com a administrador
  3. Executa:
Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass
C:\tts\instal·la-veus-onecore.ps1

🔁 Després de córrer-lo

  1. Reinicia l’ordinador
  2. Executa el teu script Python de detecció (Speech_OneCore) per veure les veus noves
  3. Ja podràs usar-les via Python o integració a la web

Publicat dins de Veus

🔊 Test de veus SAPI i OneCore (tts)

C:\
├── tts\
|   ├──tts-to-wav.py       ← Script Python per generar l’àudio
│   ├── tts-to-wav.ps1     ← Script PowerShell per generar l’àudio
│   └── list-voices.ps1    ← Script PowerShell per llistar veus
└── AppServ\
    └── www\
        └── tts\
            ├── index.php    ← Interfície web
            └── tts.wav      ← Fitxer d’àudio generat

Index.php
--------

<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);

// Executa PowerShell per obtenir la llista de veus disponibles
$voiceListCmd = "powershell -ExecutionPolicy Bypass -File C:\\tts\\list-voices.ps1";
exec($voiceListCmd, $voices, $voiceStatus);

// Si falla, utilitza una llista per defecte
if ($voiceStatus !== 0 || empty($voices)) {
    $voices = [
        "SAPI5|Microsoft Helena Desktop",
        "SAPI5|Microsoft Hazel Desktop",
        "SAPI5|Microsoft Zira Desktop"
    ];
}else 
//-----------provisional fins la sol·lucio definitiva----
{
    // Afegim manualment Herena a OneCore (només si no està ja)
    $herena = "ONECORE|Microsoft Herena - Catalan (Catalan)";
    if (!in_array($herena, $voices)) {
        $voices[] = $herena;
    }
}
//------fi del provisional ❌ Error generant l’àudio.---
$text = $_GET['text'] ?? '';
$voice = $_GET['voice'] ?? $voices[0]; // 👈 AIXÒ ÉS ON TOCA
$audioFile = '/tts/tts.wav';

$audioTag = '';

if ($text) {
    $escapedText = escapeshellarg($text);
    $escapedVoice = escapeshellarg($voice);
    $escapedOutput = escapeshellarg(__DIR__ . DIRECTORY_SEPARATOR . 'tts.wav');


//$cmd = "powershell -ExecutionPolicy Bypass -File C:\\tts\\tts-to-wav.ps1 -text $escapedText -voiceName $escapedVoice -outputPath $escapedOutput";
    $cmd = "python C:\\tts\\tts-to-wav.py $escapedText $escapedVoice $escapedOutput";



    exec($cmd, $outputLines, $status);

    if ($status === 0 && file_exists(__DIR__ . '/tts.wav')) {
        $audioTag = "<audio controls autoplay style='margin-top:20px;'>
            <source src=\"$audioFile\" type=\"audio/wav\">
            El teu navegador no suporta àudio.
        </audio>";
    } else {
        $audioTag = "<p style='color:red;'>❌ Error generant l’àudio.</p>";
    }
}
?>

<!-- Interfície HTML -->
<!DOCTYPE html>
<html lang="ca">
<head>
    <meta charset="UTF-8">
    <title>Conversió TTS</title>
</head>
<body>
    <form method="get">
        <label for="text">Text:</label><br>
        <textarea name="text" rows="4" cols="50"><?= htmlspecialchars($text) ?></textarea><br><br>
        
        <label for="voice">Veu:</label>
        <select name="voice">
            <?php foreach ($voices as $v): ?>
                <option value="<?= htmlspecialchars($v) ?>" <?= $v === $voice ? 'selected' : '' ?>><?= htmlspecialchars($v) ?></option>
            <?php endforeach; ?>
        </select><br><br>

        <button type="submit">Generar àudio</button>
    </form>

    <?= $audioTag ?>
</body>
</html>

=========


 tts-to-wav.ps1 (usarem tts-to-wav.py)

param (
    [string]$text = "Hola món",
    [string]$voiceName,
    [string]$outputPath = "C:\tts\tts.wav"
)

try {
    if ($voiceName -like "ONECORE|*") {
        $realVoice = $voiceName.Substring(8)

        Add-Type -AssemblyName Windows.Media.SpeechSynthesis
        $synth = New-Object Windows.Media.SpeechSynthesis.SpeechSynthesizer

        $voice = $synth.AllVoices | Where-Object { $_.DisplayName -eq $realVoice }

        if (-not $voice) {
            Write-Error "Veu '$realVoice' no trobada a OneCore."
            exit 1
        }

        $synth.Voice = $voice

        $streamOp = $synth.SynthesizeTextToStreamAsync($text)
        $streamOp.Wait()
        $stream = $streamOp.GetResults()

        # Llegir el stream
        $reader = New-Object Windows.Storage.Streams.DataReader($stream)
        $reader.LoadAsync($stream.Size).AsTask().Wait()
        $data = New-Object byte[] $stream.Size
        $reader.ReadBytes($data)

        [System.IO.File]::WriteAllBytes($outputPath, $data)
    }
    elseif ($voiceName -like "SAPI5|*") {
        $realVoice = $voiceName.Substring(6)

        Add-Type -AssemblyName System.Speech
        $synth = New-Object System.Speech.Synthesis.SpeechSynthesizer
        $synth.SelectVoice($realVoice)
        $synth.SetOutputToWaveFile($outputPath)
        $synth.Speak($text)
    }
    else {
        Write-Error "Format de veu desconegut: $voiceName"
        exit 1
    }
}
catch {
    Write-Error "Error inesperat: $_"
    exit 1
}
========================
tts-to-wav.py

import sys
import os
import pyttsx3

text = sys.argv[1]
voice_name = sys.argv[2]
output_path = sys.argv[3]

engine = pyttsx3.init()

# Buscar la veu exacta
found = False
for voice in engine.getProperty('voices'):
    if voice.name.strip() == voice_name.strip():
        engine.setProperty('voice', voice.id)
        found = True
        break

if not found:
    print(f"❌ Veu '{voice_name}' no trobada.")
    sys.exit(1)

engine.save_to_file(text, output_path)
engine.runAndWait()
print(f"✅ Àudio generat a {output_path}")

======================

list-voices.ps1

# Llista de veus SAPI5
Add-Type -AssemblyName System.Speech
$sapi = New-Object System.Speech.Synthesis.SpeechSynthesizer
$sapiVoices = $sapi.GetInstalledVoices() | ForEach-Object { $_.VoiceInfo.Name }

# Llista de veus OneCore
Add-Type -AssemblyName Windows.Media.SpeechSynthesis
$core = New-Object Windows.Media.SpeechSynthesis.SpeechSynthesizer
$coreVoices = $core.AllVoices | ForEach-Object { $_.DisplayName }

# Sortida combinada amb etiquetes
$sapiVoices | ForEach-Object { "SAPI5|$_" }
$coreVoices | ForEach-Object { "ONECORE|$_" }
Publicat dins de Veus

🛠️ Script PowerShell complet i robust per instal·lar veus OneCore

powershell:

Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass
C:\tts\instal·la-veus-onecore.ps1

Idiomes i veus que vols instal·lar

$languageTags = @(
“en-US”, # English (US)
“en-GB”, # English (UK)
“es-ES”, # Spanish (Spain)
“ca-ES”, # Catalan (Spain)
“fr-FR”, # French (France)
“de-DE” # German
)

Obté la llista actual d’idiomes

$currentLangs = Get-WinUserLanguageList | ForEach-Object { $_.LanguageTag }

foreach ($lang in $languageTags) {
Write-Host “🔄 Processant $lang…” -ForegroundColor Cyan
try {
# Afegeix l’idioma si no hi és
if (-not ($currentLangs -contains $lang)) {
Write-Host “➕ Afegint idioma $lang…”
$langList = Get-WinUserLanguageList
$langList.Add($lang)
Set-WinUserLanguageList $langList -Force
} else {
Write-Host “✔️ L’idioma $lang ja està instal·lat.”
}

    # Comprova si la veu bàsica ja està instal·lada
    $capability = "Language.$lang~Basic~~~"
    $installed = Get-WindowsCapability -Online | Where-Object { $_.Name -eq $capability }
    if ($installed.State -ne "Installed") {
        Write-Host "⬇️ Instal·lant veu bàsica per a $lang..."
        Add-WindowsCapability -Online -Name $capability -ErrorAction Stop
    } else {
        Write-Host "✔️ Veu bàsica ja instal·lada per a $lang."
    }

    # Instal·la reconeixement de veu si no hi és
    $speechCap = "Language.$lang~Speech~~~"
    $speechInstalled = Get-WindowsCapability -Online | Where-Object { $_.Name -eq $speechCap }
    if ($speechInstalled.State -ne "Installed") {
        Write-Host "🎙️ Instal·lant reconeixement de veu per a $lang..."
        Add-WindowsCapability -Online -Name $speechCap -ErrorAction SilentlyContinue
    } else {
        Write-Host "✔️ Reconeixement de veu ja instal·lat per a $lang."
    }

} catch {
   Write-Warning ("Error instal·lant per a $lang: " + $_.Exception.Message)

}

}

Mostra les veus disponibles al final

Write-Host “`n📋 Veus SAPI instal·lades:” -ForegroundColor Yellow
Add-Type -AssemblyName System.Speech
(New-Object System.Speech.Synthesis.SpeechSynthesizer).GetInstalledVoices() |
ForEach-Object { “• ” + $_.VoiceInfo.Name }

Publicat dins de Veus