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.
This commit is contained in:
Bruno Deanoz 2026-06-24 13:55:46 +02:00
parent b254862ef1
commit 965f1876bf
2 changed files with 0 additions and 34 deletions

View file

@ -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 ──────────────────────────────────────────────────────────

View file

@ -168,7 +168,6 @@ class LiveCallService : Service() {
connectTimeoutJob?.cancel()
_status.value = CallStatus.LISTENING
pipeline.enterCallMode()
if (!pipeline.initPlayback()) {
Log.e(TAG, "Playback init failed")
}