🎬 第十章 SWResample音频重采样理论及案例实战
📄 一句话概括

重采样器不只管采样率,它也是「换存法」的官方工具。 采样率一个没改、样本一个没少,只把摆法换了。

📝 本节和前面几节的关系

10.2.2 讲了两种摆法的差别,10.3.2 讲了转采样率,这一节把两件事合起来。 这是第十章最后一节。

一、只换存法,别的都不动

s16 packed 转 s16p planar,参数只改一个字母。 项 进 出


样本格式 s16 s16p 存法 packed planar shape (1, 32) (2, 16) 平面数 1 2 字节 64 64

采样率没改、样本数没少、字节数没变,只把 32 个数字换了位置。所以这一步几乎不花时间。 名字上的差别只有一个 p,但数组形状从一维变成二维——代码里读法要跟着改,否则会读错声道。

二、真实音轨:planar 浮点 → packed 整数

这是最常见的实际需求。 项 进 出


格式 fltp s16 存法 planar packed shape (2, 1024) (1, 2048) 值域 −1.0 到 1.0 −32768 到 32767

解码器给 fltp,落盘交付要 s16,这一步就是靠重采样器做的。 它顺手把精度也换了(浮点转整数,带舍入与限幅)。 所以「转存法」这一步往往顺手就把精度也转了, 两件事在同一次调用里完成,不需要中间再折腾一次。

三、为什么内部偏爱 planar

三条理由,都是性能层面的。 理由 说明


编码器按声道算 声道分开存,不用来回拆 滤波按声道做 内存连续,预取有效 混音矩阵好写 每个声道一个指针

这是「内部用 planar、对外给 packed」的分工。解码器给 planar、滤镜要 planar、 编码器内部按 planar 算;而落盘成 WAV 或裸 PCM、送硬件,多是 packed。 所以真实链路里几乎一定有一次转换,只是它通常藏在你调的某个函数肚子里,你看不见。

四、不转存法会怎样

不转不会崩,但会读错。 场合 后果


按 packed 读 planar 读到的是两个声道拼一起的错位数据 按 planar 读 packed 第二个平面越界或为空 送编码器时格式不对 可能报错,也可能悄悄降质

第一行的表现是「声音变闷或有回声」,因为左右声道错开了几个样本。 这种错很难联想到「存法搞错了」,所以排查时容易走远。 最省事的排查动作:在链的每一段打印 format.name 与 shape, 两头一对比,就知道是哪一步换的。

五、第十章的账

四句话结掉这一章。 记住的 一句话


时间怎么算 样本数除以采样率,没有第二个公式 两种摆法 packed 按时间、planar 按声道 重采样要冲空包 不冲就丢几毫秒,还不报错 它不只转采样率 格式、布局、采样率三样一起转

四句里有两句是「不报错」的事:冲空包、格式被改。 音频的坑大多属于这一类——听不出、看不见、也不报。 第十一章换到画面那头的「换尺寸与换颜色」,要处理的还是格式与刻度,只是从一维变成二维。

六、动手 Demo:顺带把 packed 转成 planar

先只改存法不改采样率,再把真实音轨从 planar 浮点转成 packed 整数。

import av

# 只换存法:s16 packed -> s16p planar,采样率不动
f = av.AudioFrame(format="s16", layout="stereo", samples=16)
f.sample_rate = 8000
r = av.AudioResampler(format="s16p", layout="stereo", rate=8000)
o = r.resample(f)[0]
print(f.format.name, f.to_ndarray().shape, len(f.planes))
print(o.format.name, o.to_ndarray().shape, len(o.planes))
print(f.to_ndarray().nbytes, o.to_ndarray().nbytes)

# 真实音轨:fltp planar -> s16 packed
c = av.open("tiny.mp4")
st = c.streams.audio[0]
for pk in c.demux(st):
    if pk.size == 0:
        break
    fs = st.codec_context.decode(pk)
    if not fs:
        continue
    src = fs[0]
    r2 = av.AudioResampler(format="s16", layout="stereo", rate=44100)
    got = list(r2.resample(src)) + list(r2.resample(None))
    g = got[0]
    print(src.format.name, src.to_ndarray().shape)
    print(g.format.name, g.to_ndarray().shape)
    break
c.close()

实跑输出:

--------------------------------------------------------------------
顺带把 packed 转成 planar
--------------------------------------------------------------------

**① 重采样器不只管采样率**

输入:s16(packed)立体声,shape (1, 32)

  项           进          出
  ------------------------------------
  样本格式     s16         s16p
  存法         packed      planar
  shape        (1, 32)     (2, 16)
  平面数       1           2
  字节         64          64

**采样率一个没改、样本数一个没少,只把摆法换了。**
**所以「重采样器」这个名字其实窄了**,它是音频格式转换器。

**② 真实音轨:planar 浮点 → packed 整数**

  项        进              出
  ---------------------------------------------
  格式      fltp            s16
  存法      planar          packed
  shape     (2, 1024)       (1, 2048)
  值域      −1.0 到 1.0     −32768 到 32767

**解码器给的是 fltp,编码器往往要 s16**,中间这一步就是靠重采样器做的。
**它顺手把精度也换了**(浮点转整数,带舍入与限幅)。

**③ 为什么内部偏爱 planar,交货却给 packed**

  理由                      说明
  ----------------------------------------------------
  AAC/MP3 内部按声道算     声道分开存,不用来回拆
  滤波按声道做              一块内存连续,缓存友好
  混音矩阵好写              每个声道一个指针

**这是「内部用 planar、对外给 packed」的分工。**解码器给 planar、滤镜要 planar、
编码器内部也按 planar 算;**而落盘成交付文件(WAV 与裸 PCM)与送硬件,多是 packed。**

**所以真实链路里几乎一定有一次「planar→packed」或反过来**:
**它不在你写的代码里,而在某个封装函数的肚子里。**

**④ 这一章的账**

  记住的                 一句话
  -----------------------------------------------------------
  时间怎么算             样本数除以采样率,没有第二个公式
  两种摆法               packed 按时间、planar 按声道
  重采样要冲空包         不冲就丢几毫秒,还不报错
  它其实不只转采样率     格式、布局、采样率三样一起转

**四句话结完账。** 第十一章换到画面那头的「换尺寸与换颜色」,
**处理的还是格式与刻度,只是从一维变成二维。**

采样率没动、字节数一样(64 对 64),只是 shape 从 (1,32) 变 (2,16)。 真实音轨是 fltp → s16,它顺手把精度也换了。

30 秒实验

  1. 把目标改成 format="s16p"(同精度换存法),看真实音轨那一步的输出 shape。
  2. 把目标 layout 改成 mono,看字节数变成几分之一。
  3. 对比转前转后的时长,确认没变(提示:samples / rate)。

常见误区

  1. 以为换存法会损失音质。 字节原样搬,只是位置变了。
  2. 按 packed 的形状读 planar。 读到的是错位数据,听感是回声。
  3. 手工 reshape 转存法却忘了改精度。 该转的时候还是让库转。
自测三问(点开对答案) 1. 只改存法时,哪三样东西不变? —— 采样率、样本数、字节数。 2. 真实音轨解码后是什么存法? —— planar(fltp),而落盘交付多用 packed s16。 3. 按 packed 读 planar 会听到什么? —— 声音变闷或有回声,因为两个声道错位了。

相关笔记