/**
* Copyright (c) 2010-2016 by the respective copyright holders.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.openhab.io.multimedia.internal.tts;
import java.io.BufferedInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.SequenceInputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Dictionary;
import java.util.List;
import java.util.Vector;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.openhab.io.multimedia.tts.TTSService;
import org.osgi.service.cm.ConfigurationException;
import org.osgi.service.cm.ManagedService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javazoom.jl.decoder.JavaLayerException;
import javazoom.jl.player.Player;
/**
* A TTS service implementation utilizing the text-to-speech functionality of Google Translate.
*
* @author Dominic Lerbs
* @since 1.7.0
*
*/
public class TTSServiceGoogleTTS implements TTSService, ManagedService {
private static final Logger logger = LoggerFactory.getLogger(TTSServiceGoogleTTS.class);
/** User agent for HTTP requests as requests without agent are ignored */
private static final String USER_AGENT = "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:34.0) "
+ "Gecko/20100101 Firefox/34.0";
/** Maximum sentence length. Google translate only supports 100 characters at a time. */
private static final int MAX_SENTENCE_LENGTH = 100;
private static final String SENTENCE_DELIMITERS_PROPERTY = "sentenceDelimiters";
private static final String LANGUAGE_PROPERTY = "language";
private static final String TRANSLATE_URL_PROPERTY = "translateUrl";
private String ttsLanguage = "en";
private String translateUrl = "http://translate.google.com/translate_tts?tl=%s&q=%s&client=t";
private final GoogleTTSTextProcessor textProcessor = new GoogleTTSTextProcessor(MAX_SENTENCE_LENGTH);
public void activate() {
logger.debug("GoogleTTS service has been activated");
}
public void deactivate() {
logger.debug("GoogleTTS service has been deactivated");
}
/**
* {@inheritDoc}
*/
public void say(String text, String voiceName, String outputDevice) {
logger.info("Executing GoogleTTS for text '{}' in language {}", text, ttsLanguage);
BufferedInputStream stream = null;
try {
List<String> sentences = textProcessor.splitIntoChunks(text);
InputStream completeStream = getSpeechForText(sentences);
Player playMP3 = new Player(completeStream);
playMP3.play();
} catch (IOException e) {
logger.warn("Error while connecting to Google translate service", e);
if (e instanceof FileNotFoundException) {
logger.warn("Possibly unsupported language '{}'?", ttsLanguage);
}
} catch (JavaLayerException e) {
logger.warn("Unable to play InputStream for text " + text, e);
} finally {
IOUtils.closeQuietly(stream);
}
}
/**
* Converts the given sentences into audio and returns it as an {@link InputStream}.
*
* @param sentences
* The text to be converted to audio
* @return {@link InputStream} with audio output
* @throws IOException
* Exception if the connection could not be established properly
*/
private InputStream getSpeechForText(List<String> sentences) throws IOException {
Vector<InputStream> inputStreams = new Vector<InputStream>(sentences.size());
for (String sentence : sentences) {
String encodedSentence = GoogleTTSTextProcessor.urlEncodeSentence(sentence);
URL url = new URL(String.format(translateUrl, ttsLanguage, encodedSentence));
inputStreams.add(getInputStreamFromUrl(url));
}
return new SequenceInputStream(inputStreams.elements());
}
/**
* Connects to the given {@link URL} and returns the response as an {@link InputStream}.
*
* @param url
* The {@link URL} to connect to
* @return The response as {@link InputStream}
* @throws IOException
* Exception if the connection could not be established properly
*/
private InputStream getInputStreamFromUrl(URL url) throws IOException {
logger.debug("Connecting to URL " + url.toString());
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.addRequestProperty("User-Agent", USER_AGENT);
connection.connect();
return new BufferedInputStream(connection.getInputStream());
}
/**
* {@inheritDoc}
*/
public void updated(Dictionary<String, ?> properties) throws ConfigurationException {
if (properties != null) {
String language = (String) properties.get(LANGUAGE_PROPERTY);
if (!StringUtils.isBlank(language)) {
logger.debug("Using TTS language from config: " + ttsLanguage);
ttsLanguage = language;
}
String delimiters = (String) properties.get(SENTENCE_DELIMITERS_PROPERTY);
if (!StringUtils.isBlank(delimiters)) {
logger.debug("Using custom sentence delimiters from config: " + delimiters);
textProcessor.setCustomSentenceDelimiters(delimiters);
}
String configTranslateUrl = (String) properties.get(TRANSLATE_URL_PROPERTY);
if (!StringUtils.isBlank(configTranslateUrl)) {
logger.debug("Using custom translate URL from config: " + configTranslateUrl);
translateUrl = configTranslateUrl;
}
}
}
}