如何从零开始构建一个WAV音频文件?

摘要:从0构建WAV文件:读懂计算机文件的本质 虽然接触计算机有一段时间了,但是我的视野一直局限于一个较小的范围之内,往往只能看到于算法竞赛相关的内容,计算机各种文件在我看来十分复杂,认为构建他们并能达到目的是一件困难的事情,然而近期我观看了油管
从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 我们接下来的代码,就是严格按照这个模板,把每个部分的二进制数据“写”进文件里。
阅读全文