Java实现WAV文件信息解析与打印

举报
柠檬🍋 发表于 2025/10/31 17:39:49 2025/10/31
【摘要】 Java实现WAV文件信息解析与打印在日常音频处理工作中,我们经常会遇到各种音频文件格式,其中最经典的无损音频格式之一就是 WAV(Waveform Audio File Format)。WAV 文件以 PCM(脉冲编码调制)形式存储音频数据,结构相对简单,但了解其内部细节对于音频处理、剪辑、合并和分析都非常重要。本文将分享一个纯 Java 实现的 WAV 文件信息解析工具,能够从文件头到...

Java实现WAV文件信息解析与打印

在日常音频处理工作中,我们经常会遇到各种音频文件格式,其中最经典的无损音频格式之一就是 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 文件还可能包含其他扩展块,如 LISTfact 等,但对于 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("===========================");
        }
    }



三、代码解析

  1. 文件头验证

    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 文件。

  2. 读取 fmt 块

    fmt 块是 WAV 文件的核心,存储 PCM 或压缩格式信息:

    • AudioFormat:1 表示 PCM,其他值表示压缩音频
    • NumChannels:声道数(1 = 单声道,2 = 双声道)
    • SampleRate:采样率(如 44100 Hz)
    • ByteRate:每秒字节数 = SampleRate × NumChannels × BitsPerSample/8
    • BlockAlign:每个采样块字节数 = NumChannels × BitsPerSample/8
    • BitsPerSample:每个样本的位深度
  3. 读取 data 块

    data 块存储实际音频数据,长度就是 Subchunk2Size,可用于计算音频时长:

    if (byteRate > 0)
        System.out.printf("音频时长         : %.2f 秒%n", dataSize * 1.0 / byteRate);
    

    通过 dataSize / byteRate 可以得到音频时长(秒)。

  4. 跳过其他扩展块

    WAV 文件可能包含扩展块,如 LISTfact,使用 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===========================

通过这个输出,我们可以快速获得音频的各项参数,并为后续处理(如剪辑、拼接、变速)提供依据。
在这里插入图片描述


在这里插入图片描述

五、扩展思路

  1. 支持更多格式
    可以增加对 factLIST 等扩展块的解析,也可支持非 PCM 压缩 WAV 文件。

  2. 批量解析
    printWavInfo 封装成工具类,可遍历目录下所有 WAV 文件,批量打印信息或导出到 CSV。

  3. 音频处理集成

    • 拼接多个 WAV 文件
    • 剪切指定时间段
    • 改变采样率或位深度

    通过 Java 内存流操作(ByteArrayOutputStream + AudioInputStream)可实现高性能纯本地音频处理,无需 FFmpeg。

  4. 音频可视化
    结合 Java 图形库(如 Swing、JavaFX)绘制波形图,实现音频可视化分析。


六、总结

本文介绍了一个 纯 Java 实现的 WAV 文件信息打印工具,从文件头解析到 fmt 块和 data 块,能够打印:

  • 音频格式(PCM 或压缩)
  • 声道数
  • 采样率
  • 每秒字节数
  • 每个采样块字节数
  • 位深度
  • 音频时长

相比依赖 FFmpeg 等外部工具,这种纯 Java 方法更轻量、可嵌入 Java 项目,并能在内存中快速处理 WAV 文件。

掌握 WAV 文件结构和 Java 文件流操作之后,你就可以在此基础上扩展音频处理功能,例如音频拼接、分割、变速甚至实时处理。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。