6
6
7
7
package com.almasb.fxgl.intelligence.tts
8
8
9
-
import com.almasb.fxgl.core.EngineService
10
9
import com.almasb.fxgl.core.concurrent.Async
11
10
import com.almasb.fxgl.intelligence.WebAPI
11
+
import com.almasb.fxgl.intelligence.WebAPIService
12
12
import com.almasb.fxgl.logging.Logger
13
13
import com.almasb.fxgl.net.ws.LocalWebSocketServer
14
-
import com.almasb.fxgl.speechrecog.SpeechRecognitionService
15
14
import org.openqa.selenium.By
16
15
import org.openqa.selenium.WebDriver
17
-
import org.openqa.selenium.chrome.ChromeDriver
18
-
import org.openqa.selenium.chrome.ChromeOptions
19
16
20
17
/**
21
-
* TODO: remove duplicate code
22
-
*
23
18
* @author Almas Baim (https://github.com/AlmasB)
24
19
*/
25
-
class TextToSpeechService : EngineService() {
20
+
class TextToSpeechService : WebAPIService(
21
+
LocalWebSocketServer("TTSServer", WebAPI.TEXT_TO_SPEECH_PORT),
22
+
WebAPI.TEXT_TO_SPEECH_API
23
+
) {
26
24
27
25
private val log = Logger.get(TextToSpeechService::class.java)
28
-
private val server = LocalWebSocketServer("TTSServer", WebAPI.TEXT_TO_SPEECH_PORT)
29
26
30
-
private var webDriver: WebDriver? = null
27
+
private val synthVoices = arrayListOf<Voice>()
28
+
var selectedVoice: Voice = Voice("NULL")
31
29
32
-
override fun onInit() {
33
-
server.start()
34
-
}
35
-
36
-
/**
37
-
* Starts this service in a background thread.
38
-
* Can be called after stop() to restart the service.
39
-
* If the service has already started, then calls stop() and restarts it.
40
-
*/
41
-
fun start() {
42
-
Async.startAsync {
43
-
try {
44
-
if (webDriver != null) {
45
-
stop()
46
-
}
30
+
val voices: List<Voice>
31
+
get() = synthVoices.toList()
47
32
48
-
val options = ChromeOptions()
49
-
options.addArguments("--headless=new")
50
-
options.addArguments("--use-fake-ui-for-media-stream")
51
-
52
-
webDriver = ChromeDriver(options)
53
-
webDriver!!.get(WebAPI.TEXT_TO_SPEECH_API)
33
+
override fun onWebDriverLoaded(webDriver: WebDriver) {
34
+
// force it to play, so the audio output is initialized
35
+
webDriver.findElement(By.id("play")).click()
36
+
}
54
37
55
-
// TODO: update web-api impl
56
-
// force it to play, so the audio output is initialized
57
-
webDriver!!.findElement(By.id("play")).click()
38
+
private fun initVoices(voiceNames: List<String>) {
39
+
synthVoices += voiceNames.map { Voice(it) }
58
40
59
-
// we are ready to use the web api service
60
-
} catch (e: Exception) {
61
-
log.warning("Failed to start Chrome web driver. Ensure Chrome is installed in default location")
62
-
log.warning("Error data", e)
63
-
}
41
+
if (synthVoices.isNotEmpty()) {
42
+
selectedVoice = synthVoices[0]
64
43
}
65
-
}
66
44
67
-
/**
68
-
* Stops this service.
69
-
* No-op if it has not started via start() before.
70
-
*/
71
-
fun stop() {
72
-
try {
73
-
if (webDriver != null) {
74
-
webDriver!!.quit()
75
-
webDriver = null
76
-
}
77
-
} catch (e: Exception) {
78
-
log.warning("Failed to quit web driver", e)
45
+
Async.startAsyncFX {
46
+
isReady = true
79
47
}
80
48
}
81
49
82
50
fun speak(text: String) {
83
-
server.send(text)
51
+
if (!isReady || synthVoices.isEmpty())
52
+
return
53
+
54
+
rpcRun("speak", selectedVoice.name, text)
84
55
}
85
56
86
57
override fun onExit() {
87
58
stop()
88
-
server.stop()
59
+
super.onExit()
89
60
}
90
-
}
61
+
}
62
+
63
+
data class Voice internal constructor(val name: String)
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4