当前位置: 首页 > news >正文

我用 AI 辅助开发了一系列小工具():文件提取工具兆

从0构建WAV文件:读懂计算机文件的本质

虽然接触计算机有一段时间了,但是我的视野一直局限于一个较小的范围之内,往往只能看到于算法竞赛相关的内容,计算机各种文件在我看来十分复杂,认为构建他们并能达到目的是一件困难的事情,然而近期我观看了油管上Magicalbat大神的视频,发现其实它们的本质都惊人地简单:所有计算机文件,都是按特定规则组织的二进制数据,是人为规定好格式再由计算机解析,对于我们来说,只要根据规定格式进行编辑,就能够成功构建。

今天,我们就从最朴素的方式入手,通过手动构建一个WAV音频文件,拆解WAV格式的底层逻辑,同时理解一个核心认知:只要掌握了文件的格式规范,任何类型的文件都能像搭积木一样,一行行代码“拼”出来。

先认识WAV:WAV文件的格式

WAV是微软开发的无损音频格式,相比于压缩后的MP3,它的结构更直白,没有复杂的编码压缩,因此我们能够通过C++文件写入的方式直接完成wav文件的构建,wav文件的核心由三个关键的“数据块(Chunk)”组成:

RIFF块:文件的“身份卡”,告诉计算机“我是一个WAV文件”;

fmt块:音频的“参数说明”,记录采样率、声道数、位深等核心参数;

data块:真正的音频数据,存储着声音的数字信号。

而每个块的内容又如下图所示:

RIFF:

字段名 字节数 数据类型 固定值/计算规则

ChunkID 4 ASCII字符 固定为"RIFF"(无终止符,严格4字节)

ChunkSize 4 32位无符号整数 取值 = 整个WAV文件大小 - 8字节(减去ChunkID和ChunkSize自身的8字节)

Format 4 ASCII字符 固定为"WAVE"(无终止符,严格4字节)

fmt:

字段名 字节数 数据类型 固定值/计算规则

ChunkID 4 ASCII字符 固定为"fmt "(末尾空格,无终止符)

ChunkSize 4 32位无符号整数 PCM编码(最常用)下固定为16(代表后续字段的总字节数,不含ChunkID和ChunkSize)

AudioFormat(代码中Tag) 2 16位无符号整数 编码格式:1=PCM(无压缩,通用);3=IEEE浮点;6=μ律;7=A律等

NumChannels(代码中Chnnels,拼写笔误) 2 16位无符号整数 声道数:1=单声道;2=立体声;>2=多声道

SampleRate 4 32位无符号整数 采样率(每秒采样次数):常见44100Hz(CD音质)、48000Hz、22050Hz等

ByteRate 4 32位无符号整数 每秒音频数据字节数 = SampleRate × NumChannels × BitsPerSample / 8

BlockAlign(代码中BloclAlign,拼写笔误) 2 16位无符号整数 每个“采样帧”的字节数 = NumChannels × BitsPerSample / 8(播放器一次读取的最小单位)

BitsPerSample(代码中BitsperSample) 2 16位无符号整数 采样位深(每个采样点的比特数):8/16/24/32,16位最常用

data:

字段名 字节数 数据类型 固定值/计算规则

ChunkID(代码中DataId) 4 ASCII字符 固定为"data"(无终止符,严格4字节)

DataSize 4 32位无符号整数 音频数据总字节数 = 采样总数 × BlockAlign;采样总数 = SampleRate × 音频时长

音频数据区 可变 二进制流 PCM编码下为线性整数/浮点数:16位位深对应int16_t,8位对应uint8_t,32位浮点对应float

我们接下来的代码,就是严格按照这个模板,把每个部分的二进制数据“写”进文件里。

从零构建WAV:一行代码拆解核心逻辑

下面是完整的C++代码(新手也能看懂),我们逐段拆解,看如何从0生成一个能播放的440Hz正弦波WAV文件:

#include

using namespace std;

// 类型别名:让代码更易读,明确数据的字节长度

#define u32 uint32_t // 32位无符号整数(4字节)

#define u16 uint16_t // 16位无符号整数(2字节)

#define f32 float // 32位浮点数(4字节)

#define i16 int16_t // 16位有符号整数(2字节)

#define HZ 44100 // 采样率:每秒采集44100个声音样本(标准音频采样率)

#define DURATION 5 // 音频时长:5秒

// 1. 定义WAV的三个核心数据块结构(对应格式规范)

// RIFF块:文件整体标识

struct chunk1{

char ChunkID[4]; // 块标识,固定为"RIFF"

u32 ChunkSize; // 从该字段到文件末尾的字节数(总字节数-8)

char Format[4]; // 格式类型,固定为"WAVE"

}RIFF;

// fmt块:音频参数配置

struct chunk2{

char ChunkID[4]; // 块标识,固定为"fmt "(注意末尾有空格)

u16 Tag; // 编码格式,1代表PCM(无压缩)

u32 ChunkSize; // fmt块的大小,PCM格式固定为16

u16 Chnnels; // 声道数:1=单声道,2=立体声

u32 SampleRate; // 采样率

u32 ByteRate; // 每秒数据量 = 采样率×声道数×位深/8

u16 BloclAlign; // 每个采样的总字节数 = 声道数×位深/8

u16 BitsperSample; // 每个采样的位深:16位(常见)

}Fmt;

// data块:音频数据存储区

struct chunk3{

char DataId[4]; // 块标识,固定为"data"

u32 DataSize; // 音频数据的总字节数

}Data;

signed main(int argc,char* argv[]){

// 打开文件:"wb"表示以二进制模式写入(关键!文件本质是二进制)

FILE *fp = fopen("test.wav","wb");

// 计算总采样数:采样率×时长(5秒×44100=220500个样本)

u32 NumSamples = HZ * DURATION;

// 2. 填充RIFF块并写入文件

memcpy(RIFF.ChunkID,"RIFF",4); // 写入块标识

RIFF.ChunkSize = NumSamples*sizeof(u16)+36; // 计算块大小

memcpy(RIFF.Format,"WAVE",4); // 声明为WAVE格式

fwrite(RIFF.ChunkID,sizeof(char),4,fp); // 写入4个字符的ChunkID

fwrite(&RIFF.ChunkSize,sizeof(u32),1,fp); // 写入4字节的ChunkSize

fwrite(RIFF.Format,sizeof(char),4,fp); // 写入4个字符的Format

// 3. 填充fmt块并写入文件

memcpy(Fmt.ChunkID,"fmt ",4);

Fmt.ChunkSize = 16; // PCM格式下fmt块固定16字节

Fmt.Tag = 1; // PCM无压缩编码

Fmt.Chnnels = 1; // 单声道

Fmt.SampleRate = HZ; // 44100Hz采样率

Fmt.ByteRate = HZ*sizeof(u16); // 每秒字节数:44100×2=88200

Fmt.BloclAlign = Fmt.Chnnels * sizeof(u16); // 每个采样2字节

Fmt.BitsperSample = 16; // 16位位深

// 按顺序写入fmt块的所有参数(严格遵循格式规范)

fwrite(&Fmt.ChunkID,sizeof(char),4,fp);

fwrite(&Fmt.ChunkSize,sizeof(u32),1,fp);

fwrite(&Fmt.Tag,sizeof(u16),1,fp);

fwrite(&Fmt.Chnnels,sizeof(u16),1,fp);

fwrite(&Fmt.SampleRate,sizeof(u32),1,fp);

fwrite(&Fmt.ByteRate,sizeof(u32),1,fp);

fwrite(&Fmt.BloclAlign,sizeof(u16),1,fp);

fwrite(&Fmt.BitsperSample,sizeof(u16),1,fp);

// 4. 填充data块并写入文件

memcpy(Data.DataId,"data",4);

Data.DataSize = NumSamples * sizeof(u16); // 音频数据总字节数

fwrite(&Data.DataId,sizeof(char),4,fp);

fwrite(&Data.DataSize,sizeof(u32),1,fp);

// 5. 生成音频数据并写入(440Hz正弦波,标准A调)

for(int i=0;i

f32 t = (f32)i/HZ; // 计算当前时间点(秒)

// 生成440Hz正弦波的数值(声音的本质是振动,正弦波模拟声波)

f32 y =sinf(t*440.0f*2.0f*3.1415926f);

// 转换为16位整数(适配16位位深的音频)

i16 sample = (i16)(y*INT16_MAX);

// 写入单个音频样本(2字节)

fwrite(&sample,sizeof(i16),1,fp);

}

fclose(fp); // 关闭文件

return 0;

}

所有文件,都是“按规则写二进制”的产物

写完这段代码,你可能会发现:生成WAV文件的过程,就是“按格式规范往文件里写二进制数据”的过程。而这个逻辑,适用于所有计算机文件:

TXT文档:本质是字符的ASCII/UTF-8编码(比如字符'A'对应二进制01000001),我们按顺序写入这些编码,就成了TXT文件;

BMP图片:由文件头(记录宽、高、位深)+ 像素数据(每个像素的RGB值)组成,按BMP格式写这些数据,就能生成图片;

MP4视频:哪怕是压缩过的视频,也是按MP4的格式规范,把编码后的视频帧、音频帧组织成二进制数据;

EXE可执行文件:遵循PE格式,把指令、数据、资源按规则写入,操作系统就能识别并运行。

计算机之所以能“看懂”不同的文件,不是因为文件有“魔法”,而是因为程序员提前约定了“格式规范”——就像我们约定“RIFF”开头的是WAV文件,播放器读到这个标识,就按WAV的规则解析后续数据。

计算机的本质是“朴素的规则”

对刚接触计算机的人来说,各种文件、软件、系统看似复杂,但拆解到最底层,都是“数据+规则”的组合,

只要我们对着格式手册,即便使用最朴素的方式,也能够成功构建出可以使用的音频文件。计算机的世界没有想象中那般复杂,计算机只在乎那最终排好队的 0 和 1。

进一步思考:从文件到软件

了解了各类文件本质,我们自然能理解计算机中各个编辑软件的原理是什么了,就比如今天举的wav的例子,如果我们将示例程序改进一下,加入输入,那么这是否就成了一个简单的音频编辑软件了呢,所有的复杂软件(如 Photoshop、Premiere),底层逻辑都是如此:读取特定规则的二进制 -> 在内存中加工处理 -> 按规则写回二进制。当你不再把文件看作“黑盒”,你便拥有了重塑数字世界的能力。啦莆林谎

http://www.jsqmd.com/news/620079/

相关文章:

  • Qwen3-VL-4B Pro实战:手把手教你搭建智能图片分析工具
  • XGBoost-原理推导(中):从目标函数到最优切分点
  • 2026年发泡水泥优质企业推荐榜:黄湖节能领衔,专业轻质水泥实力企业精选 - 海棠依旧大
  • 2026成都别墅装修口碑王炸榜!TOP10实力公司全解析,从设计到落地一篇搞定 - 推荐官
  • 2026评测:波光跳泉领域这些企业表现亮眼,市场评价高的波光跳泉生产厂家坤湛喷泉专注行业多年经验,口碑良好 - 品牌推荐师
  • 浏览器自动化六大技术路线深度对比:从模拟点击到 Chrome 扩展注入既
  • 海外项目实战:用uniapp搞定谷歌登录,绕过网络限制的纯前端方案(附完整代码)
  • 生物显微镜设计避坑指南:Zemax仿真中那些容易忽略的‘可制造性’细节
  • Windows下PostgreSQL 14安装失败?手把手教你解决‘Problem running post-install step‘错误
  • 2026年4月海口美兰半挂租赁买卖,文昌半挂技术实力与市场口碑领航者 - 品牌推荐师
  • 批量音频音量调整工具使用说明:固定增减分贝与目标响度两种模式怎么选
  • uniapp消息推送权限处理指南:如何优雅地引导用户开启通知权限
  • 深入解读ATPG Pattern类型:除了Basic Scan,Clock PO和RAM Sequential模式怎么用?
  • 从金牛奖到数据实验室:一家头部公募如何在数智化深水区重构投研生产力
  • MindSpore 环境配置完全指南雀
  • 别再为3D模型发愁了!用HelixToolkit.Wpf在WPF里加载并操控模型(附完整代码)
  • 2026成都全包装修公司实力红黑榜:深扒10家高口碑品牌,附真实案例与报价陷阱解析 - 推荐官
  • 【仅限头部AI实验室内部流通】:LLM训练流水线版本控制Checklist v2.1(含SHA-3哈希校验模板)
  • Detectron2实战:从零构建自定义目标检测模型的完整指南
  • NumPy怎么删去单维度_np.squeeze()移除shape中长度为1的冗余轴
  • 怎样跨库跨表导出JSON数据_结构与数据分离提取
  • TI IWR1843+DCA1000数据采集实战:手把手教你用Matlab调用LUA脚本配置mmWave Studio参数
  • 【模拟IC】从指标到参数:二级运放GBW与相位裕度的设计实战
  • 新都装修公司实力大起底!2026最新TOP10排名,专治老房翻新与别墅豪宅装修 - 推荐官
  • USB运动控制五轴雕刻机系统完全开源资料:PCB生产支持,多版本C++源码,五轴联动与RTCP...
  • 计算机毕业设计:Python智慧天气数据采集与可视化系统 Django框架 线性回归 数据分析 大数据 机器学习 大模型 气象数据(建议收藏)✅
  • SkyWalking Web UI 实战指南:从入门到精通
  • Oracle归档日志爆满急救指南
  • 如何解决ORA-28040没有匹配的验证协议_sqlnet.ora版本兼容设置
  • DDR5内存实战:如何优化读操作性能(附BL32模式配置指南)