语音数据增强及python实现
<p>博客作者:凌逆战</p><p>博客地址:https://www.cnblogs.com/LXP-Never/p/13404523.html</p>
<hr>
<p> 音频时域波形具有以下特征:音调,响度,质量。我们在进行数据增强时,最好只做一些小改动,使得增强数据和源数据存在较小差异即可,切记不能改变原有数据的结构,不然将产生“脏数据”,通过对音频数据进行数据增强,能有助于我们的模型避免过度拟合并变得更加通用。</p>
<p> 我发现对声波的以下改变是有用的:<span style="color: rgba(0, 128, 128, 1)"><strong>Noise addition</strong></span><span style="color: rgba(136, 136, 136, 1)">(增加噪音)、<span style="color: rgba(0, 128, 128, 1)"><strong>Add reverb</strong><span style="color: rgba(128, 128, 128, 1)">(</span></span></span><span style="color: rgba(128, 128, 128, 1)">增加混响)</span>、<span style="color: rgba(0, 128, 128, 1)"><strong>Time shifting</strong></span><span style="color: rgba(136, 136, 136, 1)">(时移)</span>、<span style="color: rgba(0, 128, 128, 1)"><strong>Pitch shifting</strong></span><span style="color: rgba(136, 136, 136, 1)">(改变音调)</span>和<span style="color: rgba(0, 128, 128, 1)"><strong>Time stretching</strong></span><span style="color: rgba(136, 136, 136, 1)">(时间拉伸)</span>。</p>
<p>本章需要使用的python库:</p>
<ul>
<li><strong>matplotlib</strong>:绘制图像</li>
<li><strong>librosa</strong>:音频数据处理</li>
<li><strong>numpy</strong>:矩阵数据处理</li>
</ul>
<p>常见的失真有:</p>
<ol>
<li>加性声学噪声:加性噪声与期望信号不相干,平稳加性噪声(背景环境声音、嗡嗡声、功放噪音),非平稳加性噪声(媒体干扰、非期望语音干扰和一些电子干扰)</li>
<li>声学混响:多径反射引起的叠加效应<span style="color: rgba(136, 136, 136, 1)">(与期望信号相关)</span></li>
<li>卷积信道效应:导致不均匀或带宽限制响应,为了去除信道脉冲响应,做信道均衡时对通信信道没有有效建模</li>
<li>非线性失真:信号输入时不适当的增益,常出现与幅度限制、麦克风功放等</li>
<li>加性宽带电子噪声</li>
<li>电器干扰</li>
<li>编码失真:比如压缩编码</li>
<li>录音仪器引起的失真:麦克风频率响应不足</li>
</ol><hr>
<p> 使用先画出原始语音数据的语谱图和波形图</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> librosa
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> numpy as np
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> matplotlib.pyplot as plt
plt.rcParams[</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">font.sans-serif</span><span style="color: rgba(128, 0, 0, 1)">'</span>] = [<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">SimHei</span><span style="color: rgba(128, 0, 0, 1)">'</span>]<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 用来正常显示中文标签</span>
plt.rcParams[<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">axes.unicode_minus</span><span style="color: rgba(128, 0, 0, 1)">'</span>] = False<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 用来正常显示符号</span>
fs = 16000<span style="color: rgba(0, 0, 0, 1)">
wav_data, _ </span>= librosa.load(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">./p225_001.wav</span><span style="color: rgba(128, 0, 0, 1)">"</span>, sr=fs, mono=<span style="color: rgba(0, 0, 0, 1)">True)
</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> ########### 画图</span>
plt.subplot(2, 2, 1<span style="color: rgba(0, 0, 0, 1)">)
plt.title(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">语谱图</span><span style="color: rgba(128, 0, 0, 1)">"</span>, fontsize=15<span style="color: rgba(0, 0, 0, 1)">)
plt.specgram(wav_data, Fs</span>=16000, scale_by_freq=True, sides=<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">default</span><span style="color: rgba(128, 0, 0, 1)">'</span>, cmap=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">jet</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
plt.xlabel(</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">秒/s</span><span style="color: rgba(128, 0, 0, 1)">'</span>, fontsize=15<span style="color: rgba(0, 0, 0, 1)">)
plt.ylabel(</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">频率/Hz</span><span style="color: rgba(128, 0, 0, 1)">'</span>, fontsize=15<span style="color: rgba(0, 0, 0, 1)">)
plt.subplot(</span>2, 2, 2<span style="color: rgba(0, 0, 0, 1)">)
plt.title(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">波形图</span><span style="color: rgba(128, 0, 0, 1)">"</span>, fontsize=15<span style="color: rgba(0, 0, 0, 1)">)
time </span>= np.arange(0, len(wav_data)) * (1.0 /<span style="color: rgba(0, 0, 0, 1)"> fs)
plt.plot(time, wav_data)
plt.xlabel(</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">秒/s</span><span style="color: rgba(128, 0, 0, 1)">'</span>, fontsize=15<span style="color: rgba(0, 0, 0, 1)">)
plt.ylabel(</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">振幅</span><span style="color: rgba(128, 0, 0, 1)">'</span>, fontsize=15<span style="color: rgba(0, 0, 0, 1)">)
plt.tight_layout()
plt.show()</span></pre>
</div>
<p style="text-align: center"><img src="https://img2020.cnblogs.com/blog/1433301/202010/1433301-20201002213623124-2103237528.png" alt="" width="484" height="203" loading="lazy"></p>
<h1>时域增强</h1>
<h2>噪声增强</h2>
<p> 通过信噪比的公式推导出噪声的增益系数k。</p>
<p>$$SNR=10*log_{10}(\frac{S^2}{(kN)^2}) \Rightarrow k=\sqrt{\frac{S^2}{N^2*10^{\frac{SNR}{10}}}}$$</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> snr_aug_changeNoise(clean, noise, snr):
</span><span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(128, 0, 0, 1)"> 保持语音不变,改变噪声的幅度
snr = 10 * log10(signal_power / k*noise_power) </span><span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(0, 0, 0, 1)">
p_clean </span>= np.mean(clean ** 2)<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 纯净语音功率</span>
p_noise = np.mean(noise ** 2)<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 噪声功率</span>
noise_scale = np.sqrt(p_clean / (p_noise * 10 ** (snr / 10) +<span style="color: rgba(0, 0, 0, 1)"> EPS))
noisy </span>= clean + noise_scale *<span style="color: rgba(0, 0, 0, 1)"> noise
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> noisy, noise_scale
</span><span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> snr_aug_changeClean(clean, noise, snr):
</span><span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(128, 0, 0, 1)"> 保持噪声不变,改变语音的幅度
snr = 10 * log10(k*signal_power/ noise_power)</span><span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(0, 0, 0, 1)">
clean_power </span>= np.mean(clean ** 2<span style="color: rgba(0, 0, 0, 1)">)
noise_power </span>= np.mean(noise ** 2<span style="color: rgba(0, 0, 0, 1)">)
clean_scale </span>= np.sqrt(noise_power * 10 ** (snr / 10) / (clean_power + 1e-8<span style="color: rgba(0, 0, 0, 1)">))
noisy </span>= clean * clean_scale +<span style="color: rgba(0, 0, 0, 1)"> noise
</span><span style="color: rgba(0, 0, 255, 1)">return</span> noisy, clean_scale</pre>
</div>
<p><img src="https://img2020.cnblogs.com/blog/1433301/202010/1433301-20201002213835250-938287616.png" alt="" width="437" height="322" loading="lazy" style="display: block; margin-left: auto; margin-right: auto"></p>
<h2>音量增强</h2>
<p> 语音音量的单位为dB,音量增益可以基于平均音量或者最大瞬时音量,下面公式是基于平均音量推得dB增益:</p>
<p>$$dB=10*log_{10}(kS)^2\Rightarrow k=\sqrt{\frac{10^{\frac{dB}{10}}}{S^2}}$$</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> volumeAument1(wav, dB):
</span><span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(128, 0, 0, 1)">
:param wav: 语音
:param dB: 音量
:return:返回以指定dB增益后的语音
</span><span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(0, 0, 0, 1)">
power </span>= np.mean(wav ** 2)<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 平均功率</span>
scalar = np.sqrt(10 ** (dB / 10) / (power +<span style="color: rgba(0, 0, 0, 1)"> np.finfo(np.float32).eps))
wav </span>*=<span style="color: rgba(0, 0, 0, 1)"> scalar
</span><span style="color: rgba(0, 0, 255, 1)">return</span> wav, scalar</pre>
</div>
<p>$$dB=20*log_{10}kS\Rightarrow k=\frac{10^{\frac{db}{20}}}{A}$$</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> volumeAument2(wav, dB):
</span><span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(128, 0, 0, 1)">
:param wav: 语音
:param dB: 音量
:return:返回以指定dB增益后的语音
</span><span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(0, 0, 0, 1)">
rmswav </span>= (wav ** 2).mean() ** 0.5<span style="color: rgba(0, 0, 0, 1)">
scalar </span>= 10 ** (dB / 20) / (rmswav +<span style="color: rgba(0, 0, 0, 1)"> np.finfo(np.float32).eps)
wav </span>= wav *<span style="color: rgba(0, 0, 0, 1)"> scalar
</span><span style="color: rgba(0, 0, 255, 1)">return</span> wav, scalar</pre>
</div>
<p>其实这两个函数都可以,都可以达到目的,本质上都一样。</p>
<div class="cnblogs_code"><img src="https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" id="code_img_closed_b1eb73c6-21dd-448f-9a7a-b8f35ef98da2" class="code_img_closed"><img src="https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif" id="code_img_opened_b1eb73c6-21dd-448f-9a7a-b8f35ef98da2" class="code_img_opened" style="display: none">
<div id="cnblogs_code_open_b1eb73c6-21dd-448f-9a7a-b8f35ef98da2" class="cnblogs_code_hide">
<pre><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> -*- coding:utf-8 -*-</span><span style="color: rgba(0, 128, 0, 1)">
#</span><span style="color: rgba(0, 128, 0, 1)"> Author:凌逆战 | Never</span><span style="color: rgba(0, 128, 0, 1)">
#</span><span style="color: rgba(0, 128, 0, 1)"> Date: 2023/3/17</span>
<span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(128, 0, 0, 1)">
音量增强
</span><span style="color: rgba(128, 0, 0, 1)">"""</span>
<span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> numpy as np
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> librosa
EPS </span>=<span style="color: rgba(0, 0, 0, 1)"> np.finfo(float).eps
</span><span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> mean_dbfs(sample_data):
rms </span>= np.sqrt(np.mean(np.square(sample_data, dtype=<span style="color: rgba(0, 0, 0, 1)">np.float64)))
dbfs </span>= 20.0 * np.log10(max(1e-16<span style="color: rgba(0, 0, 0, 1)">, rms))
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> dbfs
</span><span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> volumeAument1(wav, dB):
</span><span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(128, 0, 0, 1)">
:param wav: 语音
:param dB: 音量
:return:返回以指定dB增益后的语音
</span><span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(0, 0, 0, 1)">
power </span>= np.mean(wav ** 2)<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 平均功率</span>
scalar = np.sqrt(10 ** (dB / 10) / (power +<span style="color: rgba(0, 0, 0, 1)"> np.finfo(np.float32).eps))
wav </span>*=<span style="color: rgba(0, 0, 0, 1)"> scalar
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> wav, scalar
</span><span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> volumeAument2(wav, dB):
</span><span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(128, 0, 0, 1)">
:param wav: 语音
:param dB: 音量
:return:返回以指定dB增益后的语音
</span><span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(0, 0, 0, 1)">
rmswav </span>= (wav ** 2).mean() ** 0.5<span style="color: rgba(0, 0, 0, 1)">
scalar </span>= 10 ** (dB / 20) / (rmswav +<span style="color: rgba(0, 0, 0, 1)"> np.finfo(np.float32).eps)
wav </span>= wav *<span style="color: rgba(0, 0, 0, 1)"> scalar
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> wav, scalar
sr </span>= 16000<span style="color: rgba(0, 0, 0, 1)">
wav </span>= librosa.load(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">./wavdata/TIMIT.WAV</span><span style="color: rgba(128, 0, 0, 1)">"</span>, sr=sr)<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> (46797,)</span>
<span style="color: rgba(0, 0, 255, 1)">print</span><span style="color: rgba(0, 0, 0, 1)">(wav.shape)
wav, scalar </span>= volumeAument1(wav, dB=15<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">print</span>(mean_dbfs(wav))<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 18.0103004778581</span>
<span style="color: rgba(0, 0, 0, 1)">
wav, scalar </span>= volumeAument2(wav, 15<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">print</span>(mean_dbfs(wav))<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 18.010299731550788</span></pre>
</div>
<span class="cnblogs_code_collapse">View Code</span></div>
<p>另外,如果我们是想对带噪语音进行音量增益,为了不破坏带噪语音的信噪比,还需要对纯净语音语音进行相同的增益</p>
<div class="cnblogs_code">
<pre>noisy, noisy_scalar = dB_gain(noisy, 20) <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 得到增益后的带噪语音和增益系数</span>
clean *= noisy_scalar <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 为了控制snr不变,clean需要进行相同的增益</span></pre>
</div>
<h2>混响增强</h2>
<p> 我这里使用的是Image Source Method(镜像源方法)来实现语音加混响,我想用两种方法来给大家实现,第一种是直接调用python库—— Pyroomacoustics来实现音频加混响,第二种就是按照公式推导一步一步来实现,两种效果一样,想看细节的可以参考第二种方法,只想开始实现效果的可以只看第一种方法:</p>
<h3>方法一: Pyroomacoustics实现音频加混响</h3>
<p> 首先需要安装 Pyroomacoustics,这个库非常强大,感兴趣也可以多看看其他API接口</p>
<div class="cnblogs_code">
<pre>pip installPyroomacoustics</pre>
</div>
<p>步骤:</p>
<ol>
<li>创建房间<span style="color: rgba(128, 128, 128, 1)">(定义房间大小、所需的混响时间、墙面材料、允许的最大反射次数、)</span></li>
<li>在房间内创建信号源</li>
<li>在房间内放置麦克风</li>
<li>创建房间冲击响应</li>
<li>模拟声音传播</li>
</ol>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> Author:凌逆战</span><span style="color: rgba(0, 128, 0, 1)">
#</span><span style="color: rgba(0, 128, 0, 1)"> -*- coding:utf-8 -*-</span>
<span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> pyroomacoustics as pra
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> numpy as np
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> matplotlib.pyplot as plt
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> librosa
</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 1、创建房间</span><span style="color: rgba(0, 128, 0, 1)">
#</span><span style="color: rgba(0, 128, 0, 1)"> 所需的混响时间和房间的尺寸</span>
rt60_tgt = 0.5<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 所需的混响时间,秒</span>
room_dim = <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 我们定义了一个9m x 7.5m x 3.5m的房间,米</span>
<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 我们可以使用Sabine’s公式来计算壁面能量吸收和达到预期混响时间所需的ISM的最大阶数(RT60,即RIR衰减60分贝所需的时间)</span>
e_absorption, max_order = pra.inverse_sabine(rt60_tgt, room_dim) <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 返回 墙壁吸收的能量 和 允许的反射次数</span><span style="color: rgba(0, 128, 0, 1)">
#</span><span style="color: rgba(0, 128, 0, 1)"> 我们还可以自定义 墙壁材料 和 最大反射次数</span><span style="color: rgba(0, 128, 0, 1)">
#</span><span style="color: rgba(0, 128, 0, 1)"> m = pra.Material(energy_absorption="hard_surface") # 定义 墙的材料,我们还可以定义不同墙面的的材料</span><span style="color: rgba(0, 128, 0, 1)">
#</span><span style="color: rgba(0, 128, 0, 1)"> max_order = 3</span>
<span style="color: rgba(0, 0, 0, 1)">
room </span>= pra.ShoeBox(room_dim, fs=16000, materials=pra.Material(e_absorption), max_order=<span style="color: rgba(0, 0, 0, 1)">max_order)
</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 在房间内创建一个位于的源,从0.3秒开始向仿真中发出wav文件的内容</span>
audio, _ = librosa.load(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">speech.wav</span><span style="color: rgba(128, 0, 0, 1)">"</span>,sr=16000)<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 导入一个单通道语音作为源信号 source signal</span>
room.add_source(, signal=audio, delay=0.3<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 3、在房间放置麦克风</span><span style="color: rgba(0, 128, 0, 1)">
#</span><span style="color: rgba(0, 128, 0, 1)"> 定义麦克风的位置:(ndim, nmics) 即每个列包含一个麦克风的坐标</span><span style="color: rgba(0, 128, 0, 1)">
#</span><span style="color: rgba(0, 128, 0, 1)"> 在这里我们创建一个带有两个麦克风的数组,</span><span style="color: rgba(0, 128, 0, 1)">
#</span><span style="color: rgba(0, 128, 0, 1)"> 分别位于和。</span>
mic_locs =<span style="color: rgba(0, 0, 0, 1)"> np.c_[
[</span>6.3, 4.87, 1.2],<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> mic 1</span>
,<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> mic 2</span>
<span style="color: rgba(0, 0, 0, 1)">]
room.add_microphone_array(mic_locs) </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 最后将麦克风阵列放在房间里</span>
<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 4、创建房间冲击响应(Room Impulse Response)</span>
<span style="color: rgba(0, 0, 0, 1)">room.compute_rir()
</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 5、模拟声音传播,每个源的信号将与相应的房间脉冲响应进行卷积。卷积的输出将在麦克风上求和。</span>
<span style="color: rgba(0, 0, 0, 1)">room.simulate()
</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 保存所有的信号到wav文件</span>
room.mic_array.to_wav(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">./guitar_16k_reverb_ISM.wav</span><span style="color: rgba(128, 0, 0, 1)">"</span>, norm=True, bitdepth=<span style="color: rgba(0, 0, 0, 1)">np.float32,)
</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 测量混响时间</span>
rt60 =<span style="color: rgba(0, 0, 0, 1)"> room.measure_rt60()
</span><span style="color: rgba(0, 0, 255, 1)">print</span>(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">The desired RT60 was {}</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">.format(rt60_tgt))
</span><span style="color: rgba(0, 0, 255, 1)">print</span>(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">The measured RT60 is {}</span><span style="color: rgba(128, 0, 0, 1)">"</span>.format(rt60))
plt.figure()
</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 绘制其中一个RIR. both can also be plotted using room.plot_rir()</span>
rir_1_0 = room.rir <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 画出 mic 1和 source 0 之间的 RIR</span>
plt.subplot(2, 1, 1<span style="color: rgba(0, 0, 0, 1)">)
plt.plot(np.arange(len(rir_1_0)) </span>/<span style="color: rgba(0, 0, 0, 1)"> room.fs, rir_1_0)
plt.title(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">The RIR from source 0 to mic 1</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
plt.xlabel(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Time </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 绘制 microphone 1 处接收到的信号</span>
plt.subplot(2, 1, 2<span style="color: rgba(0, 0, 0, 1)">)
plt.plot(np.arange(len(room.mic_array.signals[</span>1, :])) / room.fs, room.mic_array.signals)
plt.title(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Microphone 1 signal</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
plt.xlabel(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Time </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
plt.tight_layout()
plt.show()</span></pre>
</div>
<div class="cnblogs_code"><img src="https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" id="code_img_closed_cf97e841-c7fd-47bb-93eb-f33a2c705ef3" class="code_img_closed"><img src="https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif" id="code_img_opened_cf97e841-c7fd-47bb-93eb-f33a2c705ef3" class="code_img_opened" style="display: none">
<div id="cnblogs_code_open_cf97e841-c7fd-47bb-93eb-f33a2c705ef3" class="cnblogs_code_hide">
<pre>room =<span style="color: rgba(0, 0, 0, 1)"> pra.ShoeBox(
room_dim,
fs</span>=16000<span style="color: rgba(0, 0, 0, 1)">,
materials</span>=<span style="color: rgba(0, 0, 0, 1)">pra.Material(e_absorption),
max_order</span>=3<span style="color: rgba(0, 0, 0, 1)">,
ray_tracing</span>=<span style="color: rgba(0, 0, 0, 1)">True,
air_absorption</span>=<span style="color: rgba(0, 0, 0, 1)">True,
)
</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 激活射线追踪</span>
room.set_ray_tracing()</pre>
</div>
<span class="cnblogs_code_collapse">混合ISM/射线跟踪房间模拟器</span></div>
<div class="cnblogs_code"><img src="https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" id="code_img_closed_4e4cd141-2352-4c54-bf84-752d6b12370f" class="code_img_closed"><img src="https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif" id="code_img_opened_4e4cd141-2352-4c54-bf84-752d6b12370f" class="code_img_opened" style="display: none">
<div id="cnblogs_code_open_4e4cd141-2352-4c54-bf84-752d6b12370f" class="cnblogs_code_hide">
<pre>room.simulate(reference_mic=0, snr=10) <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 控制信噪比</span></pre>
</div>
<span class="cnblogs_code_collapse">控制信噪比</span></div>
<p><img src="https://img2020.cnblogs.com/blog/1433301/202012/1433301-20201231114906950-1280504017.png" alt="" loading="lazy" style="display: block; margin-left: auto; margin-right: auto"></p>
<p><img src="https://img2020.cnblogs.com/blog/1433301/202012/1433301-20201231114801743-1598415517.png" alt="" width="535" height="394" loading="lazy" style="display: block; margin-left: auto; margin-right: auto"></p>
<h3>方法二:Image Source Method 算法讲解</h3>
<p> 从这里要讲算法和原理了,</p>
<p>代码参考:matlab版本:RIR-Generator,python版本:rir-generator</p>
<p>镜像源法简介:</p>
<p style="text-align: center"> <img src="https://img2020.cnblogs.com/blog/1433301/202101/1433301-20210105114728229-1383941046.png" alt="" loading="lazy"></p>
<p> 将反射面等效为一个虚像,或者说镜像。比如说,在一个开放空间里有一面平整墙面,那么一个声源可以等效为2两个声源;一个开放空间里有两面垂直的平整墙面,那么一个声源可以等效为4个;同理三面的话是8个。原理上就是这样,但是封闭的三维空间里情况有那么点复杂,</p>
<p> 一般来说,家里的空房间可以一定程度上近似为矩形盒子,假设房间尺寸为:</p>
<p>$$L=\left$$</p>
<p>元素大小分别代表长宽高,而声源的三维坐标为</p>
<p>$$S=\left$$</p>
<p>麦克风的三维坐标为</p>
<p>$$M=\left$$</p>
<p>镜像声源$(i,j,k)$到麦克风距离在三个坐标轴上的位置为</p>
<p>$$x_{i}=(-1)^{i} x_{s}+\left x_{r}-x_{m}$$</p>
<p>$$y_{j}=(-1)^{j} y_{s}+\left y_{r}-y_{m}$$</p>
<p>$$z_{k}=(-1)^{k} z_{s}+\left z_{r}-z_{m}$$</p>
<p>那么声源$(i,j,k)$距离麦克风的距离为</p>
<p>$$d_{i j k}=\sqrt{\left(x_{i}^{2}+y_{j}^{2}+z_{k}^{2}\right)}$$</p>
<p>相对于直达声的到达延迟时间为</p>
<p>$$\tau_{i j k}=\left(d_{i j k}-r\right) / c$$</p>
<p>其中$c$为声速,$r$为声源到麦克风的直线距离。那么,混响效果等效为不同延迟的信号的叠加,即混响效果可以表示为一个FIR滤波器与信号源卷积的形式,此滤波器可写为如下形式</p>
<p>$$h(t)=\sum_{i} \sum_{j} \sum_{k}\left$$</p>
<p>滤波器的抽头系数与镜面的反射系数与距离相关,如果每个面的反射系数不同则形式略复杂。详细代码还是要看RIR-Generator,我这里只做抛转引玉,写一个最简单的。</p>
<p>模拟镜像源:</p>
<p>房间尺寸(m):4 X 4 X 3</p>
<p>声源坐标(m):2 X 2 X 0</p>
<p>麦克风坐标(m):2 X 2 X 1.5</p>
<p>混响时间(s):0.2</p>
<p>RIR长度:512</p>
<div class="cnblogs_code"><img src="https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" id="code_img_closed_620392c2-e038-4d0b-b378-234e09ca8499" class="code_img_closed"><img src="https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif" id="code_img_opened_620392c2-e038-4d0b-b378-234e09ca8499" class="code_img_opened" style="display: none">
<div id="cnblogs_code_open_620392c2-e038-4d0b-b378-234e09ca8499" class="cnblogs_code_hide">
<pre><span style="color: rgba(0, 0, 0, 1)">clc;clear;
c </span>= <span style="color: rgba(128, 0, 128, 1)">340</span>; % 声速 (m/<span style="color: rgba(0, 0, 0, 1)">s)
fs </span>= <span style="color: rgba(128, 0, 128, 1)">16000</span>; % Sample frequency (samples/<span style="color: rgba(0, 0, 0, 1)">s)
r </span>= [<span style="color: rgba(128, 0, 128, 1)">2</span> <span style="color: rgba(128, 0, 128, 1)">2</span> <span style="color: rgba(128, 0, 128, 1)">1.5</span>]; %<span style="color: rgba(0, 0, 0, 1)"> 麦克风位置 (m)
s </span>= [<span style="color: rgba(128, 0, 128, 1)">2</span> <span style="color: rgba(128, 0, 128, 1)">2</span> <span style="color: rgba(128, 0, 128, 1)">0</span>]; %<span style="color: rgba(0, 0, 0, 1)"> 扬声器位置 (m)
L </span>= [<span style="color: rgba(128, 0, 128, 1)">4</span> <span style="color: rgba(128, 0, 128, 1)">4</span> <span style="color: rgba(128, 0, 128, 1)">3</span>]; %<span style="color: rgba(0, 0, 0, 1)"> 房间大小 (m)
beta </span>= <span style="color: rgba(128, 0, 128, 1)">0.2</span>; %<span style="color: rgba(0, 0, 0, 1)"> 混响时间 (s)
n </span>= <span style="color: rgba(128, 0, 128, 1)">512</span>; %<span style="color: rgba(0, 0, 0, 1)"> RIR长度
h </span>=<span style="color: rgba(0, 0, 0, 1)"> rir_generator(c, fs, r, s, L, beta, n);
disp(size(h)) </span>% (<span style="color: rgba(128, 0, 128, 1)">1</span>,<span style="color: rgba(128, 0, 128, 1)">4096</span><span style="color: rgba(0, 0, 0, 1)">)
</span>= audioread(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">./test_wav/p225_001.wav</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
disp(size(speech)); </span>% (<span style="color: rgba(128, 0, 128, 1)">46797</span>,<span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">)
y </span>= conv(speech<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">, h);</span>
<span style="color: rgba(0, 0, 0, 1)">disp(length(y))
</span>%<span style="color: rgba(0, 0, 0, 1)"> 开始画图
figure(</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">color</span><span style="color: rgba(128, 0, 0, 1)">'</span>,<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">w</span><span style="color: rgba(128, 0, 0, 1)">'</span>); %<span style="color: rgba(0, 0, 0, 1)"> 背景色设置成白色
subplot(</span><span style="color: rgba(128, 0, 128, 1)">3</span>,<span style="color: rgba(128, 0, 128, 1)">1</span>,<span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">)
plot(h)
title(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">房间冲击响应 RIR</span><span style="color: rgba(128, 0, 0, 1)">"</span>,<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">FontSize</span><span style="color: rgba(128, 0, 0, 1)">"</span>,<span style="color: rgba(128, 0, 128, 1)">14</span><span style="color: rgba(0, 0, 0, 1)">)
subplot(</span><span style="color: rgba(128, 0, 128, 1)">3</span>,<span style="color: rgba(128, 0, 128, 1)">2</span>,<span style="color: rgba(128, 0, 128, 1)">3</span><span style="color: rgba(0, 0, 0, 1)">)
plot(speech)
title(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">原语音波形</span><span style="color: rgba(128, 0, 0, 1)">"</span>,<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">FontSize</span><span style="color: rgba(128, 0, 0, 1)">"</span>,<span style="color: rgba(128, 0, 128, 1)">14</span><span style="color: rgba(0, 0, 0, 1)">)
subplot(</span><span style="color: rgba(128, 0, 128, 1)">3</span>,<span style="color: rgba(128, 0, 128, 1)">2</span>,<span style="color: rgba(128, 0, 128, 1)">4</span><span style="color: rgba(0, 0, 0, 1)">)
plot(y)
title(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">加混响语音波形</span><span style="color: rgba(128, 0, 0, 1)">"</span>,<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">FontSize</span><span style="color: rgba(128, 0, 0, 1)">"</span>,<span style="color: rgba(128, 0, 128, 1)">14</span><span style="color: rgba(0, 0, 0, 1)">)
subplot(</span><span style="color: rgba(128, 0, 128, 1)">3</span>,<span style="color: rgba(128, 0, 128, 1)">2</span>,<span style="color: rgba(128, 0, 128, 1)">5</span><span style="color: rgba(0, 0, 0, 1)">)
specgram(speech,</span><span style="color: rgba(128, 0, 128, 1)">512</span>,fs,<span style="color: rgba(128, 0, 128, 1)">512</span>,<span style="color: rgba(128, 0, 128, 1)">256</span><span style="color: rgba(0, 0, 0, 1)">);
title(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">原语音频谱</span><span style="color: rgba(128, 0, 0, 1)">"</span>,<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">FontSize</span><span style="color: rgba(128, 0, 0, 1)">"</span>,<span style="color: rgba(128, 0, 128, 1)">14</span><span style="color: rgba(0, 0, 0, 1)">)
subplot(</span><span style="color: rgba(128, 0, 128, 1)">3</span>,<span style="color: rgba(128, 0, 128, 1)">2</span>,<span style="color: rgba(128, 0, 128, 1)">6</span><span style="color: rgba(0, 0, 0, 1)">)
specgram(y,</span><span style="color: rgba(128, 0, 128, 1)">512</span>,fs,<span style="color: rgba(128, 0, 128, 1)">512</span>,<span style="color: rgba(128, 0, 128, 1)">256</span><span style="color: rgba(0, 0, 0, 1)">);
title(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">加混响语音频谱</span><span style="color: rgba(128, 0, 0, 1)">"</span>,<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">FontSize</span><span style="color: rgba(128, 0, 0, 1)">"</span>,<span style="color: rgba(128, 0, 128, 1)">14</span><span style="color: rgba(0, 0, 0, 1)">)
audiowrite(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">./test_wav/matlab_p225_001_reverber.wav</span><span style="color: rgba(128, 0, 0, 1)">"</span>,y,fs)</pre>
</div>
<span class="cnblogs_code_collapse">Image Source方法</span></div>
<p><img src="https://img2020.cnblogs.com/blog/1433301/202103/1433301-20210326180655255-1618003965.png" alt="" width="635" height="450" loading="lazy" style="display: block; margin-left: auto; margin-right: auto"></p>
<p class="title mathjax">gpuRIR:使用图像源方法(ISM)和GPU加速进行房间脉冲响应(RIR)模拟</p>
<ul>
<li class="title mathjax">【论文】gpuRIR: 基于GPU加速的房间脉冲响应仿真的Python库</li>
<li class="title mathjax">【代码】https://github.com/DavidDiazGuerra/gpuRIR</li>
</ul>
<p><span>一种专用于机器学习应用中音频数据增强的随机房间脉冲响应生成方法。</span><span>与图像源或光线追踪等几何方法相反,这种技术不需要预先定义房间几何形状、吸收系数或麦克风和源位置,并且仅依赖于房间的声学参数。</span><span>该方法直观、易于实施,并允许生成非常复杂的外壳的 RIR</span></p>
<ul>
<li class="title mathjax">【论文】StoRIR: 用于音频数据增强的随机房间脉冲响应生成</li>
<li class="title mathjax">【代码】https://github.com/SRPOL-AUI/storir</li>
</ul>
<p class="title mathjax">基于神经网络的快速房间脉冲响应生成器</p>
<ul>
<li>【论文】FAST-RIR: 快速神经扩散房间脉冲响应发生器</li>
<li>【代码】https://github.com/anton-jeran/FAST-RIR</li>
</ul>
<p>基于生成对抗网络 (GAN) 的房间脉冲响应发生器 (IR-GAN)</p>
<ul>
<li>【论文】IR-GAN:用于远场语音识别的房间脉冲响应发生器</li>
<li>【代码】https://github.com/GAMMA-UMD/IR-GAN</li>
</ul>
<p>一种提高远场语音识别合成房间脉冲响应质量的方法</p>
<ul>
<li>【论文】TS-RIR:翻译合成房间脉冲反应语音增强</li>
<li>【代码】https://github.com/GAMMA-UMD/TS-RIR</li>
</ul>
<p>在房间脉冲响应 (RIR) 生成中加入以下因素的影响:空气吸收、真实材料的表面和频率相关系数以及随机光线追踪</p>
<ul>
<li>【论文】2020_远场关键字识别的更真实的房间模拟研究</li>
<li>【代码】https://github.com/ebezzam/room-simulation</li>
</ul>
<h3>方法三:利用RIR生成混响</h3>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> add_pyreverb(wav, rir):
reverb_wav </span>= signal.fftconvolve(wav, rir, mode=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">full</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
reverb_wav </span>= reverb_wav]<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 使 reverb_wav 和 wav 具有相同的长度</span>
<span style="color: rgba(0, 0, 255, 1)">return</span> reverb_wav</pre>
</div>
<h2>指定SER生成远端语音</h2>
<p> SER的公式为</p>
<p>$$SER=10\log_{10}\frac{E\{s^2(n)\}}{E\{d^2(n)\}}$$</p>
<p>其中E是统计 期望操作,$s(n)$是近端语音,$d(n)$是远端回声,</p>
<p> 由于我们需要根据指定的SER求混响信号,并且近端语音和远端混响都是已知的,我们只需要求得一个系数,来调整回声信号的能量大小,与远端混响相乘即可得我们想要的混响语音,即调整后的回声信号为$kd(n)$</p>
<p>根据以上公式,可以推导出$k$的值</p>
<p>$$k=\sqrt{\frac{E\{s^2(n)\}}{E\{d^2(n)\}*10^{\frac{SER}{10}}}}$$</p>
<p>最终$kd(n)$即我们所求的指定SER的混响。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> add_echo_ser(near_speech, far_echo, SER):
</span><span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(128, 0, 0, 1)">根据指定的SER求回声
:param near_speech: 近端语音
:param far_echo: 远端回声
:param SER: 指定的SER
:return: 指定SER的回声
</span><span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(0, 0, 0, 1)">
p_near_speech </span>= np.mean(near_speech ** 2)<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 近端语音功率</span>
p_far_echo = np.mean(far_echo ** 2)<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 远端回声功率</span>
<span style="color: rgba(0, 0, 0, 1)">
k </span>= np.sqrt(p_near_speech / (10 ** (SER / 10)) /<span style="color: rgba(0, 0, 0, 1)"> p_far_echo)
</span><span style="color: rgba(0, 0, 255, 1)">return</span> k * far_echo</pre>
</div>
<p><img src="https://img2020.cnblogs.com/blog/1433301/202101/1433301-20210107150628285-1924499876.png" alt="" width="565" height="419" loading="lazy" style="display: block; margin-left: auto; margin-right: auto"></p>
<div> </div>
<h2>波形位移</h2>
<p> 语音波形移动使用numpy.roll函数向右移动shift距离</p>
<p>numpy.roll(a, shift, axis=None)</p>
<p><strong>参数</strong>:</p>
<ul>
<li>a:数组</li>
<li>shift:滚动的长度</li>
<li>axis:滚动的维度。0为垂直滚动,1为水平滚动,参数为None时,会先将数组扁平化,进行滚动操作后,恢复原始形状</li>
</ul>
<div class="cnblogs_code">
<pre>x = np.arange(10<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> array()</span>
<span style="color: rgba(0, 0, 255, 1)">print</span>(np.roll(x, 2<span style="color: rgba(0, 0, 0, 1)">))
</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> array()</span></pre>
</div>
<p>波形位移函数:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> time_shift(x, shift):
</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> shift:移动的长度</span>
<span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> np.roll(x, int(shift))
Augmentation </span>= time_shift(wav_data, shift=fs//2)</pre>
</div>
<p><img src="https://img2020.cnblogs.com/blog/1433301/202010/1433301-20201002214715571-915114976.png" alt="" width="514" height="381" loading="lazy" style="display: block; margin-left: auto; margin-right: auto"></p>
<h2 id="blogTitle11">谐波失真</h2>
<p>参考自:soundpy</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> harmonic_distortion(wav):
wav </span>= 2 * np.pi *<span style="color: rgba(0, 0, 0, 1)"> wav
count </span>=<span style="color: rgba(0, 0, 0, 1)"> 0
</span><span style="color: rgba(0, 0, 255, 1)">while</span> count < 5<span style="color: rgba(0, 0, 0, 1)">:
wav </span>=<span style="color: rgba(0, 0, 0, 1)"> np.sin(wav)
count </span>+= 1
<span style="color: rgba(0, 0, 255, 1)">return</span> wav</pre>
</div>
<p><img src="https://img2023.cnblogs.com/blog/1433301/202303/1433301-20230320121727066-1933499933.png" alt="" width="441" height="327" loading="lazy" style="display: block; margin-left: auto; margin-right: auto"></p>
<h2>重采样数据增强</h2>
<p> 重采样后语音数据会丢失 重采样采样率到源采样值之间的频谱信息。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> augment_resample(wav, sr):
resample_sr </span>= np.random.uniform(sr) <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 从一个均匀分布中随机采样</span>
<span style="color: rgba(0, 0, 255, 1)">print</span>(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">target_sr</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">, resample_sr)
resample </span>= librosa.resample(wav, orig_sr=sr, target_sr=<span style="color: rgba(0, 0, 0, 1)">resample_sr)
resample </span>= librosa.resample(resample, orig_sr=resample_sr, target_sr=<span style="color: rgba(0, 0, 0, 1)">sr)
</span><span style="color: rgba(0, 0, 255, 1)">return</span> resample</pre>
</div>
<p><img src="https://img2020.cnblogs.com/blog/1433301/202110/1433301-20211031171654953-1544219624.png" alt="" width="501" height="376" loading="lazy" style="display: block; margin-left: auto; margin-right: auto"></p>
<h2>编解码数据增强</h2>
<p> 这个我有空再来补全,参考DeepSpeech,先numpu to pcm,在转成 opus。</p>
<h1>频域增强</h1>
<h2>音高增强(Pitch Shifting)</h2>
<p> 在频率轴上缩放频谱图,从而改变音高。音高修正只改变音高而不影响音速,<span class="tgt" style="color: rgba(255, 0, 0, 1)" data-section="0" data-sentence="0" data-group="0-0">我发现-5到5之间的步数更合适</span></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> sr: 音频采样率</span><span style="color: rgba(0, 128, 0, 1)">
#</span><span style="color: rgba(0, 128, 0, 1)"> n_steps: 要移动多少步</span><span style="color: rgba(0, 128, 0, 1)">
#</span><span style="color: rgba(0, 128, 0, 1)"> bins_per_octave: 每个八度音阶(半音)多少步</span>
<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 上移大三度(如果bins_per_octave为12,则4步)</span>
augment = librosa.effects.pitch_shift(wav_data, sr=sr, n_steps=4, bins_per_octave=12<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 向下移动一个三全音(如果bins_per_octave是 12,则为六步)</span>
augment = librosa.effects.pitch_shift(wav_data, sr=sr, n_steps=-6, bins_per_octave=12<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 上移 3 个四分音符</span>
augment = librosa.effects.pitch_shift(wav_data, sr=sr, n_steps=3, bins_per_octave=24)</pre>
</div>
<p><img src="https://img2020.cnblogs.com/blog/1433301/202110/1433301-20211031211623546-262509465.png" alt="" width="480" height="356" loading="lazy" style="display: block; margin-left: auto; margin-right: auto"></p>
<h2>速度增强(<strong>Tempo</strong>)</h2>
<p><span>在时间轴上缩放频谱图,从而改变播放速度。</span></p>
<h3>变速不变调</h3>
<p><strong>方法一:ffmpeg</strong></p>
<p> 在变速之前我们需要安装 <span class="cnblogs_code">pip install ffmpeg </span> </p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">from</span> ffmpeg <span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> audio
</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 加快2倍速度</span>
audio.a_speed(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">./sample/p225_001.wav</span><span style="color: rgba(128, 0, 0, 1)">"</span>,speed=2,out_file=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">./sample/p225_001_2.wav</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 放慢2倍速度</span>
audio.a_speed(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">./sample/p225_001.wav</span><span style="color: rgba(128, 0, 0, 1)">"</span>,speed=0.5,out_file=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">./sample/p225_001_0.5.wav</span><span style="color: rgba(128, 0, 0, 1)">"</span>)</pre>
</div>
<p>ffmpeg是基于fmpeg开发的,Python的这个库不能加载太大的文件,但是原生的fmpeg。或者我们可以直接使用原生的ffmpeg工具包</p>
<p><img src="https://img2020.cnblogs.com/blog/1433301/202110/1433301-20211031211859530-1890650943.png" alt="" width="528" height="396" loading="lazy" style="display: block; margin-left: auto; margin-right: auto"></p>
<p>我们可以看到变速前后的波形图和语谱图没变,但是他们的时间维度却减少了一半。</p>
<p><strong>方法二:SoundTorch</strong></p>
<p> SoundTouch 是一个开源音频处理库,用于更改音频流或音频文件的速度、音高和播放速率。该库还支持估计音轨的稳定每分钟节拍率。</p>
<p>命令实例见:https://www.surina.net/soundtouch/soundstretch.html</p>
<p>速度增加100%</p>
<div class="cnblogs_code">
<pre>soundstretch input.wav output.wav -tempo=100</pre>
</div>
<p>速度降低50%</p>
<div class="cnblogs_code">
<pre>soundstretch input.wav output.wav -tempo=-50</pre>
</div>
<p><img src="https://img2020.cnblogs.com/blog/1433301/202110/1433301-20211031212039182-308366139.png" alt="" width="576" height="432" loading="lazy" style="display: block; margin-left: auto; margin-right: auto"></p>
<h3>变速变调</h3>
<p><strong>方法一:SOX</strong></p>
<p> 需要在linux上运行,具体参考https://github.com/rabitt/pysox</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> soundfile
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> sox
sr </span>= 16000<span style="color: rgba(0, 0, 0, 1)">
tfm </span>= sox.Transformer() <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> create transformer</span>
tfm.speed(2) <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 变速2倍</span>
<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 创建输出文件</span><span style="color: rgba(0, 128, 0, 1)">
#</span><span style="color: rgba(0, 128, 0, 1)"> tfm.build_file("./sample/p225_001.wav", "./sample/pysox_2x.wav")</span>
<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 内存中以numpy数组的形式获取输出</span>
array_out = tfm.build_array(input_filepath=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">./sample/p225_001.wav</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
soundfile.write(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">./sample/pysox_2x.wav</span><span style="color: rgba(128, 0, 0, 1)">"</span>,data=array_out,samplerate=sr)</pre>
</div>
<p>或者我们直接使用原生的sox工具包</p>
<div class="cnblogs_code">
<pre>$ sox input.wav output.wav speed 1.3 <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)">速度变为原来的1.3倍</span>
$ sox input.wav output.wav speed 0.8 <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)">速度变为原来的0.8倍</span></pre>
</div>
<p><img src="https://img2020.cnblogs.com/blog/1433301/202110/1433301-20211031212140600-1676171515.png" alt="" width="490" height="367" loading="lazy" style="display: block; margin-left: auto; margin-right: auto"></p>
<p><strong>方法二:librosa</strong></p>
<p> 按固定速率对音频系列进行时间拉伸。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> time_stretch(x, rate):
</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> rate:拉伸的尺寸,</span>
<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> rate > 1 加快速度</span>
<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> rate < 1 放慢速度</span>
<span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> librosa.effects.time_stretch(x, rate)
Augmentation </span>= time_stretch(wav_data, rate=2)</pre>
</div>
<p><img src="https://img2020.cnblogs.com/blog/1433301/202110/1433301-20211031212213347-132573291.png" alt="" width="497" height="367" loading="lazy" style="display: block; margin-left: auto; margin-right: auto"></p>
<p> 我们来观察语谱图和波形图,发现形状变了,并且变速后的语音波形振幅降低了,为什么呢?难道变速还会减少语音的音量?求解答</p>
<hr>
<p> SpecAugment 通过在时间方向上通过在时间方向上扭曲来增强,并屏蔽(多个)连续时间步长(垂直掩模)和 mel 频率通道(水平掩模)的块</p>
<ol>
<li>帮助网络在时间方向上的变形、频率信息的部分丢失和输入的小段语音的部分丢失方面具有鲁棒性</li>
<li>防止网络过度拟合</li>
</ol>
<p>SpecAugment 中有三种增强策略:</p>
<ul>
<li><strong>时间扭曲</strong><span style="color: rgba(136, 136, 136, 1)">(Time Warping)</span>:在时间轴上随机扭曲频谱图。与速度扰动不同,这种方法不会增加或减少持续时间,而是在局部压缩和拉伸频谱图。</li>
<li><strong>频率掩蔽</strong><span style="color: rgba(136, 136, 136, 1)">(Frequency Mask)</span>:频谱图的 连续频率bin被随机掩蔽</li>
<li><strong>时间掩蔽</strong><span style="color: rgba(136, 136, 136, 1)">(Time Mask)</span>:频谱图的 连续时间帧被掩蔽</li>
</ul>
<p><img src="https://img2020.cnblogs.com/blog/1433301/202110/1433301-20211028115534027-2062185206.png" alt="" width="447" height="233" loading="lazy" style="display: block; margin-left: auto; margin-right: auto"></p>
<p>paperwithcode:SpecAugment几乎所有的代码都列出来了</p>
<ul>
<li>tensorflow和pytorch实现:SpecAugment</li>
<li>numpy实现:SpecAugment_numpy_scipy</li>
<li>numpy实现:SpecAugment (很好理解)</li>
</ul>
<h2>扭曲增强(Warp)</h2>
<p> 将非线性图像扭曲应用于频谱图。这是通过沿时间和频率轴随机移动均匀分布的扭曲点网格来实现的。代码修改自:DeepSpeech</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">def</span> tf_pick_value_from_range(value, r, clock=None, double_precision=<span style="color: rgba(0, 0, 0, 1)">False):
clock </span>= (tf.random.stateless_uniform([], seed=(-1, 1), dtype=tf.float64) <span style="color: rgba(0, 0, 255, 1)">if</span> clock <span style="color: rgba(0, 0, 255, 1)">is</span><span style="color: rgba(0, 0, 0, 1)"> None
</span><span style="color: rgba(0, 0, 255, 1)">else</span> tf.maximum(tf.constant(0.0, dtype=tf.float64), tf.minimum(tf.constant(1.0, dtype=<span style="color: rgba(0, 0, 0, 1)">tf.float64), clock)))
value </span>=<span style="color: rgba(0, 0, 0, 1)"> tf.random.stateless_uniform([],
minval</span>=value -<span style="color: rgba(0, 0, 0, 1)"> r,
maxval</span>=value +<span style="color: rgba(0, 0, 0, 1)"> r,
seed</span>=(clock * tf.int32.min, clock *<span style="color: rgba(0, 0, 0, 1)"> tf.int32.max),
dtype</span>=<span style="color: rgba(0, 0, 0, 1)">tf.float64)
</span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> isinstance(value, int):
</span><span style="color: rgba(0, 0, 255, 1)">return</span> tf.cast(tf.math.round(value), tf.int64 <span style="color: rgba(0, 0, 255, 1)">if</span> double_precision <span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> tf.int32)
</span><span style="color: rgba(0, 0, 255, 1)">return</span> tf.cast(value, tf.float64 <span style="color: rgba(0, 0, 255, 1)">if</span> double_precision <span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> tf.float32)
</span><span style="color: rgba(0, 0, 255, 1)">def</span> Warp(spectrogram, num_t=1, num_f=1, warp_t=0.1, warp_f=0.0, r=0, clock=0.0<span style="color: rgba(0, 0, 0, 1)">):
</span><span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(128, 0, 0, 1)">
:param spectrogram: tensor (batch size,t,f)
:param num_t:
:param num_f:
:param warp_t:
:param warp_f:
:param r: 波动范围
:param clock:
:return:
</span><span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(0, 0, 0, 1)">
size_t, size_f </span>=<span style="color: rgba(0, 0, 0, 1)"> spectrogram.shape
seed </span>= (clock * tf.int32.min, clock *<span style="color: rgba(0, 0, 0, 1)"> tf.int32.max)
num_t </span>= tf_pick_value_from_range(num_t, r, clock=<span style="color: rgba(0, 0, 0, 1)">clock)
num_f </span>= tf_pick_value_from_range(num_f, r, clock=<span style="color: rgba(0, 0, 0, 1)">clock)
</span><span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> get_flows(n, size, warp, r):
warp </span>= tf_pick_value_from_range(warp, range, clock=<span style="color: rgba(0, 0, 0, 1)">clock)
warp </span>= warp * tf.cast(size, dtype=tf.float32) / tf.cast(2 * (n + 1), dtype=<span style="color: rgba(0, 0, 0, 1)">tf.float32)
f </span>= tf.random.stateless_normal(, seed, mean=0.0, stddev=warp, dtype=<span style="color: rgba(0, 0, 0, 1)">tf.float32)
</span><span style="color: rgba(0, 0, 255, 1)">return</span> tf.pad(f, tf.constant([, ]), <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">CONSTANT</span><span style="color: rgba(128, 0, 0, 1)">'</span>)<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> zero flow at all edges</span>
<span style="color: rgba(0, 0, 0, 1)">
flows </span>= tf.stack(, axis=2<span style="color: rgba(0, 0, 0, 1)">)
flows </span>=<span style="color: rgba(0, 0, 0, 1)"> tf.image.resize_bicubic(tf.expand_dims(flows, 0), )
spectrogram_aug </span>= tf.contrib.image.dense_image_warp(tf.expand_dims(spectrogram, -1<span style="color: rgba(0, 0, 0, 1)">), flows)
spectrogram_aug </span>= tf.reshape(spectrogram_aug, shape=(1, -1<span style="color: rgba(0, 0, 0, 1)">, size_f))
</span><span style="color: rgba(0, 0, 255, 1)">return</span> spectrogram_aug</pre>
</div>
<h2>频率掩膜(<strong>Frequency Mask</strong>)</h2>
<p>在幅度谱随机的将频点置零。推荐使用单个掩码(num_mask=1)</p>
<ul class="nested">
<li class="nested" data-indent="4"><span class="markdown-bold-text">模拟真实场景:在真实世界中,影响频率的因素(如不同的麦克风、不同的通话信道)通常是持续性的。一个单一、连续的频带掩码能很好地模拟这种“信道变化”,强迫模型不要过度依赖某几个特定的频率通道,而是学会从整个频谱中综合信息。</span></li>
<li class="nested" data-indent="4"><span class="markdown-bold-text">避免过度破坏:多个离散的频率掩码会像“盐和胡椒”噪声一样,把频谱弄得“千疮百孔”,这很容易破坏掉对识别至关重要的谐波结构(比如元音的共振峰)。一个单一的掩码更有可能只遮挡部分信息,而不是摧毁整体结构。</span></li>
</ul>
<p>有关更多详细信息,请参阅 SpecAugment 论文:SpecAugment: A Simple Data Augmentation Method for Automatic Speech Recognition</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">def</span> FreqMask(mag, num_mask=1, mask_percentage=0.01<span style="color: rgba(0, 0, 0, 1)">):
</span><span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(128, 0, 0, 1)">
:param mag: (F,T)
:param num_freq_mask: mask的数量
:param mask_percentage: mask的百分比 0.001~0.015
</span><span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(0, 0, 0, 1)">
F </span>= mag.shape<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 频点数</span>
mask_width = int(mask_percentage * F)<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> mask的宽度</span>
<span style="color: rgba(0, 0, 255, 1)">for</span> i <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> range(num_mask):
mask_start </span>= np.random.randint(low=0, high=F - mask_width)<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> mask的index</span>
mag = 0 <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 掩码F维度</span>
<span style="color: rgba(0, 0, 255, 1)">return</span> mag</pre>
</div>
<p><span><img src="https://img2023.cnblogs.com/blog/1433301/202303/1433301-20230320163620899-990607474.png" alt="" width="468" height="344" loading="lazy" style="display: block; margin-left: auto; margin-right: auto"></span></p>
<h2><span>时间掩码(Time Mask)</span></h2>
<p><span>在频谱上在时间维度将帧置零</span>。代码修改自:DeepSpeech</p>
<p><span class="markdown-bold-text">主流推荐:使用少量固定的掩码,通常是 1 到 2 个 (<span class="markdown-inline-code leading-">num_masks = 1 或 <span class="markdown-inline-code leading-">num_masks = 2)。</span></span></span></p>
<ul class="nested">
<li class="nested" data-indent="4"><span class="markdown-bold-text">模拟真实干扰:在一段话中,可能会出现一两个短暂的干扰音,比如一声咳嗽、一次关门声、或者一个短暂的信号中断。使用 1-2 个时间掩码正好可以模拟这种场景</span></li>
</ul>
<p class="nested" data-indent="4"><span class="markdown-bold-text">控制信息损失:</span></p>
<ul class="nested">
<li class="nested" data-indent="8">如果只用<span class="markdown-bold-text">一个很长的时间掩码,可能会把一整个关键词完全抹掉,这样模型就彻底学不到东西了,这对训练是有害的。</span></li>
<li class="nested" data-indent="8">如果用<span class="markdown-bold-text">非常多(比如5个以上)的短掩码,又会把语音切得过于零碎,同样可能破坏语音的连贯性。</span></li>
<li class="nested" data-indent="8"><span class="markdown-bold-text">可调超参:在 SpecAugment 论文中,作者把时间掩码的数量 <span class="markdown-inline-code leading-">m_T 作为一个可以调整的超参数,并在实验中发现 <span class="markdown-inline-code leading-">m_T = 2 是一个很好的选择。</span></span></span></li>
</ul>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">def</span> TimeMask(mag, num_mask=1, mask_percentage=0.01<span style="color: rgba(0, 0, 0, 1)">):
</span><span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(128, 0, 0, 1)">
:param mag: (F,T)
:param num_freq_mask: mask的数量
:param mask_percentage: mask的百分比 0.001~0.015
</span><span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(0, 0, 0, 1)">
T </span>= mag.shape<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 频点数</span>
mask_width = int(mask_percentage * T)<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> mask的宽度</span>
<span style="color: rgba(0, 0, 255, 1)">for</span> i <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> range(num_mask):
mask_start </span>= np.random.randint(low=0, high=T -<span style="color: rgba(0, 0, 0, 1)"> mask_width)
mag[:, mask_start:mask_start </span>+ mask_width] = 0<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 掩码T维度</span>
<span style="color: rgba(0, 0, 255, 1)">return</span> mag</pre>
</div>
<p><img src="https://img2023.cnblogs.com/blog/1433301/202303/1433301-20230320164336290-998200386.png" alt="" width="454" height="333" loading="lazy" style="display: block; margin-left: auto; margin-right: auto"></p>
<h2>频谱交换(SpecSwap)</h2>
<blockquote>
<p>X. Song, Z. Wu, Y. Huang, D. Su, and H. Meng, "SpecSwap: A Simple Data Augmentation Method for End-to-End Speech Recognition", in INTERSPEECH, 2020.</p>
</blockquote>
<p>频谱交换提供两个增强策略:</p>
<ul>
<li><strong>频率交换</strong>:随机交换频谱图的两个频率块</li>
<li><strong>时间交换</strong>:在时间轴上随机交换频谱图的两个帧块</li>
</ul>
<p><span>SpecSwap 增强策略在 E2E ASR 模型中也运行良好,但缺乏与 SpecAugment 的比较。</span></p>
<h1><span>多领域增强</span></h1>
<h2>drop增强</h2>
<p> 将目标数据表示的随机数据点归零。代码修改自:DeepSpeech</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">def</span> tf_pick_value_from_range(value, r, clock=None, double_precision=<span style="color: rgba(0, 0, 0, 1)">False):
clock </span>= (tf.random.stateless_uniform([], seed=(-1, 1), dtype=tf.float64) <span style="color: rgba(0, 0, 255, 1)">if</span> clock <span style="color: rgba(0, 0, 255, 1)">is</span><span style="color: rgba(0, 0, 0, 1)"> None
</span><span style="color: rgba(0, 0, 255, 1)">else</span> tf.maximum(tf.constant(0.0, dtype=tf.float64), tf.minimum(tf.constant(1.0, dtype=<span style="color: rgba(0, 0, 0, 1)">tf.float64), clock)))
value </span>=<span style="color: rgba(0, 0, 0, 1)"> tf.random.stateless_uniform([],
minval</span>=value -<span style="color: rgba(0, 0, 0, 1)"> r,
maxval</span>=value +<span style="color: rgba(0, 0, 0, 1)"> r,
seed</span>=(clock * tf.int32.min, clock *<span style="color: rgba(0, 0, 0, 1)"> tf.int32.max),
dtype</span>=<span style="color: rgba(0, 0, 0, 1)">tf.float64)
</span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> isinstance(value, int):
</span><span style="color: rgba(0, 0, 255, 1)">return</span> tf.cast(tf.math.round(value), tf.int64 <span style="color: rgba(0, 0, 255, 1)">if</span> double_precision <span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> tf.int32)
</span><span style="color: rgba(0, 0, 255, 1)">return</span> tf.cast(value, tf.float64 <span style="color: rgba(0, 0, 255, 1)">if</span> double_precision <span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> tf.float32)
</span><span style="color: rgba(0, 0, 255, 1)">def</span> Dropout(tensor, rate=0.05, r=0, transcript=None, clock=0.0<span style="color: rgba(0, 0, 0, 1)">):
rate </span>= tf_pick_value_from_range(rate, r, clock=<span style="color: rgba(0, 0, 0, 1)">clock)
rate </span>= tf.math.maximum(0.0<span style="color: rgba(0, 0, 0, 1)">, rate)
factors </span>=<span style="color: rgba(0, 0, 0, 1)"> tf.random.stateless_uniform(tf.shape(tensor),
(clock </span>* tf.int32.min, clock *<span style="color: rgba(0, 0, 0, 1)"> tf.int32.max),
minval</span>=0.0<span style="color: rgba(0, 0, 0, 1)">,
maxval</span>=1.0<span style="color: rgba(0, 0, 0, 1)">,
dtype</span>=<span style="color: rgba(0, 0, 0, 1)">tf.float32)
</span><span style="color: rgba(0, 0, 255, 1)">return</span> tensor * tf.math.sign(tf.math.floor(factors + rate))</pre>
</div>
<h2>添加增强</h2>
<p>将从正态分布(均值为 0.0)中选取的随机值添加到目标数据表示的所有数据点。代码修改自:DeepSpeech</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">def</span> tf_pick_value_from_range(value, r, clock=None, double_precision=<span style="color: rgba(0, 0, 0, 1)">False):
clock </span>= (tf.random.stateless_uniform([], seed=(-1, 1), dtype=tf.float64) <span style="color: rgba(0, 0, 255, 1)">if</span> clock <span style="color: rgba(0, 0, 255, 1)">is</span><span style="color: rgba(0, 0, 0, 1)"> None
</span><span style="color: rgba(0, 0, 255, 1)">else</span> tf.maximum(tf.constant(0.0, dtype=tf.float64), tf.minimum(tf.constant(1.0, dtype=<span style="color: rgba(0, 0, 0, 1)">tf.float64), clock)))
value </span>=<span style="color: rgba(0, 0, 0, 1)"> tf.random.stateless_uniform([],
minval</span>=value -<span style="color: rgba(0, 0, 0, 1)"> r,
maxval</span>=value +<span style="color: rgba(0, 0, 0, 1)"> r,
seed</span>=(clock * tf.int32.min, clock *<span style="color: rgba(0, 0, 0, 1)"> tf.int32.max),
dtype</span>=<span style="color: rgba(0, 0, 0, 1)">tf.float64)
</span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> isinstance(value, int):
</span><span style="color: rgba(0, 0, 255, 1)">return</span> tf.cast(tf.math.round(value), tf.int64 <span style="color: rgba(0, 0, 255, 1)">if</span> double_precision <span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> tf.int32)
</span><span style="color: rgba(0, 0, 255, 1)">return</span> tf.cast(value, tf.float64 <span style="color: rgba(0, 0, 255, 1)">if</span> double_precision <span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> tf.float32)
</span><span style="color: rgba(0, 0, 255, 1)">def</span> Add(tensor, stddev=5, r=0, transcript=None, clock=0.0<span style="color: rgba(0, 0, 0, 1)">):
stddev </span>= tf_pick_value_from_range(stddev, r, clock=<span style="color: rgba(0, 0, 0, 1)">clock)
seed </span>= (clock * tf.int32.min, clock *<span style="color: rgba(0, 0, 0, 1)"> tf.int32.max)
</span><span style="color: rgba(0, 0, 255, 1)">return</span> tensor + tf.random.stateless_normal(tf.shape(tensor), seed, mean=0.0, stddev=stddev)</pre>
</div>
<h2>乘法增强</h2>
<p>将目标数据表示的所有数据点与从正态分布(均值为 1.0)中选取的随机值相乘。代码修改自:DeepSpeech</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">def</span> tf_pick_value_from_range(value, r, clock=None, double_precision=<span style="color: rgba(0, 0, 0, 1)">False):
clock </span>= (tf.random.stateless_uniform([], seed=(-1, 1), dtype=tf.float64) <span style="color: rgba(0, 0, 255, 1)">if</span> clock <span style="color: rgba(0, 0, 255, 1)">is</span><span style="color: rgba(0, 0, 0, 1)"> None
</span><span style="color: rgba(0, 0, 255, 1)">else</span> tf.maximum(tf.constant(0.0, dtype=tf.float64), tf.minimum(tf.constant(1.0, dtype=<span style="color: rgba(0, 0, 0, 1)">tf.float64), clock)))
value </span>=<span style="color: rgba(0, 0, 0, 1)"> tf.random.stateless_uniform([],
minval</span>=value -<span style="color: rgba(0, 0, 0, 1)"> r,
maxval</span>=value +<span style="color: rgba(0, 0, 0, 1)"> r,
seed</span>=(clock * tf.int32.min, clock *<span style="color: rgba(0, 0, 0, 1)"> tf.int32.max),
dtype</span>=<span style="color: rgba(0, 0, 0, 1)">tf.float64)
</span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> isinstance(value, int):
</span><span style="color: rgba(0, 0, 255, 1)">return</span> tf.cast(tf.math.round(value), tf.int64 <span style="color: rgba(0, 0, 255, 1)">if</span> double_precision <span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> tf.int32)
</span><span style="color: rgba(0, 0, 255, 1)">return</span> tf.cast(value, tf.float64 <span style="color: rgba(0, 0, 255, 1)">if</span> double_precision <span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> tf.float32)
</span><span style="color: rgba(0, 0, 255, 1)">def</span> Multiply(self, tensor, stddev=5, r=0, transcript=None, clock=0.0<span style="color: rgba(0, 0, 0, 1)">):
stddev </span>= tf_pick_value_from_range(stddev, r=0, clock=<span style="color: rgba(0, 0, 0, 1)">clock)
seed </span>= (clock * tf.int32.min, clock *<span style="color: rgba(0, 0, 0, 1)"> tf.int32.max)
</span><span style="color: rgba(0, 0, 255, 1)">return</span> tensor * tf.random.stateless_normal(tf.shape(tensor), seed, mean=1.0, stddev=stddev)</pre>
</div>
<h1>通道Shuffle</h1>
<p>打乱原先通道顺序,这可以帮助对抗位置偏见。但是需要考虑打乱后会不会破坏麦克风阵列原始排布顺序,举个例子:</p>
<blockquote>
<p>双麦:01,10可以随机打乱</p>
<p>三麦(等间隔环形):012、120、201、210、102、021。这6个位置都是等价的。</p>
<p>3麦(等间隔线性):012和120就完全不一样了,只能012,210两种</p>
</blockquote>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">def</span> ChannelShuffle(wav, ratio=0.7<span style="color: rgba(0, 0, 0, 1)">):
</span><span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(128, 0, 0, 1)"> 打乱通道顺序
Args:
wav: (T, C)
ratio: 打乱的概率
Returns:
</span><span style="color: rgba(128, 0, 0, 1)">"""</span>
<span style="color: rgba(0, 0, 255, 1)">if</span> random.random() <<span style="color: rgba(0, 0, 0, 1)"> ratio:
channel_num </span>= wav.shape[-1<span style="color: rgba(0, 0, 0, 1)">]
shuffled_channel_indexes </span>=<span style="color: rgba(0, 0, 0, 1)"> list(range(channel_num))
random.shuffle(shuffled_channel_indexes)
wav </span>=<span style="color: rgba(0, 0, 0, 1)"> wav[..., shuffled_channel_indexes]
</span><span style="color: rgba(0, 0, 255, 1)">return</span> wav</pre>
</div>
<h1>参考</h1>
<p>【python 音频处理库】</p>
<ul>
<li>librosa</li>
<li>Pyroomacoustics</li>
<li class="_mce_tagged_br">PythonAudioEffects:专注音效</li>
<li class="_mce_tagged_br">WavAugment</li>
<li class="_mce_tagged_br">Audiogmenter:一个用于音频数据增强的MATLAB工具箱</li>
<li class="_mce_tagged_br"><strong>audiomentations</strong>:我愿称他为最强王者,专门的数据增强库,很全面,没事可以多看看</li>
</ul>
<p>【知乎文章】简单地为语音加混响</p>
<p>【国际音频实验室EmanuëlHabets提供的代码】International Audio Laboratories Erlangen</p>
<p>【Image-source method】Image-source method for room acoustics</p>
<p>【Image-source 原理讲解】Image-source Model</p>
<p>【CSDN】变速变调原理与方法总结</p>
<p>【CSDN】音频倍速(变速不变调)的实现</p>
<p>【CSDN】音频变调算法小结</p>
<p>【CSDN】python 音频变调不变速方法</p>
<p>【论文】用于语音识别的音频增强</p>
<p>【论文】SpecAugment:一种简单的自动语音识别数据增强方法</p>
<p>【CSDN】SoX 音频处理工具使用方法</p>
<p>还有写没有跑通,但是总感觉有些价值的代码,记录在这里:</p>
<ul>
<li>py-RIR-Generator(没跑通的原因是我是window系统)</li>
<li>gpuRIR(这个我跑通了,但是需要较大的计算资源)</li>
<li>去github找代码的时候,不一定要搜索“回声”,“混响”,也可以通过搜索"RIR"同样可以得到想要的结果</li>
</ul>
<p>本文画图代码:</p>
<div class="cnblogs_code"><img src="https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" id="code_img_closed_aff3d3be-31ac-4f49-b2f3-87750aca0fef" class="code_img_closed"><img src="https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif" id="code_img_opened_aff3d3be-31ac-4f49-b2f3-87750aca0fef" class="code_img_opened" style="display: none">
<div id="cnblogs_code_open_aff3d3be-31ac-4f49-b2f3-87750aca0fef" class="cnblogs_code_hide">
<pre><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> Author:凌逆战</span><span style="color: rgba(0, 128, 0, 1)">
#</span><span style="color: rgba(0, 128, 0, 1)"> -*- coding:utf-8 -*-</span>
<span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> matplotlib.pyplot as plt
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> librosa
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> numpy as np
plt.rcParams[</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">font.sans-serif</span><span style="color: rgba(128, 0, 0, 1)">'</span>]=[<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">SimHei</span><span style="color: rgba(128, 0, 0, 1)">'</span>] <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)">用来正常显示中文标签</span>
plt.rcParams[<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">axes.unicode_minus</span><span style="color: rgba(128, 0, 0, 1)">'</span>]=False <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)">用来正常显示符号</span>
<span style="color: rgba(0, 0, 0, 1)">
y1, _ </span>= librosa.load(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">./speech.wav</span><span style="color: rgba(128, 0, 0, 1)">"</span>, sr=16000<span style="color: rgba(0, 0, 0, 1)">)
y2, _ </span>= librosa.load(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">./guitar_16k_reverb_ISM.wav</span><span style="color: rgba(128, 0, 0, 1)">"</span>, sr=16000<span style="color: rgba(0, 0, 0, 1)">)
plt.subplot(</span>2, 2, 1<span style="color: rgba(0, 0, 0, 1)">)
plt.specgram(y1, Fs</span>=16000, scale_by_freq=True, sides=<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">default</span><span style="color: rgba(128, 0, 0, 1)">'</span>, cmap=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">jet</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
plt.title(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">语谱图</span><span style="color: rgba(128, 0, 0, 1)">"</span>, fontsize=13<span style="color: rgba(0, 0, 0, 1)">)
plt.xlabel(</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">时间/s</span><span style="color: rgba(128, 0, 0, 1)">'</span>, fontsize=13<span style="color: rgba(0, 0, 0, 1)">)
plt.ylabel(</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">频率/Hz</span><span style="color: rgba(128, 0, 0, 1)">'</span>, fontsize=13<span style="color: rgba(0, 0, 0, 1)">)
plt.subplot(</span>2, 2, 2<span style="color: rgba(0, 0, 0, 1)">)
plt.plot(np.arange(len(y1)) </span>/ 16000<span style="color: rgba(0, 0, 0, 1)">, y1)
plt.title(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">波形图</span><span style="color: rgba(128, 0, 0, 1)">"</span>, fontsize=13<span style="color: rgba(0, 0, 0, 1)">)
plt.xlabel(</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">时间/s</span><span style="color: rgba(128, 0, 0, 1)">'</span>, fontsize=13<span style="color: rgba(0, 0, 0, 1)">)
plt.ylabel(</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">振幅</span><span style="color: rgba(128, 0, 0, 1)">'</span>, fontsize=13<span style="color: rgba(0, 0, 0, 1)">)
plt.subplot(</span>2, 2, 3<span style="color: rgba(0, 0, 0, 1)">)
plt.specgram(y2, Fs</span>=16000, scale_by_freq=True, sides=<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">default</span><span style="color: rgba(128, 0, 0, 1)">'</span>, cmap=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">jet</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
plt.title(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">语谱图(加混响)</span><span style="color: rgba(128, 0, 0, 1)">"</span>, fontsize=13<span style="color: rgba(0, 0, 0, 1)">)
plt.xlabel(</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">时间/s</span><span style="color: rgba(128, 0, 0, 1)">'</span>, fontsize=13<span style="color: rgba(0, 0, 0, 1)">)
plt.ylabel(</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">频率/Hz</span><span style="color: rgba(128, 0, 0, 1)">'</span>, fontsize=13<span style="color: rgba(0, 0, 0, 1)">)
plt.subplot(</span>2, 2, 4<span style="color: rgba(0, 0, 0, 1)">)
plt.plot(np.arange(len(y2)) </span>/ 16000<span style="color: rgba(0, 0, 0, 1)">, y2)
plt.title(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">波形图(加混响)</span><span style="color: rgba(128, 0, 0, 1)">"</span>, fontsize=13<span style="color: rgba(0, 0, 0, 1)">)
plt.xlabel(</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">时间/s</span><span style="color: rgba(128, 0, 0, 1)">'</span>, fontsize=13<span style="color: rgba(0, 0, 0, 1)">)
plt.ylabel(</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">振幅</span><span style="color: rgba(128, 0, 0, 1)">'</span>, fontsize=13<span style="color: rgba(0, 0, 0, 1)">)
plt.tight_layout()
plt.show()</span></pre>
</div>
<span class="cnblogs_code_collapse">View Code</span></div>
<p> J,B. Allen and D. A. Berkley, Image method for efficiently simulating small-room acoustics, J. Acoust. Soc. Am., vol. 65, no. 4, p. 943, 1979.</p>
<p> M, Vorlaender, Auralization, 1st ed. Berlin: Springer-Verlag, 2008, pp. 1-340.</p>
<p> D.Schroeder, Physically based real-time auralization of interactive virtual environments. PhD Thesis, RWTH Aachen University, 2011.</p>
<div id="gtx-trans" style="position: absolute; left: 847px; top: 17331.9px"> </div><br><br>
来源:https://www.cnblogs.com/LXP-Never/p/13404523.html
頁:
[1]