数据压缩第三周作业——WAV文件分析

WAV文件概述

WAVE(Waveform Audio File Format):是微软与IBM公司所开发在个人电脑存储音频流的编码格式。其采用RIFF(Resource Interchange File Format)文件格式结构(资源互换文件格式),通常用来保存PCM格式的原始音频数据,不对原有文件进行压缩,所以在音质方面无失真情况。
通常可以用于保存AVI(视音频交错数据)、WAV(波形格式)、RDI(位图数据)等格式的数据。


文件组成

WAV文件主要由以下三部分组成:

  • RIFF Chunk:文件标识符,WAV身份判断
  • FORMAT Chunk:文件参数模块
  • DATA Chunk:实际数据块

此外,文件中还可能包含一些可选的区块,如:Fack Chunk、Cue Points Chunk等,只有通过某些软件进行转化的过程中可能会加入该子块,其主要存储一些关于该文件的重要信息(如压缩编码信息等)

WAVE文件是以RIFF(Resource Interchange File Format, 资源交互文件格式)格式来组织内部结构的,在了解WAV文件之前,我们首先对RIFF格式进行简单了解。


RIFF格式

RIFF格式是一种包含多个嵌套的二进制文件格式,其基本构成单元是chunk。

chunk表示数据的一个基本逻辑单元,例如视频的一帧数据、音频的一帧数据等等。每个RIFF块的基本结构如下:

typedef struct Chunk
{
	DWORD ChunkId;              // 用于表示块的类型(chunkID) 
	DWORD ChunkSize;            // 用于表明数据域的长度,注意是数据域
	BYTE ChunkData[ChunkSize];  // 数据部分 
} Chunk;

ChunkId:四字节,用于标识块的类型,例如RIFF、LIST、AVI等。(一般是四个字符,用FOURCC表示,如果少于四个字符,用补空格填满四个字节)
ChunkSize:四字节,用于块中数据长度(不包括填充添加的0,也不包括开头的基本信息情况)
ChunkData:n字节,存储数据。注意必须是偶数。如果实际数据是奇数(字节),则在数据末尾补0。

值得注意的是,RIFF格式允许chunk嵌套,一个chunk中的chunk被称之为子块(subchunk),但只有ID为“RIFF”(RIFF块)或者“LIST”(列表块)的chunk允许拥有子块,其它的块不允许嵌套,仅仅只包含数据。

我们重点对RIFF块进行分析:

RIFF chunk

ChunkId为RIFF的块是RIFF块,根据规定,RIFF文件第一个chunk开头标识符必须为RIFF,并且一个RIFF文件只能有这一个标志位RIFF的块。

以实例讲解作为分析,如下图所示RIFFchunk展示。

  • RIFF块,头部ID固定为RIFF,size为数据域的长度,后面部分为数据域。在数据域开头有一个FOURCC(四字节码),用于标识子块的数据类型,接下来是子块部分。
  • 图中的子块为LISTchunk,ID即为LIST,size为列表块数据域的长度,后面为数据域。在数据里又包含多个子块。

在这里插入图片描述

WAV文件格式

WAV文件遵循RIFF规则,内容以chunk为最小单位存储,一般由RIFF chunk、format chunk和datachunk组成。此外,文件中还可能包含其余chunk,例如Fact chunk、Associated data list chunk等。
接下来分别对RIFFchunk、format chunk、datachunk进行介绍:

RIFFchunk
typedef struct chunk
{
    DWORD ChunkId;   // 块标志
    DWORD size; // 块大小
    DWORD Type; // 后续跟块介绍
}chunk;
名称偏移地址说明
chunkId0x00固定为0x5249 4646(表示为RIFF)
Chunksize0x04size为wav文件的长度减去ID和Size的长度
Type0x08固定为0x5741 5645(表示WAVE)
Format chunk
typedef struct Format chunk
{
    DWORD ChunkId;   // 块标志
    DWORD size; // 块大小
    WORD AudioFormat; // 表明音频格式
    WORD NumChannels;//表明声道数
    DWORD SampleRate;//采样率
    DWORD ByteRate;//每秒字节数
    WORD BlockAlign;//数据块对齐
    WORD BitsPerSample;//采样位数
}Format chunk;
名称偏移地址说明
chunkId0x00固定为0x666D7420(表示为fmt)
Chunksize0x04表示该区块的数据大小(不包含ID和size)
AudioFormat0x08音频格式,PCM音频数据的值为1;大于1表示有压缩的编码
NumChannels0x0A声道数,1表示单声道,2表示双声道
SampleRate0x0C表示音频数据的采样率
ByteRate0x10每秒数据字节数(ByteRate = SampleRate * NumChannels * BitsPerSample / 8)
BlockAlign0x14每个采样所需的字节数 (BlockAlign = NumChannels * BitsPerSample / 8)
BitsPerSample0x16表明每个采样存储的bit数,8:8bit,16:16bit,32:32bit,值越大,声音还原越好
注意:只有'RIFF','LIST'chunk有Type
Data Chunk
typedef struct Data Chunk
{
    DWORD ChunkId;   // 块标志(以data为标识)
    DWORD size; // 数据块的长度
    nByte Data; // 具体数据
}Data Chunk;
名称偏移地址说明
chunkId0x00固定为0x64617461(表示为data)
Chunksize0x04表示整个wav文件的数据大小
Data0x08为具体的音频数据

对于数据部分:特别强调的是:
8bit单声道:

采样1(8bit)采样2(8bit)
数据1数据2

8bit双声道:

采样1(8bit)采样2(8bit)
数据1左声道(4bit)数据1右声道(4bit)数据2左声道(4bit)数据2右声道(4bit)

16bit单声道相同:

采样1(16bit)采样2(16bit)
数据1低字节(8bit)数据1高字节(8bit)数据2低字节(8bit)数据2高字节(8bit)

16bit双声道:

采样1(16bit)
左声道(8bit)右声道(8bit)
低字节(4bit)高字节(4bit)低字节(4bit)高字节(4bit)

图片实例讲解

选取时长为1分钟的part1.wav文件进行分析:
在这里插入图片描述
利用二进制编辑器打开进行具体分析:在这里插入图片描述
· RIFF chunk:

数字名称实际大小说明
52 49 46 46chunkID固定为0x52494646(大端)表示此块为RIFFchunk
06 80 A1 00chunksize0xA18006=10,584,070D表示整个wav文件块共有10,584,070字节
57 41 56 45type固定为0x57415645(大端)表示为wave文件

在这里插入图片描述
· Format Chunk:

数字名称实际大小说明
66 6D 74 20chunkID固定为0x666D7420(大端)表示此块为format chunk(fmt)
10 00 00 00chunksize0x10=16D表示fmt块共有16字节
01 00Audio Format0x01=1D表示为PCM音频数据
02 00numchannels0x02=2D表示为双声道
44 AC 00 00sampleRate0xAC44=44100D表示音频数据采样率为44.1kHz
10 B1 02 00byteRate0x02B110=176400D表示每秒数据共有176400个字节
04 00blockalign0x04=4D每个采样数据所需4个字节
10 00bitspersample0x10=16D每个采样存储为16bit

· LIST chunk
在这里插入图片描述
我们可以观察到,本例子中的WAV文件夹杂一个LIST chunk(list chunk的格式如前面第一段介绍的,这里我们简单分析下)

数字名称实际大小说明
4C 49 53 54chunkID固定为0x4C495354(大端)表示此块为LISTchunk
1A 00 00 00chunksize0x1A=26D表示list块共有26字节
49 4E 46 4Ftype固定为0x494E464F(大端)表示为LISTchunk(?)
data

经过格式转换后的chunk文件含有list chunk,存储格式转换信息,我们从chunksize开始跳过26字节直接进入data部分分析:
在这里插入图片描述
· Data Chunk:

数字名称实际大小说明
64 61 74 61chunkID固定为0x64617461(大端)表示此块为Data chunk
C0 7F A1 00chunksize0xA17FC0=10584000D表示共有10584000字节数据
data(此文件为双声道16bit量化,参照上面的声道采样表进行分析)

根据分析结果画出实例文件结构:
在这里插入图片描述

实验总结

本次实验通过查阅资料了解了WAV文件格式,利用格式工厂转换mp4文件为wav文件,对转换的wav文件进行了简单分析。具体的实验方法和前几次的文件分析方法一致,本次实验过程中没有遇到太多问题,但遗憾的寻找了很久资料源,都没有找到list chunk数据的具体分析,不过后续如果用到wav文件分析数据,我们可以直接根据chunksize跳过listchunk部分进入data!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要实现将OPUS文件解压缩为WAV文件,可以使用libopus库和libsndfile库来进行操作。以下是一个基本的C++代码示例: ```cpp #include <iostream> #include <cstring> #include <opus/opus.h> #include <sndfile.h> int main() { const char* opusFile = "input.opus"; const char* wavFile = "output.wav"; // 打开 OPUS 文件 SF_INFO opusInfo; SNDFILE* opusFilePtr = sf_open(opusFile, SFM_READ, &opusInfo); if (opusFilePtr == nullptr) { std::cerr << "无法打开 OPUS 文件" << std::endl; return 1; } // 配置解码器 int error; OpusDecoder* decoder = opus_decoder_create(opusInfo.samplerate, opusInfo.channels, &error); if (error != OPUS_OK) { std::cerr << "无法创建解码器: " << opus_strerror(error) << std::endl; sf_close(opusFilePtr); return 1; } // 打开 WAV 文件 SF_INFO wavInfo; wavInfo.samplerate = opusInfo.samplerate; wavInfo.channels = opusInfo.channels; wavInfo.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16; SNDFILE* wavFilePtr = sf_open(wavFile, SFM_WRITE, &wavInfo); if (wavFilePtr == nullptr) { std::cerr << "无法创建 WAV 文件" << std::endl; opus_decoder_destroy(decoder); sf_close(opusFilePtr); return 1; } // 解压缩 OPUS 数据并写入 WAV 文件 const int bufferSize = 960 * opusInfo.channels; float buffer[bufferSize]; const int frameSize = 960; unsigned char opusData[frameSize * opusInfo.channels]; int numFrames; do { numFrames = sf_readf_float(opusFilePtr, buffer, frameSize); if (numFrames > 0) { const int sampleCount = numFrames * opusInfo.channels; for (int i = 0; i < sampleCount; ++i) { opusData[i] = static_cast<unsigned char>(buffer[i] * 32768.0f + 32768.0f); } sf_write_raw(wavFilePtr, opusData, sampleCount * sizeof(unsigned char)); } } while (numFrames > 0); // 清理资源 opus_decoder_destroy(decoder); sf_close(wavFilePtr); sf_close(opusFilePtr); std::cout << "解压缩完成" << std::endl; return 0; } ``` 请确保已经安装了libopus库和libsndfile库,并在编译时链接这些库。这个示例代码打开一个OPUS文件,将其解码为PCM数据,并将PCM数据写入一个WAV文件

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值