www.beanizer.org Un servizio TextToSpeech con FreeTTS Un semplice server text to speech interrogabile via http Introduzione Una delle tecnologie che più stimolano il mio lato fanciullesco è la domotica. Non posso permettermi un forno con dentro Java, ma con alcuni adattatori x10 mi sono recentemente divertito a costruire un sistema in cui una Midlet sul mio cellulare periodicamente comunica al mio server casalingo la propria cella GSM, così il mio forno elettrico può venir acceso quando mi dirigo verso casa. Lo so, è kitsch ( e costoso,a seconda del provider gprs), ma vado matto per questo genere di inutilità. Il mese scorso mi ha riaggredito un vecchio sogno; un sistema parlante in casa. Facile da fare oggi!! Sono piuttosto ossessionato da web service e integrazione di sistemi, per cui volevo un sorta di blackbox parlante, direttamente esposta a tutti i miei sistemi casalinghi(desktop, portatile e cellulare), per cui per questa volta ho optato per un semplice approccio http, niente SOAP, xml-rpc o socket. Poi dovevo scegliere il motore di sintesi vocale. Avevo già messo le mani su FreeTTS, un sintetizzatore vocale scritto interamente in JavaTM, e ne ero rimasto soddisfatto. Era anche una buona occasione per usare la classe com.sun.net.httpserver.HttpServer del jdk per implementare il semplice server http di cui avevo bisogno. A questo punto avevo tutto per costruire la mia "macchina parlante". Da sottolineare il fatto che FreTTS include negli esempi una soluzione client/server con scopi però differenti da quella esposta in questo articolo. Nel nostro caso abbiamo un server che possegga anche scheda audio e casse acustiche, mentre nell'esempio client/server di FreeTTS, il server produce l'audio che però viene "suonato" dal client. Panoramica Ho usato jdk 1.5 e FreeTTS 1.2 . Naturalmete i jars di FreeTTS dovranno essere nel classpath. Non è richiesto nient'altro e, essendo il motore tts 100% java, è anche utilizzabile su qualsiasi piattaforma Java. Con sole 2 classi, il nostro motore parlante sarà pronto. Non entrerò nei dettagli di com.sun.net.httpserver.HttpServer, l'unica cosa che ci serve sapere è che possiamo associare un contesto ad un handler. Il codice chiarirà tutto questo. Immaginiamo di volere un servizio in ascolto sulla porta 8000, con un contesto http tipo "/ttsserver/say", che "vocalizzerà" il testo della URI che si trova dopo il punto interrogativo. Una tipica chiamata sarà qualcosa del genere: "http://localhost:8000/ttsserver/say?Che tempo fa oggi" Il codice Scriviamo il codice della prima classe. Dovrà semplicemente inizializzare e far partire il server http. Ecco il codice: package org.beanizer.ttsserver; import com.sun.net.httpserver.HttpServer; import java.io.IOException; import java.net.InetSocketAddress; public class Server { private HttpServer server=null; public void start(){ try { server = HttpServer.create(new InetSocketAddress(8000), 0); server.createContext("/ttsserver/say", new VoiceHandler()); server.setExecutor(null); server.start(); } catch (IOException ex) { ex.printStackTrace(); } } public static void main(String args[]){ Server s=new Server(); s.start(); } } Il metodo main crea un'istanza della classe e ne chiama il metodo start. Analizziamo il codice racchiuso nel blocco try/catch in start: 1) viene creato un server http in ascolto sulla porta tcp 8000. http://www.beanizer.org/site Realizzata con Joomla! Generata: 9 June, 2017, 16:50 www.beanizer.org 2) Il contesto "/ttsserver/say" sarà gestito da VoiceHandler, che è la seconda classe che scriveremo. 3) Creiamo un esecutore di default (un oggetto che esegue processi Runnable, in questo caso le singole chiamate http). 4) Alla fine il server http viene fatto partire. Abbiamo bisogno solo di una ulteriore semplice classe per la gestione delle richieste http ricevute sul contesto creato. La vediamo alla prossime pagina. package org.beanizer.ttsserver; import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import com.sun.speech.freetts.Voice; import com.sun.speech.freetts.VoiceManager; import com.sun.speech.freetts.util.Utilities; public class VoiceHandler implements HttpHandler{ private Voice voice; public VoiceHandler() { try { voice = VoiceManager.getInstance().getVoice( Utilities.getProperty("voice16kName", "kevin16")); voice.allocate(); } catch (Exception e) { e.printStackTrace(); System.exit(1); } } public void handle(HttpExchange t) throws IOException { voice.speak(t.getRequestURI().getQuery()); String response = "Ok"; t.sendResponseHeaders(200, response.length()); OutputStream os = t.getResponseBody(); os.write(response.getBytes()); os.close(); } } La nostra classe VoiceHandler estende com.sun.net.httpserver.HttpHandler, in particolare il metodo handle per gestire le richieste in arrivo. Nel costruttore otteniamo una istanza di VoiceManager, da questo otteniamo una Voice utilizzando la classe Utilities, e allochiamo la voce. A questo punto la voce è pronta per essere usata con una semplce chiamata speak(String). Una nota sulla selezione della voce. FreeTTS include alcune voci: una di qualità 8k, una 16k(quella che stiamo utilizzando noi) e una di miglior qualità ma limitata al dominio di tempo/data. In rete è possibile trovare altre voci, anche per lingue diverse dall'inglese e anche produrne di proprie(anche se la procedura è complessa). Qui ci sono informazioni al riguardo. Torniamo alla nostra classe. Il metodo handle: 1) "vocalizza" ciò che arriva con la chiamata http (tutto quello che segue "?" nella URI) 2) invia un "Ok" al chiamante. Per far partire il servizio assicurarsi che i jar di FreeTTS siano nel classpath e lanciare la classe org.beanizer.ttsserver.Server con java. Conclusioni Questo è più o meno tutto. é possibile estendere la struttura per controllare gli accessi, parametrizzare la voce da utilizzare etc., ma semplicemente con le 2 classi proposte ogni dispositivo in grado di fare chiamate http( ad es. un http://www.beanizer.org/site Realizzata con Joomla! Generata: 9 June, 2017, 16:50 www.beanizer.org browser) può far parlare il nostro accrocchio. A casa lo utilizzo per le più disparate fesserie, ed è veramente divertente. Alcuni usi: 1) Alcuni messaggi di log (opportunamente filtrati per evitare "spam") 2) Odio le suonerie telefoniche, per cui ho istruito il mio server asterisk a far dire al server tts chi mi chiama, usando il nome al posto del numero in caso di persone note. Al momento stò lavorando su un servizio simile per il riconoscimento vocale, ma questa è una bestia totalmente diversa..... Hasta la proxima. http://www.beanizer.org/site Realizzata con Joomla! Generata: 9 June, 2017, 16:50