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.