<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Multimedia on zed的博客</title><link>https://www.dust-zed.site/tags/multimedia/</link><description>Recent content in Multimedia on zed的博客</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><lastBuildDate>Fri, 14 Aug 2026 00:00:00 +0800</lastBuildDate><atom:link href="https://www.dust-zed.site/tags/multimedia/index.xml" rel="self" type="application/rss+xml"/><item><title>PCM 和 WAV 入门学习记录</title><link>https://www.dust-zed.site/computer-science/audio/pcm-and-wav-basics/</link><pubDate>Fri, 14 Aug 2026 00:00:00 +0800</pubDate><guid>https://www.dust-zed.site/computer-science/audio/pcm-and-wav-basics/</guid><description>&lt;h2 id="pcm-是原始采样数据">PCM 是原始采样数据
&lt;/h2>&lt;p>PCM可以理解为一串按时间排列的采样值。&lt;/p>
&lt;p>每个sample表示某一个时间点的瞬时振幅：&lt;/p>
&lt;pre tabindex="0">&lt;code>0, 1000, 2000, 1000, 0, -1000, -2000
&lt;/code>&lt;/pre>&lt;p>PCM本身不是波形图。波形图是把这些采样值画出来之后得到的可视化结果。&lt;/p>
&lt;pre tabindex="0">&lt;code>PCM 数据 -&amp;gt; 可视化 -&amp;gt; 波形图
&lt;/code>&lt;/pre>&lt;p>一次采样只是一个点，很多连续采样点才构成一段波形。&lt;/p>
&lt;h3 id="sample-和-frame">sample 和 frame
&lt;/h3>&lt;p>音频里有两个很重要的概念：&lt;/p>
&lt;pre tabindex="0">&lt;code>sample = 单个声道的一次采样值
frame = 同一时刻所有声道 sample 的组合
&lt;/code>&lt;/pre>&lt;p>单声道 PCM:&lt;/p>
&lt;pre tabindex="0">&lt;code>S0 S1 S2 S3
&lt;/code>&lt;/pre>&lt;p>双声道 interleaved PCM:&lt;/p>
&lt;pre tabindex="0">&lt;code>L0 R0 L1 R1 L2 R2 ...
&lt;/code>&lt;/pre>&lt;p>所以生成双声道 PCM 时，每一帧都要写入左右两个声道的数据。&lt;/p>
&lt;p>这里容易犯的错误是： 把&lt;code>frame_index&lt;/code>的奇偶当成左右声道。&lt;/p>
&lt;p>实际上&lt;code>frame_index&lt;/code>表示第几帧，不表示第几个声道。&lt;/p>
&lt;p>正确理解应该是:&lt;/p>
&lt;pre tabindex="0">&lt;code>第 0 帧: L0 R0
第 1 帧: L1 R1
第 2 帧: L2 R2
&lt;/code>&lt;/pre>&lt;p>如果要做“左声道有声音， 右声道静音”， 每一帧都应该写：&lt;/p>
&lt;pre tabindex="0">&lt;code>left_sample +0
&lt;/code>&lt;/pre>&lt;p>而不是偶数帧写左声道、奇数帧写右声道。&lt;/p>
&lt;h3 id="正弦波公式">正弦波公式
&lt;/h3>&lt;p>生成正弦波时用到：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-rust" data-lang="rust">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sample&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">t&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">frequency_hz&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">std&lt;/span>::&lt;span class="kt">f32&lt;/span>::&lt;span class="n">consts&lt;/span>::&lt;span class="no">TAU&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="n">sin&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>其中:&lt;/p>
&lt;pre tabindex="0">&lt;code>t = 当前时间
frequency_hz = 每秒振动多少次
TAU = 2π
&lt;/code>&lt;/pre>&lt;p>正弦函数接收的是弧度，不是秒。&lt;/p>
&lt;p>所以：&lt;/p>
&lt;pre tabindex="0">&lt;code>f * t = 当前时间已经走过多少个周期
2π * f * t = 把周期数转换成弧度
sin(...) = 当前时刻的振幅
&lt;/code>&lt;/pre>&lt;p>比如 440Hz 表示每秒 440 个周期。&lt;/p>
&lt;p>1 秒就是走过了 440 个周期。&lt;/p>
&lt;h3 id="振幅和峰值">振幅和峰值
&lt;/h3>&lt;p>PCM里的每个 sample 都是一个瞬时振幅值，它会随着时间变化。&lt;/p>
&lt;p>代码里的&lt;code>amplitude&lt;/code>更像是最大振幅比例：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-rust" data-lang="rust">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sample&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sin_value&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">amplitude&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">i16&lt;/span>::&lt;span class="no">MAX&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">as&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">f32&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>如果：&lt;/p>
&lt;pre tabindex="0">&lt;code>amplitude = 0.2
&lt;/code>&lt;/pre>&lt;p>那么 S16 PCM 的峰值大约是：&lt;/p>
&lt;pre tabindex="0">&lt;code>0.2 * 32767 ≈ 6553
&lt;/code>&lt;/pre>&lt;p>所以对于一段纯正弦波：&lt;/p>
&lt;pre tabindex="0">&lt;code>每个采样值在变化
峰值范围基本固定
&lt;/code>&lt;/pre>&lt;h3 id="wav-是-pcm-的包装">WAV 是 PCM 的包装
&lt;/h3>&lt;p>裸 PCM 只有数据，没有格式说明。播放器不知道它的采样率、声道数、位深，也不知道该如何解释这些字节。&lt;/p>
&lt;p>WAV 可以先理解为：&lt;/p>
&lt;pre tabindex="0">&lt;code>WAV = header 元数据 + PCM 数据
&lt;/code>&lt;/pre>&lt;p>更通用地说：&lt;/p>
&lt;pre tabindex="0">&lt;code>音频文件 = 容器文件 + 编码后的音频数据
&lt;/code>&lt;/pre>&lt;p>常见情况：&lt;/p>
&lt;pre tabindex="0">&lt;code>WAV 通常是 RIFF/WAVE 容器 + PCM
MP3 是压缩音频数据
M4A 通常是 MP4 容器 + AAC/ALAC
FLAC 是无损压缩音频数据
&lt;/code>&lt;/pre>&lt;p>最简单的 44 字节 PCM WAV:&lt;/p>
&lt;pre tabindex="0">&lt;code>RIFF
WAVE
fmt
data
&lt;/code>&lt;/pre>&lt;h3 id="peak-和-rms">peak 和 RMS
&lt;/h3>&lt;p>读回 PCM 后，可以计算音量信息：&lt;/p>
&lt;pre tabindex="0">&lt;code>peak = 最大瞬时振幅
RMS = 能量意义上的平均音量
&lt;/code>&lt;/pre></description></item></channel></rss>