Refactor io.bartek.ttsserver.tts package

This commit is contained in:
2020-05-15 21:45:15 +02:00
parent e756d28b45
commit 6eaf27359d
11 changed files with 97 additions and 67 deletions

View File

@@ -8,8 +8,8 @@ import dagger.Module
import dagger.Provides import dagger.Provides
import io.bartek.ttsserver.service.ForegroundNotificationFactory import io.bartek.ttsserver.service.ForegroundNotificationFactory
import io.bartek.ttsserver.sonos.SonosQueue import io.bartek.ttsserver.sonos.SonosQueue
import io.bartek.ttsserver.tts.TTS import io.bartek.ttsserver.tts.engine.TTSEngine
import io.bartek.ttsserver.tts.TTSStatusHolder import io.bartek.ttsserver.tts.status.TTSStatusHolder
import io.bartek.ttsserver.util.NetworkUtil import io.bartek.ttsserver.util.NetworkUtil
import io.bartek.ttsserver.web.server.WebServerFactory import io.bartek.ttsserver.web.server.WebServerFactory
import javax.inject.Singleton import javax.inject.Singleton
@@ -29,14 +29,14 @@ class TTSModule {
@Provides @Provides
@Singleton @Singleton
fun tts(context: Context, textToSpeech: TextToSpeech, ttsStatusHolder: TTSStatusHolder) = fun tts(context: Context, textToSpeech: TextToSpeech, ttsStatusHolder: TTSStatusHolder) =
TTS(context, textToSpeech, ttsStatusHolder) TTSEngine(context, textToSpeech, ttsStatusHolder)
@Provides @Provides
@Singleton @Singleton
fun webServerFactory( fun webServerFactory(
preferences: SharedPreferences, preferences: SharedPreferences,
context: Context, context: Context,
tts: TTS, tts: TTSEngine,
sonos: SonosQueue sonos: SonosQueue
) = ) =
WebServerFactory( WebServerFactory(
@@ -56,7 +56,7 @@ class TTSModule {
@Provides @Provides
@Singleton @Singleton
fun sonosQueue(tts: TTS, networkUtil: NetworkUtil, preferences: SharedPreferences) = fun sonosQueue(tts: TTSEngine, networkUtil: NetworkUtil, preferences: SharedPreferences) =
SonosQueue(tts, networkUtil, preferences) SonosQueue(tts, networkUtil, preferences)
@Provides @Provides

View File

@@ -5,14 +5,14 @@ import com.vmichalak.sonoscontroller.SonosDiscovery
import io.bartek.ttsserver.preference.PreferenceKey import io.bartek.ttsserver.preference.PreferenceKey
import io.bartek.ttsserver.service.ForegroundService import io.bartek.ttsserver.service.ForegroundService
import io.bartek.ttsserver.service.ServiceState import io.bartek.ttsserver.service.ServiceState
import io.bartek.ttsserver.tts.TTS import io.bartek.ttsserver.tts.engine.TTSEngine
import io.bartek.ttsserver.util.NetworkUtil import io.bartek.ttsserver.util.NetworkUtil
import io.bartek.ttsserver.web.dto.BaseDTO import io.bartek.ttsserver.web.dto.BaseDTO
import java.util.concurrent.BlockingQueue import java.util.concurrent.BlockingQueue
import java.util.concurrent.LinkedBlockingQueue import java.util.concurrent.LinkedBlockingQueue
private class Consumer( private class Consumer(
private val tts: TTS, private val tts: TTSEngine,
private val host: String, private val host: String,
private val port: Int, private val port: Int,
private val queue: BlockingQueue<BaseDTO> private val queue: BlockingQueue<BaseDTO>
@@ -40,7 +40,7 @@ private class Consumer(
} }
class SonosQueue( class SonosQueue(
private val tts: TTS, private val tts: TTSEngine,
private val networkUtil: NetworkUtil, private val networkUtil: NetworkUtil,
private val preferences: SharedPreferences private val preferences: SharedPreferences
) { ) {

View File

@@ -1,27 +1,28 @@
package io.bartek.ttsserver.tts package io.bartek.ttsserver.tts.engine
import android.content.Context import android.content.Context
import android.speech.tts.TextToSpeech import android.speech.tts.TextToSpeech
import android.speech.tts.UtteranceProgressListener import io.bartek.ttsserver.tts.exception.TTSException
import io.bartek.ttsserver.exception.TTSException import io.bartek.ttsserver.tts.listener.Lock
import io.bartek.ttsserver.tts.listener.TTSProcessListener
import io.bartek.ttsserver.tts.model.TTSStream
import io.bartek.ttsserver.tts.status.TTSStatus
import io.bartek.ttsserver.tts.status.TTSStatusHolder
import java.io.BufferedInputStream import java.io.BufferedInputStream
import java.io.File import java.io.File
import java.io.FileInputStream import java.io.FileInputStream
import java.io.InputStream
import java.security.MessageDigest import java.security.MessageDigest
import java.util.* import java.util.*
data class SpeechData(val stream: InputStream, val size: Long) class TTSEngine(
class TTS(
private val context: Context, private val context: Context,
private val tts: TextToSpeech, private val tts: TextToSpeech,
private val ttsStausHolder: TTSStatusHolder private val ttsStatusHolder: TTSStatusHolder
) { ) {
private val messageDigest = MessageDigest.getInstance("SHA-256") private val messageDigest = MessageDigest.getInstance("SHA-256")
val status: TTSStatus val status: TTSStatus
get() = ttsStausHolder.status get() = ttsStatusHolder.status
fun createTTSFile(text: String, language: Locale): File { fun createTTSFile(text: String, language: Locale): File {
val digest = hash(text, language) val digest = hash(text, language)
@@ -32,7 +33,12 @@ class TTS(
val uuid = UUID.randomUUID().toString() val uuid = UUID.randomUUID().toString()
val lock = Lock() val lock = Lock()
tts.setOnUtteranceProgressListener(TTSProcessListener(uuid, lock)) tts.setOnUtteranceProgressListener(
TTSProcessListener(
uuid,
lock
)
)
synchronized(lock) { synchronized(lock) {
tts.language = language tts.language = language
@@ -53,12 +59,17 @@ class TTS(
return digest.fold("", { str, it -> str + "%02x".format(it) }) return digest.fold("", { str, it -> str + "%02x".format(it) })
} }
fun fetchTTSStream(text: String, language: Locale): SpeechData { fun fetchTTSStream(text: String, language: Locale): TTSStream {
val file = createTempFile("tmp_tts_server", ".wav") val file = createTempFile("tmp_tts_server", ".wav")
val uuid = UUID.randomUUID().toString() val uuid = UUID.randomUUID().toString()
val lock = Lock() val lock = Lock()
tts.setOnUtteranceProgressListener(TTSProcessListener(uuid, lock)) tts.setOnUtteranceProgressListener(
TTSProcessListener(
uuid,
lock
)
)
synchronized(lock) { synchronized(lock) {
tts.language = language tts.language = language
@@ -75,13 +86,18 @@ class TTS(
file.delete() file.delete()
return SpeechData(stream, length) return TTSStream(stream, length)
} }
fun performTTS(text: String, language: Locale) { fun performTTS(text: String, language: Locale) {
val uuid = UUID.randomUUID().toString() val uuid = UUID.randomUUID().toString()
val lock = Lock() val lock = Lock()
tts.setOnUtteranceProgressListener(TTSProcessListener(uuid, lock)) tts.setOnUtteranceProgressListener(
TTSProcessListener(
uuid,
lock
)
)
synchronized(lock) { synchronized(lock) {
tts.language = language tts.language = language
@@ -95,32 +111,3 @@ class TTS(
} }
} }
@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
// TODO: Investigate the Kotlin way to achieve the same
private data class Lock(var success: Boolean = false) : Object()
private class TTSProcessListener(
private val uuid: String,
private val lock: Lock
) : UtteranceProgressListener() {
override fun onDone(utteranceId: String?) {
if (utteranceId == uuid) {
synchronized(lock) {
lock.success = true
lock.notifyAll()
}
}
}
override fun onError(utteranceId: String?) {
if (utteranceId == uuid) {
synchronized(lock) {
lock.success = false
lock.notifyAll()
}
}
}
override fun onStart(utteranceId: String?) {}
}

View File

@@ -1,3 +1,3 @@
package io.bartek.ttsserver.exception package io.bartek.ttsserver.tts.exception
class TTSException : Exception("TTS process failed") class TTSException : Exception("TTS process failed")

View File

@@ -0,0 +1,5 @@
package io.bartek.ttsserver.tts.listener
@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
// TODO: Investigate the Kotlin way to achieve the same
data class Lock(var success: Boolean = false) : Object()

View File

@@ -0,0 +1,29 @@
package io.bartek.ttsserver.tts.listener
import android.speech.tts.UtteranceProgressListener
class TTSProcessListener(
private val uuid: String,
private val lock: Lock
) : UtteranceProgressListener() {
override fun onDone(utteranceId: String?) {
if (utteranceId == uuid) {
synchronized(lock) {
lock.success = true
lock.notifyAll()
}
}
}
override fun onError(utteranceId: String?) {
if (utteranceId == uuid) {
synchronized(lock) {
lock.success = false
lock.notifyAll()
}
}
}
override fun onStart(utteranceId: String?) {}
}

View File

@@ -0,0 +1,5 @@
package io.bartek.ttsserver.tts.model
import java.io.InputStream
data class TTSStream(val stream: InputStream, val length: Long)

View File

@@ -1,4 +1,4 @@
package io.bartek.ttsserver.tts package io.bartek.ttsserver.tts.status
import android.speech.tts.TextToSpeech import android.speech.tts.TextToSpeech
@@ -10,13 +10,4 @@ enum class TTSStatus(private val status: Int) {
companion object { companion object {
fun of(status: Int) = values().firstOrNull { it.status == status } ?: UNLOADED fun of(status: Int) = values().firstOrNull { it.status == status } ?: UNLOADED
} }
}
class TTSStatusHolder : TextToSpeech.OnInitListener {
var status = TTSStatus.UNLOADED
private set
override fun onInit(status: Int) {
this.status = TTSStatus.of(status)
}
} }

View File

@@ -0,0 +1,13 @@
package io.bartek.ttsserver.tts.status
import android.speech.tts.TextToSpeech
import io.bartek.ttsserver.tts.status.TTSStatus
class TTSStatusHolder : TextToSpeech.OnInitListener {
var status = TTSStatus.UNLOADED
private set
override fun onInit(status: Int) {
this.status = TTSStatus.of(status)
}
}

View File

@@ -11,8 +11,8 @@ import io.bartek.ttsserver.preference.PreferenceKey
import io.bartek.ttsserver.service.ForegroundService import io.bartek.ttsserver.service.ForegroundService
import io.bartek.ttsserver.service.ServiceState import io.bartek.ttsserver.service.ServiceState
import io.bartek.ttsserver.sonos.SonosQueue import io.bartek.ttsserver.sonos.SonosQueue
import io.bartek.ttsserver.tts.TTS import io.bartek.ttsserver.tts.engine.TTSEngine
import io.bartek.ttsserver.tts.TTSStatus import io.bartek.ttsserver.tts.status.TTSStatus
import io.bartek.ttsserver.web.dto.BaseDTO import io.bartek.ttsserver.web.dto.BaseDTO
import io.bartek.ttsserver.web.dto.SonosDTO import io.bartek.ttsserver.web.dto.SonosDTO
import io.bartek.ttsserver.web.endpoint.Endpoint import io.bartek.ttsserver.web.endpoint.Endpoint
@@ -26,7 +26,7 @@ class WebServer(
port: Int, port: Int,
private val context: Context, private val context: Context,
private val preferences: SharedPreferences, private val preferences: SharedPreferences,
private val tts: TTS, private val tts: TTSEngine,
private val sonos: SonosQueue private val sonos: SonosQueue
) : NanoHTTPD(port) { ) : NanoHTTPD(port) {
override fun serve(session: IHTTPSession?): Response { override fun serve(session: IHTTPSession?): Response {

View File

@@ -4,12 +4,12 @@ import android.content.Context
import android.content.SharedPreferences import android.content.SharedPreferences
import io.bartek.ttsserver.preference.PreferenceKey import io.bartek.ttsserver.preference.PreferenceKey
import io.bartek.ttsserver.sonos.SonosQueue import io.bartek.ttsserver.sonos.SonosQueue
import io.bartek.ttsserver.tts.TTS import io.bartek.ttsserver.tts.engine.TTSEngine
class WebServerFactory( class WebServerFactory(
private val preferences: SharedPreferences, private val preferences: SharedPreferences,
private val context: Context, private val context: Context,
private val tts: TTS, private val tts: TTSEngine,
private val sonos: SonosQueue private val sonos: SonosQueue
) { ) {
fun createWebServer() = fun createWebServer() =