聊聊音频信号处理中一个不太起眼的算法-limiter

这篇具有很好参考价值的文章主要介绍了聊聊音频信号处理中一个不太起眼的算法-limiter。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

本文对笔者关于音频信号处理中的 Limiter 的理解作以记录。如有表述不当之处欢迎批评指正。欢迎任何形式的转载,但请务必注明出处。

1. 引言

由于工作上的需要,笔者花了一周左右的时间对 limiter(它属于动态范围控制器里面的一种算法,动态范围控制器包括 compressor, expander, limiternoise gate 等,感兴趣的读者可参考笔者的另一篇博客)进行了研究学习。期间也阅读了不少资料,对 limiter 算是有了较深入地了解。在此对整个学习过程、笔者的感悟以及算法背后所蕴含的思想作以记录,以方便后续回顾和他人学习。

相比于音频信号处理中的其它算法,limiter 是一个存在感较低的算法,各种资料上所呈现出的原理和实现过程也相对简单,基本一看就懂。但是按照该原理所实现的 limiter 仍然需要接后处理才能满足实际需要。

本文首先介绍了 Matlab 上的实现,笔者称之为一阶递归平滑版本的 limiter,该版本也是上面所说的大多数资料上呈现的版本;然后介绍了 FFMPEG 里面的实现,笔者称之为逐采样点过渡平滑的 limiter。两个版本各有优缺点,可根据具体应用的需要选择相应的版本。

2. Limiter 的主要作用

在音频信号处理中,通常的做法是先将音频采样点归一化到 [ − 1.0 , 1.0 ] [-1.0,1.0] [1.0,1.0],然后再对其施加各种各样的音频算法。然而在算法处理过程中可能会出现某些音频采样点的幅度超过 1 1 1 的情况。或者在将多路音频流混合成一路音频流的时候,采样点相加的过程中也有可能出现幅度超过 1 1 1 的情况。而使用 limiter 的主要目的就是在尽量不变动其它采样点的情况下,将这些采样点的幅度全部限制在 1 1 1 以内,以避免削波。

3. 简单粗暴做法

考虑下面的采样点序列 x ( n ) , n = 1 , 2 , ⋯   , 9 , 10 x(n), n=1,2,\cdots,9,10 x(n),n=1,2,,9,10
   0.990 ,    0.995 ,    0.997 ,    0.999 ,    1.010 ,    1.005 ,    0.998 ,    0.997 ,    0.995 ,    0.992 \; 0.990, \; 0.995, \; 0.997, \; 0.999, \; 1.010, \; 1.005, \; 0.998, \; 0.997, \; 0.995, \; 0.992 0.990,0.995,0.997,0.999,1.010,1.005,0.998,0.997,0.995,0.992

可以看到中间有两个采样点的幅度是超过 1 1 1 的,而最简单粗暴的做法就是令这两个采样点的幅度等于 1 1 1(或者小于 1 1 1 但是又特别特别接近于 1 1 1 的一个数,这个数也称为 limiter 的阈值,此处令该阈值等于 1 1 1)。经过处理之后,上面的采样点序列变为 x ^ ( n ) \hat{x}(n) x^(n)
   0.990 ,    0.995 ,    0.997 ,    0.999 ,    1.000 ,    1.000 ,    0.998 ,    0.997 ,    0.995 ,    0.992 \; 0.990, \; 0.995, \; 0.997, \; 0.999, \; 1.000, \; 1.000, \; 0.998, \; 0.997, \; 0.995, \; 0.992 0.990,0.995,0.997,0.999,1.000,1.000,0.998,0.997,0.995,0.992

而这种做法可能存在的一个问题就是,相比处理之前,处理之后的采样点之间出现了突变,变得不平滑,在频谱上看的话就是高频多了些东西出来,听着可能会有杂音。

4. 简单粗暴做法的另一种理解:增益因子

可以换个角度理解上述简单粗暴做法,即 x ^ ( n ) \hat{x}(n) x^(n) 是通过给 x ( n ) x(n) x(n) 中的每个采样点乘以相应的增益因子得到的,而这个增益因子序列 g ( n ) g(n) g(n) 为:
1 ,    1 ,    1 ,    1 ,    1 / 1.010 ,    1 / 1.005 ,    1 ,    1 ,    1 ,    1 , 1, \; 1, \; 1, \; 1, \; 1/1.010, \; 1/1.005, \; 1, \; 1, \; 1, \; 1, 1,1,1,1,1/1.010,1/1.005,1,1,1,1,

也就是说 x ^ ( n ) = x ( n ) g ( n ) \hat{x}(n) = x(n) g(n) x^(n)=x(n)g(n)。可以看到,幅度小于 limiter 阈值的采样点的增益因子为 1 1 1。幅度大于 limiter 阈值的采样点的增益因子小于 1 1 1,且幅度越大,增益因子越小。也就是说,增益因子可由以下公式计算:
g ( n ) = { 1 , abs ( x ( n ) ) ≤ l t l t / abs ( x ( n ) ) , abs ( x ( n ) ) > l t (1-1) g(n) = \begin{cases} 1, & \text{abs}(x(n)) \leq lt \\ lt / \text{abs}(x(n)), & \text{abs}(x(n)) > lt \end{cases} \tag{1-1} g(n)={1,lt/abs(x(n)),abs(x(n))ltabs(x(n))>lt(1-1)

其中 l t lt lt 表示 limiter 的阈值(此处取值为 1 1 1)。上一节提到这种简单粗暴做法可能会导致采样点之间出现突变,引入增益因子这个概念之后,这种突变就可以理解为增益因子之间的突变。因此,为了解决这种突变,目前大多数做法都是对增益因子 g ( n ) g(n) g(n) 做平滑。接下来介绍的两个版本都是用的这个思想,区别在于所使用的平滑方法。

5. 一阶递归平滑版本的 Limiter

这儿参考 Matlab 中的实现来进行说明,其它资料中的实现也大差不差,基本一致(也可参考 DAFX: Digital Audio Effects Second Edition 中的实现)。这类实现使用一阶递归平滑的方法对增益因子进行平滑,这也是音频信号处理中常用的平滑方法。平滑的增益因子 g s ( n ) g_s(n) gs(n) 可以通过以下式子计算得到:
g s ( n ) = { ( 1 − α a )   g s ( n − 1 ) + α a   g ( n ) , g ( n ) < = g s ( n − 1 ) ( 1 − α r )   g s ( n − 1 ) + α r   g ( n ) , g ( n ) > g s ( n − 1 ) (1-2) g_s(n) = \begin{cases} (1-\alpha_{a}) \, g_s(n-1) + \alpha_{a} \, g(n), & g(n) <= g_s(n-1) \\ (1-\alpha_{r}) \, g_s(n-1) + \alpha_{r} \, g(n), & g(n) > g_s(n-1) \end{cases} \tag{1-2} gs(n)={(1αa)gs(n1)+αag(n),(1αr)gs(n1)+αrg(n),g(n)<=gs(n1)g(n)>gs(n1)(1-2)

其中 α a \alpha_{a} αa 是所谓的攻击时间(attack time)系数, α r \alpha_{r} αr 是释放时间(release time)系数,可以通过下式计算:
α ∗ = 1 − exp ( − log e ( 9 ) F s × T ∗ ) \begin{align} \alpha_{*} = 1 - \text{exp}(\frac{-\text{log}_{\text{e}}(9)}{F_s \times T_{*}}) \tag{1-3} \end{align} α=1exp(Fs×Tloge(9))(1-3)

用具体的攻击时间或释放时间替换 T ∗ T_{*} T (单位为秒)即可得到对应的攻击时间系数或释放时间系数。 F s F_{s} Fs 是音频信号的采样率。可以看到平滑之后的增益因子 g s ( n ) g_s(n) gs(n) 是对当前采样点的增益因子 g ( n ) g(n) g(n) 和历史采样点的增益因子 g ( n − 1 ) ,   g ( n − 2 ) ,   g ( n − 3 ) ⋯ g ( 1 ) g(n-1), \, g(n-2), \, g(n-3) \cdots g(1) g(n1),g(n2),g(n3)g(1) 做了指数平滑。

将上述平滑之后的增益因子与原始信号相乘,就可得到一阶递归平滑版本的 limiter 的处理结果: x ^ s ( n ) = x ( n ) g s ( n ) \hat{x}_{s}(n) = x(n)g_s(n) x^s(n)=x(n)gs(n)

5.1 攻击时间和释放时间

关于攻击时间和释放时间的介绍可以参考 Matlab。下面笔者举例说明下对这两个时间的理解,后续 FFMPEG 实现的 limiter 也用到了这两个参数。

考虑一个正在参加短跑体测的大学生,他需要从 0 m/s 0\text{m/s} 0m/s 快速加速到他的最大速度 8 m/s 8\text{m/s} 8m/s,等跑到终点后再慢慢减速到 0 m/s 0\text{m/s} 0m/s。其中加速所用的时间在这里相当于攻击时间,减速所用的时间相当于释放时间。更一般的来说,攻击时间可以理解为从初始状态切换到期望状态所用的时间,释放时间可以理解为从期望状态恢复到初始状态所用的时间。在这里大学生的初始状态就是 0 m/s 0\text{m/s} 0m/s,期望状态就是 8 m/s 8\text{m/s} 8m/s。大部分情况下攻击时间短于释放时间。

再按照此方式解释下 limiter 中的攻击时间和释放时间。假设 F s = 44100 F_s = 44100 Fs=44100, T a = 0.001 s T_{a} = 0.001s Ta=0.001s, T r = 0.005 s T_{r} = 0.005s Tr=0.005s(即 α a = 0.0486 , α r = 0.0099 \alpha_{a} = 0.0486, \alpha_{r} = 0.0099 αa=0.0486,αr=0.0099)。将 g ( n ) g(n) g(n) 的值代入上述平滑公式可得 g s ( n ) = g ( n ) = 1 , n = 1 , 2 , 3 , 4 g_s(n) = g(n) = 1, n = 1,2,3,4 gs(n)=g(n)=1,n=1,2,3,4。该取值是增益因子的初始状态,而 g ( 5 ) = 1 / 1.010 = 0.990 g(5) = 1/1.010 = 0.990 g(5)=1/1.010=0.990 这是增益因子的期望状态。那么增益因子从初始状态 1 1 1 切换到期望状态 0.990 0.990 0.990 需要多长时间哪?这就是由攻击时间决定的。使用一阶递归平滑方法的话, g ( 5 ) g(5) g(5) 的值需要持续 T a = 0.001 T_{a} = 0.001 Ta=0.001 秒之多,在这么多的值上递归平滑之后, g s g_s gs 的值才会逐渐接近 0.990 0.990 0.990。不过可以看到 g ( 5 ) g(5) g(5) 的值只持续了一个采样点,而且 g ( n ) > g ( 5 ) , n = 6 , 7 , 8 , 9 , 10 g(n) > g(5), n=6,7,8,9,10 g(n)>g(5),n=6,7,8,9,10,因此 g s g_s gs 的值将不会接近 0.990 0.990 0.990。释放时间也可以这么理解,即让增益因子从小于 1 1 1(期望状态) 恢复到等于 1 1 1(初始状态),需要增益因子为 1 1 1 的采样点持续 T r = 0.005 T_{r} = 0.005 Tr=0.005 秒之多。可以看到一阶递归平滑方法存在滞后性,需要相应的采样点持续一段时间之后, g s g_s gs 的取值才能接近所要的增益因子取值。

上面只是解释了 limiter 中攻击时间和释放时间的含义与作用,如果读者想进一步了解为什么攻击时间和攻击时间系数(或释放时间和释放时间系数)是上面那样的公式关系,又或者为什么攻击时间系数(或释放时间系数)是以上面那样的方式参与到一阶递归平滑公式中的,可以查阅更多的资料学习研究。

5.2 存在的问题

按照上述方法计算出的 x ^ s ( 5 ) \hat{x}_{s}(5) x^s(5) x ^ s ( 6 ) \hat{x}_{s}(6) x^s(6) 的幅度依然是大于 1 1 1 的(感兴趣的读者可以按照上述公式计算下),只不过比原始的 x ( 5 ) x(5) x(5) x ( 6 ) x(6) x(6) 的幅度小了。但并没有从根本上解决将幅度限制在 1 1 1 以内的这个问题。

DAFX: Digital Audio Effects Second Edition 中有提到为了较好地解决这个问题,可以后接一个 soft clipping

笔者还想到另一个处理该问题的方法,但并不能保证 100% 解决。方法就是将 limiter 的阈值降低,也就是说不要用 1 1 1 或小于 1 1 1 但又特别特别接近于 1 1 1 的数来当阈值,而是用一个较小的阈值,比如 0.9 0.9 0.9

不过,经过上面的分析可以看到,这个版本的 limiter 可以做到零延迟,某些对延迟要求较高的应用,可以使用它。

6 逐采样点过渡平滑版本的 Limiter

这儿讲的是 FFMPEG 中的实现,是笔者在偶然间发现的,但又不知该怎么称呼好,因此叫了这个名字。与上述版本相比,该版本的 limiter 使用不同的平滑方法。上面提到,一阶递归平滑版本存在增益因子滞后性的问题,从而导致处理完的采样点的幅度可能大于阈值,但它可以做到零延迟。而该版本可以做到增益因子零滞后,也就是能保证处理完的采样点的幅度不大于阈值,但它却做不到零延迟。也就是说该版本要计算当前采样点最终的增益因子,必须要用到未来采样点的信息。

考虑一个简单的采样点序列 x 1 ( n ) , n = 1 , 2 , ⋯   , 9 , 10 x_{1}(n), n=1,2,\cdots,9,10 x1(n),n=1,2,,9,10
0.990 ,    0.995 ,    0.997 ,    0.999 ,    1.010 ,    0.999 ,    0.998 ,    0.997 ,    0.995 ,    0.992 0.990, \; 0.995, \; 0.997, \; 0.999, \; 1.010, \; 0.999, \; 0.998, \; 0.997, \; 0.995, \; 0.992 0.990,0.995,0.997,0.999,1.010,0.999,0.998,0.997,0.995,0.992

当阈值 l t = 1 lt=1 lt=1 时,该序列所对应的增益因子 g 1 ( n ) g_{1}(n) g1(n) 为:
1 ,    1 ,    1 ,    1 ,    1 / 1.010 ,    1 ,    1 ,    1 ,    1 ,    1 1, \; 1, \; 1, \; 1, \; 1/1.010, \; 1, \; 1, \; 1, \; 1, \; 1 1,1,1,1,1/1.010,1,1,1,1,1

可以看到除了 g 1 ( 5 ) = 1 / 1.010 = 0.990 g_{1}(5) = 1/1.010 = 0.990 g1(5)=1/1.010=0.990 之外,其余增益因子均等于 1 1 1FFMPEG 中的做法就是让增益因子从 1 1 1 均匀地过渡到 0.990 0.990 0.990, 然后再从 0.990 0.990 0.990 均匀地过渡到 1 1 1。具体做法如下:
在攻击阶段,让增益因子从 g 1 ( 2 ) g_{1}(2) g1(2) 开始逐渐平滑过渡到 g 1 ( 5 ) = 0.990 g_{1}(5) = 0.990 g1(5)=0.990,一个可取的做法就是从 g 1 ( 2 ) g_{1}(2) g1(2) 开始,每个增益因子在前一个增益因子的基础上加 ( 0.990 − 1 ) / ( 5 − 2 + 1 ) = − 0.0025 (0.990-1)/(5-2+1)=-0.0025 (0.9901)/(52+1)=0.0025。经过运算后,前五个增益因子的取值分别为 1 ,    0.9975 ,    0.995 ,    0.9925 ,    0.990 1, \; 0.9975, \; 0.995, \; 0.9925, \; 0.990 1,0.9975,0.995,0.9925,0.990 达到了逐渐平滑到 g 1 ( 5 ) = 0.990 g_{1}(5) = 0.990 g1(5)=0.990 的目的,且不存在滞后性。同理,在释放阶段,可以让增益因子从 g 1 ( 5 ) = 0.990 g_{1}(5)=0.990 g1(5)=0.990 逐渐平滑过渡到 g 1 ( 10 ) = 1 g_{1}(10)=1 g1(10)=1。即从 g 1 ( 6 ) g_{1}(6) g1(6) 开始,每个增益因子在前一个增益因子的基础上加 ( 1 − 0.990 ) / ( 10 − 6 + 1 ) = 0.002 (1-0.990)/(10-6+1)=0.002 (10.990)/(106+1)=0.002。经过运算后,后五个增益因子的取值分别为 0.992 ,    0.994 ,    0.996 ,    0.998 ,    1 0.992, \; 0.994, \; 0.996, \; 0.998, \; 1 0.992,0.994,0.996,0.998,1。该版本最终平滑过后的增益因子 g 1 s ( n ) g_{1s}(n) g1s(n) 为:
1 ,    0.9975 ,    0.995 ,    0.9925 ,    0.990 ,    0.992 ,    0.994 ,    0.996 ,    0.998 ,    1 1, \; 0.9975, \; 0.995, \; 0.9925, \; 0.990, \; 0.992, \; 0.994, \; 0.996, \; 0.998, \; 1 1,0.9975,0.995,0.9925,0.990,0.992,0.994,0.996,0.998,1

最后,再将 x 1 ( n ) x_{1}(n) x1(n) g 1 s ( n ) g_{1s}(n) g1s(n) 相乘,就得到了 FFMPEG 版的结果。可以看到,经过处理之后,采样点的幅度都不大于阈值。

上面就是 FFMPEGlimiter 的实现原理。相信读者看完之后应该能理解笔者为什么起这个名字称呼它了。读者可以算算上述例子中的攻击时间和释放时间分别是多少,攻击时间和释放时间越大,最终计算的增益因子之间的差异就越小,增益因子之间就越平滑。还需要注意到的是 g 1 s ( 2 ) g_{1s}(2) g1s(2) 的值与 g 1 ( 5 ) g_{1}(5) g1(5) 的值有关,因此该方法无法做到零延迟,而且延迟时间等于攻击时间。

上面只是简单说明了该版本的思想,看着也挺简单,没什么难度,但实际情况往往更加复杂。比如一段音频序列中短时间内出现多个幅度超出阈值的采样点,应该怎么处理才能保证让每个采样点都能满足要求那。这才是该版本的难点之一,笔者能力有限,不能用简洁的语言描述清楚这个过程。感兴趣的读者可以阅读 FFMPEG 的实现,了解了其思想之后,看起来就能容易些。

7 总结

在初步接到这个需求的时候,是想实现一个延迟可设的 limiter, 本以为不会花太多的时间和精力,应该很快就能完成。但随着深入学习研究,才发现一个小小的算法,要想一步一步优化它,让它更加完美,也并不是像它表面看起来那么简单。从发现问题到提出解决方案,这后面蕴含的思想是十分美妙的,这也是算法的魅力之一。文章来源地址https://www.toymoban.com/news/detail-833750.html

到了这里,关于聊聊音频信号处理中一个不太起眼的算法-limiter的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Matlab】音频信号谱分析及椭圆滤波处理

    一个使用matlab对音频信号进行频谱分析及滤波处理的学习笔记,本文使用的是椭圆滤波器。 音频下载 demo.mp3 读取音频信号进行傅里叶变换 结果如下 在谱分析中使用matlab自带的快速傅里叶变换函数进行变换 在IIR滤波器设计中使用椭圆低通滤波器 椭圆滤波器使用 入门级教程

    2024年02月12日
    浏览(43)
  • 【FPGA-DSP】第九期:音频信号处理

    从本文开始将记录一些简单的 音频信号处理算法 在System Generator中的实现方法。本文将介绍如何搭建音频信号的采集与输出模型。 音频信号属于一维信号,一些基本概念如下: 采样频率:根据奈奎斯特采样定理,采样频率Fs应该不低于声音信号中最高频率2倍。常见的音频格

    2024年02月01日
    浏览(53)
  • 嵌入式操作教程_数字信号处理_音频编解码:3-6 AAC音频解码实验

    了解AAC音频格式,掌握AAC音频解码的原理,并实现将AAC格式的音频解码为PCM 音频编解码的主要对象是音乐和语音,音频的编解码格式可分为无压缩的格式、无损压缩格式、有损音乐压缩格式、有损语音压缩格式和合成算法。本实验中使用的AAC格式属于有损音乐压缩格式。音频

    2024年04月15日
    浏览(67)
  • 【Matlab】音频信号分析及FIR滤波处理——凯泽(Kaiser)窗

    1.1 课题内容: 利用麦克风采集语音信号(人的声音、或乐器声乐),人为加上环境噪声(窄带) 分析上述声音信号的频谱,比较两种情况下的差异 根据信号的频谱分布,选取合适的滤波器指标(频率指标、衰减指标),设计对应的 FIR 滤波器 实现数字滤波,将滤波前、后的声

    2024年02月21日
    浏览(44)
  • 数字信号处理音频FIR去噪滤波器(基于MATLAB GUI的开发)

    利用MATLAB GUI设计平台,用窗函数法设计FIR数字滤波器,对所给出的含有噪声的声音信号进行数字滤波处理,得到降噪的声音信号,进行时域频域分析,同时分析不同窗函数的效果。将文件解压至一个目录下,运行m文件即可使用。 读取.wav音频文件函数 :audioread();(老版

    2024年02月08日
    浏览(56)
  • 【FPGA数据采集测试系统】——基于FPGA的通用数据采集测试系统是当今数字电路领域最热门的研究课题之一。它不仅可以用于数字信号处理,而且也被广泛应用于音频、视...

    【FPGA数据采集测试系统】——基于FPGA的通用数据采集测试系统是当今数字电路领域最热门的研究课题之一。它不仅可以用于数字信号处理,而且也被广泛应用于音频、视频以及其他多媒体领域。 此外,基于FPGA的数据采集测试系统还可以用于传感器接口、机器视觉、自动控制

    2024年02月09日
    浏览(47)
  • 数字信号处理、语音信号处理、现代信号处理

    推荐他的博客: 手撕《数字信号处理》——通俗易懂的数字信号处理章节详解集合 手撕《语音信号处理》——通俗易懂的语音信号处理章节详解集合 手撕《现代信号处理》——通俗易懂的现代信号处理章节详解集合

    2024年02月08日
    浏览(69)
  • 【linux】信号——信号保存+信号处理

    自我名言 : 只有努力,才能追逐梦想,只有努力,才不会欺骗自己。 喜欢的点赞,收藏,关注一下把! 上一篇博客,我们已经学过信号预备知识和信号的产生,今天讲讲信号保存+信号处理以及其他补充知识。 补充一些概念。 实际执行信号的处理动作称为 信号递达(Delive

    2024年02月04日
    浏览(44)
  • 现代信号处理——阵列信号处理(空域滤波原理及其算法)

    一、阵列信号处理简介 1、阵列信号处理的研究内容:检测、估计、滤波、成像等。 2、阵列信号处理的研究对象:空间传播波携带信号(空域滤波) 3、阵列信号处理方法:统计与自适应信号处理技术(如谱估计、最优与自适应、滤波) 4、阵列信号处理的目的:①滤波:增强

    2024年02月02日
    浏览(94)
  • 数字信号处理8:利用Python进行数字信号处理基础

    我前两天买了本MATLAB信号处理,但是很无语,感觉自己对MATLAB的语法很陌生,看了半天也觉得自己写不出来,所以就对着MATLAB自己去写用Python进行的数字信号处理基础,我写了两天左右,基本上把matlab书上的代码全部用Python实现了,所以,今天贴的代码和图有些多, 要用到的

    2024年02月13日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包