必须手动解析WAV二进制头以获取fmt块扩展字段和fact块数据;需用RandomAccessFile按RIFF规范逐块读取,注意小端序,并根据cbSize动态处理扩展字段。

Java读取WAV文件头需要绕过AudioSystem的封装
Java标准库的 AudioSystem.getAudioFileFormat() 对WAV支持有限——它能返回采样率、位深、声道数等基础信息,但**不暴露WAV特有的fmt块字段(如cbSize、validBitsPerSample、channelMask)或fact块数据**。要获取完整头参数,必须手动解析RIFF/WAV二进制结构。
WAV头解析的关键是按RIFF规范逐块读取
WAV本质是RIFF容器格式,头部由"RIFF"标识开头,后跟文件总大小,接着是"WAVE"标识,然后是若干子块("fmt "、"fact"、"data"等)。每个块结构为:4字节ID + 4字节长度 + N字节内容。手动解析时需严格按字节顺序读取,且注意小端序(little-endian)。
常见踩坑点:
-
AudioInputStream会跳过"fmt "块前的RIFF头,直接定位到音频数据起始,无法回溯读取原始头 - 用
DataInputStream.readInt()读取长度字段时,若未确认字节序,会导致数值错误(例如读出0x12345678却解析成2018915346) - 忽略
"fmt "块长度字段(cbSize),直接按固定16字节解析,会漏掉扩展字段(如WAVE_FORMAT_EXTENSIBLE下的channelMask)
用RandomAccessFile安全读取WAV头字段
相比FileInputStream,RandomAccessFile可自由定位、避免流提前消耗,更适合头解析。重点字段提取逻辑如下:
RandomAccessFile raf = new RandomAccessFile("audio.wav", "r");
// 读RIFF头
if (raf.readInt() != 0x46464952) throw new IOException("Not a RIFF file"); // "RIFF"
raf.readInt(); // skip file size
if (raf.readInt() != 0x45564157) throw new IOException("Not a WAVE file"); // "WAVE"
// 找fmt块
int chunkId, chunkSize;
do {
chunkId = raf.readInt();
chunkSize = raf.readInt();
} while (chunkId != 0x20746D66); // "fmt "
// 读fmt块内容(至少16字节)
byte[] fmtData = new byte[Math.max(16, chunkSize)];
raf.readFully(fmtData);
// 解析关键字段(小端序)
int formatTag = (fmtData[0] & 0xFF) | ((fmtData[1] & 0xFF) << 8);
int channels = (fmtData[2] & 0xFF) | ((fmtData[3] & 0xFF) << 8);
int sampleRate = (fmtData[4] & 0xFF) | ((fmtData[5] & 0xFF) << 8) |
((fmtData[6] & 0xFF) << 16) | ((fmtData[7] & 0xFF) << 24);
int bitsPerSample = (fmtData[14] & 0xFF) | ((fmtData[15] & 0xFF) << 8);
// 若cbSize > 0,继续读扩展字段(如channelMask在偏移22处)
if (chunkSize > 16) {
int channelMask = (fmtData[22] & 0xFF) | ((fmtData[23] & 0xFF) << 8) |
((fmtData[24] & 0xFF) << 16) | ((fmtData[25] & 0xFF) << 24);
}
raf.close();
AudioSystem.getAudioFileFormat()只适合快速获取基础参数
如果只需要采样率、帧长、编码类型等通用信息,用标准API更稳妥:
try (FileInputStream fis = new FileInputStream("audio.wav")) {
AudioFileFormat aff = AudioSystem.getAudioFileFormat(fis);
AudioFormat fmt = aff.getFormat();
System.out.println("Sample rate: " + fmt.getSampleRate());
System.out.println("Channels: " + fmt.getChannels());
System.out.println("Sample size: " + fmt.getSampleSizeInBits());
System.out.println("Encoding: " + fmt.getEncoding());
}
但它无法告诉你这个WAV是否含BEXT块(广播级元数据)、是否为24位packed格式、或validBitsPerSample是否小于bitsPerSample——这些都得靠手动解析头才能确认。
真正做专业音频处理时,别依赖封装层;WAV头就那么几百字节,自己读,才最可控。










