Kotlin(Android)保存WAV文件时多出12字节问题求助
问题排查与修正
你的WAV文件大小校验不通过,核心原因是比特数与字节数的概念混淆,导致文件头中多个字段计算错误,具体问题和修正方案如下:
错误点分析
音频数据长度计算错误
你设置RECORDER_BPP=8(每个样本8位),意味着每个音频样本占1字节,因此音频数据的总字节数就是data.size,但你错误地除以8得到totalDataLen,后续计算又通过乘以UByte.SIZE_BITS(8)强行修正,这会在data.size不是8的倍数时出现偏差。RIFF Chunk Size计算冗余
RIFF块的Size字段定义是整个文件的字节数减去8(RIFF标识4字节+Size字段4字节),正确值应为36 + totalDataLen(36是WAV头中除RIFF块外的固定字节数:WAVE标识4字节+fmt块20字节+data标识4字节+dataSize字段4字节),不需要额外乘以8。ByteRate字段写入错误
你已经正确计算出byteRate=4000(字节/秒),但写入文件头时错误地乘以UByte.SIZE_BITS,导致ByteRate字段被设置为32000,这不符合WAV格式规范。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
相关产品推荐
相关产品推荐

