From 965f1876bfd53104601cd0ad11fb612bf16d9191 Mon Sep 17 00:00:00 2001 From: Bruno Deanoz Date: Wed, 24 Jun 2026 13:55:46 +0200 Subject: [PATCH] simplify Live Call audio: remove MODE_IN_COMMUNICATION MODE_IN_COMMUNICATION activates the telephony DSP pipeline which degrades audio quality and requires setCommunicationDevice hacks for speaker routing. The Gemini reference implementation doesn't use it. Echo protection now relies on: 1. VOICE_COMMUNICATION AudioSource (built-in AEC/AGC/NS) 2. AcousticEchoCanceler on AudioRecord session (hardware AEC) 3. Hard ducking (zero mic chunks during model speech) AudioTrack stays USAGE_ASSISTANT for full-quality speaker output. This matches the WebRTC.ventures Gemini prototype architecture. --- .../java/dev/kaizen/app/live/AudioPipeline.kt | 33 ------------------- .../dev/kaizen/app/live/LiveCallService.kt | 1 - 2 files changed, 34 deletions(-) diff --git a/app/src/main/java/dev/kaizen/app/live/AudioPipeline.kt b/app/src/main/java/dev/kaizen/app/live/AudioPipeline.kt index c984a9a..5ac6bad 100644 --- a/app/src/main/java/dev/kaizen/app/live/AudioPipeline.kt +++ b/app/src/main/java/dev/kaizen/app/live/AudioPipeline.kt @@ -5,7 +5,6 @@ import android.content.Context import android.content.pm.PackageManager import android.media.AudioAttributes import android.media.AudioFormat -import android.media.AudioManager import android.media.AudioRecord import android.media.AudioTrack import android.media.MediaRecorder @@ -52,8 +51,6 @@ class AudioPipeline(private val context: Context) { var onChunk: ((base64Pcm: String) -> Unit)? = null var onAmplitude: ((Float) -> Unit)? = null - private val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager - private var previousAudioMode = AudioManager.MODE_NORMAL private var recorder: AudioRecord? = null private var track: AudioTrack? = null private var aec: AcousticEchoCanceler? = null @@ -62,35 +59,6 @@ class AudioPipeline(private val context: Context) { @Volatile private var ducking = false @Volatile private var capturing = false - // ─── Audio Mode ─────────────────────────────────────────────────────── - - fun enterCallMode() { - previousAudioMode = audioManager.mode - audioManager.mode = AudioManager.MODE_IN_COMMUNICATION - - if (android.os.Build.VERSION.SDK_INT >= 31) { - val speaker = audioManager.availableCommunicationDevices - .firstOrNull { it.type == android.media.AudioDeviceInfo.TYPE_BUILTIN_SPEAKER } - if (speaker != null) { - audioManager.setCommunicationDevice(speaker) - Log.i(TAG, "Communication device → speaker") - } - } else { - @Suppress("DEPRECATION") - audioManager.isSpeakerphoneOn = true - } - } - - fun exitCallMode() { - if (android.os.Build.VERSION.SDK_INT >= 31) { - audioManager.clearCommunicationDevice() - } else { - @Suppress("DEPRECATION") - audioManager.isSpeakerphoneOn = false - } - audioManager.mode = previousAudioMode - } - // ─── Capture ────────────────────────────────────────────────────────── fun startCapture(scope: CoroutineScope): Boolean { @@ -243,7 +211,6 @@ class AudioPipeline(private val context: Context) { fun release() { stopCapture() stopPlayback() - exitCallMode() } // ─── Helpers ────────────────────────────────────────────────────────── diff --git a/app/src/main/java/dev/kaizen/app/live/LiveCallService.kt b/app/src/main/java/dev/kaizen/app/live/LiveCallService.kt index c2651b9..0af13da 100644 --- a/app/src/main/java/dev/kaizen/app/live/LiveCallService.kt +++ b/app/src/main/java/dev/kaizen/app/live/LiveCallService.kt @@ -168,7 +168,6 @@ class LiveCallService : Service() { connectTimeoutJob?.cancel() _status.value = CallStatus.LISTENING - pipeline.enterCallMode() if (!pipeline.initPlayback()) { Log.e(TAG, "Playback init failed") }