mirror of
https://github.com/twinksoftware/Gearslip.git
synced 2026-11-10 16:02:25 +01:00
Spoken directions start the speech engine again if it didn't start
A failed or unanswered start was final, so one bad start left every turn of a drive unspoken (#35).
This commit is contained in:
1 parent
588d13c3a8
commit
a1a7eca5cb
1 file changed
+27
-1
@@ -3,6 +3,9 @@ package app.seb3thehacker.gearslip.car
|
|||||||
import app.seb3thehacker.gearslip.R
|
import app.seb3thehacker.gearslip.R
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.os.Handler
|
||||||
|
import android.os.Looper
|
||||||
|
import android.os.SystemClock
|
||||||
import android.speech.tts.TextToSpeech
|
import android.speech.tts.TextToSpeech
|
||||||
import android.speech.tts.UtteranceProgressListener
|
import android.speech.tts.UtteranceProgressListener
|
||||||
import androidx.car.app.model.CarText
|
import androidx.car.app.model.CarText
|
||||||
@@ -40,7 +43,10 @@ object DirectionsVoice {
|
|||||||
private var appContext: Context? = null
|
private var appContext: Context? = null
|
||||||
private var tts: TextToSpeech? = null
|
private var tts: TextToSpeech? = null
|
||||||
@Volatile private var ttsReady = false
|
@Volatile private var ttsReady = false
|
||||||
|
/** When the engine was last started; one not ready by [RESTART_MS] later gets another try. */
|
||||||
|
@Volatile private var ttsStartedAt = 0L
|
||||||
private val pending = ConcurrentHashMap<String, CountDownLatch>()
|
private val pending = ConcurrentHashMap<String, CountDownLatch>()
|
||||||
|
private val main = Handler(Looper.getMainLooper())
|
||||||
|
|
||||||
@Volatile private var link: AudioLink? = null
|
@Volatile private var link: AudioLink? = null
|
||||||
@Volatile private var running = false
|
@Volatile private var running = false
|
||||||
@@ -56,7 +62,16 @@ object DirectionsVoice {
|
|||||||
fun init(context: Context) {
|
fun init(context: Context) {
|
||||||
if (tts != null) return
|
if (tts != null) return
|
||||||
appContext = context.applicationContext
|
appContext = context.applicationContext
|
||||||
tts = TextToSpeech(appContext) { status -> ttsReady = status == TextToSpeech.SUCCESS }.apply {
|
start()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun start() {
|
||||||
|
ttsReady = false
|
||||||
|
ttsStartedAt = SystemClock.elapsedRealtime()
|
||||||
|
tts = TextToSpeech(appContext) { status ->
|
||||||
|
ttsReady = status == TextToSpeech.SUCCESS
|
||||||
|
if (!ttsReady) GearslipLog.w("guidance: text-to-speech didn't start (status $status)")
|
||||||
|
}.apply {
|
||||||
setOnUtteranceProgressListener(object : UtteranceProgressListener() {
|
setOnUtteranceProgressListener(object : UtteranceProgressListener() {
|
||||||
override fun onStart(utteranceId: String?) {}
|
override fun onStart(utteranceId: String?) {}
|
||||||
override fun onDone(utteranceId: String?) { pending.remove(utteranceId)?.countDown() }
|
override fun onDone(utteranceId: String?) { pending.remove(utteranceId)?.countDown() }
|
||||||
@@ -232,6 +247,16 @@ object DirectionsVoice {
|
|||||||
val sink = link ?: return
|
val sink = link ?: return
|
||||||
if (!ttsReady) {
|
if (!ttsReady) {
|
||||||
GearslipLog.w("guidance: text-to-speech isn't ready, a turn went unspoken")
|
GearslipLog.w("guidance: text-to-speech isn't ready, a turn went unspoken")
|
||||||
|
// A failed or unanswered start was final, so one bad start silenced a whole drive
|
||||||
|
// (issue #35). Try again, once the last try has had its time.
|
||||||
|
if (SystemClock.elapsedRealtime() - ttsStartedAt > RESTART_MS) {
|
||||||
|
ttsStartedAt = SystemClock.elapsedRealtime()
|
||||||
|
GearslipLog.i("guidance: starting text-to-speech again")
|
||||||
|
main.post {
|
||||||
|
runCatching { engine.shutdown() }
|
||||||
|
start()
|
||||||
|
}
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val file = File(context.cacheDir, "directions.wav")
|
val file = File(context.cacheDir, "directions.wav")
|
||||||
@@ -360,6 +385,7 @@ object DirectionsVoice {
|
|||||||
private const val LEAD_MS = 100L
|
private const val LEAD_MS = 100L
|
||||||
private const val HOLD_MS = 2_000L
|
private const val HOLD_MS = 2_000L
|
||||||
private const val SYNTH_TIMEOUT_S = 10L
|
private const val SYNTH_TIMEOUT_S = 10L
|
||||||
|
private const val RESTART_MS = 30_000L
|
||||||
private val ORDINALS = listOf(
|
private val ORDINALS = listOf(
|
||||||
R.string.dir_ordinal_1, R.string.dir_ordinal_2, R.string.dir_ordinal_3, R.string.dir_ordinal_4,
|
R.string.dir_ordinal_1, R.string.dir_ordinal_2, R.string.dir_ordinal_3, R.string.dir_ordinal_4,
|
||||||
R.string.dir_ordinal_5, R.string.dir_ordinal_6, R.string.dir_ordinal_7, R.string.dir_ordinal_8,
|
R.string.dir_ordinal_5, R.string.dir_ordinal_6, R.string.dir_ordinal_7, R.string.dir_ordinal_8,
|
||||||
|
|||||||
Reference in new issue
Block a user