利用Java实现一个WAV文件信息解析与打印工具

 更新时间:2025年11月03日 09:59:48   作者:一键难忘  
在日常音频处理工作中,我们经常会遇到各种音频文件格式,其中最经典的无损音频格式之一就是 WAV,所以本文将分享一个纯 Java 实现的 WAV 文件信息解析工具,能够从文件头到数据块完整打印 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("===========================");
        }
    }


三、代码解析

文件头验证

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

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

五、扩展思路

支持更多格式
可以增加对 factLIST 等扩展块的解析,也可支持非 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信息解析与打印内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • java多线程编程之使用Synchronized块同步变量

    java多线程编程之使用Synchronized块同步变量

    我们可以通过synchronized块来同步特定的静态或非静态方法。要想实现这种需求必须为这些特性的方法定义一个类变量,然后将这些方法的代码用synchronized块括起来,并将这个类变量作为参数传入synchronized块
    2014-01-01
  • Java ResultSet案例讲解

    Java ResultSet案例讲解

    这篇文章主要介绍了Java ResultSet案例讲解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-08-08
  • SpringCloud Stream RabbitMQ动态路由Key问题

    SpringCloud Stream RabbitMQ动态路由Key问题

    文章主要描述了使用消息中间件实现不同操作后发送不同邮件的功能,通过设置路由key和group,使得生产者能够根据邮件类型发送消息至对应队列,消费者则通过路由key筛选消息
    2026-05-05
  • 浅谈Java中几种常见的比较器的实现方法

    浅谈Java中几种常见的比较器的实现方法

    下面小编就为大家带来一篇浅谈Java中几种常见的比较器的实现方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-10-10
  • 将bean注入到Spring中的方式总结

    将bean注入到Spring中的方式总结

    在Java的Spring框架中,将bean注入到容器中是核心概念之一,这是实现依赖注入的基础,Spring提供了多种方式来将bean注入到容器中,本文给大家总结了将bean注入到Spring中的几种方式,需要的朋友可以参考下
    2023-12-12
  • RabbitMQ,RocketMQ,Kafka 事务性,消息丢失,消息顺序性和消息重复发送的处理策略问题

    RabbitMQ,RocketMQ,Kafka 事务性,消息丢失,消息顺序性和消息重复发送的处理策略问题

    这篇文章主要介绍了RabbitMQ,RocketMQ,Kafka 事务性,消息丢失,消息顺序性和消息重复发送的处理策略,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-03-03
  • java图片和文本同时提交到表单的实例代码

    java图片和文本同时提交到表单的实例代码

    在本篇文章里小编给大家整理的是关于java实现图片和文本同时提交到表单的相关内容,有需要的朋友们可以学习下。
    2020-02-02
  • 轻松掌握Java备忘录模式

    轻松掌握Java备忘录模式

    这篇文章主要帮助大家轻松掌握Java备忘录模式,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-09-09
  • Spring事务管理中关于数据库连接池详解

    Spring事务管理中关于数据库连接池详解

    事务的作用就是为了保证用户的每一个操作都是可靠的,事务中的每一步操作都必须成功执行,只要有发生异常就 回退到事务开始未进行操作的状态。事务管理是Spring框架中最为常用的功能之一,我们在使用Spring Boot开发应用时,大部分情况下也都需要使用事务
    2022-12-12
  • Java把list分成多个list的方法示例

    Java把list分成多个list的方法示例

    在编程中将大的list集合拆分为多个小集合可以提高处理效率,主要拆分方法包括等量拆分适用于均匀处理数据、 指定数量拆分按固定子集大小划分、根据条件拆分按特定规则进行,这些方法有助于多线程异步处理,需要的朋友可以参考下
    2024-11-11

最新评论