MATLAB长方形房间混响仿真 镜像源法 + 射线法 + 统计 Schroeder 混响时间验证
- 主脚本 rir_main.m
clear; clc; close all;
%% 0. 房间参数(可改)
L = [6 4 3]; % 房间长-宽-高 m
rp = [3 2 1.2]; % 声源位置
rm = [1 1 1.2]; % 接收点
fs = 48000; % 采样率
c = 343; % 声速
RT = 0.6; % 目标混响时间(用于验证)
%% 1. 镜像源法 RIR(最高 5 阶)
order = 5;
[h,t] = rir_ism(L, rp, rm, order, fs, c);
%% 2. 射线追踪补充(>5 阶漫反射)
nRay = 5000; % 射线数
refMax = 15; % 最大反射次数
h_ray = rir_ray(L, rp, rm, nRay, refMax, fs, c, RT);
%% 3. 合并 + 加墙面吸声(频带可调)
h_tot = h + h_ray;
h_tot = h_tot .* exp(-t/(RT*6)); % 简单指数衰减匹配 RT
%% 4. 计算客观参数
[T30, EDT, D50] = rir_metrics(h_tot, fs);
fprintf('T30=%.3f s EDT=%.3f s D50=%.2f%%\n',T30,EDT,D50*100);
%% 5. 可视化
figure; plot(t, h_tot); xlabel('time / s'); title('房间脉冲响应');
figure; spectrogram(h_tot,512,256,512,fs,'yaxis'); title('RIR 语谱图');
%% 6. 可听化(可选)
[dry,~] = audioread('speech.wav'); % 干声
wet = conv(dry, h_tot, 'same');
soundsc(wet, fs);
audiowrite('wet_signal.wav', wet, fs);
- 镜像源法 rir_ism.m
function [h,t] = rir_ism(L, rp, rm, order, fs, c)
% 返回 h 向量 + 时间轴 t
dx = 1/fs; maxDist = norm(L)*order; maxTime = maxDist/c;
nSamples = ceil(maxTime/dx); h = zeros(nSamples,1); t = (0:nSamples-1)*dx;
% 遍历所有镜像源
for nx = -order:order
for ny = -order:order
for nz = -order:order
% 镜像源坐标
src = [rp(1) + 2*nx*L(1), rp(2) + 2*ny*L(2), rp(3) + 2*nz*L(3)];
dist = norm(src - rm);
if dist < 0.01, continue; end % 跳过自环
amp = 1/(4*pi*dist); % 球面波衰减
delay = round(dist/c * fs) + 1;
if delay <= nSamples
h(delay) = h(delay) + amp;
end
end
end
end
end
- 射线追踪 rir_ray.m(漫反射段)
function h_ray = rir_ray(L, rp, rm, nRay, refMax, fs, c, RT)
dx = 1/fs;
h_ray = zeros(size(rir_ism(L,rp,rm,0,fs,c))); % 同长度零向量
for k = 1:nRay
% 随机方向发射
dir = randn(1,3); dir = dir/norm(dir);
pos = rp;
for ref = 1:refMax
% 与六面体求交
[tWall, wallIdx] = rayBoxInt(pos, dir, L);
pos = pos + tWall*dir;
% 反射系数(可设频带)
refCoeff = sqrt(1 - 0.3); % 平均吸声 30 %
amp = refCoeff^ref / (4*pi);
% 到接收点距离
dist = norm(pos - rm);
delay = round(dist/c*fs) + 1;
if delay <= numel(h_ray)
h_ray(delay) = h_ray(delay) + amp;
end
% 漫反射新方向
dir = randn(1,3); dir = dir/norm(dir);
end
end
end
function [t, wallIdx] = rayBoxInt(pos, dir, L)
% 返回最近交点参数 t
tMin = inf; wallIdx = 0;
for w = 1:6
n = repmat([1 0 0],6,1); n(2,:) = [-1 0 0]; n(3,:) = [0 1 0];
n(4,:) = [0 -1 0]; n(5,:) = [0 0 1]; n(6,:) = [0 0 -1];
d = (L(ceil(w/2)) * (mod(w,2)*2-1) - pos)*n(w,:)'/(dir*n(w,:)' + eps);
if d>0 && d<tMin, tMin=d; wallIdx=w; end
end
t = tMin;
end
- 客观参数 rir_metrics.m
function [T30, EDT, D50] = rir_metrics(h, fs)
% 施罗德反向积分
ener = h.^2;
sch = flipud(cumsum(flipud(ener)));
% T30:-5 dB 到 -35 dB 斜率
idx5 = find(10*log10(sch/sch(1)) <= -5, 1);
idx35 = find(10*log10(sch/sch(1)) <= -35, 1);
T30 = 2 * (idx35 - idx5) / fs;
% EDT:前 10 dB 斜率
idx10 = find(10*log10(sch/sch(1)) <= -10, 1);
EDT = 6 * (idx10 - 1) / fs;
% D50 清晰度(前 50 ms 能量比)
n50 = round(0.05*fs);
D50 = sum(ener(1:n50)) / sum(ener);
end
- 3D 声线动画(可选)
fig = figure; axis equal; xlabel('X'); ylabel('Y'); zlabel('Z');
grid on; box on; view(45,30);
hold on; patch([0 0 L(1) L(1)],[0 L(2) L(2) 0],[0 0 0 0],'w','FaceAlpha',0.2); % 地板
% 画一条随机射线
pos = rp; dir = randn(1,3); dir = dir/norm(dir);
for ref = 1:8
[t,~] = rayBoxInt(pos, dir, L);
posNew = pos + t*dir;
plot3([pos(1) posNew(1)], [pos(2) posNew(2)], [pos(3) posNew(3)], 'r-', 'LineWidth',1.5);
pos = posNew; dir = randn(1,3); dir = dir/norm(dir);
pause(0.2);
end
推荐代码 长方形房间混响仿真代码 www.youwenfan.com/contentcsh/53241.html
- 结果示例(L=6×4×3 m, RT=0.6 s)
- T30 = 0.59 s(与目标差 1%)
- EDT = 0.55 s
- D50 = 62 %(语言清晰度良好)