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

Android(Kotlin):使用AudioRecord转WAV格式(无需存储文件)

问题需求

我使用AudioRecord录制音频,需要将录制内容以可播放的WAV格式保存为内存数组/变量,不写入文件。由于录制同时要运行语音识别,无法使用MediaRecorder。现有方案依赖文件存储,求全程内存操作的实现,最终得到音频数组传递给Flutter。

解决方案

核心思路:录制时将PCM数据缓存到内存流,录制结束后直接在内存中生成WAV头并与PCM数据合并,全程无文件IO操作。

1. 内存缓存版录制函数

替换文件输出流为ByteArrayOutputStream,直接在内存中缓存录制的PCM数据:

private val pcmDataStream = ByteArrayOutputStream()
private val stopFlag = AtomicBoolean(false)

private fun startRecording() {
    val sampleRate = 16000
    val frameLength = 512
    val minBufferSize = AudioRecord.getMinBufferSize(
        sampleRate,
        AudioFormat.CHANNEL_IN_MONO,
        AudioFormat.ENCODING_PCM_16BIT
    )
    val bufferSize = (sampleRate / 2).coerceAtLeast(minBufferSize)
    var audioRecord: AudioRecord? = null
    val buffer = ShortArray(frameLength)

    try {
        audioRecord = AudioRecord(
            MediaRecorder.AudioSource.MIC,
            sampleRate,
            AudioFormat.CHANNEL_IN_MONO,
            AudioFormat.ENCODING_PCM_16BIT,
            bufferSize
        )
        audioRecord.startRecording()
        pcmDataStream.reset() // 重置缓存流,清理旧数据

        while (!stopFlag.get()) {
            val readLength = audioRecord.read(buffer, 0, buffer.size)
            if (readLength > 0) {
                // 仅转换并写入实际读取到的有效数据
                val bufferByte = shortToByte(buffer.copyOf(readLength))
                pcmDataStream.write(bufferByte)
            }
        }
    } catch (e: Exception) {
        e.printStackTrace()
    } finally {
        audioRecord?.stop()
        audioRecord?.release()
    }
}

2. 内存版WAV头生成函数

直接基于内存中的PCM数据生成完整的WAV格式字节数组,无需文件操作:

// 将内存中的PCM数据转换为WAV格式字节数组
private fun pcmToWaveInMemory(pcmData: ByteArray, sampleRate: Int): ByteArray {
    val wavStream = ByteArrayOutputStream()
    val dataSize = pcmData.size

    // 写入WAV头信息
    wavStream.write("RIFF".toByteArray())
    writeIntToStream(wavStream, 36 + dataSize) // ChunkSize
    wavStream.write("WAVE".toByteArray())
    wavStream.write("fmt ".toByteArray())
    writeIntToStream(wavStream, 16) // Subchunk1Size(PCM固定为16)
    writeShortToStream(wavStream, 1.toShort()) // AudioFormat(PCM为1)
    writeShortToStream(wavStream, 1.toShort()) // 声道数(单声道)
    writeIntToStream(wavStream, sampleRate) // 采样率
    writeIntToStream(wavStream, sampleRate * 2) // 字节率(采样率*声道*位深/8)
    writeShortToStream(wavStream, 2.toShort()) // 块对齐(声道*位深/8)
    writeShortToStream(wavStream, 16.toShort()) // 位深(16BIT)
    wavStream.write("data".toByteArray())
    writeIntToStream(wavStream, dataSize) // 数据块大小

    // 写入PCM原始数据
    wavStream.write(pcmData)
    return wavStream.toByteArray()
}

// 辅助函数:以小端序写入Int值
private fun writeIntToStream(stream: ByteArrayOutputStream, value: Int) {
    stream.write(value shr 0)
    stream.write(value shr 8)
    stream.write(value shr 16)
    stream.write(value shr 24)
}

// 辅助函数:以小端序写入Short值
private fun writeShortToStream(stream: ByteArrayOutputStream, value: Short) {
    val intValue = value.toInt()
    stream.write(intValue shr 0)
    stream.write(intValue shr 8)
}

// 复用原有Short数组转ByteArray函数
fun shortToByte(shortArray: ShortArray): ByteArray {
    val buffer = ByteBuffer.allocate(shortArray.size * 2)
    buffer.order(ByteOrder.LITTLE_ENDIAN)
    buffer.asShortBuffer().put(shortArray)
    return buffer.array()
}

3. 获取WAV数组并传递给Flutter

停止录制后生成WAV数组,通过MethodChannel传递给Flutter:

private fun stopRecordingAndGetWav(): ByteArray {
    stopFlag.set(true)
    // 若录制在异步线程执行,需等待线程结束(可根据实际逻辑调整)
    // 示例:使用CountDownLatch或协程等待
    val pcmData = pcmDataStream.toByteArray()
    return pcmToWaveInMemory(pcmData, 16000)
}

// 通过MethodChannel将WAV数组传递给Flutter
private fun sendWavToFlutter(wavData: ByteArray) {
    // 假设已初始化MethodChannel
    methodChannel.invokeMethod("receiveAudioData", wavData)
}

关键注意事项

  • 录制时长较长时需关注内存占用,可添加最大录制时长限制或分段处理逻辑
  • 确保只写入实际读取到的有效音频数据(buffer.copyOf(readLength)),避免无效空数据
  • 若录制在子线程执行,需处理线程同步问题,确保缓存流数据完整

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 22:17:53