You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Android平台同步实现语音识别与音频录制的技术方案咨询

Android平台同步实现语音识别与音频录制的技术方案咨询

我特别理解你现在遇到的这个两难问题——既要实时转写语音内容,又要同步保存原始录音,但Android的麦克风独占机制确实卡了不少开发者的脖子。之前我也帮不少人处理过类似需求,给你几个经过验证的实用方案,你可以根据自己的场景灵活选择:


核心解决方案:用AudioRecord获取原始流,分发给双模块

这是绕开麦克风独占问题的最直接思路,因为AudioRecord是Android提供的底层音频采集API,能直接拿到原始PCM音频数据,之后我们可以把这份数据同时喂给语音识别引擎和录音写入模块:

  • 第一步:初始化AudioRecord采集原始PCM
    要注意统一采样率、声道数、位深这些参数,比如选移动端常用的44100Hz采样率、单声道、16位PCM,这样既能保证识别准确率,又能兼容大部分STT引擎:
  • 第二步:多线程分发数据流
    启动两个独立的工作线程:一个线程把PCM数据喂给语音识别引擎(比如Vosk或本地版Whisper),另一个线程把PCM数据写入文件,之后可以用MediaCodec把原始PCM转成AAC/MP3格式(节省存储空间)。
  • 适配系统SpeechRecognizer的替代方案
    如果你原本依赖系统的SpeechRecognizer,确实没法直接对接数据流,但可以换成支持手动传入音频缓冲区的STT实现——比如Vosk的Android SDK就支持通过acceptWaveForm()方法手动喂入PCM数据,完美适配这个方案。

给你一段简化的核心代码示例,方便你快速上手:

// 初始化AudioRecord参数
val audioFormat = AudioFormat.Builder()
    .setSampleRate(44100)
    .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
    .setChannelMask(AudioFormat.CHANNEL_IN_MONO)
    .build()
val minBufferSize = AudioRecord.getMinBufferSize(
    44100,
    AudioFormat.CHANNEL_IN_MONO,
    AudioFormat.ENCODING_PCM_16BIT
)
val audioRecord = AudioRecord(
    MediaRecorder.AudioSource.MIC,
    44100,
    AudioFormat.CHANNEL_IN_MONO,
    AudioFormat.ENCODING_PCM_16BIT,
    minBufferSize * 2 // 扩大缓冲区避免丢包
)

// 启动采集与处理线程
var isRecording = true
val recordThread = Thread {
    audioRecord.startRecording()
    val buffer = ByteArray(minBufferSize)
    while (isRecording) {
        val readSize = audioRecord.read(buffer, 0, buffer.size)
        if (readSize > 0) {
            // 复制两份缓冲区,分别给识别和录音模块
            val sttBuffer = buffer.copyOfRange(0, readSize)
            val saveBuffer = buffer.copyOfRange(0, readSize)

            // 喂给Vosk识别引擎
            voskRecognizer.acceptWaveForm(sttBuffer, readSize)
            val result = voskRecognizer.result
            // 处理识别结果(比如实时显示)

            // 写入PCM文件(后续可转码)
            fileOutputStream.write(saveBuffer)
        }
    }
    audioRecord.stop()
    audioRecord.release()
    fileOutputStream.close()
}
recordThread.start()

优化Vosk专有名词识别率的小技巧

你提到Vosk对“首尔”“釜山”这类专有名词识别不准,其实不用直接换引擎——Vosk支持用自定义语料训练轻量模型:

  • 把你需要优化的专有名词(比如目标城市名、品牌名)加到训练语料里,用Vosk的训练工具生成适配的小模型包;
  • 把训练好的模型放到Android app的assets目录下,替换默认模型,识别率会有明显提升,而且全程离线,不用依赖服务器。

本地部署Whisper,兼顾准确率与离线体验

如果你还是更倾向Whisper的识别效果,现在不用必须搭服务器了——Whisper有官方的Android本地化方案(比如whisper.cpp的Android绑定):

  • 可以选择体积较小的tiny.enbase.en模型(几十MB大小),打包到app里;
  • 同样可以对接AudioRecord的PCM数据流,在本地实时处理识别,同时保存录音,完全满足你的需求。

开源项目参考

不少开源项目已经实现了类似功能,比如Vosk官方的Android示例项目里就有“录音+离线识别”的完整流程,还有一些专注于语音采集与识别同步的小项目,你可以直接参考它们的线程调度和数据流处理逻辑。

内容来源于stack exchange

火山引擎 最新活动