Enable synthesizing to wav files
This commit is contained in:
@@ -15,29 +15,27 @@ import io.bartek.R
|
|||||||
import io.bartek.web.TTSServer
|
import io.bartek.web.TTSServer
|
||||||
|
|
||||||
class WebService : Service() {
|
class WebService : Service() {
|
||||||
|
private var port: Int = 8080
|
||||||
private var wakeLock: PowerManager.WakeLock? = null
|
private var wakeLock: PowerManager.WakeLock? = null
|
||||||
private var isServiceStarted = false
|
private var isServiceStarted = false
|
||||||
private var ttsServer: TTSServer? = null
|
private var ttsServer: TTSServer? = null
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
Log.d("TTSService", "Service has been created")
|
|
||||||
startForeground(1, createNotification())
|
startForeground(1, createNotification())
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createNotification(): Notification {
|
private fun createNotification(): Notification {
|
||||||
val notificationChannelId = "HTTP SERVER CHANNEL"
|
|
||||||
|
|
||||||
// depending on the Android API that we're dealing with we will have
|
// depending on the Android API that we're dealing with we will have
|
||||||
// to use a specific method to create the notification
|
// to use a specific method to create the notification
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager;
|
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager;
|
||||||
val channel = NotificationChannel(
|
val channel = NotificationChannel(
|
||||||
notificationChannelId,
|
NOTIFICATION_CHANNEL_ID,
|
||||||
"HTTP Server",
|
resources.getString(R.string.service_notification_category_name),
|
||||||
NotificationManager.IMPORTANCE_HIGH
|
NotificationManager.IMPORTANCE_HIGH
|
||||||
).let {
|
).let {
|
||||||
it.description = "HTTP Server channel with keeps the service running"
|
it.description = resources.getString(R.string.service_notification_category_description)
|
||||||
it.enableLights(true)
|
it.enableLights(true)
|
||||||
it.lightColor = Color.RED
|
it.lightColor = Color.RED
|
||||||
it.enableVibration(true)
|
it.enableVibration(true)
|
||||||
@@ -53,12 +51,12 @@ class WebService : Service() {
|
|||||||
|
|
||||||
val builder: Notification.Builder = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) Notification.Builder(
|
val builder: Notification.Builder = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) Notification.Builder(
|
||||||
this,
|
this,
|
||||||
notificationChannelId
|
NOTIFICATION_CHANNEL_ID
|
||||||
) else Notification.Builder(this)
|
) else Notification.Builder(this)
|
||||||
|
|
||||||
return builder
|
return builder
|
||||||
.setContentTitle("Endless Service")
|
.setContentTitle(resources.getString(R.string.service_notification_title))
|
||||||
.setContentText("This is your favorite endless service working")
|
.setContentText(resources.getString(R.string.service_notification_text, port))
|
||||||
.setContentIntent(pendingIntent)
|
.setContentIntent(pendingIntent)
|
||||||
.setSmallIcon(R.mipmap.ic_launcher)
|
.setSmallIcon(R.mipmap.ic_launcher)
|
||||||
.setTicker("Ticker text")
|
.setTicker("Ticker text")
|
||||||
@@ -66,16 +64,15 @@ class WebService : Service() {
|
|||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBind(intent: Intent): IBinder? {
|
override fun onBind(intent: Intent) = null
|
||||||
Log.d("TTSService", "Something is willing to bind the service")
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||||
Log.d("TTSService", "onStartCommand with startId: $startId")
|
|
||||||
intent?.let {
|
intent?.let {
|
||||||
when(it.action) {
|
when(it.action) {
|
||||||
START -> startService(it.getIntExtra(PORT, 8080))
|
START -> {
|
||||||
|
port = it.getIntExtra(PORT, port)
|
||||||
|
startService()
|
||||||
|
}
|
||||||
STOP -> stopService()
|
STOP -> stopService()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -87,13 +84,12 @@ class WebService : Service() {
|
|||||||
ttsServer = null
|
ttsServer = null
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun startService(port: Int) {
|
private fun startService() {
|
||||||
if(isServiceStarted) return
|
if(isServiceStarted) return
|
||||||
Log.d("TTSService", "Starting service...")
|
|
||||||
isServiceStarted = true
|
isServiceStarted = true
|
||||||
wakeLock =
|
wakeLock =
|
||||||
(getSystemService(Context.POWER_SERVICE) as PowerManager).run {
|
(getSystemService(Context.POWER_SERVICE) as PowerManager).run {
|
||||||
newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "EndlessService::lock").apply {
|
newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "WebService::lock").apply {
|
||||||
acquire()
|
acquire()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -101,7 +97,6 @@ class WebService : Service() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun stopService() {
|
private fun stopService() {
|
||||||
Log.d("TTSService", "Stopping service...")
|
|
||||||
ttsServer?.stop()
|
ttsServer?.stop()
|
||||||
ttsServer = null
|
ttsServer = null
|
||||||
wakeLock?.let {
|
wakeLock?.let {
|
||||||
@@ -115,6 +110,7 @@ class WebService : Service() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
private const val NOTIFICATION_CHANNEL_ID = "TTSService.NOTIFICATION_CHANNEL"
|
||||||
const val PORT = "TTSService.PORT"
|
const val PORT = "TTSService.PORT"
|
||||||
const val START = "START"
|
const val START = "START"
|
||||||
const val STOP = "STOP"
|
const val STOP = "STOP"
|
||||||
|
|||||||
@@ -2,8 +2,13 @@ package io.bartek.web
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.speech.tts.TextToSpeech
|
import android.speech.tts.TextToSpeech
|
||||||
|
import android.speech.tts.UtteranceProgressListener
|
||||||
|
import android.util.Log
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import fi.iki.elonen.NanoHTTPD
|
import fi.iki.elonen.NanoHTTPD
|
||||||
|
import io.bartek.R
|
||||||
|
import java.io.FileInputStream
|
||||||
|
import java.io.InputStream
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class TTSServer(port: Int, private val context: Context) : NanoHTTPD(port), TextToSpeech.OnInitListener {
|
class TTSServer(port: Int, private val context: Context) : NanoHTTPD(port), TextToSpeech.OnInitListener {
|
||||||
@@ -11,20 +16,57 @@ class TTSServer(port: Int, private val context: Context) : NanoHTTPD(port), Text
|
|||||||
|
|
||||||
override fun serve(session: IHTTPSession?): Response {
|
override fun serve(session: IHTTPSession?): Response {
|
||||||
tts.language = Locale("pl_PL")
|
tts.language = Locale("pl_PL")
|
||||||
tts.speak(session?.uri, TextToSpeech.QUEUE_ADD, null, "")
|
val uuid = UUID.randomUUID().toString()
|
||||||
|
val file = createTempFile("tmp_tts_server", ".wav")
|
||||||
|
val lock = Object()
|
||||||
|
var error = false
|
||||||
|
tts.setOnUtteranceProgressListener(object : UtteranceProgressListener() {
|
||||||
|
override fun onDone(utteranceId: String?) {
|
||||||
|
if(utteranceId == uuid) {
|
||||||
|
synchronized(lock) {
|
||||||
|
lock.notifyAll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return newFixedLengthResponse(""" { "message": "ok" } """)
|
override fun onError(utteranceId: String?) {
|
||||||
|
if(utteranceId == uuid) {
|
||||||
|
error = true
|
||||||
|
synchronized(lock) {
|
||||||
|
lock.notifyAll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStart(utteranceId: String?) {}
|
||||||
|
})
|
||||||
|
|
||||||
|
synchronized(lock) {
|
||||||
|
tts.synthesizeToFile(session?.uri, null, file, uuid)
|
||||||
|
lock.wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
val stream = FileInputStream(file)
|
||||||
|
val length = file.length()
|
||||||
|
|
||||||
|
Log.d("WAV", stream.toString())
|
||||||
|
Log.d("WAV", length.toString())
|
||||||
|
|
||||||
|
return when(error) {
|
||||||
|
false -> newFixedLengthResponse(Response.Status.OK, "audio/x-wav", stream, length)
|
||||||
|
else -> newFixedLengthResponse(Response.Status.INTERNAL_ERROR, "text/plain", "TTS error on $uuid")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onInit(status: Int) = start()
|
override fun onInit(status: Int) = start()
|
||||||
|
|
||||||
override fun start() {
|
override fun start() {
|
||||||
super.start()
|
super.start()
|
||||||
Toast.makeText(context, "TTS-HTTP Server started", Toast.LENGTH_SHORT).show()
|
Toast.makeText(context, context.resources.getString(R.string.server_toast_service_started), Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun stop() {
|
override fun stop() {
|
||||||
super.stop()
|
super.stop()
|
||||||
Toast.makeText(context, "TTS-HTTP Server stopped", Toast.LENGTH_SHORT).show()
|
Toast.makeText(context, context.resources.getString(R.string.server_toast_service_stopped), Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:ems="10"
|
android:ems="10"
|
||||||
android:hint="Server port"
|
android:hint="@string/main_activity_server_port"
|
||||||
android:inputType="number"
|
android:inputType="number"
|
||||||
android:text="8080" />
|
android:text="8080" />
|
||||||
|
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="2"
|
android:layout_weight="2"
|
||||||
android:onClick="startServer"
|
android:onClick="startServer"
|
||||||
android:text="Run" />
|
android:text="@string/main_activity_run" />
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/stop"
|
android:id="@+id/stop"
|
||||||
@@ -36,7 +36,7 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="2"
|
android:layout_weight="2"
|
||||||
android:onClick="stopServer"
|
android:onClick="stopServer"
|
||||||
android:text="Stop" />
|
android:text="@string/main_activity_stop" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
@@ -44,5 +44,5 @@
|
|||||||
android:id="@+id/ttsSettings"
|
android:id="@+id/ttsSettings"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="TTS Settings" />
|
android:text="@string/main_activity_tts_settings" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
@@ -1,3 +1,15 @@
|
|||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">TTSServer</string>
|
<string name="app_name">TTS Server</string>
|
||||||
|
<string name="service_notification_category_name">HTTP Server</string>
|
||||||
|
<string name="service_notification_category_description">The fixed notification keeping the HTTP server alive</string>
|
||||||
|
<string name="service_notification_title">Server is running</string>
|
||||||
|
<string name="service_notification_text">The HTTP server is listening on port %1$d</string>
|
||||||
|
|
||||||
|
<string name="server_toast_service_started">TTS-HTTP Server started</string>
|
||||||
|
<string name="server_toast_service_stopped">TTS-HTTP Server stopped</string>
|
||||||
|
|
||||||
|
<string name="main_activity_server_port">Server port</string>
|
||||||
|
<string name="main_activity_run">Run</string>
|
||||||
|
<string name="main_activity_stop">Stop</string>
|
||||||
|
<string name="main_activity_tts_settings">TTS Settings</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
Reference in New Issue
Block a user