Karplus-Strong Algorithm 弦乐器模拟 吉他弦乐器发声原理 泛音 乐理概述

   本文是记录 CS61B HW1  学习中内容。

  这段时间在学乐理,信号与系统还有反馈控制理论,遇到这个 HW 现在杂糅一下记一下思路方便之后回顾。

    Karplus-Strong Algorithm 是一个非常简单的算法,但是我觉得,对同时在学器乐以及相关数学物理的人来说,理解一个看起来很简单的东西是很自然的事情。所以本文主要篇幅会花在建立直觉理解上的。

    这个 Hw 在做一个 Physical Modelling Synthesis 的过程,主要是用这个 Karplus-Strong 算法来模拟打鼓和弦乐器的声音(这个应用也许可以做电钢和合成器过为什么不用录音哈实际的电钢好像基本用录音),论文主要是先给出了模拟方法,构建了系统框图,然后在复频域分析为什么简单的算法能得到好效果。 该算法分为两部分,第一是用白噪声和周期重复来模拟弦乐器固有频率,第二是模拟能量衰减过程。我这里为了能方便理解平滑运算等价滤波器的部分,记录一下整个思路 What it does and Why it works.

    先复习物理的内容(太久没学物理如果有误请指出)。

    单质点在时域上来回振动,我们叫做振动,最简单的是正弦函数描述的,即时间域上的在某个方向上的简谐运动,得到一个 y-t 图像。如果质点具备能量(对外做功),对外界另一个质点产生影响,并不断地接续下去,就得到波。我们可以得到一个 y-x-t 图像,某一个时刻下可以得到 y-x 图像。

Karplus-Strong Algorithm 弦乐器模拟 吉他弦乐器发声原理 泛音 乐理概述
驻波 图片来自网络

    研究波,有波长和频率的关系 :

Karplus-Strong Algorithm 弦乐器模拟 吉他弦乐器发声原理 泛音 乐理概述

v是波速,这个公式的直观理解是无能量损失的振动沿着弦传播出去,把质点的这一秒的简谐振动位置作为相邻的点的下一秒的简谐振动位置。驻波就不讲解了,就是波在弦上传播,到两端点后反射回来两段波叠到一起导致波不沿弦向外传播而出现波节和波腹的现象。

     接下来分析吉他拨弦的过程(钢琴的击弦类似,为方便分析用吉他举例),手对弦上某个点产生一个激励输入,弦系统的某个质点位置开始简谐振动,这个振动是由随机大小的激励产生的,其频率是弦的固有频率。这里要明确的是,弦振动的频率不是由手给的激励决定的(手只是给了一个瞬时的能量冲激,没有能激发频率的因素),而是由弦固有频率决定的。

    所以还要理解这个固有频率是怎么来的,之前看弦乐器原理的一些分析看了一些通过驻波公式来分析弦的振动固有频率,一直不是很理解。为什么驻波公式能推导出固有频率,首先前面说到的波速,这个波速仅由介质决定,在弦上,可以通过力学分析得到一个波速的公式(和弦的密度反比,和弦的张力正比),所以得到了一个结论,弦的固有频率原来由这个波速谁是由材质决定的而决定的。

    所以为什么驻波条件能推导出固有频率呢们直接通过吉他弦来分析,首先驻波是一定会产生的(存在端点反射和均匀介质的条件),然后具体分析吉他弦,弦两端固定,反射波存在半波损失(这个比较复杂,不深入复习了),所以波节就是两端了。于是我们根据这个弦长和驻波条件推导出波长,然后根据波长和波速推导出频率。如果一个不符合这系列推导出来的频率在弦上传播,得到的结果就是波长和弦长不匹配,完整的波传不了就矛盾了。根据推导得到的可以产生的频率就和弦长反比,和张力正比,并且有无限个(即基波和多个高次谐波)。

    用驻波理论去分析我还是不能理解这个振动到底是怎么出现的。感觉我学模电的反馈系统的时候就很难理解反馈的过程,尽管学习了信号与系统和自动控制理论的使用惯性近似的推导过程,可能是自己不能理解推导背后的物理意义,但是还是没有很好的自觉理解整个反馈的收敛过程。根据我看到的自动控制理论教科书的分析一般都是把系统函数给近似简化了,就等于用数理方法去进行同构转换了,从此丢失了可解释性,让我想起了高中学单摆的时和大学再学单摆,同样的东西他的模型一个那么简单一个那么复杂。当然一个重点是知识的有限限制了很多东西,时间有限我只希望找到一个比较能接受的说法就终止吧。不然就像没学量子物理的时候一直在纠结为什么三极管能 work 的浪费生命了。

  我感觉数学物理分析很多都是这样,就像哥德尔不完备定理,很难说你能证明出底层的约束条件来,更多的时候人们必须戴着镣铐跳舞,因为你没有前提条件就没办法一阶逻辑推下去,比如你欧氏几何的第五公设,最后人们发现这个不能证明是因为他就是欧式几何的约束条件,因为还能做出非欧几何的一个体系出来。所以很多时候钻牛角尖也得有个度??,不如到一个自己满意的点就暂时剪枝采用机械式思路返回了,不如无穷递归最后就 StackOverflow 了。

  我再尝试直觉体会这个过程吧,拨弦一瞬间的激励给弦上质点一个位移导致该点附近的张力的分布发生了改变,张力的作用会决定在时间内引发这个振动响应,所以最后产生的频率是的确和弦的密度弦长和张力有关,说到底用驻波去分析可能本身就不是一个定义分析的过程,波最后都只是引入了一个中介来计算而已。但是为什么这个产生的频率会有多次谐波呢波理论分析其是因为条件做方程组是欠定的,感觉是知识水平限制了我理解这个物理意义。我暂时做这样的思想实验(民科警告): 对于拨弦质点来说,对他最近最近的那个质点的影响就算马上让他振动到对立方向去,是无穷频率。之后的离得远一点的作用就是低频一点的了,至于最大音量的基频则是由整个弦(即两段固定而导致)引发的,这个作用就是公式根据整个弦的张力和密度能算出来的基波频率。吉他12品泛音就是半压弦导致的空弦振动和2倍频率的高八度的谐波一起振动搞的泛音(高次谐波)共振发出来的声音。调音的共振也是由于这个固有频率,我们拨一根弦的时候他以这个频率振动空气传到另一根弦了,如果固有频率一样才能激发最大幅度的振动(即符合驻波条件的频率),是共振。

Karplus-Strong Algorithm 弦乐器模拟 吉他弦乐器发声原理 泛音 乐理概述
钢琴 A4 (440Hz) 频谱图 图片来自网络

  那就暂时这样去理解弦的固有频率先。这里有一个结论,音调由基频决定,音色由各谐波的相对大小决定。不过反正基波频率的幅度相对大小总是1,谐波的小于1。到这里又要备注一下人耳的生物分析了,我们说 C 音,感觉听到 C3 C4 C5 都有 C 的感觉,其实这是由于我们人耳的结构决定的。对于十二平均律为什么按对数划分和八度为什么按 2 倍分是由于耳蜗是对数螺线排列这里就不讲了。我主要备注的一个要点是人听声音的敏感程度,根据等响度曲线,对相同幅度不同频率的实际声波,人耳感受到的音量是不同的!人耳最清楚的主要集中在 4k 频率左右,这个位置很小的声音都能听到(也就更加刺耳)。我们的中央 C (C4)基波频率也就 256~261 Hz左右。所以人感受声音主要是感受谐波!而乐器发声在 C3 C4 C5 得到的高频谐波在刺耳范围内都有,不同的就是能感觉低频成分不一样,但是总的高频段基本差不多!而 C 和 D 的谐波列是没有这么多重合的(要最小公倍数才能得到同频谐波,这就是为什么有和弦理论)。实际上对于大跨度的大三和弦 CCGCE 比例刚好 12345,但是不会被辨认为一个 C 底音是因为他们的力度均匀,而 C 底音的谐波是越高次越弱的,这是和弦和谐波的区别。然后是音色,音色由各谐波的相对大小决定,所以当然如果演奏的时候偶然的不同谐波比重在合奏时得到调整就可能会出现区别于音符和和弦的某种新的乐器音色

    接下来就进入到本文要讲的 KS 生成弦乐器音色的算法。算法分为两部分,第一是用白噪声和周期重复来模拟弦乐器固有频率,第二是模拟能量衰减过程。

    首先回顾上面的驻波产生频率的过程,我们直接给一个频率振动的正弦波就行了,但是这样只有基波,为了模拟包括全部的高频的谐波部分,我们直接给一个白噪声序列来模拟,回顾频域的周期序列的分析,分解得到的纯三角函数分量里,基波频率是函数的频率。直观理解就是白噪声序列是一个频谱广泛的一个序列,我们以f周期性重复这个序列得到的就是含有f的各次倍频的合成序列,或者用窗口短时傅里叶小波去分析可能比较容易计算(对音符这个东西本来就要在窗下做频域分析,不然推导出来是不符合听到的效果的)。这样我们写程序的时候用的就是 SR/f 作为数组大小生成高斯白噪音模拟不同谐波。不过如果要模拟特定音色,就涉及对这个序列进行生成的问题了,录音分析频谱是一种方法,力学分析因素太多建模不够精确。我们主要研究一个快速模拟衰减的算法。

   算法第二部分要模拟弦乐器主要是模拟这个能量衰减的过程,通过数字系统来模拟构建一个符合弦乐器的一个系统响应函数的音色的过程。弦乐器的用数学物理方法进行普通简谐运动微分方程求解,初始加初速度,约束条件用驻波条件,是可以解出完整的弦乐器音色的,也就是上面提到的和张力密度有关的一些结论的来源,但是这个计算是和弦的材质无关的,实际上的音色还和振动激发的方式(钢琴是琴槌敲击,吉他是拨,小提琴是摩擦),传声方式,共鸣腔体,琴体材料,弦两端边界条件(实际的不是简单的固定条件)然而仅凭上面的分析,还不足以得到我们听到的弦乐器音色,知识限制了我的思考水平。比如电吉他不插电弹的声音很小,电吉他的音色主要是靠电子元件来搞的。但是效果器不是我们的算法涉及的,这是因为拾音器本身就能采集到弦不断衰减到不振动的整个变化(垂直于金属柱的磁感线被切割)。也就是说要模拟弦乐器必须考虑这个木板和空气的传播。现在要模拟的是这个衰减的变化。

    分析衰减的过程,这期间弦的能量(手对弦做的功提供的初始能量)通过向空气逐渐传播出去后衰减(decay)。高频衰减比低频衰减要快很多,可以想到这是和介质吸收声波能量的性质有关的,高频波传播过程引起的空气分子振动次数多,与周围空气分子摩擦更多,能量消耗快。(备注实际物理过程分析涉及很多分析,我只是简单地直观地去理解而已),研究表明(或者针对乐器直接时域录音分析频谱就能看出来)衰减程度和声源频率成正比,所以可以说这个乐音实际上是慢慢地从一个不纯的波变到更纯的正弦波最终变为0的过程而不仅仅是音量减少(论文提出这样一个算法)。当然我们可以简单地搞一个音量指数衰减,但是这样的声音很古怪,感觉还是像正弦波。所以果然要去模拟这个高频衰减的过程。很容易想到,我们搞一个随着时间不断减少截止频率的低通滤波器不就行了吗!

    现在复习低通滤波器的内容,这是为了方便理解等一下算法的简洁之处。我们一开始学电路做了一个RC串联一阶低通滤波器,实现是把 C 电压做输出,原理是低频时电容基本没有补偿,R不导通,电压就等于Ui,高频时电容会补偿,电压变低电容就放电出来补偿他变低的程度从而使电阻导通,电压被电阻分走,整体是一个近似指数衰减的 U-f 图。如果搞一个理想低通滤波,在频域的乘上一个纯门函数时域需要卷一个 Sa 函数,我们知道卷积的原理就是滑动窗口取相关度,这里 Sa 是对称的,就等价于权值乘积求和,而且窗口大小刚好是!当然实际我们应该是频域 Sa 时域门(门宽和Sa宽倒数关系),时域门作为卷积和就等于一个时域均值平滑操作!当然根据上面分析我们要的不是简单的理想低通滤波器(频域门)的实现,我们也不用分析法了,我们直接用证明的思路去看 KS 怎么实现这个频域滤波以及他的物理意义。

    现在跟随论文的脚步讲完 KS 算法,首先是最简单的无衰减模型:

Karplus-Strong Algorithm 弦乐器模拟 吉他弦乐器发声原理 泛音 乐理概述

其中 t 是当前时刻,p 是周期序列的长度(SR/f), 这就是一个普通的周期函数模型而已,然后是 Alex Strong 提出的是拨弦模型:

 

Karplus-Strong Algorithm 弦乐器模拟 吉他弦乐器发声原理 泛音 乐理概述 

主要就是引入了渐进低通滤波!这个简洁的方法可以通过含延迟单元的z域系统框图和系统函数分析他的确是逐渐降低高频(这部分知识不太记得了我只能跟论文计算一点点),我寻求的是直观/自觉上的理解,论文给了一个不是很清楚的说明,他说仅对2个sample pass(一个取样点,远小于一秒) 取平均对于低频的,即跨越很多 pass 的正弦分量,这个平均是微不足道的(因为本来就平滑曲线差不多),对于高频的而言就不同了,相邻的两个pass差距可能非常大,这样平滑主要针对高频。

    直觉上想,我们的窗口应该随着时间推移变小才对啊,不然怎么实现声音归零的么实现滤的截止频率越来越小的/p>

Karplus-Strong Algorithm 弦乐器模拟 吉他弦乐器发声原理 泛音 乐理概述
时域卷门即平均 时域乘 Sa 图片来自信号与系统课本

当然后者只要思考 Sa 函数的滤波而不是理想低通的滤波就能明白,但是衰减到基波频率的纯正弦波的时候,我不知道他怎么 消失。在网上查到一个文章里面没有 Decay Factor,但是声音播放能 Decay。(后续补充:现在配上图我就明白了,因为只有0频才有1的幅值,而乐音基频的衰减因子本来就小于1 )实际这个 hw 以及另一篇文章都要用一个 Decay Factor 来实现低频的衰减。hw 里面用的是 0.995,指数式衰减,只不过斜率很平缓,接近缓慢线性了,不过对于我们高采样率来说时间不过几秒。当然,我们频响函数的模值域是Sa,所以除了基频所有的都是以一个指数衰减的,而且越高频衰减的这个指数函数的底越小,衰减越快。所以实际上就只是在窗口上实现 Sa 低通滤波而已,我们应该计算这个第一零点截止频率看看第一轮衰减最大的频率范围,门宽 2 Pass 的时域卷积平均对应的时域门宽是:

Karplus-Strong Algorithm 弦乐器模拟 吉他弦乐器发声原理 泛音 乐理概述

其中 SR = Sample Rate, f 为乐音频率,可以计算其频域 Sa 低通滤波截止频率为:

Karplus-Strong Algorithm 弦乐器模拟 吉他弦乐器发声原理 泛音 乐理概述

考虑那奎斯特采样率最小要两倍,人耳范围能听20kHz,至少40kHz采样,截止频率为 40k/2f,20k/f,C4就250Hz,C6 1000Hz,20倍谐波以后的衰减就很厉害了,当然对C4来说都不知道多少倍了。

    我们实际实验的声音的确像论文里面说的像弦乐器声音,但是既不是钢琴也不是吉他的声音,不知道为什么有种中国的弦乐器加上金属的味道(或者是电吉他不插电的声音的感觉)。可能这个涉及木吉他的共鸣箱和钢琴共振板的原理。吉他共鸣箱那个形状应该就决定了我们模拟欠缺的那部分音色,具体的要分析箱内空气对不同频率的共振,这个超出了这个算法涉及的部分了,还有就是金属弦和尼龙弦的那个又是怎么模拟音色呢全不懂。材料上也不只有密度的差异,所以材质本身被击打时的音色又是什么决定的呢在是难以继续思想实验了。不过我们之后完成一个 MIDI 的播放调用,并且有多根弦同时弹的时候就能感觉一股电子合成钢弦音乐的味道了。

    论文还讲解了鼓的模拟(Karplus),用概率来决定是正的平均还是负的平均,这个引入了概率作为;Blend Factor 得到的不是周期性的衰减,而是一个随机性很大的序列,鼓的声音模拟我不打算分析,但是他仍然是有张力和简谐运动,不同于弦的是他是一个柱面波!我实在是看不懂相关物理力学分析,只能看看论文了,论文讲解 b 为 0 的时候衰减同时反转了,把频率降了一个八度(周期变长了一倍),仅留下奇次谐波(这个推导信号里的确是这样的,但是对音色有什么影响呢,而奇次谐波是比较刺耳的声音。b=0.5 时,变成非周期信号,延迟时间变少了,这一点看不明白,本着抓大放小的学习原则,这里就不再探究鼓的发声及物理意义了。

    实现方面,论文给出了 Decreasing-Counter 和 Circular-Buffer 两种方法,今天的 hw 就是用后者来实现这个算法。具体的实现就不记录了。关键的代码就这两行(HW 里面要求向后取的平均而不是向前,向前的话还要搞一个 -1 索引的变量。不过实际实现起来效果差不多)。

    我尝试在 hw 提供的 StdAudio 类提供的方法实现声音播放,虽然不能理解这个流播放一次给一个 double 然后内部通过 buffer 按采样率缓冲,最后调用 java 库实现声音播放的过程具体是怎么样的,有一点就是他不能搞多线程的静态类,然后我尝试调整了一种贝斯音色(网上找到一篇国外学生的作业论文就是研究改进 Karplus Strong 算法来实现贝斯的音色,主要是有贝斯音色的频谱,调整了系统函数搞了一个循环低通滤波,由于没有代码参考,我实现得不好,只搞了初始激励的简单的低通滤波)然后通过解析 midi 提取出了贝斯轨,具体实现起来只能手动合轨了(什么有序链表合并…)。做出来的效果也不好,最主要是和有序链表合并有区别是如果出现相等 tick 的时候必须对多个轨手动求和合轨的音频送给 StdAudio,后来又想把鼓的轨也分出来的但是搞不清楚已经一堆 if、else了,估计要研究一下音视频同步和音频多轨播放的方案自己写一个音频播放才好搞一些。于是这个 Hw 就做到这里了,感觉又做了很多无效工作。

   另一个想法是不用这个合成的方案,毕竟目前看到的一些MIDI播放软件好像都用的是音源,但是也不能直接把整个MIDI曲直接生成,因为这本来是设计成流式的,要支持MIDI键盘的输入,所以MIDI键盘输入的时候怎么实现音频同步的果当前在播放上一个音符的衰减音时来下一个音符,假定还是有一个环形缓冲区,这时给个延时然后直接加到缓冲区的序列上不还是 Karplus Strong 这种结构吗。我得研究一下这个手动合轨的方法,不知道直接联合下一层的音频缓冲播放一起来写程序会不会更容易操作些。不从合轨的角度去看就只能搞多线程了,我又感觉不行多线程播放涉及声卡的缓冲支持,果然还是合轨的效果好一点。问题就在这个 event 不是对齐的,不知道能不能设计某种数据结构来实现对齐扫描多个轨道然后求和。(后来发现 Java Audio 库挺齐全的,有时间再研究研究)现在继续做其他作业先。

相关的一些资料:

Digital Synthesis of Plucked-String and Drum Timbres, Kevin Karplus, Alex Strong

Model of Electric Bass Using Extended Karplus-Strong Plucked-String Algorithm

Music and Computers,music.columbia.edu

文章知识点与官方知识档案匹配,可进一步学习相关知识算法技能树首页概览34716 人正在系统学习中

来源:RzBu11d023r

声明:本站部分文章及图片转载于互联网,内容版权归原作者所有,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2022年1月26日
下一篇 2022年1月26日

相关推荐