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

如何借助Manifest.permission.RECORD_AUDIO检测用户说话状态以控制波形图标?

检测用户是否说话的技术方案及代码优化

核心实现思路

要实现「说话时显示波形、停止时隐藏」的需求,核心是实时监测麦克风输入的音频状态,具体可通过以下几点落地:

  • 音频振幅阈值判断:采集麦克风的音频振幅值,设定合理阈值(比如1000),当振幅超过阈值时判定用户正在说话
  • 超时防误判:说话是连续的音频输入,需设置超时机制——当振幅低于阈值持续一段时间(比如1秒)后,才判定为停止说话,避免环境杂音或短暂停顿导致的误触发
  • 实时采样:需要定时、持续地采集音频数据,而非仅在页面恢复时检测一次

原代码存在的问题

你提供的代码无法满足实时监测需求,主要问题包括:

  1. 仅在onResume()中执行一次检测,无法持续追踪用户说话状态
  2. 使用Thread.sleep(2000)会阻塞UI线程,导致界面卡顿甚至ANR
  3. 无超时逻辑,振幅低于阈值时直接进入分支,容易误判
  4. 重复获取amplitude的逻辑冗余,未处理连续监测逻辑

优化后的实现代码

以下是基于Kotlin协程的实时监测方案,兼顾性能与准确性:

import android.media.MediaRecorder
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch

class VoiceDetectionActivity : AppCompatActivity() {
    private var recorder: MediaRecorder? = null
    private val SPEAKING_THRESHOLD = 1000 // 振幅阈值,可根据环境调整
    private val SILENCE_TIMEOUT = 1000L // 静音超时时间,单位毫秒
    private var isUserSpeaking = false
    private var lastSpeakingTime = System.currentTimeMillis()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // 初始化麦克风录制器
        initRecorder()
        // 启动实时语音监测
        startVoiceDetection()
    }

    private fun initRecorder() {
        recorder = MediaRecorder().apply {
            setAudioSource(MediaRecorder.AudioSource.MIC)
            setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP)
            setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB)
            setOutputFile("/dev/null") // 无需保存音频,输出到空设备
            try {
                prepare()
                start()
            } catch (e: Exception) {
                e.printStackTrace()
                Toast.makeText(this@VoiceDetectionActivity, "麦克风初始化失败", Toast.LENGTH_SHORT).show()
            }
        }
    }

    private fun startVoiceDetection() {
        lifecycleScope.launch {
            while (isActive) {
                recorder?.let {
                    val amplitude = it.maxAmplitude
                    if (amplitude > SPEAKING_THRESHOLD) {
                        lastSpeakingTime = System.currentTimeMillis()
                        if (!isUserSpeaking) {
                            isUserSpeaking = true
                            runOnUiThread {
                                Toast.makeText(this@VoiceDetectionActivity, "用户正在说话", Toast.LENGTH_SHORT).show()
                                // 此处添加显示波形图标的逻辑
                            }
                        }
                    } else {
                        // 检查是否超过静音超时时间
                        if (isUserSpeaking && System.currentTimeMillis() - lastSpeakingTime > SILENCE_TIMEOUT) {
                            isUserSpeaking = false
                            runOnUiThread {
                                Toast.makeText(this@VoiceDetectionActivity, "用户停止说话", Toast.LENGTH_SHORT).show()
                                // 此处添加隐藏波形图标的逻辑
                            }
                        }
                    }
                }
                delay(100) // 每100毫秒采样一次,可按需调整频率
            }
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        recorder?.apply {
            stop()
            release()
        }
        recorder = null
    }
}

额外优化建议

  • 动态阈值调整:可先采集几秒环境噪音,取平均值作为基准阈值,适配不同场景下的噪音水平
  • 专业VAD库集成:若需更高准确性,可使用WebRTC的VAD模块,能更精准地区分人声与环境噪音
  • 权限处理:记得在AndroidManifest.xml中添加麦克风权限,并动态申请:
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 12:04:57