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

Kotlin(Android)保存WAV文件时多出12字节问题求助

问题排查与修正

你的WAV文件大小校验不通过,核心原因是比特数与字节数的概念混淆,导致文件头中多个字段计算错误,具体问题和修正方案如下:

错误点分析

  1. 音频数据长度计算错误
    你设置RECORDER_BPP=8(每个样本8位),意味着每个音频样本占1字节,因此音频数据的总字节数就是data.size,但你错误地除以8得到totalDataLen,后续计算又通过乘以UByte.SIZE_BITS(8)强行修正,这会在data.size不是8的倍数时出现偏差。

  2. RIFF Chunk Size计算冗余
    RIFF块的Size字段定义是整个文件的字节数减去8(RIFF标识4字节+Size字段4字节),正确值应为36 + totalDataLen(36是WAV头中除RIFF块外的固定字节数:WAVE标识4字节+fmt块20字节+data标识4字节+dataSize字段4字节),不需要额外乘以8。

  3. ByteRate字段写入错误
    你已经正确计算出byteRate=4000(字节/秒),但写入文件头时错误地乘以UByte.SIZE_BITS,导致ByteRate字段被设置为32000,这不符合WAV格式规范。

  4. Block Align计算逻辑冗余
    Block Align的计算公式是(channels * RECORDER_BPP) / 8,对于单声道8位音频,结果就是1,直接赋值更清晰,避免移位操作带来的潜在错误。

修正后的代码

val out = resolver.openOutputStream(uri!!)

val samplerate = 4000
val channels = 1 // 单声道
val RECORDER_BPP = 8 // 每样本8位
val totalDataLen = data.size // 音频数据总字节数
val byteRate = RECORDER_BPP * samplerate * channels / 8 // 正确的字节率:4000字节/秒

val header = ByteArray(44)

// RIFF块
header[0] = 'R'.code.toByte()
header[1] = 'I'.code.toByte()
header[2] = 'F'.code.toByte()
header[3] = 'F'.code.toByte()
val riffChunkSize = 36 + totalDataLen
header[4] = (riffChunkSize and 0xff).toByte()
header[5] = (riffChunkSize shr 8 and 0xff).toByte()
header[6] = (riffChunkSize shr 16 and 0xff).toByte()
header[7] = (riffChunkSize shr 24 and 0xff).toByte()

// WAVE标识
header[8] = 'W'.code.toByte()
header[9] = 'A'.code.toByte()
header[10] = 'V'.code.toByte()
header[11] = 'E'.code.toByte()

// fmt块
header[12] = 'f'.code.toByte()
header[13] = 'm'.code.toByte()
header[14] = 't'.code.toByte()
header[15] = ' '.code.toByte()
header[16] = 16 // PCM格式的fmt块大小
header[17] = 0
header[18] = 0
header[19] = 0
header[20] = 1 // PCM格式标识
header[21] = 0
header[22] = channels.toByte()
header[23] = 0
// 采样率
header[24] = (samplerate and 0xff).toByte()
header[25] = (samplerate shr 8 and 0xff).toByte()
header[26] = (samplerate shr 16 and 0xff).toByte()
header[27] = (samplerate shr 24 and 0xff).toByte()
// 字节率
header[28] = (byteRate and 0xff).toByte()
header[29] = (byteRate shr 8 and 0xff).toByte()
header[30] = (byteRate shr 16 and 0xff).toByte()
header[31] = (byteRate shr 24 and 0xff).toByte()
// Block Align
header[32] = ((channels * RECORDER_BPP) / 8).toByte()
header[33] = 0
// 每样本位数
header[34] = RECORDER_BPP.toByte()
header[35] = 0

// data块
header[36] = 'd'.code.toByte()
header[37] = 'a'.code.toByte()
header[38] = 't'.code.toByte()
header[39] = 'a'.code.toByte()
// 数据长度
header[40] = (totalDataLen and 0xff).toByte()
header[41] = (totalDataLen shr 8 and 0xff).toByte()
header[42] = (totalDataLen shr 16 and 0xff).toByte()
header[43] = (totalDataLen shr 24 and 0xff).toByte()

out?.write(header, 0, 44)
out?.write(data)
out?.close()

验证逻辑

修正后,RIFF Chunk Size + 8 应该等于文件总大小:

  • RIFF Chunk Size = 36 + totalDataLen
  • 文件总大小 = 44(头) + totalDataLen = (36 + totalDataLen) + 8 = RIFF Chunk Size + 8
    完全符合校验规则。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 17:10:31