概述

 

    最近因为需要生成房间内的多通道语音数据,需要生成大批量的房间脉冲响应(RIR)。而使用pyroomacoustics太过于耗时,机缘巧合之下发现了gpuRIR这个开源项目,它能够调用gpu加速RIR的生成,能够很好的解决pyroomacoustics耗时的问题。本文主要整理gpuRIR的安装流程以及基本使用。

 

安装流程

要想成功安装gpuRIR需要事先安装cuda,cmake(3.12版本以上)以及c++编译器。本文基于(cuda:10.1, cmake:3.20, c++编译器: vs 2019 community的编译器,显卡:rtx 2060s).上述条件准备完成后,直接使用命令

 

pip install https://github.com/DavidDiazGuerra/gpuRIR/zipball/master

 

安装的过程中如果出现错误CmakeError...No CUDA toolset found, 可以尝试将

C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.1\

extras\visual_studio_integration\MSBuildExtensions下的

文件手动复制到visual studio 的安装目录下

F:\vs2019\MSBuild\Microsoft\VC\v160\BuildCustomizations

(具体安装目录因人而异).然后再重新执行上述命令,出现下图表示安装成功。

gpuRIR--房间脉冲响应的加速计算_java

在之前的文章中介绍了pyroomacoustics这个工具(可以查看先前的文章),虽然两者都可以用来生成RIR,但gpuRIR的优势在于能够使用GPU加速。在需要生成大批量RIR场景下可以使用gpuRIR。

 

gpuRIR使用

本部分主要介绍一下该工具的基本使用,因为目前也是在用该工具生成大批量RIR,故借此机会记录一下使用流程。gpuRIR的主要函数就两个:simulateRIR用于生成RIR,simulateTrajectory利用所生成的RIR生成多通道语音,函数声明如下

 

def simulateRIR(room_sz, beta, pos_src, pos_rcv, nb_img, Tmax, fs, Tdiff=None, mic_pattern="omni", orV_rcv=None, c=343.0)def simulateTrajectory(source_signal, RIRs, timestamps=None, fs=None

simulateRIR

当麦克风的指向为全指向时,只需要给该函数传递7个参数即可得到RIR.

  • room_sz: 房间的尺寸大小,[x,y,z],单位为米

  • beta: 房间墙壁的反射系数,总共6个在[0,1]之间的系数。

  • pos_src: 声源的位置,二维数据,每一维包含3列,[[x1,y1,z1]...[xn,yn,zn]]这个地方特别需要注意:所给的坐标必须满足这个形式,如果传入[x1,y1,z1],那么该函数会认为这是三个声源的坐标

  • pos_rcv: 麦克风的坐标。

  • nb_img: Image方法的镜像阶数。

  • Tmax: RIR的最大长度

  • fs: 采样率

该函数的返回值是一个三维数组,第一维表示声源个数,第二维表示麦克风数目,第三维表示RIR的时间长度。如果感觉生成的RIR有问题,可以看一下返回值中的声源个数是否正确,因为好几次生成的RIR出问题都是因为传入的声源坐标形式有问题(给了个一维数组从而被函数认为是三个声源)。该函数的调用格式如下:

 

RIR = gpuRIR.simulateRIR(            room_sz=self.room_sz,            beta=self.beta,            nb_img=nb_img,            fs=self.fs,            pos_src=np.array([source_pos]),            pos_rcv=mic_pos,            Tmax=Tmax,            Tdiff=Tdiff,            mic_pattern='omni'        )
 

simulateTrajectory

RIR生成之后,接下来调用该函数将RIR和语音进行卷积得到最终的多通道语音。只需传入两个参数即可。示例如下

 

mic_sig = gpuRIR.simulateTrajectory(y, RIR, fs=self.fs)
 

代码示例

 

import gpuRIRimport numpy as npimport soundfile as sf# 生成(min, max)之间的一个随机值class Parameter:    def __init__(self, *args):        if len(args) == 1:            self.random = False            self.value = np.array(args[0])            self.min_value = None            self.max_value = None        elif len(args) == 2:            self.random = True            self.min_value = np.array(args[0])            self.max_value = np.array(args[1])            self.value = None        else:            raise Exception(                'Parammeter must be called with one (value) or two (min and max value) array_like parammeters')    def getvalue(self):        if self.random:            return self.min_value + np.random.random(self.min_value.shape) * (self.max_value - self.min_value)        else:            return self.valueclass GpuRirDemo:    def __init__(self, room_sz, t60, beta, fs, array_pos):        self.room_sz = room_sz        self.t60 = t60        self.beta = beta        self.fs = fs        self.array_pos = array_pos    def simulate(self):        if self.t60 == 0:            Tdiff = 0.1            Tmax = 0.1            nb_img = [1, 1, 1]        else:            Tdiff = gpuRIR.att2t_SabineEstimator(15, self.t60)            Tmax = gpuRIR.att2t_SabineEstimator(60, self.t60)            if self.t60 < 0.15: Tdiff = Tmax            nb_img = gpuRIR.t2n(Tdiff, self.room_sz)        # mic position        mic_pos = np.array(((-0.079, 0.000, 0.000),                            (-0.079, -0.009, 0.000),                            (0.079, 0.000, 0.000),                            (0.079, -0.009, 0.000)))        # 阵列中心的坐标        array_pos = self.array_pos * self.room_sz        mic_pos = mic_pos + array_pos        # 声源位置,这里给定一个声源        source_pos = np.random.rand(3) * self.room_sz        # 生成RIR        RIR = gpuRIR.simulateRIR(            room_sz=self.room_sz,            beta=self.beta,            nb_img=nb_img,            fs=self.fs,            pos_src=np.array([source_pos]),            pos_rcv=mic_pos,            Tmax=Tmax,            Tdiff=Tdiff,            mic_pattern='omni'        )        # 读取语音        y, sr = sf.read('q1.wav')        # 生成多通道语音        mic_sig = gpuRIR.simulateTrajectory(y, RIR, fs=self.fs)        sf.write('filter.wav', mic_sig, sr)test_code = GpuRirDemo(    room_sz=Parameter([3, 3, 2.5], [4, 5, 3]).getvalue(), # 此时得到随机得到[3,3,2.5]~[4,5,3]之间的一个房间尺寸    t60=Parameter(0.2, 1.0).getvalue(), # 0.2s~1.0s之间的一个随机值                       beta=Parameter([0.5]*6, [1.0]*6).getvalue(), # 房间反射系数                       array_pos=Parameter([0.1, 0.1, 0.1], [0.9, 0.9, 0.5]).getvalue(),# 比例系数,实际的array_pos为 array_pos * room_sz                       fs=44100)test_code.simulate()
生成的四通道语音如下。

gpuRIR--房间脉冲响应的加速计算_java_02

 

总结

    上述介绍的是静止单声源生成多通道语音的示例,这只是gpuRIR的一个简单使用。在涉及到动态声源追踪时,因为此时声源沿着某条轨迹进行运动,假设可以用N个点去近似该轨迹,那么就需要生成N个RIR。此时调用pyroomacoustics将会很耗时。而gpuRIR调用GPU进行加速,能大大加快运算速度。有关使用gpuRIR生成动态轨迹的RIR,等到需要使用时再介绍。有关Image方法生成RIR以及gpuRIR加速的设计原理可以参加论文https://arxiv.org/pdf/1810.11359.pdf,gpuRIR的GitHub地址为https://github.com/DavidDiazGuerra/gpuRIR

 

https://mp.weixin.qq.com/s/q8iBh2OO-Qz1wT7J1Uix7A