生成窄带信号_房间冲激响应RIR原理与模拟生成方法
论文"Room Impulse Response Generator"的阅读笔记。下面的链接为原论文。
·
这篇论文主要讲的是模拟生成房间声学冲激响应(Room Impulse Response,RIR)的方法。由 Allen 和 Berkley 于 1979年提出的 image 方法(也可称之为镜像声源模型)是在声学信号处理这个领域应用最广的方法。因此本文重点讨论此方法,并基于此方法,利用Matlab自带的mex函数完成编写了多通道RIR生成函数rir_generator。该函数支持设定反射阶数、房间尺寸与麦克风的指向性。
章节包括:
目前模拟房间声学响应的主要方法
Image方法简述
测试生成案例
模拟房间声学
声音的传播过程数学形式上可以用波动方程表示,可以通过求解波动方程获得声源到麦克风的冲激响应。然而由于很难表达为解析解的形式,所以多数情况都是拟合求解。目前有三种主流建模方法:基于波动方程的方法、基于光线方法和统计方法。基于光线的方法有光线追踪法(ray-tracing),是最为常用的方法。波动方程方法包括有限元( Finite Element Method ,FEM)、边界元Boundary Element Method ,BEM)和有限差分时域方法(Finite-Difference Time-Domain ,FTDT)等,这几种方法计算复杂度高,对计算机算力要求较高,所以需要进行简化建模方法,常用的思路是对直达声与早期反射声独立建模,而晚期反射声利用递归数字滤波器结构实现。而统计建模方法已经被广泛的应用于航天领域、轮船与汽车工业的高频噪声分析与声学设计中,主要方法为统计能量分析法。
波动方程方法
基于波动方程的方法可以获得最精确的结果,但是只有在房间是矩形的且墙壁是刚性壁的时候才可以求得解析解。因此数值方法如FEM和BEM经常应用,这两种方法的区别是网格结构不同。主要区别就是,在FEM中,整个空间被划分为网格;在BEM中,只有空间的边界被划分为网格。网格之间通过波动方法的基础进行信息交换,而网格的尺寸必须比最高待分析频率的波长要小。因此当需要分析高频信号时,则网格数量会变得非常庞大,计算复杂度飙升。还有FDTD方法,它的优点就是可以按需生成更加紧密的网格,以覆盖靠近边角地方以及其他非常固有挑战性的位置。因此波动方程方法最难的问题就是定义边界条件与描述待分析物体的几何轮廓。
基于光线的方法
基于光线的方法的理论基础是房间几何声学,其中最为常用的是光线跟踪方法与image方法,而这些方法的区别之处就是反射路径是如何计算的。应该指出的是,在此处我们把从声源到接收点的所有可能的声学反射路径统称为光线,而且用有限数量的光线描述从声源辐射出来的声能。光线在空间内传播时,会碰到墙壁或者其他物体被反射,在此传播过程中,声能会随着被空气、其他物体和墙壁吸收而逐渐衰减。当光线到达接收位置,能量计算过程完成,RIR也就获取了。光线选择有三种方法,1:随机选择一批分布的角度;2:均匀分布的角度;3:一批有限制条件的角度。值得一提的是,所有的基于光线的方法均是从能量传播的角度进行求解的,所有这就意味着所有关于相位的信息会被忽略,比如干扰之类的。如果我们的观测信号不是正弦波或者是一个窄带信号,这种处理思路也是可以接受的。因此这种思路下,就是假设当多个声场在某一点进行叠加时,并不会因为相位的影响进行对消,而此时会是简单的进行能量的叠加。
Allen 和 Berkley 的 Image 方法
Image 模型
图2描述了一个靠近刚性壁的点声源
图3展示了反射两次的结果,传播路径长度为
Image 方法
假设一个矩形房间,长、宽和高分别为
三元素集合
其中
声源镜像到麦克风接收位置的距离可以表示为:
任何镜像声源的到达时延为:
其中
因此,从声源到麦克风接收位置的冲激响应可以表示为:
其中,
但是这种方法在模拟离散版本的冲激响应时存在问题,就是采样点有时会对不齐,针对该问题离散版本冲激响应形式为:
其中
虽然多数应用场景可以忽略这种失真,但是对于麦克风阵列系统来说,对于麦克风之间的相位差比较敏感,所以仿真正确的到达时间很重要。一种解决思路是用非常高的采样频率计算离散冲激响应,然后和原信号卷积。Peterson 等人也针对 Image 方法提出了利用Hanning窗改善的方法,窗函数如下:
其中
混响时间是模拟房间混响时一个重要的问题,可以在程序参数中直接设定。混响时间是指在房间声音趋于稳定状态后,停止声源发声,平均声能密度自原始值衰减到其百万分之一所需要的时间,即声源停止发声后衰减60dB所需要的时间。非常著名的赛宾公式,经验公式,如下:
其中
源代码作者在其文章中已附带,然后在Matlab环境下直接调用下述指令会生成自己电脑的对应版本,比如我就生成了rir_generator.mexw64:
mex rir_generator.cpp
实际生成RIR的测试代码如下,h为需要的结果:
clear; close all; clc;
c = 340;
fs = 16000;
r = [2 1.5 2];
s = [2 2 2];
L = [5 4 3];
beta = 0.25;
n = 4096;
h = rir_generator(c, fs, r, s, L, beta, n);
下图即为时域的RIR:
下图为RIR幅度的平方:
下图为幅度的平方的后向积分,可以看到与程序设定的混响时间基本一致:
另附文章作者在论文中公开的代码:
#define _USE_MATH_DEFINES#include "matrix.h"
#include "mex.h"
#include "math.h"#define ROUND(x) ((x)>=0?(long)((x)+0.5):(long)((x)-0.5))#ifndef M_PI #define M_PI 3.14159265358979323846
#endifdouble sinc(double x)
{if (x == 0)return(1.);elsereturn(sin(x)/x);
}double sim_microphone(double x, double y, double z, double* angle, char mtype)
{if (mtype=='b' || mtype=='c' || mtype=='s' || mtype=='h'){double gain, vartheta, varphi, rho;// Polar Pattern rho// ---------------------------// Bidirectional 0// Hypercardioid 0.25 // Cardioid 0.5// Subcardioid 0.75// Omnidirectional 1switch(mtype){case 'b':rho = 0;break;case 'h':rho = 0.25; break;case 'c':rho = 0.5;break;case 's':rho = 0.75;break;};vartheta = acos(z/sqrt(pow(x,2)+pow(y,2)+pow(z,2)));varphi = atan2(y,x);gain = sin(M_PI/2-angle[1]) * sin(vartheta) * cos(angle[0]-varphi) + cos(M_PI/2-angle[1]) * cos(vartheta);gain = rho + (1-rho) * gain;return gain;}else{return 1;}
}void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{if (nrhs == 0){mexPrintf("--------------------------------------------------------------------n""| Room Impulse Response Generator |n""| |n""| Computes the response of an acoustic source to one or more |n""| microphones in a reverberant room using the image method [1,2]. |n""| |n""| Author : dr.ir. Emanuel Habets (ehabets@dereverberation.org) |n""| |n""| Version : 2.1.20141124 |n""| |n""| Copyright (C) 2003-2014 E.A.P. Habets, The Netherlands. |n""| |n""| [1] J.B. Allen and D.A. Berkley, |n""| Image method for efficiently simulating small-room acoustics,|n""| Journal Acoustic Society of America, |n""| 65(4), April 1979, p 943. |n""| |n""| [2] P.M. Peterson, |n""| Simulating the response of multiple microphones to a single |n""| acoustic source in a reverberant room, Journal Acoustic |n""| Society of America, 80(5), November 1986. |n""--------------------------------------------------------------------nn""function [h, beta_hat] = rir_generator(c, fs, r, s, L, beta, nsample,n"" mtype, order, dim, orientation, hp_filter);nn""Input parameters:n"" c : sound velocity in m/s.n"" fs : sampling frequency in Hz.n"" r : M x 3 array specifying the (x,y,z) coordinates of then"" receiver(s) in m.n"" s : 1 x 3 vector specifying the (x,y,z) coordinates of then"" source in m.n"" L : 1 x 3 vector specifying the room dimensions (x,y,z) in m.n"" beta : 1 x 6 vector specifying the reflection coefficientsn"" [beta_x1 beta_x2 beta_y1 beta_y2 beta_z1 beta_z2] orn"" beta = reverberation time (T_60) in seconds.n"" nsample : number of samples to calculate, default is T_60*fs.n"" mtype : [omnidirectional, subcardioid, cardioid, hypercardioid,n"" bidirectional], default is omnidirectional.n"" order : reflection order, default is -1, i.e. maximum order.n"" dim : room dimension (2 or 3), default is 3.n"" orientation : direction in which the microphones are pointed, specified usingn"" azimuth and elevation angles (in radians), default is [0 0].n"" hp_filter : use 'false' to disable high-pass filter, the high-pass filtern"" is enabled by default.nn""Output parameters:n"" h : M x nsample matrix containing the calculated room impulsen"" response(s).n"" beta_hat : In case a reverberation time is specified as an input parametern"" the corresponding reflection coefficient is returned.nn");return;}else{mexPrintf("Room Impulse Response Generator (Version 2.1.20141124) by Emanuel Habetsn""Copyright (C) 2003-2014 E.A.P. Habets, The Netherlands.n");}// Check for proper number of argumentsif (nrhs < 6)mexErrMsgTxt("Error: There are at least six input parameters required.");if (nrhs > 12)mexErrMsgTxt("Error: Too many input arguments.");if (nlhs > 2)mexErrMsgTxt("Error: Too many output arguments.");// Check for proper argumentsif (!(mxGetN(prhs[0])==1) || !mxIsDouble(prhs[0]) || mxIsComplex(prhs[0]))mexErrMsgTxt("Invalid input arguments!");if (!(mxGetN(prhs[1])==1) || !mxIsDouble(prhs[1]) || mxIsComplex(prhs[1]))mexErrMsgTxt("Invalid input arguments!");if (!(mxGetN(prhs[2])==3) || !mxIsDouble(prhs[2]) || mxIsComplex(prhs[2]))mexErrMsgTxt("Invalid input arguments!");if (!(mxGetN(prhs[3])==3) || !mxIsDouble(prhs[3]) || mxIsComplex(prhs[3]))mexErrMsgTxt("Invalid input arguments!");if (!(mxGetN(prhs[4])==3) || !mxIsDouble(prhs[4]) || mxIsComplex(prhs[4]))mexErrMsgTxt("Invalid input arguments!");if (!(mxGetN(prhs[5])==6 || mxGetN(prhs[5])==1) || !mxIsDouble(prhs[5]) || mxIsComplex(prhs[5]))mexErrMsgTxt("Invalid input arguments!");// Load parametersdouble c = mxGetScalar(prhs[0]);double fs = mxGetScalar(prhs[1]);const double* rr = mxGetPr(prhs[2]);int nMicrophones = (int) mxGetM(prhs[2]);const double* ss = mxGetPr(prhs[3]);const double* LL = mxGetPr(prhs[4]);const double* beta_input = mxGetPr(prhs[5]);double* beta = new double[6];int nSamples;char* microphone_type;int nOrder;int nDimension;double angle[2];int isHighPassFilter;double reverberation_time = 0;// Reflection coefficients or reverberation time?if (mxGetN(prhs[5])==1){double V = LL[0]*LL[1]*LL[2];double S = 2*(LL[0]*LL[2]+LL[1]*LL[2]+LL[0]*LL[1]);reverberation_time = beta_input[0];if (reverberation_time != 0) {double alfa = 24*V*log(10.0)/(c*S*reverberation_time);if (alfa > 1)mexErrMsgTxt("Error: The reflection coefficients cannot be calculated using the current ""room parameters, i.e. room size and reverberation time.n Please ""specify the reflection coefficients or change the room parameters.");for (int i=0;i<6;i++)beta[i] = sqrt(1-alfa);} else {for (int i=0;i<6;i++)beta[i] = 0;}}else{for (int i=0;i<6;i++)beta[i] = beta_input[i];}// High-pass filter (optional)if (nrhs > 11 && mxIsEmpty(prhs[11]) == false){isHighPassFilter = (int) mxGetScalar(prhs[11]);}else{isHighPassFilter = 1;}// 3D Microphone orientation (optional)if (nrhs > 10 && mxIsEmpty(prhs[10]) == false){const double* orientation = mxGetPr(prhs[10]);if (mxGetN(prhs[10]) == 1){ angle[0] = orientation[0];angle[1] = 0;}else{angle[0] = orientation[0];angle[1] = orientation[1];}}else{angle[0] = 0;angle[1] = 0;}// Room Dimension (optional)if (nrhs > 9 && mxIsEmpty(prhs[9]) == false){nDimension = (int) mxGetScalar(prhs[9]);if (nDimension != 2 && nDimension != 3)mexErrMsgTxt("Invalid input arguments!");if (nDimension == 2){beta[4] = 0;beta[5] = 0;}}else{nDimension = 3;}// Reflection order (optional)if (nrhs > 8 && mxIsEmpty(prhs[8]) == false){nOrder = (int) mxGetScalar(prhs[8]);if (nOrder < -1)mexErrMsgTxt("Invalid input arguments!");}else{nOrder = -1;}// Type of microphone (optional)if (nrhs > 7 && mxIsEmpty(prhs[7]) == false){microphone_type = new char[mxGetN(prhs[7])+1];mxGetString(prhs[7], microphone_type, mxGetN(prhs[7])+1);}else{microphone_type = new char[1];microphone_type[0] = 'o';}// Number of samples (optional)if (nrhs > 6 && mxIsEmpty(prhs[6]) == false){nSamples = (int) mxGetScalar(prhs[6]);}else{if (mxGetN(prhs[5])>1){double V = LL[0]*LL[1]*LL[2];double alpha = ((1-pow(beta[0],2))+(1-pow(beta[1],2)))*LL[1]*LL[2] +((1-pow(beta[2],2))+(1-pow(beta[3],2)))*LL[0]*LL[2] +((1-pow(beta[4],2))+(1-pow(beta[5],2)))*LL[0]*LL[1];reverberation_time = 24*log(10.0)*V/(c*alpha);if (reverberation_time < 0.128)reverberation_time = 0.128;}nSamples = (int) (reverberation_time * fs);}// Create output vectorplhs[0] = mxCreateDoubleMatrix(nMicrophones, nSamples, mxREAL);double* imp = mxGetPr(plhs[0]);// Temporary variables and constants (high-pass filter)const double W = 2*M_PI*100/fs; // The cut-off frequency equals 100 Hzconst double R1 = exp(-W);const double B1 = 2*R1*cos(W);const double B2 = -R1 * R1;const double A1 = -(1+R1);double X0;double* Y = new double[3];// Temporary variables and constants (image-method)const double Fc = 1; // The cut-off frequency equals fs/2 - Fc is the normalized cut-off frequency.const int Tw = 2 * ROUND(0.004*fs); // The width of the low-pass FIR equals 8 msconst double cTs = c/fs;double* LPI = new double[Tw];double* r = new double[3];double* s = new double[3];double* L = new double[3];double Rm[3];double Rp_plus_Rm[3]; double refl[3];double fdist,dist;double gain;int startPosition;int n1, n2, n3;int q, j, k;int mx, my, mz;int n;s[0] = ss[0]/cTs; s[1] = ss[1]/cTs; s[2] = ss[2]/cTs;L[0] = LL[0]/cTs; L[1] = LL[1]/cTs; L[2] = LL[2]/cTs;for (int idxMicrophone = 0; idxMicrophone < nMicrophones ; idxMicrophone++){// [x_1 x_2 ... x_N y_1 y_2 ... y_N z_1 z_2 ... z_N]r[0] = rr[idxMicrophone + 0*nMicrophones] / cTs;r[1] = rr[idxMicrophone + 1*nMicrophones] / cTs;r[2] = rr[idxMicrophone + 2*nMicrophones] / cTs;n1 = (int) ceil(nSamples/(2*L[0]));n2 = (int) ceil(nSamples/(2*L[1]));n3 = (int) ceil(nSamples/(2*L[2]));// Generate room impulse responsefor (mx = -n1 ; mx <= n1 ; mx++){Rm[0] = 2*mx*L[0];for (my = -n2 ; my <= n2 ; my++){Rm[1] = 2*my*L[1];for (mz = -n3 ; mz <= n3 ; mz++){Rm[2] = 2*mz*L[2];for (q = 0 ; q <= 1 ; q++){Rp_plus_Rm[0] = (1-2*q)*s[0] - r[0] + Rm[0];refl[0] = pow(beta[0], abs(mx-q)) * pow(beta[1], abs(mx));for (j = 0 ; j <= 1 ; j++){Rp_plus_Rm[1] = (1-2*j)*s[1] - r[1] + Rm[1];refl[1] = pow(beta[2], abs(my-j)) * pow(beta[3], abs(my));for (k = 0 ; k <= 1 ; k++){Rp_plus_Rm[2] = (1-2*k)*s[2] - r[2] + Rm[2];refl[2] = pow(beta[4],abs(mz-k)) * pow(beta[5], abs(mz));dist = sqrt(pow(Rp_plus_Rm[0], 2) + pow(Rp_plus_Rm[1], 2) + pow(Rp_plus_Rm[2], 2));if (abs(2*mx-q)+abs(2*my-j)+abs(2*mz-k) <= nOrder || nOrder == -1){fdist = floor(dist);if (fdist < nSamples){gain = sim_microphone(Rp_plus_Rm[0], Rp_plus_Rm[1], Rp_plus_Rm[2], angle, microphone_type[0])* refl[0]*refl[1]*refl[2]/(4*M_PI*dist*cTs);for (n = 0 ; n < Tw ; n++)LPI[n] = 0.5 * (1 - cos(2*M_PI*((n+1-(dist-fdist))/Tw))) * Fc * sinc(M_PI*Fc*(n+1-(dist-fdist)-(Tw/2)));startPosition = (int) fdist-(Tw/2)+1;for (n = 0 ; n < Tw; n++)if (startPosition+n >= 0 && startPosition+n < nSamples)imp[idxMicrophone + nMicrophones*(startPosition+n)] += gain * LPI[n];}}}}}}}}// 'Original' high-pass filter as proposed by Allen and Berkley.if (isHighPassFilter == 1){for (int idx = 0 ; idx < 3 ; idx++) {Y[idx] = 0;} for (int idx = 0 ; idx < nSamples ; idx++){X0 = imp[idxMicrophone+nMicrophones*idx];Y[2] = Y[1];Y[1] = Y[0];Y[0] = B1*Y[1] + B2*Y[2] + X0;imp[idxMicrophone+nMicrophones*idx] = Y[0] + A1*Y[1] + R1*Y[2];}}}if (nlhs > 1) {plhs[1] = mxCreateDoubleMatrix(1, 1, mxREAL);double* beta_hat = mxGetPr(plhs[1]);if (reverberation_time != 0) {beta_hat[0] = beta[0];}else {beta_hat[0] = 0;}}delete[] beta;delete[] microphone_type;delete[] Y;delete[] LPI; delete[] r;delete[] s;delete[] L;
}
参考资料:
1、"Room Impulse Response Generator"
生成窄带信号_房间冲激响应RIR原理与模拟生成方法相关推荐
- gamit怎么利用glred生成测站时间序列_香侬读 | 按什么套路生成?基于插入和删除的序列生成方法
"香侬读"是香侬科技(Shannon.AI)推出的栏目,每周解读NLP或前沿.或经典的论文,捕捉时下最新见解,探究未来发展趋向.欢迎大家广泛讨论.交流,也欢迎推荐优秀的文章与我们一 ...
- oss生成唯一文件名_根据结构化自然语言规范自动生成精确预言
1 引用 Tomassi D A, Dmeiri N, Wang Y, et al. Bugswarm: mining and continuously growing a dataset of re ...
- java编译后生成字节码_请问java源文件编译后怎么生成字节码文件?
比如,有的java源程序生成一个字节码文件,带有内部类的生成两个.可是有一种情况怎么回事呢?importjava.awt.*;importjavax.swing.*;importjava.awt.ev ...
- maven 生成本地库_在2017年从Maven工件生成P2存储库
maven 生成本地库 几年前,我写了一篇博客文章,介绍如何基于Maven工件生成P2存储库. 如今,这种描述的方法已经过时了,我想展示一种基于p2-maven-plugin的新方法,该方法是为解决此 ...
- datatable如何生成级联数据_如何把Excel表数据批量生成条形码
条形码属于一维条码,是将宽度不等的多个黑条和空白,按照一定的编码规则排列,用以表达一组信息的图形标识符,条形码的种类比较多,比如常用的Code128码,Code39码,Code93码,EAN-13码, ...
- python根据模板生成pdf文件_程序生成word与PDF文档的方法(python)
程序导出word文档的方法 将web/html内容导出为world文档,再java中有很多解决方案,比如使用Jacob.Apache POI.Java2Word.iText等各种方式,以及使用free ...
- python随机密码生成在26个字母中随机生成10个_习题6:二.3 随机密码生成
编写程序在26个字母大小写和9个数字组成的列表中随机生成10个8位密码. import random num_ls = [] # 创建数字.小写字母.大写字母空列表 str_ls = [] STR_l ...
- python根据模板生成pdf文件_如何使用ReportLab从各种页面模板生成PDF?
我有JSON文件和大量数据和四个PDF模板的页面.我需要用所有这些数据和页面模板生成一个PDF文件.现在我的代码如下:self.line(self.data['sendr'], 10, 250, 25 ...
- python如何生成excel文件_[原创] 如何用python3自动随机生成Excel文件内容
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 首先来看效果图 文件命名 文件内容 代码说明如下 import xlrd import xlwt from my_framework.log import ...
最新文章
- 100年前伦敦爆发的霍乱,教会了人类什么?
- 【关于重载OnBackPressed无效的解决方案】
- 窗体的常用属性和常用事件
- 怎样快速识别 英文地址中包含非英文字符_[论文笔记]端到端的场景文本识别算法--CRNN 论文笔记...
- 递归的效率问题及递归与循环比较
- Junit单元测试学习笔记(一)
- requests 返回的cookies为空_爬虫学习(2)(requests库)
- JAVA Web学习篇--Servlet
- 基于CentOS7,MySQL5.7的 读写分离
- 唐宇迪opencv-背景建模
- 城市大数据及开放数据索引
- ramda 函数 list
- AS(强直性脊柱炎)完全手册
- 再见了 VMware,一款更轻量级的虚拟机!
- Java发送http的get、post、put请求
- C# 如何取得本机网卡的型号,IP地址,子网掩码和网关
- AE插件-快速景深模糊插件 Aescripts Fast Bokeh Pro v2.0.7 WIN
- 你不具备访问 IIS 配置文件的权限。要在 IIS 上打开和创建网站,需要使用 Administrator 帐户运行 Visual Studio。
- 购物篮数据两种商品间的关联分析
- bbox regresion
热门文章
- Angular多个页面引入同一个组件报错The Component ‘MyComponentComponent‘ is declared by more than one NgModule怎么办?
- 判断为空:null、undefined、空字符串、中文空格
- 【骚气的动效】无限循环往下往复淡入淡出运动,通常用于向下箭头,提示用户可以往下滚动或者点击展开
- 安装和使用Oracle VM VirtualBox中的要点,注意事项和遇到的问题
- 读书:历史 -- 东印度公司
- [转帖]tar高级教程:增量备份、定时备份、网络备份
- Navicat新建查询快捷键
- 再谈Spring Boot中的乱码和编码问题
- 机器学习:协方差矩阵
- Javascript动画效果(四)