【基于pyAudioKits的Python音频信号处理(三)】乐音和噪音

pyAudioKits是基于librosa和其他库的强大Python音频工作流支持。

API速查手册

通过pip安装:

pip install pyAudioKits

本项目的GitHub地址,如果这个项目帮助到了你,请为它点上一颗star,谢谢你的支持!如果你在使用过程中有任何问题,请在评论区留言或在GitHub上提issue,我将持续对该项目进行维护。

import pyAudioKits.audio as ak

音频信号分为乐音与噪音两种类别。其中,发音物体有规律地振动而产生的具有固定音高的音称乐音,而噪音则是发音物体随机振动产生的没有固定音高的声音。本节将分别讨论两种类型的音频信号及各自的性质,并讨论它们之间的联系。

单音

正弦波描述了最基本的简谐振动。一个波形满足固定频率和相位的正弦波就是一个单音。而单音则是最基本的乐音。

monotone=ak.create_Single_Freq_Audio(0.02,440,44100,1)	#生成一段长为1s、振幅为0.02、频率为440Hz、采样率为44100的单音
monotone.sound()	#播放一下试试

'''
outputs:
The power of the audio:  0.00019999999999999996
'''
monotone.plot(0, 10, xlabel="t/ms")   #绘制其波形图

在这里插入图片描述

音频的功率 P = ∑ n = 0 N m a x − 1 x 2 [ n ] N m a x P=\frac{\displaystyle\sum_{n=0}^{N_{max}-1}x^2[n]}{N_{max}} P=Nmaxn=0Nmax1x2[n]决定了响度。我们播放音频的时候,它的功率会被显示出来。

import pyAudioKits.analyse as aly

aly.power(monotone)
'''
outputs:
0.00019999999999999996
'''

功率也可以被表示为增益的形式, 增益( d B ) = 10 l o g 10 功率 增益(dB)=10log_{10}功率 增益(dB=10log10功率

aly.power(monotone, dB = True)

'''
outputs:
-36.98970004336019
'''

能量 E = ∑ n = 0 N m a x − 1 x 2 [ n ] E=\displaystyle\sum_{n=0}^{N_{max}-1}x^2[n] E=n=0Nmax1x2[n]也是音频的一个特征。在音频信号时间无限的情况下,能量定义为 E = lim ⁡ N m a x → ∞ ∑ n = − N m a x N m a x x 2 [ n ] E=\displaystyle\lim_{N_{max}\rightarrow ∞}\sum_{n=-N_{max}}^{N_{max}}x^2[n] E=Nmaxlimn=NmaxNmaxx2[n],而功率定义为 P = lim ⁡ N m a x → ∞ ∑ n = − N m a x N m a x x 2 [ n ] 2 N m a x P=\displaystyle\lim_{N_{max}\rightarrow ∞}\sum_{n=-N_{max}}^{N_{max}}\frac{x^2[n]}{2N_{max}} P=Nmaxlimn=NmaxNmax2Nmaxx2[n]。此时,能量只对平方和收敛的音频信号有定义,并且此类信号功率为0。这类信号被称为有限能量信号,常常意味着一个只有在有限的时间内有取值的信号,或是随时间衰减的信号。能量无限但功率有限的信号则被称为有限功率信号。由于我们对音频信号进行了加窗截断,因此所有信号都是时间有限的,能量和功率均有限且以 N m a x N_{max} Nmax为比例。

aly.energy(monotone)    #计算能量

'''
outputs:
8.819999999999999
'''

由于在 N m a x N_{max} Nmax较大时,能量可能是一个很大的数值,我们也可以计算增益的形式,称为对数能量。

aly.energy(monotone, dB=True)    #计算对数能量

'''
outputs:
9.454685851318196
'''

单音的功率与其对应正弦波的振幅有关。

monotone=monotone*2	#将振幅变成原来的两倍
monotone.sound()	#播放一下试试

'''
outputs:
The power of the audio:  0.0007999999999999998
'''

可以看见功率变成了原来的4倍,听起来响度也变大了。

振幅的改变也可以写成增益的形式。 增益( d B ) = 20 l o g 10 新振幅 原振幅 = 10 l o g 10 新功率 原功率 增益(dB)=20log_{10}\frac{新振幅}{原振幅} = 10log_{10}\frac{新功率}{原功率} 增益(dB=20log10原振幅新振幅=10log10原功率新功率

monotone=monotone.amplify(5)    #增益5dB
monotone.sound()	#播放一下试试

'''
outputs:
The power of the audio:  0.008000000000000002
In order to protect your hearing, the amplitude is limited. 
'''

功率变为了原来的10倍。同时触发了sound函数的听觉保护机制。

下面我们创建一段频率更高的正弦波。

monotone2=ak.create_Single_Freq_Audio(0.02,880,44100,1)	#生成一段长为1s、振幅为0.02、频率为880Hz、采样率为44100的单音
monotone2.sound()	#播放一下试试

'''
outputs:
The power of the audio:  0.0002
'''

频率决定了音高 f = 440 H z f=440Hz f=440Hz被称为中央A,频率上翻一倍被称为一个八度

十二平均律中,每个半音在频率上翻了 2 1 / 12 2^{1/12} 21/12倍。每个半音的距离就是钢琴的黑白键上两个键之间的距离,从do到升do,各白键之间的差音为全全半全全全半,而每个全音中的半音被分到了黑键上。

任意两个音之间的距离称为音程,音程的单位是,相同单音之间音程为1度,之后每差一个音级增加1度。

monotone2=monotone2.pitch_shift(-12)	#降调12个半音
monotone2.sound()	#播放一下试试

'''
outputs:
The power of the audio:  0.0001967911023878219
'''

monotone2原来的频率为880Hz,降调12个半音后,可以听见音高就和440Hz的monotone一致了。

频率可以使用自相关函数来进行研究。自相关函数 ρ [ k ] = ∑ m = − ∞ ∞ x [ m ] x [ m + k ] \rho[k]=\displaystyle\sum_{m=-∞}^∞x[m]x[m+k] ρ[k]=m=x[m]x[m+k]表示一个波形偏移 k k k个采样点前后的结构相似性。

由于我们将音频信号进行了截断,因此自相关函数的计算公式为 ρ [ k ] = ∑ m = − ∞ ∞ x ′ [ m ] x ′ [ m + k ] , k ∈ { − N m a x + 1 , − N m a x + 2 , . . . , N m a x − 2 , N m a x − 1 } \rho[k]=\displaystyle\sum_{m=-∞}^∞x'[m]x'[m+k],k\in\{-N_{max}+1, -N_{max}+2, ..., N_{max}-2, N_{max}-1\} ρ[k]=m=x[m]x[m+k],k{ Nmax+1,Nmax+2,...,Nmax2,Nmax1},其中 x ′ [ n ] = { x [ n ] , 0 ≤ n < N m a x 0 , O t h e r s x'[n]=\begin{cases}x[n],&0≤n<N_{max}\\0,&Others\end{cases} x[n]={ x[n],0,0n<NmaxOthers。当 ∣ k ∣ < < N m a x |k|<<N_{max} k<<Nmax时,影响几乎可以忽略。得到的 ρ [ k ] \rho[k] ρ[k]可以用一个长度为 2 N m a x − 1 2N_{max}-1 2Nmax1的向量来表示。当 N m a x N_{max} Nmax极大时,通常截取其中一小部分。

aly.autocorr(monotone).plot(-220,221,xlabel="k")    #计算monotone的自相关函数,并截取k取[-220,221)的部分

请添加图片描述

aly.autocorr(monotone).plot(-5,5,xlabel="t/ms")    #计算monotone的自相关函数,并截取[-5ms,5ms)的部分

在这里插入图片描述

可以看到自相关函数呈现周期性,且关于 k = 0 k=0 k=0对称。其周期在 k = 100 k=100 k=100左右。由于采样率为44100Hz,100个采样点就代表了约 2.27 m s 2.27ms 2.27ms的时间长度。而monotone的频率为 440 H z 440Hz 440Hz,其周期正好就约为 2.27 m s 2.27ms 2.27ms。可见我们可以通过自相关函数中局部极大值出现的时间间隔来确定单音的周期。

基波、谐波与傅里叶级数

接下来,我们来探索复杂的乐音。首先,生成13段440Hz的单音。在这基础上,第n次生成440Hz单音时再生成1s频率为 440 ⋅ 2 ( n − 1 ) / 12 H z 440·2^{(n-1)/12}Hz 4402(n1)/12Hz的单音,与原单音线性相加进行和弦,从而模拟小二度到纯八度的所有音程

x = ak.create_Single_Freq_Audio(0.02,440,44100,1) + ak.create_Single_Freq_Audio(0.02,440,44100,1)
for i in range(12):
    x = ak.concatenate([x,ak.create_Single_Freq_Audio(0,440,44100,1)]) #在每个和弦后加1秒的空白
    x = ak.concatenate([x,ak.create_Single_Freq_Audio(0.02,440,44100,1) + ak.create_Single_Freq_Audio(0.02,440*2**((i+1)/12),44100,1)])
x.sound()

'''
outputs:
The power of the audio:  0.0002241272982532446
'''

生成的26s音频中有13段不同音程的和弦,有的和弦听起来和谐,有的和弦听起来不和谐。

  1. 小二度音程(相差一个半音)、大七度音程(相差五个全音一个半音)是极不协和音程
  2. 大二度音程(相差一个全音)、小七度音程(相差五个全音)、三全音(三个全音)是不协和音程
  3. 小三度音程(相差一个全音一个半音)、大三度音程(相差两个全音)、小六度音程(相差四个全音)、大六度音程(相差四个全音一个半音)是不完全协和音程
  4. 纯四度音程(相差两个全音一个半音)、纯五度音程(相差三个全音一个半音)是完全协和音程
  5. 纯八度音程(相差六个全音)是极完全协和音程
x.plot(4,4.1)

在这里插入图片描述

可以看到这样的和弦的波形已经远远不能被称为正弦波了,但这种奇怪的波形依然是通过两个不同频率的正弦波叠加而成的。实际上,根据傅里叶级数,任何波形的周期信号都能被展开为不同相位、不同频率、不同振幅的正弦波叠加的形式。

一个连续时间周期信号,若其周期为 T T T,则有基波频率 ω 0 = 2 π T \omega_0=\frac{2\pi}{T} ω0=T2π,可以将任意周期信号表示为基波分量k+1(k=1,2,…)次谐波分量加权和的形式,其中k+1次谐波分量的频率为基波频率 ω 0 \omega_0 ω0的k+1倍。而每个分量的权值则被称为傅里叶系数,用 a k a_k ak表示:

x ( t ) = ∑ k = − ∞ ∞ a k e j k ω 0 t = ∑ k = − ∞ ∞ a k e j k 2 π T t x(t)=\displaystyle\sum_{k=-∞}^∞a_ke^{jk\omega_0t}=\displaystyle\sum_{k=-∞}^∞a_ke^{jk\frac{2\pi}{T}t} x(t)=k=akejkω0t=k=akejkT2πt

a k = 1 T ∫ T x ( t ) e − j k ω 0 t d t = 1 T ∫ T x ( t ) e − j k 2 π T t d t a_k=\displaystyle\frac{1}{T}\int_Tx(t)e^{-jk\omega_0t}dt=\frac{1}{T}\int_Tx(t)e^{-jk\frac{2\pi}{T}t}dt ak=T1Tx(t)ejkω0tdt=T1Tx(t)ejkT2πtdt

傅里叶系数决定了每个分量的振幅。

傅里叶级数还可以写成三角函数的形式: x ( t ) = c 0 + ∑ k = 1 ∞ [ a k c o s ( 2 π k T t ) + b k s i n ( 2 π k T t ) ] x(t)=c_0+\displaystyle\sum_{k=1}^∞[a_kcos(\frac{2\pi k}{T} t)+b_k sin(\frac{2\pi k}{T} t)] x(t)=c0+k=1[akcos(T2πkt)+bksin(T2πkt)],其中 c 0 = 1 T ∫ T x ( t ) d t c_0=\frac{1}{T}\displaystyle\int_{T}x(t)dt c0=T1Tx(t)dt a k = 2 T ∫ T x ( t ) c o s ( 2 π k T t ) d t a_k=\frac{2}{T}\displaystyle\int_Tx(t)cos(\frac{2\pi k}{T}t)dt ak=T2Tx(t)cos(T2πkt)dt b k = 2 T ∫ T x ( t ) s i n ( 2 π k T t ) d t b_k=\frac{2}{T}\displaystyle\int_Tx(t)sin(\frac{2\pi k}{T}t)dt bk=T2Tx(t)sin(T2πkt)dt

现实世界中实际的乐器发出的乐音,波形都是相当复杂。但乐音基本上可以用三个要素来进行描述,分别是音高响度音色。乐音的响度如之前提到,由功率来决定。乐音的音高则由振幅最大的基波决定,其频率为基频。而除了基波以外,还有无数频率为基频整数倍的谐波泛音。这些谐波泛音的振幅决定了波形的特点,也决定了音频的音色

在220-11000Hz之间分别生成1、5、10、20、50个频率的正弦、余弦信号以模拟谐波,随机生成振幅,且随着频率增加振幅减小。

import numpy as np
import random

random.seed(0)

def generate_timbre(K):
    return ak.synthesis([ak.create_Single_Freq_Audio(random.uniform(0.0,0.05)/i,220 * i,44100,1,np.pi/2)+ak.create_Single_Freq_Audio(random.uniform(0.0,0.05)/i,220 * i,44100,1) for i in range(1,K+1)])

p = generate_timbre(1)
p.sound()
p = generate_timbre(5)
p.sound()
p = generate_timbre(10)
p.sound()
p = generate_timbre(20)
p.sound()
p = generate_timbre(50)
p.sound()

'''
outputs:
The power of the audio:  0.0016094289253369737
The power of the audio:  0.0006342329657102692
The power of the audio:  0.0014485557278367813
The power of the audio:  0.001302351598106276
The power of the audio:  0.0016324227654416416
'''

可以听到我们生成了5种音色不太相同的电子音。

aly.autocorr(p).plot(-440,441,xlabel="k")    #计算p的自相关函数,并截取k取[-440,441)的部分

在这里插入图片描述

aly.autocorr(p).plot(-10,10,xlabel="t/ms")    #计算p的自相关函数,并截取[-10ms,10ms)的部分

在这里插入图片描述

观察自相关函数可以发现,虽然音频p由多个频率的分量叠加而成,但周期依然约为200个采样点,对应约 4.54 m s 4.54ms 4.54ms的时间长度,即 220 H z 220Hz 220Hz正弦波对应的周期。

aly.getMaxFrequency(p)

'''
outputs:
(220.0, 0.0449080316623462)
'''

getMaxFrequency函数可以获取音频中振幅最大的频率成分及振幅。可见 220 H z 220Hz 220Hz是音频中的主要频率成分。

噪音

噪音是没有明显音高的音频信号,通常由随机振动产生。典型的噪音是白噪音,它的性质是 γ ( τ ) = { σ 2 , τ = 0 0 , O t h e r s \gamma(\tau)=\begin{cases}\sigma^2,&\tau=0\\0,& Others\end{cases} γ(τ)={ σ2,0,τ=0Others,即自协方差函数当且仅当时间差 τ \tau τ为0时等于方差,其余时刻协方差都为0。若白噪音每一时刻均独立服从零均值高斯分布 N ( 0 , σ 2 ) N(0,\sigma^2) N(0,σ2),则称为高斯白噪音

当一个随机过程任意有限维分布函数只与时间差有关,即随机过程 { X ( t ) , t ∈ T } \{X(t),t\in T\} { X(t),tT}若满足:对任意 n ∈ N n\in N nN,任选 t 1 , t 2 , . . . , t n ∈ T t_1,t_2,...,t_n\in T t1,t2,...,tnT,以及任意h,当 t i + h ∈ T , i = 1 , 2 , . . . , n t_i+h\in T,i=1,2,...,n ti+hT,i=1,2,...,n时,n维随机变量 ( X ( t 1 ) , X ( t 2 ) , . . . , X ( t n ) ) (X(t_1),X(t_2),...,X(t_n)) (X(t1),X(t2),...,X(tn)) ( X ( t 1 + h ) , X ( t 2 + h ) , . . . , X ( t n + h ) ) (X(t_1+h),X(t_2+h),...,X(t_n+h)) (X(t1+h),X(t2+h),...,X(tn+h))具有相同的分布函数,则称该过程为平稳随机过程。平稳随机过程的定义非常严格,平时我们使用的一般是宽平稳随机过程的定义:前两阶矩与t无关而自相关函数只与时间间隔有关的随机过程称为宽平稳过程。高斯白噪音同样也是典型的平稳随机过程。

对于平稳随机过程而言,定义随机过程 X ( t ) X(t) X(t)的时间均值 < X ( t ) > = lim ⁡ T → ∞ ∫ − T T X ( t ) d t <X(t)>=\displaystyle\lim_{T\rightarrow ∞}\int_{-T}^TX(t)dt <X(t)>=TlimTTX(t)dt,时间相关函数为 < X ( t ) X ( t + τ ) > = lim ⁡ T → ∞ ∫ − T T X ( t ) X ( t + τ ) d t <X(t)X(t+\tau)>=\displaystyle\lim_{T\rightarrow ∞}\int_{-T}^TX(t)X(t+\tau)dt <X(t)X(t+τ)>=TlimTTX(t)X(t+τ)dt,若 P { < X ( t ) > = E { X ( t ) } = μ X } = 1 P\{<X(t)>=E\{X(t)\}=\mu_X\}=1 P{ <X(t)>=E{ X(t)}=μX}=1,则称该过程的均值具有各态历经性,若 P { < X ( t + τ ) X ( t ) > = E { X ( t + τ ) X ( t ) } = R X ( τ ) } = 1 P\{<X(t+\tau)X(t)>=E\{X(t+\tau)X(t)\}=R_X(\tau)\}=1 P{ <X(t+τ)X(t)>=E{ X(t+τ)X(t)}=RX(τ)}=1,则称该过程的自协方差函数具有各态历经性。各态历经的随机过程可以使用时间均值来估计期望,还可以使用自协方差函数在时间上的积分来估计在概率密度上的积分,因此可以使用各态历经平稳随机过程的一次足够长的实现来估计其一二阶矩。

对应于各态历经的随机过程的模拟音频信号,我们只需要将其一次实现表示为足够长时间的数字音频信号,就可以根据采样得到的样本,来估计其均值、方差和自协方差函数。

噪音同样也有能量、功率,其计算方法和意义与乐音是一致的。对于高斯白噪音而言,其功率就等于方差 σ 2 \sigma^2 σ2

如果某个音频信号当中混杂着噪音,使用信号和噪音的功率我们可以定义信噪比: s n r = 10 l o g 10 P 信号 P 噪音 ( d B ) snr=10log_{10}\frac{P_{信号}}{P_{噪音}}(dB) snr=10log10P噪音P信号(dB)

monotone_without_wn = ak.create_Single_Freq_Audio(0.02,440,44100,1)
monotone_with_wn = monotone_without_wn.addWgn(10)   #Add Gaussian white noise with a signal-to-noise ratio of 10 dB to the monotone
monotone_with_wn.sound()

'''
outputs:
The power of the audio:  0.0002200717840069502
'''
monotone_with_wn.plot(0, 0.01)

在这里插入图片描述

wn = monotone_with_wn - monotone_without_wn #减去单音,只留下白噪音
wn.sound()

'''
outputs:
The power of the audio:  1.9942616922205676e-05
'''
wn.plot(0, 0.01)

在这里插入图片描述

np.mean(wn.samples), np.var(wn.samples)

'''
outputs:
(-8.061218163239382e-06, 1.99425519389674e-05)
'''

可以看见白噪音的均值接近于0,而方差等于功率。

aly.autocorr(wn).plot(-440,441,xlabel="k")

在这里插入图片描述

白噪音的自相关函数当且仅当 k = 0 k=0 k=0时显著大于0。

考虑这样一个随机过程通式: X t = c 0 + ∑ k = 1 ∞ [ a k s i n ( 2 π k T t ) + b k c o s ( 2 π k T t ) ] + α t , − ∞ < t < ∞ X_t=c_0+\displaystyle \sum_{k=1}^∞ [a_k sin(\frac{2\pi k}{T} t)+b_k cos(\frac{2\pi k}{T} t)]+\alpha_t,-∞<t<∞ Xt=c0+k=1[aksin(T2πkt)+bkcos(T2πkt)]+αt,<t<,其中 α t \alpha_t αt描述了随机振动,而 ∑ k = 1 ∞ [ a k s i n ( 2 π k T t ) + b k c o s ( 2 π k T t ) ] + c 0 \displaystyle \sum_{k=1}^∞ [a_k sin(\frac{2\pi k}{T} t)+b_k cos(\frac{2\pi k}{T} t)]+c_0 k=1[aksin(T2πkt)+bkcos(T2πkt)]+c0则是任意确定性周期信号的傅里叶级数表示形式,我们先假设 a k a_k ak b k b_k bk c 0 c_0 c0 T T T均为随时间不变的常数,且 α t \alpha_t αt是平稳随机过程的一次实现。此时该随机过程的均值随时间改变,因此显然不是平稳随机过程。但我们可以将该随机过程分解为随机和非随机的部分,这样随机部分就是一个平稳随机过程,而非随机部分则是无数个确定的正弦信号的叠加。

猜你喜欢

转载自blog.csdn.net/weixin_43441742/article/details/126093476
今日推荐