diff --git a/fxgl-intelligence/src/main/java/com/almasb/fxgl/intelligence/WebAPI.java b/fxgl-intelligence/src/main/java/com/almasb/fxgl/intelligence/WebAPI.java index a0948f65d..28ac74432 100644 --- a/fxgl-intelligence/src/main/java/com/almasb/fxgl/intelligence/WebAPI.java +++ b/fxgl-intelligence/src/main/java/com/almasb/fxgl/intelligence/WebAPI.java @@ -14,9 +14,11 @@ */ public final class WebAPI { + public static final String TEXT_TO_SPEECH_API = "https://almasb.github.io/web-api/text-to-speech-v1/"; public static final String SPEECH_RECOGNITION_API = "https://almasb.github.io/web-api/speech-recog/"; public static final String GESTURE_RECOGNITION_API = "https://almasb.github.io/web-api/gesture-recog-v1/"; + public static final int TEXT_TO_SPEECH_PORT = 55550; public static final int SPEECH_RECOGNITION_PORT = 55555; public static final int GESTURE_RECOGNITION_PORT = 55560; } diff --git a/fxgl-intelligence/src/main/kotlin/com/almasb/fxgl/intelligence/tts/TextToSpeechService.kt b/fxgl-intelligence/src/main/kotlin/com/almasb/fxgl/intelligence/tts/TextToSpeechService.kt new file mode 100644 index 000000000..0df723b8b --- /dev/null +++ b/fxgl-intelligence/src/main/kotlin/com/almasb/fxgl/intelligence/tts/TextToSpeechService.kt @@ -0,0 +1,90 @@ +/* + * FXGL - JavaFX Game Library. The MIT License (MIT). + * Copyright (c) AlmasB (almaslvl@gmail.com). + * See LICENSE for details. + */ + +package com.almasb.fxgl.intelligence.tts + +import com.almasb.fxgl.core.EngineService +import com.almasb.fxgl.core.concurrent.Async +import com.almasb.fxgl.intelligence.WebAPI +import com.almasb.fxgl.logging.Logger +import com.almasb.fxgl.net.ws.LocalWebSocketServer +import com.almasb.fxgl.speechrecog.SpeechRecognitionService +import org.openqa.selenium.By +import org.openqa.selenium.WebDriver +import org.openqa.selenium.chrome.ChromeDriver +import org.openqa.selenium.chrome.ChromeOptions + +/** + * TODO: remove duplicate code + * + * @author Almas Baim (https://github.com/AlmasB) + */ +class TextToSpeechService : EngineService() { + + private val log = Logger.get(TextToSpeechService::class.java) + private val server = LocalWebSocketServer("TTSServer", WebAPI.TEXT_TO_SPEECH_PORT) + + private var webDriver: WebDriver? = null + + override fun onInit() { + server.start() + } + + /** + * Starts this service in a background thread. + * Can be called after stop() to restart the service. + * If the service has already started, then calls stop() and restarts it. + */ + fun start() { + Async.startAsync { + try { + if (webDriver != null) { + stop() + } + + val options = ChromeOptions() + options.addArguments("--headless=new") + options.addArguments("--use-fake-ui-for-media-stream") + + webDriver = ChromeDriver(options) + webDriver!!.get(WebAPI.TEXT_TO_SPEECH_API) + + // TODO: update web-api impl + // force it to play, so the audio output is initialized + webDriver!!.findElement(By.id("play")).click() + + // we are ready to use the web api service + } catch (e: Exception) { + log.warning("Failed to start Chrome web driver. Ensure Chrome is installed in default location") + log.warning("Error data", e) + } + } + } + + /** + * Stops this service. + * No-op if it has not started via start() before. + */ + fun stop() { + try { + if (webDriver != null) { + webDriver!!.quit() + webDriver = null + } + } catch (e: Exception) { + log.warning("Failed to quit web driver", e) + } + } + + fun speak(text: String) { + server.send(text) + } + + override fun onExit() { + stop() + server.stop() + } +} \ No newline at end of file