第 4 课:声音的本质就是一串数字
# 第 4 课:声音的本质就是一串数字
本课目标:把声音的数字化讲清楚 —— 采样率、位深、声道、PCM。 学完你会发现一个让人惊喜的事实:声音和视频,在「怎么变成数字」这件事上,是同一套思路。 然后回到
libobs/media-io/audio-io.h,看它和上节课的video-io.h长得有多像。
视频那条线,我们已经讲完了「画面怎么存」(像素、分辨率、颜色)。现在换一条线 —— 声音。别担心,声音其实比颜色还好懂,而且你会一路看到它和视频的「对应关系」。
# 4.1 声音是一种「波」
📍我们在哪:视频那条线告一段落,这一课换到「声音」这条并行的流水线。第一步,先搞清声音到底是什么。
先问:声音到底是什么?
声音是空气压力的快速振动。 你拨动琴弦、敲桌子、说话,都会让周围空气一会儿被压紧、一会儿被拉疏,这种疏密变化像波纹一样传到你耳朵,推动耳膜振动,你就「听到」了。
麦克风做的事,就是把这种空气振动转成一段连续变化的电压信号 —— 振动强,电压高;振动弱,电压低。如果把它画出来,就是一条上下起伏的曲线(波形)。
但问题和视频一样:这条曲线是「连续」的,而电脑只认「离散」的数字。 中间需要一道转换 —— 把连续的波,变成一串数字。这道转换,叫采样。
# 4.2 采样:把连续的波量成一串数字
📍承上启下:声波是连续的、电脑只认离散的数字,中间靠「采样」连接 —— 这一节讲怎么把一条波量成一串数。
采样的做法朴素得出奇:每隔一小段固定的时间,就量一下声波此刻的高度,把量到的数记下来。

每量一次,得到一个数,叫一个采样点(sample)。一秒钟量多少次,就叫采样率(sample rate),单位是 Hz(赫兹)。你会反复见到两个数字:
- 44100 Hz(44.1 kHz)—— CD 音质的标准,每秒量 4.41 万次;
- 48000 Hz(48 kHz)—— 视频 / 影视行业的标准,也是 OBS 的默认值。
为什么视频偏爱 48 kHz?一个实际原因是它和常见帧率「除得尽」(48000 能被 24、25、30、60 整除),音频块和视频帧更容易对齐 —— 这对后面要讲的「音画同步」很友好。
# 4.3 采样率要多高才够?
📍承上启下:采样点越密越忠实,可到底多密才够?这一节用奈奎斯特定理回答,也解释了 44.1k / 48k 是怎么来的。
采样点是离散的,但我们最终要把它们「连」回一条曲线播放出来。那么问题来了:每秒量多少次才够还原?

看上图:采样点太稀疏时,把它们连起来重建的波形,和原波形差很远 —— 声音就失真了(这种现象叫走样 / aliasing);采样点足够密时,重建出的波形几乎贴合原波形,听感就忠实。
那「足够」是多少?有个著名结论叫奈奎斯特采样定理,直觉版很简单:
要忠实还原最高频率为 f 的声音,采样率至少要达到 2f。
人耳能听到的最高频率大约是 20 kHz。按定理,采样率只要超过 40 kHz 就够了 —— 这正是 44.1 kHz、48 kHz 的由来:它们都略高于 40 kHz,刚好覆盖人耳的听觉上限,再高就是浪费了。
# 4.4 位深:每个采样量得多精细
📍承上启下:采样率管「多久量一次」;这一节换个维度 —— 每次量到的那个数记得多精细,也就是位深。
采样率管的是「多久量一次」;还有另一个维度:每次量得到的那个数,记得多精细?
这里有个关键限制:采样值不能是任意精度的,它必须落在一组固定的「刻度」上(这一步叫量化 / quantization)。而位深(bit depth)决定了刻度有多少档:

- 位深低(比如 3 位):只有 2³ = 8 个刻度,采样值被迫「四舍五入」到最近的刻度,重建出来阶梯感很重,还会引入「量化噪声」;
- 位深高(16 位):有 2¹⁶ = 65536 个刻度,曲线被记录得非常平滑,几乎听不出损失。
常见标准:CD 用 16 位,专业录音常用 24 位。
有意思的是,OBS 内部混音用的是 32 位浮点(FLOAT)。为什么用浮点而不是 16 位整数?因为混音时要把好几路声音相加,如果用定点整数,加着加着就可能超出上限「削顶」失真;用浮点能留足运算余量,处理完再转回 16 位输出。这一点在源码里有迹可循 ——
audio-io.h里的TOTAL_AUDIO_SIZE和内部缓冲都是按float算的。
# 4.5 声道:同时有几路独立的声音
📍承上启下:前面都在说「一条」声波,可现实里声音常是多路的 —— 左右声道、环绕声,这就是声道。
到这里我们说的都是「一条」声波。但现实里声音常常是多路的 —— 左耳和右耳听到的不一样,才有立体声和环绕声。每一路独立的声音,叫一个声道(channel),各自是一条独立的采样序列,对应一个喇叭位置:

- 单声道(Mono):1 路,所有喇叭一个声;
- 立体声(Stereo):2 路,左(FL)、右(FR),这是最常见的;
- 环绕声(5.1):6 路,前左右、中置、低音炮(LFE)、后左右,影院 / 游戏常用。
OBS 支持 Mono / Stereo / 2.1 / 4.0 / 4.1 / 5.1 / 7.1,最多 8 个声道。
# 4.6 PCM:这一串数字本身
📍承上启下:采样率、位深、声道三样凑齐,把量到的数原样排好,就是音频的「裸数据」PCM —— 对应视频里那个未压缩的原始帧。
现在把三样东西凑齐:采样率(多久量一次)× 位深(每次量多细)× 声道数(几路)—— 把所有量到的采样数字,原原本本地排好,这一长串没有经过任何压缩的数字,就叫 PCM(脉冲编码调制,Pulse Code Modulation)。
PCM 就是音频里的「裸数据」,地位完全等同于视频里那个未压缩的 RGB / YUV 原始帧。你电脑里的 .wav 文件,装的通常就是 PCM。
那它有多大?算一下 CD(44100 Hz、16 位、立体声):
44100 次/秒 × 16 位 × 2 声道
= 1,411,200 位/秒
≈ 1.4 Mbps
≈ 一分钟约 10 MB
2
3
4
和视频的 356 MB/秒比,音频小得多(毕竟一条波远比一整幅画面信息少)。但 1.4 Mbps 仍然不算小 —— 所以音频和视频一样,也需要压缩:MP3、AAC、Opus 这些音频编码,能把它再压到 128~192 kbps,小上十倍。这就是模块 5(第 21 课)的内容。
# 4.7 源码印证:OBS 眼里的一段声音
📍承上启下:概念讲完回代码 —— 看 audio_output_info、audio_data 怎么把采样率、位深、声道装进结构体,和上节课的视频结构体长得有多像。
回到代码。打开 libobs/media-io/audio-io.h,本课的每个概念都能找到落点:

描述「一条音频流」的,是 struct audio_output_info —— 采样率、位深(格式)、声道,三个概念齐活:
// libobs/media-io/audio-io.h:90
struct audio_output_info {
const char *name;
uint32_t samples_per_sec; // 采样率(本课 4.2)
enum audio_format format; // 位深/格式(本课 4.4)
enum speaker_layout speakers; // 声道(本课 4.5)
...
};
2
3
4
5
6
7
8
其中位深和声道又各自是一个枚举:
// libobs/media-io/audio-io.h:43
enum audio_format {
AUDIO_FORMAT_U8BIT, AUDIO_FORMAT_16BIT, AUDIO_FORMAT_32BIT,
AUDIO_FORMAT_FLOAT, ... /* 还有各自的 _PLANAR 变体(planar 的意思见第 3 课:分声道分块存;packed 则交错存) */
};
// libobs/media-io/audio-io.h:66
enum speaker_layout {
SPEAKERS_MONO, SPEAKERS_STEREO, SPEAKERS_2POINT1,
SPEAKERS_4POINT0, SPEAKERS_4POINT1, SPEAKERS_5POINT1, SPEAKERS_7POINT1
};
2
3
4
5
6
7
8
9
10
11
而真正装着某一小块声音数据的,是 struct audio_data(audio-io.h:77):data(采样数据)、frames(这块有多少个采样帧)、timestamp(这块声音该在何时播放)。最后那个 timestamp,又是音画同步的关键。
至于「默认采样率是多少」,答案就写在前端代码里 —— 而且紧挨着上节课那个 NV12 默认值:
// frontend/widgets/OBSBasic.cpp:855
config_set_default_uint(activeConfiguration, "Audio", "SampleRate", 48000);
2
(第 3 课我们看到 :845 设置默认颜色格式 NV12,这里 :855 设置默认采样率 48000 —— 视频和音频的默认值,真的就并排躺在一起。)
# 4.8 惊人一致:音频和视频是「同一套数字化思路」
📍收束:把音频和视频并排一比你会发现 —— 它们本就是同一套数字化思路,这也预告了后面「编码/封装/同步」两边通用。
如果你有种「这些概念好像在哪见过」的既视感,没错 —— 音频的数字化,和视频几乎是一一对应的:

| 维度 | 视频(第 2–3 课) | 音频(本课) |
|---|---|---|
| 时间上的离散单位 | 帧 (frame) | 采样点 (sample) |
| 每秒多少个 | 帧率 24~60 fps | 采样率 44100 / 48000 Hz |
| 每个的精度 | 每像素字节 / 颜色位深 | 位深 16 / 24 bit / FLOAT |
| 并排几路 | 通道 RGB / 分辨率 | 声道 Mono / Stereo / 5.1 |
| 未压缩裸数据 | RGB / YUV 原始帧 | PCM |
| 描述「一条流」 | video_output_info | audio_output_info |
| 一块数据 | video_data | audio_data |
| OBS 默认值 | NV12 (OBSBasic.cpp:845) | 48000 Hz (OBSBasic.cpp:855) |
这种一致不是巧合 —— 它们本质上都在做同一件事:把现实世界连续的信号,在「时间」上切成离散单元、在「数值」上量化成有限精度,得到一串可以处理的数字。 正因为如此,后面要讲的「编码」「封装」「时间戳同步」,对音频和视频是同一套机制,你学一遍就能两边通用。
# 4.9 本课小结
- 声音是空气振动的波;麦克风把它转成连续电压,再经采样变成一串数字。
- 采样率:每秒采样次数(Hz),44100 / 48000;由奈奎斯特定理,需 ≥ 2× 最高频率,人耳 ~20 kHz 故 44.1k/48k 够用。
- 位深:每个采样的精细度(刻度档数),16 位 = 65536 档;OBS 内部用 32 位浮点混音以防削顶。
- 声道:几路独立的声音(Mono/Stereo/5.1…),OBS 最多 8 路。
- PCM = 采样率 × 位深 × 声道排成的裸数据(对应视频的 RGB);CD 约 1.4 Mbps,虽比视频小,仍需压缩。
- 源码上,
audio_output_info/audio_data与视频的video_output_info/video_data结构对称,默认值也并排(OBSBasic.cpp:845/855)—— 音视频是同一套数字化思路。
# 4.10 动手 / 观察(本课作业)
- 在 OBS 里找采样率:设置 → 音频 → 「采样率」,看到 44.1 kHz / 48 kHz 两个选项 —— 就是本课的采样率,对应
audio_output_info.samples_per_sec。 - 感受 PCM 有多大:用任意录音工具录 10 秒,分别存成
.wav(PCM 无损)和.mp3(压缩),对比两个文件大小,体会「压缩」省了多少。 - 找「双胞胎」:把
libobs/media-io/audio-io.h和上节课的libobs/media-io/video-io.h并排打开,找出至少三对结构 / 字段对应关系(提示:*_output_info、*_data、timestamp)。
# 下一课预告
视频(第 2–3 课)和音频(本课)的「原材料」都讲完了。下一课,我们把这条「模块 1」收个尾,直面那个反复出现的结论 ——
第 5 课:裸数据有多可怕 —— 我们会把视频和音频的裸数据体积摆在一起算总账,看清「不压缩,直播在物理上根本不成立」,从而为模块 5 的主角「编码」,做一个有冲击力的铺垫。
📁 本课配图:
imgs/04-01~imgs/04-06📌 源码锚点:libobs/media-io/audio-io.h:43(audio_format)、:66(speaker_layout)、:77(audio_data)、:90(audio_output_info)、:108(get_audio_channels)、frontend/widgets/OBSBasic.cpp:855(默认采样率 48000)
本留言区仅对应当前文章,欢迎补充观点、提出问题或帮助修正文中疏漏。 留言由 GitHub/Gitalk 提供,需要使用 GitHub 登录。
社区交流
讨论与留言