Wav的Header读取(Java版)

  相关知识,仅供参考: 

一、WAV文件介绍

在Windows环境下,大部分多媒体文件都是按照资源互换文件格式(Resources lnterchange File Format)存放信息,简称RIFF格式。构成RIFF文件的基本单位称之为块(chunk),每个RIFF文档是由若干个块构成。每个块(chunk)由块标识、块长度及数据等三部分所组成。

1.1 WAV文件结构

RIFF是英文Resource Interchange File Format的缩写,每个WAVE文件的头四个字节便是“RIFF”。
WAVE文件是由若干个Chunk组成的,按照在文件中的出现位置包括:

表1 WAV文件结构

RIFF WAVE Chunk 块

Format Chunk 块

Fact Chunk 块(压缩编码格式要含有该块)(可选)

Data Chunk 块

RIFF格式规定,只有RIFF及LIST块可以含有子块,其它的块不允许包含子块。
WAV文件采用的是RIFF格式结构,至少是由3个块构成,分别是RIFFChunk、fmt Chunk和Data Chunk。所有基于压缩编码的WAV文件必须含有fact块。

1.2 RIFF WAVE Chunk结构

字段 | 字节数  | 具体内容  |说明
 ID    | 4 Bytes | RIFF       | 作为标示
Size | 4 Bytes | 具体数    | 该size是整个wav文件大小减去ID和Size所占用的字节数,即FileLen - 8 = Size
Type | 4 Bytes | WAVE   | 表示是wav文件

以RIFF作为标示,然后紧跟着为size字段,该size是整个wav文件大小减去ID和Size所占用的字节数,即FileLen - 8 = Size。然后是Type字段,为WAVE表示是wav文件

1.3 Format Chunk 块

字段 | 字节数  | 具体内容  |说明
ID    | 4 Bytes | fmt |
Size | 4 Bytes | 数值为16或18,18则最后又附加信息 |
FormatTag | 2 Bytes | 编码方式,一般为0x0001 | |
Channels | 2 Bytes | 声道数目,1--单声道;2--双声道 | |
SamplesPerSec | 4 Bytes | 采样频率 | |
AvgBytesPerSec| 4 Bytes | 每秒所需字节数 | |===> WAVE_FORMAT
BlockAlign | 2 Bytes | 数据块对齐单位(每个采样需要的字节数) | |
BitsPerSample | 2 Bytes | 每个采样需要的bit数 | |
可选 | 2 Bytes | 附加信息(可选,通过Size来判断有无) | |

以fmt作为标示,一般情况下Size为16,此时最后附加信息没有;如果为18则最后多了2个字节的附加信息。主要由一些软件制成的wav格式中含有该2个字节的

1.3 Fact Chunk 块

字段 | 字节数  | 具体内容  |说明
 ID   | 4 Bytes  | fact |
Size | 4 Bytes | 数值为4 |
data | 4 Bytes | |

Fact Chunk是可选字段,一般当wav文件由某些软件转化而成,则包含该Chunk。

1.4 Data Chunk 块

字段 | 字节数  | 具体内容  |说明
 ID    | 4 Bytes | data |
Size | 4 Bytes | 数据长度|
data | | |

Data Chunk是真正保存wav数据的地方,以'data'作为该Chunk的标示。然后是数据的大小,紧接着就是wav数据。

1.5 wav数据的bit位置

根据Format Chunk中的声道数以及采样bit数,wav数据的bit位置可以分成以下几种形式:

| 单声道 | 取样1 | 取样2 | 取样3 | 取样4 |
| |--------------------------------------------------------
| 8bit量化 | 声道0 | 声道0 | 声道0 | 声道0 |
---------------------------------------------------------------------
| 双声道 | 取样1 | 取样2 |
| |--------------------------------------------------------
| 8bit量化 | 声道0(左) | 声道1(右) | 声道0(左) | 声道1(右) |
---------------------------------------------------------------------
| | 取样1 | 取样2 |
| 单声道 |--------------------------------------------------------
| 16bit量化 | 声道0 | 声道0 | 声道0 | 声道0 |
| | (低位字节) | (高位字节) | (低位字节) | (高位字节) |
---------------------------------------------------------------------
| | 取样1 |
| 双声道 |--------------------------------------------------------
| 16bit量化 | 声道0(左) | 声道0(左) | 声道1(右) | 声道1(右) |
| | (低位字节) | (高位字节) | (低位字节) | (高位字节) |
---------------------------------------------------------------------

1.6 补充说明 

采样频率:每秒钟采集音频数据的次数。采样频率越高,音频保真度越高。计算机广泛配置的16位声卡,使用的采样频率通常包括11025Hz、22050Hz、44100Hz和48000Hz四种,其中,采用11025Hz采样的声音效果相当于电话声音的效果;采用22050HZ采样的声音效果相当于FM调频广

播的效果;采用44100HZ采样的声音效果相当于CD声音的效果。
采样位数(振幅采样精度):即采样值或取样值,是用来衡量声音波动变化的一个参数,也是声卡的分辨率。它的数值越大,分辨率也就越高,发出声音的能力越强。目前计算机中配置的16位声卡的采样位数包括8位和16位两种。


声道数:有单声道和立体声之分,单声道的声音只能使用一个喇叭发声(有的声卡也将单声道信息处理成两个喇叭同时输出),立体声的WAV可以使两个喇叭都发声(一般左右声道有分工),这样更能感受到音频信息的空间效果。显然,双声道数据还原特性更接近人们的听力习惯,但采集得到的数据量会增加1倍。

二、读出WAV文件头部(Java代码)

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

/**
 *  读取wav文件表头
 * @author 赵海洋
 * @date 2024-9-16
 * @des 主要应用与ASR语音识别过程中,部分wav文件采样率与模型的不匹配,导致语音识别错误
 * */
public class WavReaderHeader {

    private String filePath;//文件路径
    private int numChannels;//声道
    private int sampleRate;//采样率
    private int bitsPerSample;//比特率,每秒的字节数
    private int numSamples;
    private byte[] audioData;

    public WavReaderHeader(String filePath) throws IOException {
        this.filePath = filePath;
        //readWavFile();
    }

    public void readWavFile() throws IOException {
        RandomAccessFile reader = new RandomAccessFile(new File(filePath), "r");
        byte[] buffer = new byte[2048];
        int bytesRead =-1;
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        while ((bytesRead = reader.read(buffer)) != -1) {
            outputStream.write(buffer,0,bytesRead);
        }
        outputStream.flush();
        byte[] binaryData = outputStream.toByteArray();
        ByteBuffer byteBuffer = ByteBuffer.wrap(binaryData);
        byteBuffer.order(ByteOrder.LITTLE_ENDIAN);

        // Read RIFF header
        byte[] riffHeader = new byte[4];
        byteBuffer.get(riffHeader);
        if (!new String(riffHeader).equals("RIFF")) {
            throw new IllegalArgumentException("Invalid WAV file");
        }
       // System.out.println("RIFF");
        // Read file size
        int fileSize = byteBuffer.getInt();
       // System.out.println("fileSize="+fileSize);
        // Read WAVE header
        byte[] waveHeader = new byte[4];
        byteBuffer.get(waveHeader);
        if (!new String(waveHeader).equals("WAVE")) {
            throw new IllegalArgumentException("Invalid WAV file");
        }
       // System.out.println("WAV file");
        // Read FMT chunk
        byte[] fmtChunk = new byte[4];
        byteBuffer.get(fmtChunk);
        if (!new String(fmtChunk).equals("fmt ")) {
            throw new IllegalArgumentException("Invalid WAV file");
        }

        // Read FMT chunk size
        int fmtChunkSize = byteBuffer.getInt();
       // System.out.println("fmtChunkSize="+fmtChunkSize);
        // Read audio format
        int audioFormat = byteBuffer.getShort();
        if (audioFormat != 1) {
            throw new IllegalArgumentException("Invalid WAV file");
        }

        // Read number of channels
        numChannels = byteBuffer.getShort();

        System.out.println("numChannels="+numChannels);

        // Read sample rate
        sampleRate = byteBuffer.getInt();
        System.out.println("sampleRate="+sampleRate);
        // Read byte rate
        int byteRate = byteBuffer.getInt();
        System.out.println("byteRate="+byteRate);

        // Read block align
        int blockAlign = byteBuffer.getShort();
       // System.out.println("blockAlign="+blockAlign);
        // Read bits per sample
        bitsPerSample = byteBuffer.getShort();
        System.out.println("bitsPerSample="+bitsPerSample);
        // Read DATA chunk
        byte[] dataChunk = new byte[4];
        byteBuffer.get(dataChunk);
        if (!new String(dataChunk).equals("data")) {
            throw new IllegalArgumentException("Invalid WAV file");
        }

        // Read data chunk size
        int dataChunkSize = byteBuffer.getInt();
       // System.out.println("dataChunkSize="+dataChunkSize);
        // Read audio data
        audioData = new byte[dataChunkSize];
        byteBuffer.get(audioData);

        numSamples = dataChunkSize / (numChannels * bitsPerSample / 8);
        System.out.println("numSamples="+numSamples);
    }

    public int getNumChannels() {
        return numChannels;
    }

    public int getSampleRate() {
        return sampleRate;
    }

    public int getBitsPerSample() {
        return bitsPerSample;
    }

    public int getNumSamples() {
        return numSamples;
    }

    public byte[] getAudioData() {
        return audioData;
    }

    public static void main(String[]args){

        try{
            new WavReaderHeader("E:\\54dc.wav").readWavFile();
        }catch (IOException e){
            e.printStackTrace();
        }

    }
}

如果wav文件有头部信息,则需要把头部信息读出来,看一下wav头部信息与模型信息是否一致,否则需要对wav文件头部信息做转化处理。如果是模型流输出文件,是没有头部信息的,如果要保存到本地被播器识别,需要补充增加头部信息。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

常生果

喜欢我,请支持我

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值