diff --git a/gearslip/src/main/java/app/seb3thehacker/gearslip/GearslipRunner.kt b/gearslip/src/main/java/app/seb3thehacker/gearslip/GearslipRunner.kt index 955655d..13c9cfc 100644 --- a/gearslip/src/main/java/app/seb3thehacker/gearslip/GearslipRunner.kt +++ b/gearslip/src/main/java/app/seb3thehacker/gearslip/GearslipRunner.kt @@ -513,7 +513,14 @@ class GearslipRunner( } val config = mic.configs.first() log.i("car microphone: channel=${mic.serviceId} ${mic.codecName} ${config.sampleRate}Hz/${config.bits}bit/x${config.channels}") - CarMic.current = CarMic(mic) { id, body -> send(id, body, encrypted = true, channel = mic.serviceId) } + CarMic.current = CarMic( + mic, + sendOnChannel = { id, body -> send(id, body, encrypted = true, channel = mic.serviceId) }, + // VoiceSessionNotification { status = 1 START, 2 END }, on the control channel. + voiceSession = { start -> + send(MSG_VOICE_SESSION_NOTIFICATION, Protobuf.varintField(1, if (start) 1L else 2L), encrypted = true) + }, + ) openChannel(mic.serviceId) } @@ -1244,6 +1251,7 @@ class GearslipRunner( const val MSG_PING_RESPONSE = 12 const val MSG_BYEBYE_REQUEST = 15 const val MSG_BYEBYE_RESPONSE = 16 + const val MSG_VOICE_SESSION_NOTIFICATION = 17 const val MSG_CHANNEL_OPEN_REQUEST = 7 const val MSG_CHANNEL_OPEN_RESPONSE = 8 diff --git a/gearslip/src/main/java/app/seb3thehacker/gearslip/audio/CarMic.kt b/gearslip/src/main/java/app/seb3thehacker/gearslip/audio/CarMic.kt index 1236601..c3ee465 100644 --- a/gearslip/src/main/java/app/seb3thehacker/gearslip/audio/CarMic.kt +++ b/gearslip/src/main/java/app/seb3thehacker/gearslip/audio/CarMic.kt @@ -12,17 +12,21 @@ import java.util.concurrent.TimeUnit * * -> CHANNEL_OPEN_REQUEST <- CHANNEL_OPEN_RESPONSE * -> Setup { config_index } <- Config + * -> VoiceSessionNotification { START } (control channel) * -> MicrophoneRequest { open } <- MicrophoneResponse { status, session_id } * <- Data { 8-byte timestamp, PCM }... -> Ack { session_id, ack } * -> MicrophoneRequest { open = false } + * -> VoiceSessionNotification { END } * - * The channel opens with the session, but the car only records between [start] and [stop]. + * The channel opens with the session, but the car only records between [start] and [stop], and + * only inside a voice session: the 2024 VW answered a bare MicrophoneRequest with status -7. * Speech wants 16 kHz mono; a car that records at a multiple of that is averaged down to it, and * one that doesn't isn't used, so voice falls back to the phone's microphone. */ class CarMic( private val service: ServiceDiscovery.AudioService, private val sendOnChannel: (messageId: Int, body: ByteArray) -> Unit, + private val voiceSession: (start: Boolean) -> Unit, ) { val channelId get() = service.serviceId @@ -43,7 +47,8 @@ class CarMic( val status = Protobuf.readInt32Field(body, 1) ?: 0 GearslipLog.i("mic: <- ChannelOpenResponse: status=$status") if (status != 0) return - sendOnChannel(MSG_SETUP, Protobuf.varintField(1, 0L)) + // Setup { type = the codec }, as for audio: 0 got UNEXPECTED_MESSAGE from the VW. + sendOnChannel(MSG_SETUP, Protobuf.varintField(1, service.codecType.toLong())) ready = usable if (!usable) { GearslipLog.w( @@ -97,6 +102,7 @@ class CarMic( chunks.clear() packets = 0 listening = true + voiceSession(true) sendOnChannel(MSG_MIC_REQUEST, Protobuf.varintField(1, 1L) + Protobuf.varintField(4, 1L)) GearslipLog.i("mic: -> MicrophoneRequest(open)") return true @@ -109,7 +115,8 @@ class CarMic( if (!listening) return listening = false sendOnChannel(MSG_MIC_REQUEST, Protobuf.varintField(1, 0L)) - GearslipLog.i("mic: -> MicrophoneRequest(close) after $packets packets") + voiceSession(false) + GearslipLog.i("mic: -> MicrophoneRequest(close) after $packets packets, voice session ended") } companion object {