引言
在日常音频处理工作中,我们经常会遇到各种音频文件格式,其中最经典的无损音频格式之一就是 wav(waveform audio file format)。wav 文件以 pcm(脉冲编码调制)形式存储音频数据,结构相对简单,但了解其内部细节对于音频处理、剪辑、合并和分析都非常重要。

本文将分享一个纯 java 实现的 wav 文件信息解析工具,能够从文件头到数据块完整打印 wav 文件信息,包括音频时长、采样率、通道数等关键信息。通过阅读本文,你不仅可以掌握 wav 文件结构,也可以基于此实现音频分析、剪辑或转换工具。

一、wav 文件结构概述
wav 文件采用 riff(resource interchange file format) 容器格式,其结构大致如下:
+------------------+------------------+-----------------+ | chunkid "riff" | chunksize | format "wave" | +------------------+------------------+-----------------+ | subchunk1id "fmt "| subchunk1size | audioformat ... | +------------------------------------------------------+ | subchunk2id "data"| subchunk2size | 音频数据 | +------------------------------------------------------+
- chunkid(4字节):标识文件类型,一般为
"riff"。 - chunksize(4字节):整个文件大小减去 8 字节。
- format(4字节):文件格式标识,一般为
"wave"。 - subchunk1(fmt 块):存储音频格式信息,如 pcm、采样率、声道数等。
- subchunk2(data 块):存储实际音频数据。
wav 文件还可能包含其他扩展块,如 list、fact 等,但对于 pcm 音频,我们最关心的还是 fmt 和 data 块。

二、java实现wav信息解析
下面是完整代码示例,我们逐步解析:
public static void main(string[] args) throws exception {
file wavfile1 = new file("d:/input/11.wav");
printwavinfo(wavfile1);
file wavfile2 = new file("d:/input/test2.wav");
printwavinfo(wavfile2);
file wavfile = new file("d:/input/1.wav");
printwavinfo(wavfile);
}
/**
* 打印wav文件头信息
*/
public static void printwavinfo(file wavfile) throws ioexception {
try (fileinputstream fis = new fileinputstream(wavfile)) {
byte[] header = new byte[12];
if (fis.read(header) != 12) throw new ioexception("wav文件头长度不足 12 字节");
string chunkid = new string(header, 0, 4);
int chunksize = bytebuffer.wrap(header, 4, 4).order(byteorder.little_endian).getint();
string format = new string(header, 8, 4);
if (!"riff".equals(chunkid) || !"wave".equals(format)) {
throw new ioexception("不是有效的wav文件");
}
system.out.println("====== wav 文件信息 ======");
system.out.println("文件路径: " + wavfile.getabsolutepath());
system.out.println("chunkid : " + chunkid);
system.out.println("chunksize : " + chunksize + " bytes");
system.out.println("format : " + format);
string subchunkid;
int subchunksize;
short audioformat = 1, numchannels = 1, bitspersample = 16;
int samplerate = 0, byterate = 0, blockalign = 0;
int datasize = 0;
byte[] chunkheader = new byte[8];
while (fis.read(chunkheader) == 8) {
subchunkid = new string(chunkheader, 0, 4);
subchunksize = bytebuffer.wrap(chunkheader, 4, 4)
.order(byteorder.little_endian)
.getint();
if ("fmt ".equals(subchunkid)) {
byte[] fmtdata = new byte[subchunksize];
fis.read(fmtdata);
bytebuffer bb = bytebuffer.wrap(fmtdata).order(byteorder.little_endian);
audioformat = bb.getshort();
numchannels = bb.getshort();
samplerate = bb.getint();
byterate = bb.getint();
blockalign = bb.getshort();
bitspersample = bb.getshort();
} else if ("data".equals(subchunkid)) {
datasize = subchunksize;
break; // 找到 data,停止读取
} else {
// 跳过不需要的块
fis.skip(subchunksize);
}
}
system.out.println("audioformat : " + audioformat + (audioformat == 1 ? " (pcm)" : " (压缩格式)"));
system.out.println("numchannels : " + numchannels);
system.out.println("samplerate : " + samplerate + " hz");
system.out.println("byterate : " + byterate);
system.out.println("blockalign : " + blockalign);
system.out.println("bitspersample : " + bitspersample);
system.out.println("subchunk2id : data");
system.out.println("subchunk2size : " + datasize + " bytes");
if (byterate > 0)
system.out.printf("音频时长 : %.2f 秒%n", datasize * 1.0 / byterate);
system.out.println("===========================");
}
}
三、代码解析
文件头验证
string chunkid = new string(header, 0, 4);
string format = new string(header, 8, 4);
if (!"riff".equals(chunkid) || !"wave".equals(format)) throw new ioexception("不是有效的wav文件");
这里读取前 12 字节并验证 "riff" 和 "wave",确保文件是标准 wav 文件。
读取 fmt 块
fmt 块是 wav 文件的核心,存储 pcm 或压缩格式信息:
- audioformat:1 表示 pcm,其他值表示压缩音频
- numchannels:声道数(1 = 单声道,2 = 双声道)
- samplerate:采样率(如 44100 hz)
- byterate:每秒字节数 = samplerate × numchannels × bitspersample/8
- blockalign:每个采样块字节数 = numchannels × bitspersample/8
- bitspersample:每个样本的位深度
读取 data 块
data 块存储实际音频数据,长度就是 subchunk2size,可用于计算音频时长:
if (byterate > 0)
system.out.printf("音频时长 : %.2f 秒%n", datasize * 1.0 / byterate);
通过 datasize / byterate 可以得到音频时长(秒)。
跳过其他扩展块
wav 文件可能包含扩展块,如 list 或 fact,使用 fis.skip(subchunksize) 跳过即可,不影响 pcm 解析。
四、运行效果示例
假设有一个单声道 16bit、采样率 44100hz 的 wav 文件 11.wav,打印结果类似:
====== wav 文件信息 ====== 文件路径: d:/input/11.wav chunkid : riff chunksize : 42221196 bytes format : wave audioformat : 1 (pcm) numchannels : 1 samplerate : 44100 hz byterate : 88200 blockalign : 2 bitspersample : 16 subchunk2id : data subchunk2size : 42221184 bytes 音频时长 : 478.57 秒 ===========================
通过这个输出,我们可以快速获得音频的各项参数,并为后续处理(如剪辑、拼接、变速)提供依据。


五、扩展思路
支持更多格式
可以增加对 fact、list 等扩展块的解析,也可支持非 pcm 压缩 wav 文件。
批量解析
将 printwavinfo 封装成工具类,可遍历目录下所有 wav 文件,批量打印信息或导出到 csv。
音频处理集成
通过 java 内存流操作(bytearrayoutputstream + audioinputstream)可实现高性能纯本地音频处理,无需 ffmpeg。
- 拼接多个 wav 文件
- 剪切指定时间段
- 改变采样率或位深度
音频可视化
结合 java 图形库(如 swing、javafx)绘制波形图,实现音频可视化分析。
六、总结
本文介绍了一个 纯 java 实现的 wav 文件信息打印工具,从文件头解析到 fmt 块和 data 块,能够打印:
- 音频格式(pcm 或压缩)
- 声道数
- 采样率
- 每秒字节数
- 每个采样块字节数
- 位深度
- 音频时长
相比依赖 ffmpeg 等外部工具,这种纯 java 方法更轻量、可嵌入 java 项目,并能在内存中快速处理 wav 文件。
掌握 wav 文件结构和 java 文件流操作之后,你就可以在此基础上扩展音频处理功能,例如音频拼接、分割、变速甚至实时处理。
到此这篇关于利用java实现一个wav文件信息解析与打印工具的文章就介绍到这了,更多相关java wav信息解析与打印内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论