pyAudioKits是基于librosa和其他库的强大Python音频工作流支持。
通过pip安装:
pip install pyAudioKits
本项目的GitHub地址,如果这个项目帮助到了你,请为它点上一颗star,谢谢你的支持!如果你在使用过程中有任何问题,请在评论区留言或在GitHub上提issue,我将持续对该项目进行维护。
import pyAudioKits.audio as ak
import pyAudioKits.analyse as aly
import pyAudioKits.algorithm as alg
本节介绍从语音信号中滤除噪声,从而增强语音信号的方法。注意这里的“噪声”和我们之前定义的物理学意义上的噪音不同,它指的是不包含有用语音信息的音频成分,通常和语音信号在时域相互叠加。噪声会掩盖语音成分,使得语音信号的质量下降。人耳会难以对语音进行分辨,机器从语音中提取的特征也会受到干扰。
在时域和语音叠加的噪声若变换到频域后可以和语音信号相互分离,则使用第五节中介绍的LTI滤波器就可以对噪声进行移除。然而,若噪声在频域依然和语音信号相互叠加,使用LTI滤波器对噪声进行滤波时必然同时会导致语音信号特定频率成分的严重损失。此时,应当使用基于噪声估计或信号估计的统计型滤波方法。
本节将对谱减法、维纳滤波和卡尔曼滤波及其使用pyAudioKits的实现进行介绍。
白噪音是一种常见的噪声,音频录制时产生的电流噪声就属于白噪音。首先从"sample_audio/test.wav"读入示例音频。
s = ak.read_Audio("sample_audio/test.wav")
s.sound()
'''
outputs:
The power of the audio: 0.0007003224173257517
'''
然后在示例音频上增加信噪比为10dB的白噪音。
f = s.addWgn(10)
f.sound()
'''
outputs:
The power of the audio: 0.0007690852704655619
'''
可以听见此时背景出现了白噪音干扰。在波形图上也有体现。
s.plot(), f.plot()


分别绘制信号加噪前后的语谱图。
aly.FFT(s.framing()).plot(freq_scale="mel", plot_type="dB"), aly.FFT(f.framing()).plot(freq_scale="mel", plot_type="dB")


白噪音的频率分布在整个频率区间上,和语音信号在频域完全无法分离。在这种情况下,无论如何设计LTI滤波器的通带阻带特性,都无法保证在尽可能保持语音信号成分的同时尽可能滤除白噪音。我们必须采用别的滤波方式。本节将介绍谱减法、维纳滤波和卡尔曼滤波三种滤波方式。
谱减法的思路非常简单:如果知道加噪信号的频谱和噪声的频谱,那么从加噪信号频谱中减去噪声的频谱即可得到原始信号的频谱。这种方法适用于加性噪声,即噪声和语音信号是线性相加的。由于傅里叶变换是线性变换,因此时域线性相加的噪声和语音信号其频谱也是线性相加的。
在进行谱减法时,我们需要先得到噪声估计。谱减法的步骤如下:
由于我们对带噪语音的每一帧都使用相同的噪声估计来运用谱减法,因此不难看出来,运用谱减法的前提是每一帧的噪声统计特性均相同。因此,谱减法要求噪声必须服从平稳随机过程。白噪音服从平稳随机过程,因此可以使用谱减法来滤除。
我们的带噪语音中后几秒是没有语音的,只存在噪声,因此我们取带噪语音的最后0.3秒作为噪声估计。
f1=alg.specSubstract(f,f[f.getDuration()-0.3:])
s.plot(), f.plot(), f1.plot() #Draw the waveform of original signal, noisy signal and spectral subtraction result in turn



从波形图上看,谱减法已经大大削除噪声的强度,同时尽可能保留了语音的强度。
aly.FFT(s.framing()).plot(freq_scale="mel", plot_type="dB"), aly.FFT(f.framing()).plot(freq_scale="mel", plot_type="dB"), aly.FFT(f1.framing()).plot(freq_scale="mel", plot_type="dB") #依次绘制原始语音、带噪语音和谱减法结果的语谱图



从语谱图上来看,全频率区间上的白噪音都得到了一定程度的消除,但在全频率区间上也都依然存在颗粒状的残留。
维纳滤波是一种理论最优的滤波方法,它利用了平稳随机过程的统计特性。
已知一个含加性背景噪声的信号 x = s + v x=s+v x=s+v,目标是解出 h = min h E { e 2 } = min h E { ( s − x ∗ h ) 2 } = min h E { ( s [ n ] − ∑ m x [ n − m ] h [ m ] ) 2 } h=\displaystyle\min_hE\{e^2\}=\min_hE\{(s-x*h)^2\}=\min_hE\{(s[n]-\sum_{m}x[n-m]h[m])^2\} h=hminE{e2}=hminE{(s−x∗h)2}=hminE{(s[n]−m∑x[n−m]h[m])2},其中 h h h是维纳滤波器系统单位冲激响应, y = h ∗ x y=h*x y=h∗x。
对 h [ j ] h[j] h[j]求偏导,得 ∂ e 2 ∂ h [ j ] = 2 E { ( ∑ m x [ n − m ] h [ m ] − s [ n ] ) x [ n − j ] } = 2 E { ∑ m h [ m ] x [ n − m ] x [ n − j ] − s [ n ] x [ n − j ] } \displaystyle\frac{\partial e^2}{\partial h[j]}=2E\{(\sum_{m}x[n-m]h[m]-s[n])x[n-j]\}=2E\{\sum_{m}h[m]x[n-m]x[n-j]-s[n]x[n-j]\} ∂h[j]∂e2=2E{(m∑x[n−m]h[m]−s[n])x[n−j]}=2E{m∑h[m]x[n−m]x[n−j]−s[n]x[n−j]}
对原始信号进行分帧加窗。窗长不应该太长,以保证 x [ n ] x[n] x[n]和 s [ n ] s[n] s[n]在窗口内的平稳性;窗长也不应该太短,以保证可以用 x [ n ] x[n] x[n]和 s [ n ] s[n] s[n]在窗口内信息来估计各态历经过程的统计特性。这样就可以将上式改写为: 2 E { ∑ m h [ m ] x [ n − m ] x [ n − j ] − s [ n ] x [ n − j ] } = 2 ∑ m = 0 N h [ m ] x [ n − m ] x [ n − j ] − ∑ n = s [ n ] x [ n − j ] = 2 ∑ m = 0 N h [ m ] R x x [ n − m ] − 2 R x s [ n ] \begin{aligned}\displaystyle2E\{\sum_{m}h[m]x[n-m]x[n-j]-s[n]x[n-j]\}&=2\sum_{m=0}^Nh[m]x[n-m]x[n-j]-\sum_{n=}s[n]x[n-j]\\&=2\sum_{m=0}^N h[m]R_{xx}[n-m]-2R_{xs}[n]\end{aligned} 2E{m∑h[m]x[n−m]x[n−j]−s[n]x[n−j]}=2m=0∑Nh[m]x[n−m]x[n−j]−n=∑s[n]x[n−j]=2m=0∑Nh[m]Rxx[n−m]−2Rxs[n]
令导数为0,得到 R x s [ n ] = ∑ m h [ m ] R x x [ n − m ] , n ≥ 0 \displaystyle R_{xs}[n]=\sum_mh[m]R_{xx}[n-m],n≥0 Rxs[n]=m∑h[m]Rxx[n−m],n≥0
这样我们就可以得到FIR滤波器 h h h满足的等式: R x s [ n ] = ∑ m = 0 N − 1 h [ m ] R x x [ n − m ] R_{xs}[n]=\displaystyle\sum_{m=0}^{N-1}h[m]R_{xx}[n-m] Rxs[n]=m=0∑N−1h[m]Rxx[n−m]。只要知道 R x s R_{xs} Rxs和 R x x R_{xx} Rxx中的N个点,利用线性代数手段就可以计算出 h ( m ) h(m) h(m)的N个值,从而得到N阶的FIR滤波器 h h h。在实际应用中直接用矩阵求逆的方法来求解是比较困难的,但可以使用Levinson-Durbin迭代算法来进行求解。
为了求解 h h h,我们需要知道 R x s R_{xs} Rxs和 R x x R_{xx} Rxx,这需要通过原始信号 s s s的估计来进行计算。
f2=alg.wienerFilter(f,s)
s.plot(), f.plot(), f2.plot() #依次绘制原始信号、加噪信号和维纳滤波结果的波形图



aly.FFT(s.framing()).plot(freq_scale="mel", plot_type="dB"), aly.FFT(f.framing()).plot(freq_scale="mel", plot_type="dB"), aly.FFT(f2.framing()).plot(freq_scale="mel", plot_type="dB") #依次绘制原始语音、带噪语音和维纳滤波结果的语谱图



通过波形图和语谱图可以看出来,维纳滤波的效果是非常好的。它的缺点是需要原始语音信号的估计。
在了解卡尔曼滤波之前,需要先了解马尔可夫假设:设有一个随机过程 { X t , t = 0 , 1 , . . . } \{X_t,t=0,1,...\} {Xt,t=0,1,...}满足 P ( X t ∣ X 0 : t − 1 ) = P ( X t ∣ X t − 1 ) P(X_t|X_{0:t-1})=P(X_t|X_{t-1}) P(Xt∣X0:t−1)=P(Xt∣Xt−1),其中 P ( X t ) P(X_t) P(Xt)是随机过程 X X X在时间 t t t的概率分布, X 0 : t − 1 X_{0:t-1} X0:t−1表示 { X 0 , . . . , X t − 1 } \{X_0,...,X_{t-1}\} {X0,...,Xt−1}。在这里,时间被离散化为一个个时刻。随机过程 { X t } \{X_t\} {Xt}的概率分布并非时间独立,但也并非任意时刻之间都有依赖性。 P ( X t ∣ X 0 : t − 1 ) = P ( X t ∣ X t − 1 ) P(X_t|X_{0:t-1})=P(X_t|X_{t-1}) P(Xt∣X0:t−1)=P(Xt∣Xt−1)的意义在于 t t t时刻 { X t } \{X_t\} {Xt}的概率分布只受 t − 1 t-1 t−1时刻 X X X的取值影响。我们称 P ( X t ∣ X t − 1 ) P(X_t|X_{t-1}) P(Xt∣Xt−1)为满足马尔可夫假设的转移模型。
假设 { X t , t = 0 , 2 , . . . } \{X_t,t=0,2,...\} {Xt,t=0,2,...}是隐状态变量,我们没有办法直接观察到这些变量的取值。但我们能观察到另外一组变量 { E t , t = 0 , 1 , . . . } \{E_t,t=0,1,...\} {Et,t=0,1,...}的取值,这组变量称为证据变量。证据变量构成另一个随机过程,且该随机过程和 { X t } \{X_t\} {Xt}之间是概率非独立的,有 P ( E t ∣ X 1 : t , E 1 : t − 1 ) = P ( E t ∣ X t ) P(E_t|X_{1:t},E_{1:t-1})=P(E_t|X_t) P(Et∣X1:t,E1:t−1)=P(Et∣Xt),其中 E 0 : t − 1 E_{0:t-1} E0:t−1表示 { E 0 , . . . , E t − 1 } \{E_0,...,E_{t-1}\} {E0,...,Et−1},则称 P ( E t ∣ X t ) P(E_t|X_t) P(Et∣Xt)为传感器模型。
卡尔曼滤波基于马尔可夫假设,且设所有随机变量都是连续变量,则有 P ( x t + 1 ∣ e 0 : t ) = ∫ x t P ( x t + 1 ∣ x t ) P ( x t ∣ e 0 : t ) d x t P(x_{t+1}|e_{0:t})=\displaystyle\int_{x_t}P(x_{t+1}|x_t)P(x_t|e_{0:t})dx_t P(xt+1∣e0:t)=∫xtP(xt+1∣xt)P(xt∣e0:t)dxt。它还假设所有的概率分布都是正态分布,且若 { x t } \{x_t\} {xt}和 { e t } \{e_t\} {et}是多维随机变量 { x ⃗ t } \{\vec x_t\} {xt}、 { e ⃗ t } \{\vec e_t\} {et}的话则为多维正态分布。设每一时刻 { x ⃗ t } \{\vec x_t\} {xt}满足的多维正态分布的均值向量为 { μ ⃗ t , t = 0 , 1 , . . . } \{\vec\mu_t,t=0,1,...\} {μt,t=0,1,...}、协方差矩阵为 { Σ t , t = 0 , 1 , . . . } \{\Sigma_t,t=0,1,...\} {Σt,t=0,1,...},则卡尔曼滤波这样对变量之间关系进行建模:
此时,任意时刻 { x ⃗ t } \{\vec x_t\} {xt}的均值向量和协方差矩阵都可以通过下面的公式来递推解出:
{ μ t + 1 ⃗ = F μ t ⃗ + K t + 1 ( e t + 1 ⃗ − H F μ t ⃗ ) Σ t + 1 = ( I − K t + 1 H ) ( F Σ t F T + Σ x ) \begin{cases}\vec{\mu_{t+1}}=F\vec{\mu_t}+K_{t+1}(\vec{e_{t+1}}-HF\vec{\mu_t})\\\Sigma_{t+1}=(I-K_{t+1}H)(F\Sigma_tF^T+\Sigma_x)\end{cases} {μt+1=Fμt+Kt+1(et+1−HFμt)Σt+1=(I−Kt+1H)(FΣtFT+Σx)
其中 K t + 1 = ( F Σ t F T + Σ x ) H T ( H ( F Σ t F T + Σ x ) H T + Σ e ) − 1 K_{t+1}=(F\Sigma_tF^T+\Sigma_x)H^T(H(F\Sigma_tF^T+\Sigma_x)H^T+\Sigma_e)^{-1} Kt+1=(FΣtFT+Σx)HT(H(FΣtFT+Σx)HT+Σe)−1,称为卡尔曼增益矩阵
那么,如何将卡尔曼滤波的模型应用到语音增强问题中呢?对于语音增强问题,我们观察到的是含噪的信号,而我们要估计的是原始信号。也就是说我们可以把含噪信号看作是证据变量,而原始信号是隐变量。假如噪声是加性高斯噪声,则传感器方程为 y n ⃗ = H x n ⃗ + v n ⃗ \vec{y_n}=H\vec{x_n}+\vec{v_n} yn=Hxn+vn,其中 H = [ 0 , 0 , . . . , 1 ] H=[0,0,...,1] H=[0,0,...,1], x ⃗ n \vec x_n xn、 v ⃗ n \vec v_n vn和 y ⃗ n \vec y_n yn分别是原始信号 x [ n ] x[n] x[n]、噪声 v [ n ] v[n] v[n]和含噪信号 y [ n ] y[n] y[n]的向量表示形式,我们先假设其长度是任意的。而为了得到转移方程,卡尔曼滤波考虑了发音模型中的系统:如果忽视嘴唇的影响,则声带和声道可以被建模为全极点系统,系统函数为 H ( z ) = G 1 − ∑ m = 1 p z − m H(z)=\frac{\displaystyle G}{\displaystyle 1-\sum_{m=1}^{p}z^{-m}} H(z)=1−m=1∑pz−mG。对系统进行激励的气流 w [ n ] w[n] w[n]近似于高斯白噪音信号,因此有产生的语音 x [ n ] = ∑ m = 1 p a [ m ] x [ n − m ] + G w [ n ] x[n]=\displaystyle\sum_{m=1}^{p}a[m]x[n-m]+Gw[n] x[n]=m=1∑pa[m]x[n−m]+Gw[n],则有转移方程为 x n ⃗ = F x n − 1 ⃗ + G w n ⃗ \vec{x_n}=F\vec{x_{n-1}}+G\vec{w_n} xn=Fxn−1+Gwn,其中 x n ⃗ = [ x [ n − p + 1 ] , x [ n − p + 2 ] , . . . , x [ n ] ] T \vec{x_n}=[x[n-p+1],x[n-p+2],...,x[n]]^T xn=[x[n−p+1],x[n−p+2],...,x[n]]T且 F = [ 0 1 . . . 0 ⋮ ⋮ ⋮ ⋮ 0 0 . . . 1 a [ p ] a [ p − 1 ] . . . a [ 1 ] ] F=\left[\begin{matrix}0&1&...&0\\\vdots&\vdots&\vdots&\vdots\\0&0&...&1\\a[p]&a[p-1]&...&a[1]\end{matrix}\right] F= 0⋮0a[p]1⋮0a[p−1]...⋮......0⋮1a[1] 。因此对于p阶的卡尔曼滤波器, x ⃗ n \vec x_n xn、 v ⃗ n \vec v_n vn和 y ⃗ n \vec y_n yn的长度为p。
为了计算 a a a,我们需要使用一种名为线性预测编码(LPC)的技术,它本质上就是对p阶自回归(AR)模型系数的计算。自回归模型有很多种参数估计方法,LPC所用的方法是Levinson递推法。
运用自回归模型的前提是 x [ n ] x[n] x[n]必须是平稳随机过程的一次实现,而语音信号往往是不平稳的。为此,我们需要将语音信号分帧加窗。然后在每帧内就可以使用LPC系数代替 a a a,输出误差e则为对白噪声 G w [ n ] Gw[n] Gw[n]的估计。
Σ x \Sigma_x Σx是一个对角线上元素均为e的 p × p p\times p p×p对角阵。 Σ e \Sigma_e Σe通过计算噪声估计的协方差得到。
最后,我们设初始状态 μ 0 ⃗ = [ y [ 0 ] , y [ 1 ] , . . . , y [ p − 1 ] ] T \vec{\mu_0}=[y[0],y[1],...,y[p-1]]^T μ0=[y[0],y[1],...,y[p−1]]T,而 Σ 0 \Sigma_0 Σ0为一个对角线上元素均为噪声方差的 p × p p\times p p×p对角阵,这样就可以对 μ n ⃗ \vec{\mu_n} μn进行迭代估计。 μ n ⃗ \vec{\mu_n} μn是 x ⃗ n \vec x_n xn的均值,而 x n ⃗ = [ x [ n − p + 1 ] , x [ n − p + 2 ] , . . . , x [ n ] ] T \vec{x_n}=[x[n-p+1],x[n-p+2],...,x[n]]^T xn=[x[n−p+1],x[n−p+2],...,x[n]]T,也就是说我们可以用 μ n ⃗ \vec{\mu_n} μn来估计 x [ m ] , m = n − p + 1 , n − p + 2 , . . . , n x[m],m=n-p+1,n-p+2,...,n x[m],m=n−p+1,n−p+2,...,n。注意 μ n ⃗ \vec{\mu_n} μn和 μ n + 1 ⃗ \vec{\mu_{n+1}} μn+1对应的信号 x [ n ] x[n] x[n]有重叠的部分,此时使用 μ n + 1 ⃗ \vec{\mu_{n+1}} μn+1的估计来覆盖使用 μ n ⃗ \vec{\mu_n} μn的估计。
估计要分帧进行:
f3=alg.kalmanFilter(f,f[f.getDuration()-0.3:])
s.plot(), f.plot(), f3.plot() #依次绘制原始信号、加噪信号和卡尔曼滤波结果的波形图



aly.FFT(s.framing()).plot(freq_scale="mel", plot_type="dB"), aly.FFT(f.framing()).plot(freq_scale="mel", plot_type="dB"), aly.FFT(f3.framing()).plot(freq_scale="mel", plot_type="dB") #依次绘制原始语音、带噪语音和卡尔曼滤波结果的语谱图



卡尔曼滤波对于高斯白噪声有非常好的效果,这是因为高斯白噪声符合卡尔曼滤波建模时的假设。
接下来,我们从示例音频中导入另一段噪声样本。该噪声数据来自https://www.kaggle.com/datasets/bharatsahu/speech-commands-classification-dataset
noise=ak.read_Audio("sample_audio/_background_noise_/exercise_bike.wav")
aly.FFT(noise.framing()).plot(freq_scale="mel", plot_type="dB")

该噪声的频率成分分布在[0,采样率/2]上,在低频部分略大。
noise.plot()

noise.getDuration(), noise.sr, s.getDuration(), s.sr
'''
outputs:
(61.253875, 16000, 4.992290249433107, 22050)
'''
噪声样本的时长远大于示例音频时长,且采样率仅为16000Hz,小于示例音频的采样率22050Hz。因此我们需要对噪声样本进行切片使其匹配示例音频的时长,还要对噪声样本进行重采样。
noise = noise.resample(s.sr)
noise.sr
'''
outputs:
22050
'''
重采样后,噪声样本的采样率也变为了22050Hz。我们取其前5秒,使其匹配示例音频时长后,与示例音频以5dB的信噪比混合。
snr=5
f = ak.mixWithSNR(s,noise[0:s.getDuration()],snr,maintain="signal")
aly.FFT(f.framing()).plot(freq_scale="mel", plot_type="dB")

重采样后的噪声信号的频率成分仍旧分布在[0,8000Hz]上,其中8000Hz是原采样率16000Hz的一半,没有随着采样率的提高而拓展到11025Hz,即22050Hz的一半。这是因为原始的噪音样本采用16000Hz采样后,最多只能保留[0,采样率/2]的频谱信息,高于采样率/2的频谱信息将会丢失,并无法被还原。采样率/2被称为奈奎斯特率,可逆的采样满足被采样信号的所有频率成分都小于奈奎斯特率,否则采样就会是非可逆的。
f3=alg.kalmanFilter(f,f[f.getDuration()-0.3:])
s.plot(), f.plot(), f3.plot()



aly.FFT(s.framing()).plot(freq_scale="mel", plot_type="dB"), aly.FFT(f.framing()).plot(freq_scale="mel", plot_type="dB"), aly.FFT(f3.framing()).plot(freq_scale="mel", plot_type="dB")



对于非高斯白噪声,卡尔曼滤波的效果便有所下降。
关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。
Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack
这个问题在这里已经有了答案:关闭10年前。PossibleDuplicate:Pythonconditionalassignmentoperator对于这样一个简单的问题表示歉意,但是谷歌搜索||=并不是很有帮助;)Python中是否有与Ruby和Perl中的||=语句等效的语句?例如:foo="hey"foo||="what"#assignfooifit'sundefined#fooisstill"hey"bar||="yeah"#baris"yeah"另外,类似这样的东西的通用术语是什么?条件分配是我的第一个猜测,但Wikipediapage跟我想的不太一样。
什么是ruby的rack或python的Java的wsgi?还有一个路由库。 最佳答案 来自Python标准PEP333:Bycontrast,althoughJavahasjustasmanywebapplicationframeworksavailable,Java's"servlet"APImakesitpossibleforapplicationswrittenwithanyJavawebapplicationframeworktoruninanywebserverthatsupportstheservletAPI.ht
导读:随着叮咚买菜业务的发展,不同的业务场景对数据分析提出了不同的需求,他们希望引入一款实时OLAP数据库,构建一个灵活的多维实时查询和分析的平台,统一数据的接入和查询方案,解决各业务线对数据高效实时查询和精细化运营的需求。经过调研选型,最终引入ApacheDoris作为最终的OLAP分析引擎,Doris作为核心的OLAP引擎支持复杂地分析操作、提供多维的数据视图,在叮咚买菜数十个业务场景中广泛应用。作者|叮咚买菜资深数据工程师韩青叮咚买菜创立于2017年5月,是一家专注美好食物的创业公司。叮咚买菜专注吃的事业,为满足更多人“想吃什么”而努力,通过美好食材的供应、美好滋味的开发以及美食品牌的孵
华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o
我想解析一个已经存在的.mid文件,改变它的乐器,例如从“acousticgrandpiano”到“violin”,然后将它保存回去或作为另一个.mid文件。根据我在文档中看到的内容,该乐器通过program_change或patch_change指令进行了更改,但我找不到任何在已经存在的MIDI文件中执行此操作的库.他们似乎都只支持从头开始创建的MIDI文件。 最佳答案 MIDIpackage会为您完成此操作,但具体方法取决于midi文件的原始内容。一个MIDI文件由一个或多个音轨组成,每个音轨是十六个channel中任何一个上的
C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.
本文主要介绍在使用Selenium进行自动化测试或者任务时,对于使用了iframe的页面,如何定位iframe中的元素文章目录场景描述解决方案具体代码场景描述当我们在使用Selenium进行自动化测试的时候,可能会遇到一些界面或者窗体是使用HTML的iframe标签进行承载的。对于iframe中的标签,如果直接查找是无法找到的,会抛出没有找到元素的异常。比如近在咫尺的例子就是,CSDN的登录窗体就是使用的iframe,大家可以尝试通过F12开发者模式查看到的tag_name,class_name,id或者xpath来定位中的页面元素,会抛出NoSuchElementException异常。解决
需求:要创建虚拟机,就需要给他提供一个虚拟的磁盘,我们就在/opt目录下创建一个10G大小的raw格式的虚拟磁盘CentOS-7-x86_64.raw命令格式:qemu-imgcreate-f磁盘格式磁盘名称磁盘大小qemu-imgcreate-f磁盘格式-o?1.创建磁盘qemu-imgcreate-fraw/opt/CentOS-7-x86_64.raw10G执行效果#ls/opt/CentOS-7-x86_64.raw2.安装虚拟机使用virt-install命令,基于我们提供的系统镜像和虚拟磁盘来创建一个虚拟机,另外在创建虚拟机之前,提前打开vnc客户端,在创建虚拟机的时候,通过vnc