【音效处理】Reverb 混响算法简介

2023-11-08

系列文章目录



一、混响

混响是一种自然发生的声学现象。在房间中放置一个扬声器用于发声,放置一个麦克风用于收集声音。当声音与墙壁或者其他材料相遇时,声音发生反射,因此麦克风收集到的信号,除了扬声器到麦克风的径直路径外,还有很多其他方式到达的声音,如下图所示。

在这里插入图片描述
由于声波的传播距离稍长,反射的声波到达我们耳朵的时间会比直达的声波晚一点。声波的振幅会弱一点,因为墙面会吸收一些声音的能量。根据房间的大小,时间和振幅的差异是不同的。这就是为什么我们可以在音乐厅里比在客厅里更容易分辨出反射效果。

在一个足够大的房间里,反射会重复许多次,然后一系列延迟和衰减的声波,这被称为回声,到达我们的耳朵。这就是我们如何感受到一个房间的“空间感”

二、人工混响

对人工混响的需求首先出现在录音广播和音乐背景下,在录音室录制的声音往往过于 “干” 了,缺乏音乐表演所需的音乐厅声学效果。早在 20 世纪 20 年代,混响是通过将 “干” 的录音室信号发送到一个混响环境中,通常是一个专门建造的回音室,来人为地制造混响。人们在浴室唱歌时会感觉更有效果,浴室、卫生间等可以认为就是一种回音室。

虽然回音室可以产生高质量的混响,但它们的物理性质通常限制了它们在录音室和广播场合的使用。它们通常很难甚至不可能运输,对外部的声学或机械干扰很敏感,并且需要专门的知识来维护和调音。相比之下,由计算过程产生的人工混响具有便利性、便携性和可重复性,并允许自动化(记录、编辑和回放混响器控制参数随时间变化的情况)。这促使人们研究数字合成高质量混响的方法。

三、数字混响算法

由于数字处理设备和计算机技术的发展,人们能够完成复杂的模拟,找到自然产生混响的数字解决方案。这个混响问题的最早的数字解决方案是由贝尔电话实验室的Manfred Schroeder在1961年建立的(Schroeder)。这个解决方案被称为 Schroeder 的混响器,它是本文的重点,将在下面的章节中详细说明。

3.1 混响的脉冲响应信号

为了研究混响器的工作原理,我们首先要了解声音在房间中的发射性质。
在这里插入图片描述
上图是某个房间的脉冲响应信号,从图中可以观察到:

  1. 直达声以直线路径传播,是第一个到达听众耳朵的声波。
  2. 离散的早期反射在直达声到达后 80ms 内进入听众耳朵。
  3. 接下来的后期混响包含了成千上万的紧密间隔的回声,但需要超过80毫秒的时间来建立,然后逐渐消失。

下图是两个真实环境的脉冲响应:
在这里插入图片描述
总的来说,reverb 信号可以分为三个部分:pre-delay、early reflections 和 late reverberation,因此一种可行 reverb 模型为:
在这里插入图片描述

3.2 RT60

不同的空间、场所有着不同的混响,如何区分混响的不同呢?有一个重要的指标:混响时间(Reverberation Time),也叫 RT60,具体的定义是指声场衰减 60 dB 所用的时间,单位为「秒」。通俗的说就是,你在一个房间里,“啊”的大叫了一声,这一声“啊”的分贝衰减了 60db 所花费的时间。

有些研究给出了 RT60 估计模型,例如在公式【1】
R T 60 = 0.5 V R S R A R A v e (1) R T_{60}=0.5 \frac{V_{R}}{S_{R} A_{R A v e}} \tag{1} RT60=0.5SRARAveVR(1)
其中

  • V R V_R VR 表示房间体积(立方英寸)
  • S R S_R SR 表示房间表面积(平方英寸)
  • A R A v e A_{R A v e} ARAve 表示平均吸收系数

不同的材料有着不同的吸收系数,例如墙面、瓷砖、沙发、床等都有着不同吸收系数,你需要单独计算这些物品的面积,加权求平均得到平均吸收系数。

几乎所有的混响插件都会有混响时间的参数控制,我们在分析各类算法时,重要的一点是知道哪些参数控制这混响时间。

3.3 Schroeder 混响算法

现在让我们回到 1960s,跟着 Schroeder 的论文来窥探当时是如何创造出第一个混响算法的,相关论文有两篇:

  1. “Colorless” Artificial Reverberation - 1961
  2. Natural Sounding Artificial Reverberation - 1962

3.3.1 梳妆滤波(Comb Filter)混响器

作者首先想 “咦,这个混响不就是声音在空间中不断的碰撞,产生很多回声的过程吗?那我搞一个系统让它产生很多很多回声就可以了”,于是乎,作者发明了第一个混响器:梳妆滤波混响器。浅浅地用公式描述下推导过程。假定 h ( t ) h(t) h(t) 是这个系统的单位冲击响应,如果只有一个回声的情况,那么:
h ( t ) = δ ( t − τ ) h(t) = \delta(t - \tau) h(t)=δ(tτ)
其中 δ ( t ) \delta(t) δ(t) 为狄拉克函数(即理想的冲击信号), τ \tau τ 为回声的延迟时间。 h ( t ) h(t) h(t) 的频谱长啥样呢?我们对它做傅里叶变换得到:
H ( ω ) = ∫ − ∞ + ∞ h ( t ) e − i ω t d t = e − i ω τ H(\omega)=\int_{-\infty}^{+\infty} h(t) e^{-i \omega t}d t= e^{-i\omega\tau} H(ω)=+h(t)eiωtdt=eiωτ

那么多个回声,且回声音量以指数函数衰减的 h ( t ) h(t) h(t) 为:
h ( t ) = δ ( t − τ ) + g δ ( t − 2 τ ) + g 2 δ ( t − 3 τ ) + ⋯ h(t)=\delta(t-\tau)+g \delta(t-2 \tau)+g^{2} \delta(t-3 \tau)+\cdots h(t)=δ(tτ)+gδ(t2τ)+g2δ(t3τ)+
其中 g g g 表示衰减系数,对上述的 h ( t ) h(t) h(t) 做傅里叶变换得到:
H ( ω ) = e − i ω τ + g e − 2 i ω τ + g 2 e − 3 i ω τ + ⋯ H(\omega)=e^{-i \omega \tau}+g e^{-2 i \omega \tau}+g^{2} e^{-3 i \omega \tau}+\cdots H(ω)=eiωτ+ge2iωτ+g2e3iωτ+
简易起见,我们令 a = e − i ω τ a = e^{-i \omega \tau} a=eiωτ,那么:
H ( ω ) = a + g a 2 + g 2 a 3 + ⋯ g H ( ω ) = g a + g 2 a 2 + g 3 a 3 + ⋯ \begin{aligned} H(\omega) &= a + ga^2 + g^2a^3 + \cdots \\ gH(\omega) &= ga + g^2a^2 + g^3a^3 + \cdots \\ \end{aligned} H(ω)gH(ω)=a+ga2+g2a3+=ga+g2a2+g3a3+
你看, g H ( ω ) gH(\omega) gH(ω) 是一个等比数列,我们用等比数列求和公式对 g H ( ω ) gH(\omega) gH(ω) 求和可得:
g H ( ω ) = g a 1 − g a gH(\omega) = \frac{ga}{1-ga} gH(ω)=1gaga
最后两边除以 g g g 得:
H ( ω ) = a 1 − g a = e − i ω τ 1 − g e − i ω τ (2) H(\omega) = \frac{a}{1-ga} = \frac{e^{-i \omega \tau}}{1-ge^{-i \omega \tau}} \tag{2} H(ω)=1gaa=1geiωτeiωτ(2)

H ( ω ) H(\omega) H(ω) 就是该系统的传递函数,我们计算它的频谱响应来观察该系统对频率的影响:
∣ H ( ω ) ∣ 2 = ∣ e − i ω τ ∣ 2 ∣ 1 − g e − i ω τ ∣ 2 = ∣ cos ⁡ ( ω τ ) − i sin ⁡ ( ω τ ) ∣ 2 ∣ 1 − g ( cos ⁡ ( ω τ ) − i sin ⁡ ( ω τ ) ) ∣ 2 = 1 1 + g 2 − 2 g cos ⁡ ω τ . \begin{aligned} |H(\omega)|^{2} &= \frac{|e^{-i \omega \tau}|^2}{|1-ge^{-i \omega \tau}|^2} \\ &= \frac{|\cos(\omega \tau) -i\sin(\omega\tau)|^2}{|1 - g(\cos(\omega\tau) -i\sin(\omega\tau))|^2} \\ &=\frac{1}{1+g^{2}-2 g \cos \omega \tau} .\\ \end{aligned} H(ω)2=1geiωτ2eiωτ2=1g(cos(ωτ)isin(ωτ))2cos(ωτ)isin(ωτ)2=1+g22gcosωτ1.

ω = 2 n π / τ \omega=2 n \pi / \tau ω=2nπ/τ 时, ∣ H ( ω ) ∣ 2 |H(\omega)|^{2} H(ω)2 有最大值:
H max ⁡ = 1 1 − g H_{\max }=\frac{1}{1-g} Hmax=1g1
ω = ( 2 n + 1 ) π / τ \omega=(2n+1) \pi / \tau ω=(2n+1)π/τ 时, ∣ H ( ω ) ∣ 2 |H(\omega)|^{2} H(ω)2 有最小值:
H min ⁡ = 1 1 + g H_{\min }=\frac{1}{1+g} Hmin=1+g1

∣ H ( ω ) ∣ 2 |H(\omega)|^{2} H(ω)2整体呈现一种周期性,像是梳子一样,因此名为 “梳妆滤波器”,画图的话长这样:
在这里插入图片描述
那么在具体代码中如何实现梳妆滤波混响器呢?我们现在从公式(2)中知道了转换函数,Z变换的逆变换,令 z = e i ω z=e^{i\omega} z=eiω 有:
H ( z ) = Y ( z ) X ( z ) = z − τ 1 − g z − τ Y ( z ) − g z − τ Y ( z ) = X ( z ) z − τ y ( t ) = x ( t − τ ) + g y ( t − τ ) \begin{aligned} H(z) = \frac{Y(z)}{X(z)} = \frac{z^{-\tau}}{1-g z^{-\tau}} \\ \\ Y(z) - g z^{-\tau}Y(z) = X(z)z^{-\tau} \\ \\ y(t) = x(t - \tau) + g y(t -\tau) \end{aligned} H(z)=X(z)Y(z)=1gzτzτY(z)gzτY(z)=X(z)zτy(t)=x(tτ)+gy(tτ)

根据 y ( t ) y(t) y(t) 就能很容易得到块状图了:
在这里插入图片描述

3.3.2 全通滤波(All-Pass Filter,APF)混响器

Schroeder 还发明了全通滤波(All-Pass Filter,APF)混响器,它不会对频率产生任何影响,其频谱响应是一条笔直的线。它的冲击响应信号为:
h ( t ) = − g δ ( t ) + ( 1 − g 2 ) ⋅ [ δ ( t − τ ) + g δ ( t − 2 τ ) + ⋯   ] \begin{aligned} h(t)=&-g \delta(t)+\left(1-g^{2}\right) \\ & \cdot[\delta(t-\tau)+g \delta(t-2 \tau)+\cdots] \end{aligned} h(t)=gδ(t)+(1g2)[δ(tτ)+gδ(t2τ)+]
h ( t ) h(t) h(t) 做傅里叶变换得:
H ( ω ) = e − i ω τ − g 1 − g e − i ω τ H(\omega)=\frac{e^{-i \omega \tau}-g}{1-g e^{-i \omega \tau}} H(ω)=1geiωτeiωτg

H ( ω ) H(\omega) H(ω) 求频谱响应可以发现它居然神奇的等于 1,即 ∣ H ( ω ) ∣ 2 = 1 |H(\omega)|^2 = 1 H(ω)2=1

与梳妆滤波器类似,我们通过 z 变换的逆变换得到 y ( t ) y(t) y(t) 的差分方程为:
y ( t ) = − g x ( t ) + x ( t − τ ) + g y ( t − τ ) y(t) = -gx(t) + x(t - \tau) + gy(t - \tau) y(t)=gx(t)+x(tτ)+gy(tτ)
y ( t ) y(t) y(t)的一种块状图实现方案为:
在这里插入图片描述
其中 D = τ ∗ s r D = \tau * sr D=τsr s r sr sr 为采样率。

Schroeder 混响算法

现在我们有了制造回声的梳妆滤波混响器和全通滤波混响器,那么我们应该如何安排这些混响器以制造更加真实的混响效果呢?

首先,我们考虑一个问题:我们到底要制造多少回声?是每秒1000 个?还是每秒10000 个?为了回答这个问题,先定义一个叫 “回声密度” 概念,即每秒有几个回声,Schroeder 认为每秒至少需要 1000 个回声,为了得到高密度的回声,他将 4 个 Comb Filter 先并列起来,且每个 Comb Filter 的延迟 D D D 是不同的:
在这里插入图片描述
为了更模拟更真实的混响,Kahrs 提出了一种根据 RT60 设置 Comb Filter 中 g 的值,即:
R T 60 = 3 D T s log ⁡ ( 1 / g ) 1 / g = 1 0 3 D T s R T 60 g = 1 0 − 3 D T s R T 60 (3) \begin{aligned} R T_{60}=\frac{3 D T_{s}}{\log (1 / g)} \\ 1 / g=10^{\frac{3 D T_{s}}{R T_{60}}} \\ g=10^{\frac{-3 D T_{s}}{R T_{60}}} \\ \end{aligned} \tag{3} RT60=log(1/g)3DTs1/g=10RT603DTsg=10RT603DTs(3)
其中 T s T_s Ts 为采样率,根据 Comb Filter 的 D 和 RT60 的值,我们就能算出 g 的值。

我们利用公式(3)对 4 个并行的 Comb Filter 设置不同的 g,此外每隔一个 comb filter,对结果乘上 -1 这样就能得到有正有负的回声,这样真实多了:
在这里插入图片描述

Schroeder 混响算法最终的结构如下图,有 4 个 Comb Filter 后接 2 个 All-pass Filter。前面四个 Comb Filter 就像在做加法,叠加回声的数量,后面的 All-pass Filter 就像在做乘法,进一步增加回声的数量。
在这里插入图片描述
其中,Comb Filter 有以下特性:

  • 选择延迟时间的比例为1:1.5。
  • 选择没有公因数或除数的延迟时间。例如 31、37、41 等
  • 根据公式(3)设置 g 值

All-Pass Filter 具有以下特性。

  • 选择比梳状滤波器短得多的延时。1mSec到5mSec。
  • 设置两个增益值相同:0.5和0.707之间。

Schroeder 混响算法非常简单,但效果也还不错,以现在的视角来看它有很多可以改进的点,包括:

  • 增加更多的平行的 Comb Filter。
  • 增加预梳理APF(两个在 APF 的输入端,两个 APF 在输出端)。
  • 将两个 APF 改为嵌套形式的 APF
  • 增加一个预延时模块

总之,Schroeder 第一次提出了对混响算法的实现方案,开启了人们对混响算法的更多研究,后续很多知名的混响算法都是基于 Schroeder 结构进行的改进,例如 Freeverb 它算法的主要实现逻辑代码如下,就是 Comb Filter + APF 的组合

void revmodel::processreplace(float *inputL, float *inputR, 
  float *outputL, float *outputR, long numsamples, int skip)
{
  float outL,outR,input;
  int i;

  while(numsamples-- > 0)
    {
      outL = outR = 0;
      input = (*inputL + *inputR) * gain;

      // Accumulate comb filters in parallel
      for(i=0; i<numcombs; i++) {
        outL += combL[i].process(input);
        outR += combR[i].process(input);
      }

      // Feed through allpasses in series
      for(i=0; i<numallpasses; i++) {
        outL = allpassL[i].process(outL);
        outR = allpassR[i].process(outR);
      }

      // Calculate output REPLACING anything already there
      *outputL = outL*wet1 + outR*wet2 + *inputL*dry;
      *outputR = outR*wet1 + outL*wet2 + *inputR*dry;

      // Increment sample pointers, allowing for interleave 
      // (if any)
      inputL += skip; // For stereo buffers, skip = 2
      inputR += skip;
      outputL += skip;
      outputR += skip;
    }
}

下面的视频是 freeverb 的算法效果展示:

freeverb_video

总结

这篇文章中,我们介绍了混响的声学现象,以及数字混响的需求背景,重点介绍了 Schroeder 混响算法的实现。

参考

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【音效处理】Reverb 混响算法简介 的相关文章

随机推荐

  • 万能密码原理和总结

    我们所常说的 or or 万能密码的原理是这样的 SQL语句sql select from user where username username and pass pass 当我们用户名和密码都填写 or or 提交的时候 即此时语句中
  • Kubernetes中的etcd访问

    前言 Kubernetes中的etcd访问 正常安装了k8s 没有特意去安装etcd 利用K8s中附带的etcd 感受一下etcd的读写操作 提示 以下是本篇文章正文内容 下面案例可供参考 一 etcd是什么 etcd是一个分布式的key
  • UE4换装系统(合并骨骼模型)

    前面那篇UE4换装系统https blog csdn net luomogenhaoqi article details 88350580 事实上每个身体模型还是各自渲染 现在介绍把每个身体模型合并输出一个模型 把Lod 材质 网格等合并
  • 软件工程第二版(判断题答案)

    判断题 第一章 软件就是程序 编写软件就是编写程序 软件危机的主要表现是软件需求增加 软件价格上升 软件工程学科出现的主要原因是软件危机的出现 软件工具的作用是为了延长软件产品的寿命 第二章 瀑布模型的最大优点是将软件开发的各个阶段划分得十
  • Notepad++ 配置 支持jquery、html、css、javascript、php代码提示

    官网下载 http notepad plus plus org 获取插件的方法 打开软件 窗口工具栏有有一个问号 点获取插件 我使用的插件 安装方法都是官方的方法 QuickText v0 2 1 zip 自定义缩写词 按快捷键后输出 定义
  • Java网络编程Socket(使用字节流)

    套接字 Socket 开发网络应用程序被广泛采用 以至于成为事实上的标准 Socket通信原理 通信的两端都要有Socket 是两台机器间通信的端点 网络通信其实就是Socket间的通信 Socket允许程序把网络连接当成一个流 数据在两个
  • java private 构造函数_JAVA private私有类的 默认构造函数 的生成过程

    如果一个类没有定义任何构造函数 则编译器将生成一个缺省的构造函数 该构造函数的访问修改符和类的访问修改符相同 例如 class test将生成test 构造函数 public class test将生成public test 构造函数 在使
  • JPA使用雪花算法生成主键ID

    实现方式 通过 GenericGenerator注解自定义主键生成策略 需要实现org hibernate id IdentifierGenerator接口 根据官网例子进行改造 官网链接 https docs jboss org hibe
  • Qt中多线程的使用(二)

    线程池 当线程的任务量比较大时 频繁创建和销毁线程会有很大的内存开销 此时使用QThread的方法就不合适 应该使用线程池QThreadPool QThread适用于常驻内存的任务 QThreadPool适用于不常驻内存 任务量比较大的情况
  • Element-UI官方文档阅读笔记(VUE)—持续更新中····

    前言 本人前端新手一枚 目前工作中接触Element UI较多 但其中很多组件布局什么的都不是很清楚 所以想稍微花点时间简单过一遍Element UI官方文档 并作以记录 其中有什么不对的地方 还请各位路过的大佬不吝赐教 以下内容按elem
  • Hive建表实例——定义serdeproperties属性

    创建table时 直接定义serdeproperties属性 create table wzhg c0 string c1 string c2 string row format serde org apache hadoop hive c
  • 代理模式 【设计模式之禅作者】

    代理模式 12 1 我是游戏至尊 2007年 感觉很无聊 于是就玩了一段时间的网络游戏 游戏名就不说了 要不就有做广告的嫌疑 反正就是打怪 升级 砍人 被人砍 然后继续打怪 升级 打怪 升级 我花了两个月的时间升级到80级 已经很有成就感了
  • Leetcode每日一题:57. 插入区间

    原题 给你一个 无重叠的 按照区间起始端点排序的区间列表 在列表中插入一个新的区间 你需要确保列表中的区间仍然有序且不重叠 如果有必要的话 可以合并区间 示例 1 输入 intervals 1 3 6 9 newInterval 2 5 输
  • 00000000000000000000.timeindex.swap: 另一个程序正在使用此文件,进程无法访问(kafka)

    产生此问题的原因 在window下使用kafka导致 在linux下使用kafka没有此问题 window下kafka报错 linux下kafka正常
  • 文本挖掘学习笔记(二):文档信息向量化与主题关键词提取

    注 学习笔记基于文彤老师文本挖掘的系列课程 全文基于 射雕英雄传 语料库 下面是读入数据的一个基于Pandas的通用操作框架 读入为数据框 import pandas as pd from matplotlib import pyplot
  • element表格复选框,弹框关闭取消选择

    弹框表格复选框清空 this nextTick gt this refs tabledata clearSelection
  • 面向工业物联网的拍赫兹通信

    摘要 相比于传统无线射频通信 拍赫兹通信 PetaCom petahertz communication 具有高速率 低时延和高确定性等显著优势 在工业物联网 IIoT industrial internet of things 中可以发挥
  • [1178]数据库like和rlike区别

    like 通配符 使用时需指定具体值 如 用like筛选某张表姓张的人全部信息 或名字叫张三的信息 张或张三就必须写为具体值 sql语法的 模糊匹配 通配符 代表零个或任意字符 代表1个字符 rlike 正则 模糊查询 区间范围判断 如 用
  • 链表问题处理

    今天主要是对一些链表相关问题的理解和处理 下面所有问题处理的都是这种链表 struct ListNode int val struct ListNode next 话不多说 我直接进入正题 1 删除链表中等于给定值 val 的所有节点 给你
  • 【音效处理】Reverb 混响算法简介

    系列文章目录 Delay Line 简介及其 C C 实现 LFO 低频振荡器简介及其 C C 实现 音效处理 Delay Echo 简介 音效处理 Vibrato 简介 文章目录 系列文章目录 一 混响 二 人工混响 三 数字混响算法 3