You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

AudioManager MODE_IN_COMMUNICATION致蓝牙音频爆音音质下降问题

问题描述

当应用通过蓝牙耳机连接时,调用audioManager.startBluetoothSco()接口,相关实现代码如下。蓝牙连接功能运行正常,但MediaPlayer播放的音频存在噼啪爆音问题:每次播放动作提示音(例如R.raw.record_start、R.raw.success_action、R.raw.failure_action)时,都会出现音质劣化或爆音现象。

测试所用设备

  • 蓝牙耳机:AfterShokx OpenComm、Jabra Evolve 65
  • 测试手机:三星A52、三星A50、Pixel 3、一加6 Pro

已尝试的排查方案

  1. 尝试将audioManager.mode = AudioManager.MODE_IN_COMMUNICATION修改为AudioManager.MODE_IN_CALL或AudioManager.MODE_NORMAL。
    • 修改为MODE_IN_CALL时audioManager.mode未发生变化,值始终为0(即MODE_NORMAL)。
  2. 修改为MODE_NORMAL仅在三星A52上生效,无爆音问题、运行完全符合预期,是目标实现效果。
    • 但其余所有测试手机都会切换到其他音频流,导致无法听到任何媒体声音。
    • 例如在Pixel 3上连接Jabra Evolve 65并设置为MODE_NORMAL时,无法听到任何媒体声音,包括Youtube、Spotify等第三方应用音频以及系统音;但切换为MODE_IN_COMMUNICATION时可以听到声音,仅存在爆音问题。
  3. 尝试修改MediaPlayer的AudioAttributes配置,音质问题仍然存在。
    val actionSound = if (triggerErrorEarcon) R.raw.failure_action else if (longRecordingBreak) R.raw.success_action else R.raw.record_start
    val md = MediaPlayer.create(context, actionSound)
    val streamType = if (audioDeviceManager.headsetConnected) AudioManager.STREAM_VOICE_CALL else AudioManager.STREAM_MUSIC
    md.setAudioAttributes(
            AudioAttributes.Builder()
                .setLegacyStreamType(streamType)
                .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
                .build())
    md.play()
    

待验证的解决方向

  • 调整audioManager.mode或audioManager的其他相关属性修复问题(暂未找到具体调整方式)。
  • 继续使用MODE_IN_COMMUNICATION模式,调整MediaPlayer相关属性解决蓝牙设备上的音质劣化问题。
  • 目前暂未找到其他可行排查方向,希望能获得相关问题的解决方案,在此先行致谢。

AudioDeviceManager.kt 代码

class AudioDeviceManager(val context: Context) {
    internal val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
    internal val headsetConnectedSubject = BehaviorSubject.createDefault(false)
    internal val headsetConnected: Boolean get() = headsetConnectedSubject.value ?: false
    private val intentFilter = IntentFilter().apply { addAction(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED) }
    private val audioDeviceCallback = object: AudioDeviceCallback() {
        override fun onAudioDevicesAdded(addedDevices: Array<out AudioDeviceInfo>?) {
            super.onAudioDevicesAdded(addedDevices)
            updateBluetoothHeadsetState()
        }

        override fun onAudioDevicesRemoved(removedDevices: Array<out AudioDeviceInfo>?) {
            super.onAudioDevicesRemoved(removedDevices)
            updateBluetoothHeadsetState()
        }
    }
    private val scoReceiver = object: BroadcastReceiver() {
        override fun onReceive(context: Context?, intent: Intent?) {
            if (intent?.getIntExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, -1) == AudioManager.SCO_AUDIO_STATE_CONNECTED) {
                // SCO 连接完成
                audioManager.mode = AudioManager.MODE_IN_COMMUNICATION
                audioManager.isSpeakerphoneOn = false
                audioManager.isBluetoothScoOn = true
            }
        }
    }

    fun start() {
        if (headsetConnected.not())
            audioManager.registerAudioDeviceCallback(audioDeviceCallback, null)
    }

    fun stop(unregisterAudioDeviceCallback: Boolean = false) {
        if (unregisterAudioDeviceCallback)
            unregisterDeviceCallback()
        audioManager.mode = AudioManager.MODE_NORMAL
        audioManager.isSpeakerphoneOn = true
        audioManager.isBluetoothScoOn = false
        audioManager.stopBluetoothSco()
    }

    private fun unregisterDeviceCallback() = audioManager.unregisterAudioDeviceCallback(audioDeviceCallback)

    fun updateBluetoothHeadsetState() {
        val headset = audioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)?.firstOrNull { it.type == AudioDeviceInfo.TYPE_BLUETOOTH_SCO }
        val headsetConnected = headset != null
        headsetConnectedSubject.onNext(headsetConnected)

        if (headsetConnected) {
            audioManager.startBluetoothSco()
            context.registerReceiver(scoReceiver, intentFilter)
        } else {
            audioManager.mode = AudioManager.MODE_NORMAL
            audioManager.isSpeakerphoneOn = true
            audioManager.isBluetoothScoOn = false
            audioManager.stopBluetoothSco()
        }
    }
}

应用内播放动作提示音代码

MediaPlayer.create(context, R.raw.record_start)
        .play()
        .subscribe()
        .addTo(disposable)

MediaPlayer扩展函数代码

fun MediaPlayer.play(): Completable {
    return Completable.create { emitter ->

        var isCancelled = false
        emitter.setCancellable {
            isCancelled = true
        }
        setOnCompletionListener {
            GlobalScope.launch {
                // 延迟释放资源,避免CompletionListener触发过快导致异常
                delay(3000)
                it.release()
            }
            if (isCancelled) { return@setOnCompletionListener }
            emitter.onComplete()
        }
        start()
    }
}

内容的提问来源于stack exchange,提问作者Orcun Sevsay

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 23:51:24