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

Android平台同时使用AudioRecord与Vosk离线语音识别的麦克风冲突及架构方案咨询

Android平台同时使用AudioRecord与Vosk离线语音识别的麦克风冲突及架构方案咨询

核心基础问题解答

1. Android允许多个麦克风消费者同时工作吗?

Android的麦克风访问默认是独占式的——同一时间只能有一个App(或App内的一个实例)独占麦克风资源,这和iOS的音频会话共享机制有本质区别。iOS允许同一App内的多个音频消费者共享同一条输入流,甚至不同App也能通过音频会话协作共享,但Android的音频输入栈天生对独占性管控更严格,不同厂商的硬件实现还会放大这种差异。

2. 能不能在录音和识别之间共享同一条音频流?

当然可以,而且这是唯一正确的解决方案。你尝试的「用单个AudioRecord读取buffer,同时喂给Vosk和录音逻辑」方向没错,现在遇到的不稳定问题,大概率是配置、线程调度或buffer处理不当导致的,不是共享流本身的问题。

3. 为啥iOS能正常工作,Android不行?

核心是两个平台的音频架构设计差异

  • iOS的AudioSession系统天生支持音频流共享,只要配置正确,同一App内的录音和识别可以无缝复用同一条输入流
  • Android的音频输入栈默认采用独占模式,加上硬件抽象层(HAL)的厂商定制化程度高,共享流时容易出现时序延迟、数据丢失问题,进而影响Vosk的识别精度

Android上同时录音+实时离线转录的推荐架构

标准方案:单音频流复用+线程分离

这是绝大多数正规App的实现方式,不需要特殊API,关键是做好配置和线程调度:

  1. 统一音频源配置:把音频源从普通MIC改成MediaRecorder.AudioSource.VOICE_RECOGNITION——这个源是系统专门为语音识别优化的,会自动做降噪、自动增益等预处理,比普通MIC更适配Vosk模型,还能减少硬件层面的冲突概率
  2. 单AudioRecord实例:只创建一个AudioRecord读取原始PCM buffer
  3. 多线程并行处理:用线程池或独立工作线程分离三个核心逻辑,避免阻塞音频读取线程:
    • 主线程:控制录音启停、UI交互
    • 读取线程:专门负责从AudioRecord读取buffer,不做任何耗时操作
    • 处理分支1:把buffer写入文件(注意先复制buffer避免数据覆盖)
    • 处理分支2:把buffer喂给Vosk的recognizer做识别
  4. 线程安全的buffer队列:用BlockingQueue缓存读取到的buffer,让两个处理分支异步消费,保证读取线程的实时性

代码实现示例(简化版)

import java.util.concurrent.LinkedBlockingQueue
import android.media.AudioFormat
import android.media.AudioRecord
import android.media.MediaRecorder
import org.vosk.Model
import org.vosk.Recognizer
import java.io.FileOutputStream

// 线程安全的buffer队列,存储PCM数据和有效长度
val bufferQueue = LinkedBlockingQueue<Pair<ByteArray, Int>>()
val isRecording = java.util.concurrent.atomic.AtomicBoolean(true)

// 1. 启动音频读取线程(核心:只做读取,不做其他耗时操作)
Thread {
    val sampleRate = 16000 // Vosk模型默认推荐16kHz,必须匹配
    val bufferSize = AudioRecord.getMinBufferSize(
        sampleRate,
        AudioFormat.CHANNEL_IN_MONO,
        AudioFormat.ENCODING_PCM_16BIT
    ) * 2 // 缓冲加倍,避免数据丢失

    val recorder = AudioRecord(
        MediaRecorder.AudioSource.VOICE_RECOGNITION, // 改用优化后的音频源
        sampleRate,
        AudioFormat.CHANNEL_IN_MONO,
        AudioFormat.ENCODING_PCM_16BIT,
        bufferSize
    )

    recorder.startRecording()
    val tempBuffer = ByteArray(4096) // 合适的单次读取大小,适配16kHz采样率

    while (isRecording.get()) {
        val readLength = recorder.read(tempBuffer, 0, tempBuffer.size)
        if (readLength > 0) {
            // 复制buffer避免后续覆盖
            val copiedBuffer = tempBuffer.copyOf(readLength)
            bufferQueue.put(Pair(copiedBuffer, readLength))
        }
    }

    recorder.stop()
    recorder.release()
}.start()

// 2. 启动文件写入线程
Thread {
    val outputStream = FileOutputStream("/sdcard/recorded_audio.pcm")
    while (isRecording.get() || !bufferQueue.isEmpty()) {
        val (buffer, length) = bufferQueue.take()
        outputStream.write(buffer, 0, length)
    }
    outputStream.close()
    // 可选:把PCM转成WAV格式(需要手动写入RIFF文件头)
}.start()

// 3. 启动Vosk识别线程
Thread {
    val model = Model("/sdcard/vosk-model-small-en-us-0.15") // 替换为你的Vosk模型路径
    val recognizer = Recognizer(model, sampleRate.toFloat())

    while (isRecording.get() || !bufferQueue.isEmpty()) {
        val (buffer, length) = bufferQueue.take()
        recognizer.acceptWaveForm(buffer, length)
        val result = recognizer.result
        // 处理识别结果(比如回调到Flutter层更新UI)
        println("识别结果:$result")
    }

    recognizer.close()
    model.close()
}.start()

关于你提到的其他疑问

1. 那些App是怎么做到同时录音+转录的?

  • 都是用单音频流复用的方式,没有黑科技
  • 会优先用VOICE_RECOGNITION音频源,而不是普通MIC
  • 不会使用未公开的系统API,都是基于标准的AudioRecord和识别库实现

2. iOS和Android的差异核心原因

主要是音频架构设计不同,和云端API、预处理的关系不大:

  • iOS的音频会话天生支持共享,Android的音频输入默认独占
  • 云端API(比如谷歌Speech-to-Text)会用系统级优化,但离线方案一样可以做到稳定,只是需要更注意配置和线程调度
  • 高级预处理(降噪、AGC)会提升识别精度,但不是解决「同时工作」的核心原因

3. Vosk的精度问题

Vosk作为离线引擎,和在线引擎(比如谷歌Speech-to-Text)比确实有精度差距——在线引擎有更大的模型、云端实时优化,但这不是你现在遇到「不稳定」的主要原因。你当前的问题更多是线程调度、音频源配置或buffer处理不当导致的时序问题,进而影响了识别精度。

4. 你遇到的不稳定问题的可能原因

  • 音频源用了普通MIC,缺少系统级预处理
  • 读取线程被阻塞:比如在读取线程里直接做文件写入或Vosk识别,导致读取不及时,buffer丢失
  • 采样率不匹配:Vosk模型默认是16kHz,如果你的AudioRecord用了其他采样率(比如44.1kHz),会导致识别精度急剧下降
  • buffer大小不合适:太小会导致频繁读取,太大会导致延迟过高,Vosk推荐用基于16kHz采样率的4096/8192字节buffer

最终实现建议

  1. 优先配置VOICE_RECOGNITION音频源,匹配Vosk模型的16kHz采样率
  2. 严格分离读取线程和处理线程,用BlockingQueue做buffer缓存
  3. 测试不同设备:Android厂商的音频HAL定制化程度高,部分设备可能需要微调buffer大小或采样率
  4. 可选添加基础预处理:比如用Android的AudioEffect API做简单降噪,进一步提升Vosk的识别精度

火山引擎 最新活动