car audio: send Start once, even when the car answers the focus request while it's starting

Starting the stream at once and the car's focus answer could both get through, so the car got
Start twice and two AAC encoders ran. A 2014 aftermarket head unit dropped the connection on the
first audio. Directions now also log why a turn went unspoken.
This commit is contained in:
Seb3thehacker committed 2026-10-23 13:21:33 -07:00
1 parent 405cecf2e9
commit 456dda897e
2 files changed
+29 -3

No files matched your search

@@ -233,7 +233,19 @@ class AudioLink(
startStream() startStream()
} }
/**
* Guards starting and stopping. [start] starts the stream at once for head units that never
* answer the focus request, and the car's answer arrives on another thread and starts it
* too: without this both got through, so the car got Start twice and two AAC encoders, and a
* 2014 head unit dropped the connection on the first audio.
*/
private val startLock = Any()
private fun startStream() { private fun startStream() {
synchronized(startLock) { startStreamLocked() }
}
private fun startStreamLocked() {
if (!ready || started) return if (!ready || started) return
sendOnChannel( sendOnChannel(
MSG_START, MSG_START,
@@ -252,6 +264,10 @@ class AudioLink(
} }
override fun stop() { override fun stop() {
synchronized(startLock) { stopLocked() }
}
private fun stopLocked() {
if (!started) return if (!started) return
started = false started = false
synchronized(ackLock) { ackLock.notifyAll() } synchronized(ackLock) { ackLock.notifyAll() }
@@ -87,16 +87,26 @@ object DirectionsVoice {
running = false running = false
} }
/** Why the last turn wasn't spoken, so the log says it once rather than every tick. */
private var silentBecause: String? = null
private fun quiet(reason: String) {
if (silentBecause == reason) return
silentBecause = reason
GearslipLog.i("guidance: not speaking - $reason")
}
private fun tick() { private fun tick() {
if (!CarSettings.speakDirections.value) return
val turn = NavTurn.current() val turn = NavTurn.current()
if (turn == null) { if (turn == null) {
turnKey = null turnKey = null
return return
} }
if (turn.rerouting) return if (!CarSettings.speakDirections.value) return quiet("Spoken directions is off in the car's Settings")
if (turn.rerouting) return quiet("the map app is rerouting")
val step = turn.step val step = turn.step
val instruction = instruction(step) ?: return val instruction = instruction(step) ?: return quiet("the map app gave this turn no words")
silentBecause = null
val meters = turn.distance?.let(NavTurn::toMeters) val meters = turn.distance?.let(NavTurn::toMeters)
val close = (turn.seconds != null && turn.seconds <= NEAR_SECONDS) || (meters != null && meters <= NEAR_METERS) val close = (turn.seconds != null && turn.seconds <= NEAR_SECONDS) || (meters != null && meters <= NEAR_METERS)