From 2fd0da2362b0b401031082208e3e8ad303fd13a2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bart=C5=82omiej=20Przemys=C5=82aw=20Pluta?=
Date: Fri, 19 Jun 2020 20:51:53 +0200
Subject: [PATCH] Implement FFmpeg converter and enable mp3 conversion for
/sonos
---
README.md | 11 +++--
app/build.gradle | 1 +
app/src/main/assets/help/help.html | 11 +++--
.../core/sonos/worker/SonosWorker.kt | 3 +-
.../ttsserver/core/tts/engine/TTSEngine.kt | 35 ++++++++++----
.../tts/exception/AudioConversionException.kt | 6 +++
.../core/tts/listener/ConverterListener.kt | 20 ++++++++
.../ttsserver/core/util/AudioConverter.kt | 47 +++++++++++++++++++
.../ttsserver/core/web/endpoint/Endpoint.kt | 13 +++--
.../core/web/endpoint/EndpointMatcher.kt | 2 +-
.../ttsserver/core/web/mime/MimeType.kt | 28 +++++++++++
.../ttsserver/core/web/server/WebServer.kt | 22 +++++----
.../ttsserver/di/module/TTSModule.kt | 10 +++-
.../ttsserver/ui/preference/Preference.kt | 2 +-
.../ui/preference/PreferencesFragment.kt | 7 +--
app/src/main/res/values/strings.xml | 4 +-
app/src/main/res/xml/preferences.xml | 6 +--
17 files changed, 183 insertions(+), 45 deletions(-)
create mode 100644 app/src/main/java/com/bartlomiejpluta/ttsserver/core/tts/exception/AudioConversionException.kt
create mode 100644 app/src/main/java/com/bartlomiejpluta/ttsserver/core/tts/listener/ConverterListener.kt
create mode 100644 app/src/main/java/com/bartlomiejpluta/ttsserver/core/util/AudioConverter.kt
create mode 100644 app/src/main/java/com/bartlomiejpluta/ttsserver/core/web/mime/MimeType.kt
diff --git a/README.md b/README.md
index b55b8eb..e1e5753 100644
--- a/README.md
+++ b/README.md
@@ -41,21 +41,22 @@ old PC-speakers you are able to connect your device to them via line port and
get a complete speech-based notification system.
-## The `/wave` endpoint
+## The `/{wave,acc,mp3,m4a,wma,flac}` endpoint
```
-POST /wave
+POST /{wave,acc,mp3,m4a,wma,flac}
{
"text": "The text to be spoken",
"language": "en_US"
}
```
-*Returns:* `200 OK` with wave file (`Content-Type: audio/x-wav`)
+*Returns:* `200 OK` with proper audio file
-The `/wave` endpoint enables you to download a wav file containing speech of the
+This endpoint enables you to download an audio file file containing speech of the
provided text. The goal of this endpoint is to provide interface allowing you establishment
of the connection between the TTS Server and some other kind of already running TTS system,
which can invoke the HTTP request to your Android device and do something with returned
-wav file.
+audio file. Note that all files but wav originates right from the wav file through FFmpeg
+converter.
## The `/sonos` endpoint
```
diff --git a/app/build.gradle b/app/build.gradle
index 8c696ae..7a99dd6 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -37,6 +37,7 @@ dependencies {
implementation 'com.bartlomiejpluta:sonos-controller:v.0.2'
implementation 'com.google.dagger:dagger-android:2.15'
implementation 'com.google.dagger:dagger-android-support:2.15'
+ implementation 'com.github.adrielcafe:AndroidAudioConverter:0.0.8'
kapt 'com.google.dagger:dagger-android-processor:2.15'
kapt 'com.google.dagger:dagger-compiler:2.15'
testImplementation 'junit:junit:4.12'
diff --git a/app/src/main/assets/help/help.html b/app/src/main/assets/help/help.html
index aa7638e..4dfe2d3 100644
--- a/app/src/main/assets/help/help.html
+++ b/app/src/main/assets/help/help.html
@@ -58,21 +58,22 @@ POST /say
get a complete speech-based notification system.
-The /wave endpoint
+The /{wave,acc,mp3,m4a,wma,flac} endpoint
-POST /wave
+POST /{wave,acc,mp3,m4a,wma,flac}
{
"text": "The text to be spoken",
"language": "en_US"
}
-Returns: 200 OK with wave file (Content-Type: audio/x-wav)
+Returns: 200 OK with proper audio file
- The /wave endpoint enables you to download a wav file containing speech of the
+ This endpoint enables you to download an audio file file containing speech of the
provided text. The goal of this endpoint is to provide interface allowing you establishment
of the connection between the TTS Server and some other kind of already running TTS system,
which can invoke the HTTP request to your Android device and do something with returned
- wav file.
+ audio file. Note that all files but wav originates right from the wav file through FFmpeg
+ converter.
The /sonos endpoint
diff --git a/app/src/main/java/com/bartlomiejpluta/ttsserver/core/sonos/worker/SonosWorker.kt b/app/src/main/java/com/bartlomiejpluta/ttsserver/core/sonos/worker/SonosWorker.kt
index 62143f1..ad100e6 100644
--- a/app/src/main/java/com/bartlomiejpluta/ttsserver/core/sonos/worker/SonosWorker.kt
+++ b/app/src/main/java/com/bartlomiejpluta/ttsserver/core/sonos/worker/SonosWorker.kt
@@ -1,6 +1,7 @@
package com.bartlomiejpluta.ttsserver.core.sonos.worker
import android.content.SharedPreferences
+import cafe.adriel.androidaudioconverter.model.AudioFormat
import com.bartlomiejpluta.ttsserver.core.tts.engine.TTSEngine
import com.bartlomiejpluta.ttsserver.core.web.dto.SonosDTO
import com.bartlomiejpluta.ttsserver.core.web.endpoint.Endpoint
@@ -40,7 +41,7 @@ class SonosWorker(
}
private fun prepareTTSFile(data: SonosDTO): String {
- val filename = tts.createTTSFile(data.text, data.language).name
+ val filename = tts.createTTSFile(data.text, data.language, AudioFormat.MP3).name
return "$announcementUrl$filename"
}
diff --git a/app/src/main/java/com/bartlomiejpluta/ttsserver/core/tts/engine/TTSEngine.kt b/app/src/main/java/com/bartlomiejpluta/ttsserver/core/tts/engine/TTSEngine.kt
index 9357046..c59c1b9 100644
--- a/app/src/main/java/com/bartlomiejpluta/ttsserver/core/tts/engine/TTSEngine.kt
+++ b/app/src/main/java/com/bartlomiejpluta/ttsserver/core/tts/engine/TTSEngine.kt
@@ -6,12 +6,14 @@ import android.media.AudioAttributes
import android.media.MediaPlayer
import android.net.Uri
import android.speech.tts.TextToSpeech
+import cafe.adriel.androidaudioconverter.model.AudioFormat
import com.bartlomiejpluta.ttsserver.core.tts.exception.TTSException
import com.bartlomiejpluta.ttsserver.core.tts.listener.GongListener
import com.bartlomiejpluta.ttsserver.core.tts.listener.TTSProcessListener
import com.bartlomiejpluta.ttsserver.core.tts.model.TTSStream
import com.bartlomiejpluta.ttsserver.core.tts.status.TTSStatus
import com.bartlomiejpluta.ttsserver.core.tts.status.TTSStatusHolder
+import com.bartlomiejpluta.ttsserver.core.util.AudioConverter
import com.bartlomiejpluta.ttsserver.ui.preference.PreferenceKey
import java.io.BufferedInputStream
import java.io.File
@@ -23,29 +25,41 @@ class TTSEngine(
private val context: Context,
private val tts: TextToSpeech,
private val ttsStatusHolder: TTSStatusHolder,
- private val preferences: SharedPreferences
+ private val preferences: SharedPreferences,
+ private val audioConverter: AudioConverter
) {
private val messageDigest = MessageDigest.getInstance("SHA-256")
val status: TTSStatus
get() = ttsStatusHolder.status
- fun createTTSFile(text: String, language: Locale): File {
+ fun createTTSFile(text: String, language: Locale, audioFormat: AudioFormat = AudioFormat.WAV): File {
val digest = hash(text, language)
- val filename = "tts_$digest.wav"
- val file = File(context.cacheDir, filename)
+ val targetFilename = "tts_$digest.${audioFormat.format}"
+ val wavFilename = "tts_$digest.wav"
+ val wavFile = File(context.cacheDir, wavFilename)
+ val targetFile = File(context.cacheDir, targetFilename)
- file.takeIf { it.exists() }?.let { return it }
+ targetFile.takeIf { it.exists() }?.let { return it }
val uuid = UUID.randomUUID().toString()
val listener = TTSProcessListener(uuid)
tts.setOnUtteranceProgressListener(listener)
tts.language = language
- tts.synthesizeToFile(text, null, file, uuid)
+ tts.synthesizeToFile(text, null, wavFile, uuid)
listener.await()
- return file
+ return convertFile(wavFile, audioFormat)
+ }
+
+ private fun convertFile(file: File, audioFormat: AudioFormat): File {
+ if (audioFormat == AudioFormat.WAV) {
+ return file
+ }
+
+
+ return audioConverter.convert(file, audioFormat).also { file.delete() }
}
private fun hash(text: String, language: Locale): String {
@@ -54,7 +68,7 @@ class TTSEngine(
return digest.fold("", { str, it -> str + "%02x".format(it) })
}
- fun fetchTTSStream(text: String, language: Locale): TTSStream {
+ fun fetchTTSStream(text: String, language: Locale, audioFormat: AudioFormat = AudioFormat.WAV): TTSStream {
val file = createTempFile("tmp_tts_server", ".wav")
val uuid = UUID.randomUUID().toString()
@@ -65,8 +79,9 @@ class TTSEngine(
tts.synthesizeToFile(text, null, file, uuid)
listener.await()
- val stream = BufferedInputStream(FileInputStream(file))
- val length = file.length()
+ val converted = convertFile(file, audioFormat)
+ val stream = BufferedInputStream(FileInputStream(converted))
+ val length = converted.length()
file.delete()
diff --git a/app/src/main/java/com/bartlomiejpluta/ttsserver/core/tts/exception/AudioConversionException.kt b/app/src/main/java/com/bartlomiejpluta/ttsserver/core/tts/exception/AudioConversionException.kt
new file mode 100644
index 0000000..e48674d
--- /dev/null
+++ b/app/src/main/java/com/bartlomiejpluta/ttsserver/core/tts/exception/AudioConversionException.kt
@@ -0,0 +1,6 @@
+package com.bartlomiejpluta.ttsserver.core.tts.exception
+
+class AudioConversionException : Exception {
+ constructor(message: String) : super(message)
+ constructor(message: String, cause: Throwable) : super(message, cause)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/bartlomiejpluta/ttsserver/core/tts/listener/ConverterListener.kt b/app/src/main/java/com/bartlomiejpluta/ttsserver/core/tts/listener/ConverterListener.kt
new file mode 100644
index 0000000..a90eae0
--- /dev/null
+++ b/app/src/main/java/com/bartlomiejpluta/ttsserver/core/tts/listener/ConverterListener.kt
@@ -0,0 +1,20 @@
+package com.bartlomiejpluta.ttsserver.core.tts.listener
+
+import cafe.adriel.androidaudioconverter.callback.IConvertCallback
+import com.bartlomiejpluta.ttsserver.core.tts.exception.AudioConversionException
+import java.io.File
+import java.util.concurrent.LinkedBlockingQueue
+
+class ConverterListener : IConvertCallback {
+ private val queue = LinkedBlockingQueue()
+
+ fun await() = queue.take()
+
+ override fun onSuccess(convertedFile: File?) {
+ queue.add(convertedFile)
+ }
+
+ override fun onFailure(error: Exception?) {
+ error?.let { throw AudioConversionException("Conversion failed", error) }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/bartlomiejpluta/ttsserver/core/util/AudioConverter.kt b/app/src/main/java/com/bartlomiejpluta/ttsserver/core/util/AudioConverter.kt
new file mode 100644
index 0000000..2e1f0ba
--- /dev/null
+++ b/app/src/main/java/com/bartlomiejpluta/ttsserver/core/util/AudioConverter.kt
@@ -0,0 +1,47 @@
+package com.bartlomiejpluta.ttsserver.core.util
+
+import android.content.Context
+import cafe.adriel.androidaudioconverter.AndroidAudioConverter
+import cafe.adriel.androidaudioconverter.callback.ILoadCallback
+import cafe.adriel.androidaudioconverter.model.AudioFormat
+import com.bartlomiejpluta.ttsserver.core.tts.exception.AudioConversionException
+import com.bartlomiejpluta.ttsserver.core.tts.listener.ConverterListener
+import java.io.File
+
+class AudioConverter(private val context: Context) {
+ var state: State = State.UNLOADED
+ private set
+
+ enum class State {
+ READY,
+ ERROR,
+ UNLOADED
+ }
+
+ init {
+ AndroidAudioConverter.load(context, object : ILoadCallback {
+ override fun onSuccess() {
+ state = State.READY
+ }
+
+ override fun onFailure(error: Exception?) {
+ state = State.ERROR
+ }
+ })
+ }
+
+ fun convert(file: File, format: AudioFormat): File {
+ if(state != State.READY) {
+ throw AudioConversionException("Converter is not ready")
+ }
+
+ val listener = ConverterListener()
+ AndroidAudioConverter.with(context)
+ .setFile(file)
+ .setFormat(format)
+ .setCallback(listener)
+ .convert()
+
+ return listener.await()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/bartlomiejpluta/ttsserver/core/web/endpoint/Endpoint.kt b/app/src/main/java/com/bartlomiejpluta/ttsserver/core/web/endpoint/Endpoint.kt
index 50b92ae..b10cb45 100644
--- a/app/src/main/java/com/bartlomiejpluta/ttsserver/core/web/endpoint/Endpoint.kt
+++ b/app/src/main/java/com/bartlomiejpluta/ttsserver/core/web/endpoint/Endpoint.kt
@@ -4,14 +4,19 @@ enum class Endpoint(val uri: String, val id: Int) {
UNKNOWN("/", 1),
SAY("/say", 2),
WAVE("/wave", 3),
- SONOS("/sonos", 4),
- SONOS_CACHE("/sonos/*", 5),
- GONG("/gong.wav", 6);
+ AAC("/aac", 4),
+ MP3("/mp3", 5),
+ M4A("/m4a", 6),
+ WMA("/wma", 7),
+ FLAC("/flac", 8),
+ SONOS("/sonos", 9),
+ SONOS_CACHE("/sonos/*", 10),
+ GONG("/gong.wav", 11);
val trimmedUri: String
get() = uri.replace("*", "")
companion object {
- fun of(id: Int) = values().firstOrNull { it.id == id } ?: UNKNOWN
+ fun of(ordinal: Int) = values().firstOrNull { it.ordinal == ordinal } ?: UNKNOWN
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/bartlomiejpluta/ttsserver/core/web/endpoint/EndpointMatcher.kt b/app/src/main/java/com/bartlomiejpluta/ttsserver/core/web/endpoint/EndpointMatcher.kt
index 91d311f..1eb1626 100644
--- a/app/src/main/java/com/bartlomiejpluta/ttsserver/core/web/endpoint/EndpointMatcher.kt
+++ b/app/src/main/java/com/bartlomiejpluta/ttsserver/core/web/endpoint/EndpointMatcher.kt
@@ -8,7 +8,7 @@ object EndpointMatcher {
init {
Endpoint.values().forEach {
- uriMatcher.addURI("", it.uri, it.id)
+ uriMatcher.addURI("", it.uri, it.ordinal)
}
}
diff --git a/app/src/main/java/com/bartlomiejpluta/ttsserver/core/web/mime/MimeType.kt b/app/src/main/java/com/bartlomiejpluta/ttsserver/core/web/mime/MimeType.kt
new file mode 100644
index 0000000..7756de4
--- /dev/null
+++ b/app/src/main/java/com/bartlomiejpluta/ttsserver/core/web/mime/MimeType.kt
@@ -0,0 +1,28 @@
+package com.bartlomiejpluta.ttsserver.core.web.mime
+
+import cafe.adriel.androidaudioconverter.model.AudioFormat
+import java.io.File
+import java.util.*
+
+enum class MimeType(val mimeType: String) {
+ AAC("audio/aac"),
+ MP3("audio/mpeg"),
+ M4A("audio/m4a"),
+ WMA("audio/x-ms-wma"),
+ WAV("audio/x-wav"),
+ FLAC("audio/x-wav"),
+ JSON("application/json");
+
+ companion object {
+ fun forAudioFormat(audioFormat: AudioFormat) = when(audioFormat) {
+ AudioFormat.AAC -> AAC
+ AudioFormat.MP3 -> MP3
+ AudioFormat.M4A -> M4A
+ AudioFormat.WMA -> WMA
+ AudioFormat.WAV -> WAV
+ AudioFormat.FLAC -> FLAC
+ }
+
+ fun forFile(file: File) = valueOf(file.extension.toUpperCase(Locale.ROOT))
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/bartlomiejpluta/ttsserver/core/web/server/WebServer.kt b/app/src/main/java/com/bartlomiejpluta/ttsserver/core/web/server/WebServer.kt
index 268c279..85e682f 100644
--- a/app/src/main/java/com/bartlomiejpluta/ttsserver/core/web/server/WebServer.kt
+++ b/app/src/main/java/com/bartlomiejpluta/ttsserver/core/web/server/WebServer.kt
@@ -5,6 +5,7 @@ import android.content.Intent
import android.content.SharedPreferences
import android.net.Uri
import androidx.localbroadcastmanager.content.LocalBroadcastManager
+import cafe.adriel.androidaudioconverter.model.AudioFormat
import com.bartlomiejpluta.ttsserver.core.sonos.queue.SonosQueue
import com.bartlomiejpluta.ttsserver.core.tts.engine.TTSEngine
import com.bartlomiejpluta.ttsserver.core.tts.exception.TTSException
@@ -14,6 +15,7 @@ import com.bartlomiejpluta.ttsserver.core.web.dto.SonosDTO
import com.bartlomiejpluta.ttsserver.core.web.endpoint.Endpoint
import com.bartlomiejpluta.ttsserver.core.web.endpoint.EndpointMatcher
import com.bartlomiejpluta.ttsserver.core.web.exception.WebException
+import com.bartlomiejpluta.ttsserver.core.web.mime.MimeType
import com.bartlomiejpluta.ttsserver.service.foreground.ForegroundService
import com.bartlomiejpluta.ttsserver.service.state.ServiceState
import com.bartlomiejpluta.ttsserver.ui.preference.PreferenceKey
@@ -57,7 +59,12 @@ class WebServer(
private fun dispatch(it: IHTTPSession): Response {
return when (EndpointMatcher.match(it.uri)) {
Endpoint.SAY -> say(it)
- Endpoint.WAVE -> wave(it)
+ Endpoint.WAVE -> file(it, AudioFormat.WAV)
+ Endpoint.AAC -> file(it, AudioFormat.AAC)
+ Endpoint.MP3 -> file(it, AudioFormat.MP3)
+ Endpoint.M4A -> file(it, AudioFormat.M4A)
+ Endpoint.WMA -> file(it, AudioFormat.WMA)
+ Endpoint.FLAC -> file(it, AudioFormat.FLAC)
Endpoint.SONOS -> sonos(it)
Endpoint.SONOS_CACHE -> sonosCache(it)
Endpoint.GONG -> gong(it)
@@ -103,8 +110,8 @@ class WebServer(
}
}
- private fun wave(session: IHTTPSession): Response {
- if (!preferences.getBoolean(PreferenceKey.ENABLE_WAVE_ENDPOINT, true)) {
+ private fun file(session: IHTTPSession, audioFormat: AudioFormat): Response {
+ if (!preferences.getBoolean(PreferenceKey.ENABLE_FILE_ENDPOINTS, true)) {
throw WebException(NOT_FOUND)
}
@@ -118,8 +125,8 @@ class WebServer(
val dto = extractBody(session) { BaseDTO(it) }
- val (stream, size) = tts.fetchTTSStream(dto.text, dto.language)
- return newFixedLengthResponse(OK, MIME_WAVE, stream, size)
+ val (stream, size) = tts.fetchTTSStream(dto.text, dto.language, audioFormat)
+ return newFixedLengthResponse(OK, MimeType.forAudioFormat(audioFormat).mimeType, stream, size)
}
private fun sonos(session: IHTTPSession): Response {
@@ -160,7 +167,7 @@ class WebServer(
val stream = BufferedInputStream(FileInputStream(file))
val size = file.length()
- return newFixedLengthResponse(OK, MIME_WAVE, stream, size)
+ return newFixedLengthResponse(OK, MimeType.forFile(file).mimeType, stream, size)
}
private fun gong(session: IHTTPSession): Response {
@@ -183,7 +190,7 @@ class WebServer(
context.contentResolver.openInputStream(uri) ?: throw TTSException()
)
- return newFixedLengthResponse(OK, MIME_WAVE, stream, size)
+ return newFixedLengthResponse(OK, MimeType.WAV.mimeType, stream, size)
}
override fun start() {
@@ -208,7 +215,6 @@ class WebServer(
companion object {
private const val MIME_JSON = "application/json"
- private const val MIME_WAVE = "audio/x-wav"
private const val CONTENT_TYPE = "content-type"
private val SUCCESS_RESPONSE = response("Request has been completed")
private val QUEUED_RESPONSE = response("Request has been queued")
diff --git a/app/src/main/java/com/bartlomiejpluta/ttsserver/di/module/TTSModule.kt b/app/src/main/java/com/bartlomiejpluta/ttsserver/di/module/TTSModule.kt
index 35839de..4a4f9b4 100644
--- a/app/src/main/java/com/bartlomiejpluta/ttsserver/di/module/TTSModule.kt
+++ b/app/src/main/java/com/bartlomiejpluta/ttsserver/di/module/TTSModule.kt
@@ -7,6 +7,7 @@ import androidx.preference.PreferenceManager
import com.bartlomiejpluta.ttsserver.core.sonos.queue.SonosQueue
import com.bartlomiejpluta.ttsserver.core.tts.engine.TTSEngine
import com.bartlomiejpluta.ttsserver.core.tts.status.TTSStatusHolder
+import com.bartlomiejpluta.ttsserver.core.util.AudioConverter
import com.bartlomiejpluta.ttsserver.core.util.NetworkUtil
import com.bartlomiejpluta.ttsserver.core.web.server.WebServerFactory
import com.bartlomiejpluta.ttsserver.service.notification.ForegroundNotificationFactory
@@ -32,8 +33,9 @@ class TTSModule {
context: Context,
textToSpeech: TextToSpeech,
ttsStatusHolder: TTSStatusHolder,
- preferences: SharedPreferences
- ) = TTSEngine(context, textToSpeech, ttsStatusHolder, preferences)
+ preferences: SharedPreferences,
+ converter: AudioConverter
+ ) = TTSEngine(context, textToSpeech, ttsStatusHolder, preferences, converter)
@Provides
@Singleton
@@ -58,6 +60,10 @@ class TTSModule {
fun networkUtil(context: Context, preferences: SharedPreferences) =
NetworkUtil(context, preferences)
+ @Provides
+ @Singleton
+ fun adudioConverter(context: Context) = AudioConverter(context)
+
@Provides
@Singleton
fun sonosQueue(tts: TTSEngine, preferences: SharedPreferences, networkUtil: NetworkUtil) =
diff --git a/app/src/main/java/com/bartlomiejpluta/ttsserver/ui/preference/Preference.kt b/app/src/main/java/com/bartlomiejpluta/ttsserver/ui/preference/Preference.kt
index 171810d..da588c9 100644
--- a/app/src/main/java/com/bartlomiejpluta/ttsserver/ui/preference/Preference.kt
+++ b/app/src/main/java/com/bartlomiejpluta/ttsserver/ui/preference/Preference.kt
@@ -5,7 +5,7 @@ object PreferenceKey {
const val PORT = "preference_port"
const val ENABLE_HTTP_DEBUG = "preference_http_debug"
const val ENABLE_SAY_ENDPOINT = "preference_enable_say_endpoint"
- const val ENABLE_WAVE_ENDPOINT = "preference_enable_wave_endpoint"
+ const val ENABLE_FILE_ENDPOINTS = "preference_enable_file_endpoints"
const val ENABLE_SONOS_ENDPOINT = "preference_enable_sonos_endpoint"
const val ENABLE_GONG = "preference_enable_gong"
const val GONG = "preference_gong"
diff --git a/app/src/main/java/com/bartlomiejpluta/ttsserver/ui/preference/PreferencesFragment.kt b/app/src/main/java/com/bartlomiejpluta/ttsserver/ui/preference/PreferencesFragment.kt
index f138ab7..754e0cb 100644
--- a/app/src/main/java/com/bartlomiejpluta/ttsserver/ui/preference/PreferencesFragment.kt
+++ b/app/src/main/java/com/bartlomiejpluta/ttsserver/ui/preference/PreferencesFragment.kt
@@ -12,6 +12,7 @@ import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.SwitchPreference
import com.bartlomiejpluta.R
+import com.bartlomiejpluta.ttsserver.core.web.mime.MimeType
import com.bartlomiejpluta.ttsserver.service.foreground.ForegroundService
import com.bartlomiejpluta.ttsserver.service.state.ServiceState
@@ -19,7 +20,7 @@ import com.bartlomiejpluta.ttsserver.service.state.ServiceState
class PreferencesFragment : PreferenceFragmentCompat() {
private lateinit var portPreference: IntEditTextPreference
private lateinit var sayEndpointPreference: SwitchPreference
- private lateinit var waveEndpointPreference: SwitchPreference
+ private lateinit var fileEndpointPreference: SwitchPreference
private lateinit var sonosEndpointPreference: SwitchPreference
private lateinit var httpDebugPreference: SwitchPreference
private lateinit var enableGongPreference: SwitchPreference
@@ -61,7 +62,7 @@ class PreferencesFragment : PreferenceFragmentCompat() {
portPreference.setOnBindEditTextListener { it.inputType = InputType.TYPE_CLASS_NUMBER }
httpDebugPreference = findPreference(PreferenceKey.ENABLE_HTTP_DEBUG)!!
sayEndpointPreference = findPreference(PreferenceKey.ENABLE_SAY_ENDPOINT)!!
- waveEndpointPreference = findPreference(PreferenceKey.ENABLE_WAVE_ENDPOINT)!!
+ fileEndpointPreference = findPreference(PreferenceKey.ENABLE_FILE_ENDPOINTS)!!
sonosEndpointPreference = findPreference(PreferenceKey.ENABLE_SONOS_ENDPOINT)!!
enableGongPreference = findPreference(PreferenceKey.ENABLE_GONG)!!
enableGongPreference.setOnPreferenceClickListener { preference ->
@@ -90,7 +91,7 @@ class PreferencesFragment : PreferenceFragmentCompat() {
private fun openFilePicker(preference: Preference?) {
if ((preference as SwitchPreference).isChecked) {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
- .apply { type = "audio/x-wav" }
+ .apply { type = MimeType.WAV.mimeType }
.let { Intent.createChooser(it, getString(R.string.preference_gong_picker_prompt)) }
startActivityForResult(intent, PICKFILE_RESULT_CODE)
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index c007723..0964b6b 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -19,8 +19,8 @@
Server port
Allow HTTP clients to use /say endpoint which enables them to say message using builtin speakers or external ones connected to the device
Enable /say endpoint
- Allow HTTP clients to use /wave endpoint which enables them to retrieve TTS message as wave file
- Enable /wave endpoint
+ Allow HTTP clients to use file endpoints (/wav, /mp3, /flac etc.) which enables them to retrieve TTS message as audio file
+ Enable file endpoints
Allow HTTP clients to use /sonos endpoint which enables them to send TTS messages directly to Sonos devices
Enable /sonos endpoint
Attach the stacktrace to each error HTTP response if available
diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml
index cbcfca7..0e05302 100644
--- a/app/src/main/res/xml/preferences.xml
+++ b/app/src/main/res/xml/preferences.xml
@@ -27,9 +27,9 @@