news 2026/4/16 17:04:54

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
从0构建WAV文件:读懂计算机文件的本质

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

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

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

先认识WAV:WAV文件的格式

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

  • RIFF块:文件的“身份卡”,告诉计算机“我是一个WAV文件”;
  • fmt块:音频的“参数说明”,记录采样率、声道数、位深等核心参数;
  • data块:真正的音频数据,存储着声音的数字信号。

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

RIFF:

字段名字节数数据类型固定值/计算规则
ChunkID4ASCII字符固定为"RIFF"(无终止符,严格4字节)
ChunkSize432位无符号整数取值 = 整个WAV文件大小 - 8字节(减去ChunkID和ChunkSize自身的8字节)
Format4ASCII字符固定为"WAVE"(无终止符,严格4字节)

fmt:

字段名字节数数据类型固定值/计算规则
ChunkID4ASCII字符固定为"fmt "(末尾空格,无终止符)
ChunkSize432位无符号整数PCM编码(最常用)下固定为16(代表后续字段的总字节数,不含ChunkID和ChunkSize)
AudioFormat(代码中Tag)216位无符号整数编码格式:1=PCM(无压缩,通用);3=IEEE浮点;6=μ律;7=A律等
NumChannels(代码中Chnnels,拼写笔误)216位无符号整数声道数:1=单声道;2=立体声;>2=多声道
SampleRate432位无符号整数采样率(每秒采样次数):常见44100Hz(CD音质)、48000Hz、22050Hz等
ByteRate432位无符号整数每秒音频数据字节数 = SampleRate × NumChannels × BitsPerSample / 8
BlockAlign(代码中BloclAlign,拼写笔误)216位无符号整数每个“采样帧”的字节数 = NumChannels × BitsPerSample / 8(播放器一次读取的最小单位)
BitsPerSample(代码中BitsperSample)216位无符号整数采样位深(每个采样点的比特数):8/16/24/32,16位最常用

data:

字段名字节数数据类型固定值/计算规则
ChunkID(代码中DataId)4ASCII字符固定为"data"(无终止符,严格4字节)
DataSize432位无符号整数音频数据总字节数 = 采样总数 × BlockAlign;采样总数 = SampleRate × 音频时长
音频数据区可变二进制流PCM编码下为线性整数/浮点数:16位位深对应int16_t,8位对应uint8_t,32位浮点对应float

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

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

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

/* by yours.tools - online tools website : yours.tools/zh/htpasswd.html */ #include <bits/stdc++.h> 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<NumSamples;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),底层逻辑都是如此:读取特定规则的二进制 -> 在内存中加工处理 -> 按规则写回二进制。当你不再把文件看作“黑盒”,你便拥有了重塑数字世界的能力。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/16 9:09:23

外文文献查找的6个途径及相关方法指南

刚开始做科研的时候&#xff0c;我一直以为&#xff1a; 文献检索就是在知网、Google Scholar 里反复换关键词。 直到后来才意识到&#xff0c;真正消耗精力的不是“搜不到”&#xff0c;而是—— 你根本不知道最近这个领域发生了什么。 生成式 AI 出现之后&#xff0c;学术检…

作者头像 李华
网站建设 2026/4/16 9:04:48

使用 LoRA 进行大模型微调:原理、实现与效果评估

前言 随着 Llama、Qwen、ChatGLM 等开源大语言模型&#xff08;LLM&#xff09;的普及&#xff0c;如何在有限算力下高效微调模型成为开发者关注的核心问题。全参数微调&#xff08;Full Fine-tuning&#xff09;动辄需要数十 GB 显存&#xff0c;而 LoRA&#xff08;Low-Rank…

作者头像 李华
网站建设 2026/4/16 9:08:31

WORD上传至CKEDITOR后图片无法显示怎么办?

山东某国企项目需求实现记录&#xff1a;基于CKEditor4的Word/微信内容集成方案 一、需求分析与技术选型 核心需求&#xff1a; 编辑器插件需支持&#xff1a; Word粘贴&#xff08;保留格式图片自动上传&#xff09;Word/Excel/PPT/PDF导入&#xff08;保留格式图片&#xff…

作者头像 李华
网站建设 2026/4/16 11:12:07

CNC雕刻机STM32F407源码及原理图,含详细中文注解

CNC雕刻机STM32F407源码&#xff0c;有详细中文注解&#xff0c;有原理图 最近在折腾CNC雕刻机项目&#xff0c;用的是STM32F407做主控&#xff0c;顺手把源码和原理图整理出来了。这板子最骚的地方是直接用铜柱当支撑架&#xff0c;调试时摔了三次愣是没坏&#xff0c;看来工…

作者头像 李华
网站建设 2026/4/16 12:21:32

进击的“产线专家”:琥崧科技破浪港股,智能制造的黄金门票已就位!

紧跟龙头“智造”的新一轮出海大潮&#xff0c;又一位手握硬核技术的“隐形冠军”已经登场。2025年12月31日&#xff0c;国内锂电池智能产线领域的实力选手——琥崧科技集团股份有限公司&#xff0c;正式向港交所递交了上市申请&#xff0c;联席保荐人是业内知名的中金公司和浦…

作者头像 李华