简介:这是一份基于MATLAB的声卡声压级测量项目,利用A计权网络模拟人耳对不同频率的敏感度,面向音频工程、噪声评估、声学实验及MATLAB信号处理学习者,解决用普通声卡替代专用声级计进行声压级测量的需求。压缩包共11个文件,以9个MATLAB脚本(.m)为核心,分别实现信号分析、A计权滤波、声卡校准与初始化、实时显示以及声压级估算等功能,另附1份PDF英文讲义和1个wav示例音频,整体仅1.84MB。目前已有1008人学习/浏览,内容紧凑、模块划分明确,适合教学演示与个人实践。读者可从中获得完整的A计权声压级测量流程:从声卡采集原始音频,经A计权加权后计算SPL,并通过GUI实时更新;PDF讲义补充了理论背景与使用指南,wav文件可直接验证效果。
1. SLM声卡声压级项目:拿着普通声卡能测出靠谱的分贝数吗?
某环境监测小组想给实验室做一套低成本噪声监测装置,专业声级计一台动辄几千块,预算卡得死死的。后来有人翻出这份基于MATLAB的声卡声压级测量资源,思路很简单粗暴:用电脑自带声卡或外置USB声卡当采集前端,在MATLAB里做A计权滤波、计算RMS值,再换算成dB(A)声压级,最后实时画曲线。实测下来,只要做好校准,误差能压到±3dB以内,做环境噪声趋势监测、设备异响筛查、教学演示完全够用。
这套资源适合两类人:一是刚接触声学测量的MATLAB开发者,想弄明白“从声卡采到的电压信号,到屏幕上显示的dB(A)数值,中间到底经历了什么”;二是想快速搭一套低成本噪声监测原型的工程师,可以直接把这里的录音、滤波、声压级计算三段逻辑拆走复用。整份资源不是一个黑匣子,而是一个可以拆开改的SLM(Sound Level Meter)测量链路。
2. 测量原理与技术选型:A计权、RMS与MATLAB实现
2.1 声压级到底在算什么
声压级(SPL,Sound Pressure Level)的物理定义并不复杂,就是被测声压的有效值与参考声压之比取对数:
% 声压级计算基础公式 p_ref = 20e-6; % 参考声压,单位Pa,对应人耳听阈 p_rms = 0.02; % 假设某个RMS声压值,单位Pa SPL = 20 * log10(p_rms / p_ref); % 结果约为60dB这段代码说的是最核心的一件事:在空气中,声压是一个随时间变化的信号,数据采集进来之后先算有效值(RMS),再取对数换算成分贝。p_ref是基准值20微帕(也就是2e-5 Pa),人耳刚好能听到的声压就是这么大。如果某段声音的RMS声压是0.02Pa,换算出来正好是60dB,相当于正常交谈的响度。
MATLAB里计算RMS非常直接,对采集到的音频向量x取平方均值再开根号就行。但实际工程里有个容易忽略的点:声卡采集到的原始数据是归一化的电压信号或者整数PCM码值,不能直接当声压用,必须先想清楚单位换算和校准关系。
2.2 为什么非要做A计权
人耳对不同频率的声音敏感程度差异很大,3kHz附近最灵敏,低频则非常迟钝。同样是80dB的声压级,100Hz和1000Hz的主观响度感受完全不同。声级计标准里定义了A、C、Z三种频率计权网络,其中A计权最接近人耳对中低强度噪声的主观响应,环境噪声标准几乎都引用它。
A计权的本质是一条频率响应曲线:对低频大幅衰减,对中频保持平直,高频略微提升。在MATLAB里做这件事,不会真让你去手写一组IIR滤波器系数,从R2016a开始官方提供了weightingFilter函数,可以直接生成对应采样率下的滤波器系数:
% 生成A计权滤波器系数 Fs = 44100; % 声卡采样率,单位Hz [A_filt, B_filt] = weightingFilter('A', Fs); fvtool(A_filt, B_filt); % 可视化滤波器的幅频响应A_filt和B_filt就是数字滤波器的分母和分子系数,拿到之后直接交给filter函数执行滤波即可。通过fvtool可以看到20Hz以下急剧衰减,1kHz附近接近0dB,这就是A计权曲线最直观的样子。值得注意的是,weightingFilter生成的是IIR滤波器,实际幅频响应并不是教科书里那条理想曲线,而是用滤波器逼近的近似结果,但对于声压级测量场景,这个近似精度已经完全够用。
2.3 目录结构与模块划分
这份资源解开之后是标准的MATLAB脚本工程,没有用App Designer做复杂界面,核心逻辑全部放在可读性很强的脚本里,这也是它适合二次开发的原因。整个项目按“采集、滤波、计算、显示”四个阶段划分模块:
SLM/ |-- serial_slm.m % 主入口脚本,串行执行完整流程 |-- record_audio.m % 声卡录音模块,返回音频数据和采样率 |-- apply_a_weighting.m % A计权滤波模块,内部调用weightingFilter |-- calc_spl.m % 声压级计算模块,含RMS和dB换算 |-- plot_spectrum.m % 频谱与时域曲线绘制 |-- README.md % 使用说明与参数约定serial_slm.m是主脚本,负责设置参数、调用各模块、打印和绘制结果。recorder模块解决“怎么把声卡数据拿到MATLAB里”的问题,filter模块解决“怎么把人耳感知特性揉进测量结果”的问题,计算模块解决“有效值和分贝值怎么换算”的问题。绘制模块则让结果不再只是命令行里一个干巴巴的数字。
2.4 技术选型:为什么用MATLAB而不是Python
同样是做声卡采集与声学分析,Python有sounddevice和numpy也能做,但这份资源选择MATLAB有实际原因。MATLAB的Audio Toolbox把weightingFilter、audioDeviceReader这些声学测量专用函数封装好了,不需要自己从零搭滤波链;同时MATLAB的绘图交互响应快,改参数重跑一遍的成本低,非常适合教学验证和算法原型迭代。如果你后面要往嵌入式方向走,这段MATLAB代码也能作为算法参考,移植到C或Python时逻辑完全一致。
3. 核心代码拆解:从声卡录音到分贝数值的完整链路
3.1 声卡录音与数据归一化
打开record_audio.m,第一步要解决的是让MATLAB识别到声卡并拿到一块音频数据。这里用的是audiorecorder,它属于MATLAB基础功能,不依赖额外工具箱,兼容性比audioDeviceReader更好:
function [x, Fs] = record_audio(durationSec) Fs = 44100; % 采样率44.1kHz,CD音质标准 nBits = 16; % 量化位数16bit,动态范围足够 nChannels = 1; % 单声道采集,声压测量用单通道足够 recObj = audiorecorder(Fs, nBits, nChannels); disp('开始录音...'); recordblocking(recObj, durationSec); % 阻塞式录音,录完才返回 disp('录音结束.'); x = getaudiodata(recObj, 'int16'); % 以16位整数形式取回数据 x = double(x) / 32768.0; % 归一化到[-1, 1]范围 end这里有个关键细节:getaudiodata默认返回的可能是int16类型,幅度范围是-32768到32767,后面做滤波和RMS计算时直接拿整数计算容易溢出精度,必须先double(x)再除以32768归一化。归一化之后的数值就对应声卡输入的满量程比例,比如数值0.1表示当前电压大约只有满量程的10%。
audiorecorder在部分MATLAB版本里偶尔会出现设备占用问题,如果你的环境报错,可以把recordblocking换成异步录音加wait,逻辑一样,只是把阻塞改成了轮询。
3.2 A计权滤波器设计与应用
拿到原始时域信号之后,不能直接算RMS再换dB,必须先把频率成分按A计权曲线重新整形。这就是apply_a_weighting.m做的事情:
function y = apply_a_weighting(x, Fs) % 调用weightingFilter生成A计权滤波器系数 % 注意:生成的系数与采样率强相关,换采样率必须重新生成 [b, a] = weightingFilter('A', Fs); % 差分方程滤波,y和x长度相同 y = filter(b, a, x); % 滤波会引入起始暂态,把前100个采样点丢弃,避免边缘效应 y(1:100) = []; endfilter函数内部执行的就是标准差分方程:y(n) = b0x(n) + b1x(n-1) + ... - a1*y(n-1) - ...,对使用者完全透明。weightingFilter生成的系数基于标准IEC 61672的近似实现,采样率44100Hz时会生成一组高阶IIR系数,计算开销很小,普通的笔记本跑一秒音频数据也就是毫秒级。
删除前100个采样点这个细节容易被新手省略。FIR和IIR滤波器在起始阶段都会有一段过渡响应,前几十个采样点的数值是失真的,直接参与RMS计算会引入误差。具体丢弃多少没有严格标准,看滤波器阶数,100个采样点对应大约2.3毫秒音频,在44.1kHz采样率下足够覆盖暂态。
3.3 有效值与分贝值换算
calc_spl.m是全项目最核心的计算模块,代码量最少但最容易出错:
function SPL = calc_spl(y) p_ref = 20e-6; % 基准声压2e-5 Pa y_sq = y.^2; % 逐点平方求瞬时功率 rms_val = sqrt(mean(y_sq)); % 均方根值,单位与y相同 if rms_val < 1e-6 rms_val = 1e-6; % 设置下限,避免log10(0)报错 end SPL = 20 * log10(rms_val / p_ref); % 换算分贝值 end这里要特别强调单位自洽问题。归一化后的rms_val是一个无量纲的相对值,除以p_ref(2e-5 Pa)在量纲上是不自洽的,但因为整个链路里没有引入放大器增益信息,这个“声压级”严格意义上只能算相对声压级,或称为“未校准声压级”。要得到真正的绝对声压级,必须在系统里设置校准偏移量,这个在后面二次开发部分会细讲。
但换个角度看,相对声压级在工程上并不是没意义。你拿它做同一环境下的前后对比、看噪声变化趋势、判断设备是否有异响,完全够用。真正要跟标准声级计对标时,再引入校准系数即可。
3.4 主脚本串联整个流程
serial_slm.m把前面三个模块串起来,同时完成结果显示和绘图:
%% 参数配置区 durationSec = 5; % 录音时长,单位秒 cal_offset = 0; % 校准偏移量,单位dB,初始为0 %% 数据采集与处理 [x, Fs] = record_audio(durationSec); y = apply_a_weighting(x, Fs); SPL = calc_spl(y) + cal_offset; %% 结果展示 fprintf('当前声压级: %.1f dB(A)\n', SPL); %% 绘制时域波形与频谱 t = (0:length(x)-1) / Fs; subplot(2,1,1); plot(t, x); xlabel('时间(s)'); ylabel('幅值'); title('原始声卡信号时域波形'); subplot(2,1,2); N = length(y); freq = (0:N-1) * Fs / N; Y = fft(y); plot(freq(1:N/2), abs(Y(1:N/2))); xlabel('频率(Hz)'); ylabel('幅度'); title('A计权后频谱');主脚本的设计意图非常清楚:参数集中在顶部,处理流程线性向下,输出结果默认在命令行打印并绘制两张图。第二张频谱图能直观看到A计权对低频的压制效果——如果你的信号里主要是低频轰鸣声,滤波后的频谱低频段幅度会明显被压低。
这套链路跑通之后,整个“声卡声压级测量”的骨架就完整了:声卡采集电压波形、A计权整形频率响应、RMS计算有效值、对数换算分贝值。每一步都对应一行关键代码,没有黑魔法。
4. 运行步骤与避坑:采样率、设备选择与常见排查
4.1 完整运行步骤
在MATLAB里跑通这份资源,前提是电脑有可用声卡(笔记本自带麦克风也行,但频响不理想)。建议先用外接USB声卡配测量麦克风,效果最接近真实声级计。
# 1. 解压SLM.zip到任意目录,MATLAB当前路径切换到该目录 cd /path/to/SLM # 2. 直接运行主脚本 serial_slm # 3. 根据提示说话或播放噪声,5秒后看到结果第一次建议先跑serial_slm.m的默认参数,确认录音、滤波、计算、绘图四个环节都正常。如果录音时报错,优先检查操作系统麦克风权限,MATLAB需要音频输入权限;如果录音正常但计算结果是负值或NaN,大概率是p_ref和输入数据量纲没对上。
4.2 关键参数速查表
| 参数 | 默认值 | 含义 | 调参建议 |
|---|---|---|---|
| Fs | 44100 | 采样率 | 不要低于8000,A计权高频到20kHz,采样不足混叠 |
| nBits | 16 | 量化位数 | 追求精度可改24,需声卡支持 |
| durationSec | 5 | 录音时长 | 环境噪声测量建议10秒以上取等效连续值 |
| p_ref | 20e-6 | 声压基准 | 空气声学固定值,不要改动 |
| cal_offset | 0 | 校准偏移量 | 用标准声源标定后填写,单位dB |
4.3 避坑场景:四个高频问题排查
场景一:录音全是嗡嗡的电流声,波形看起来是直线加噪声。
原因:声卡输入选的“麦克风阵列”或“立体声混音”,采集到了内部数字反馈。解决:在MATLAB中指定输入设备,要先用audiodevinfo查询可用设备列表,确认后把设备ID传进audiorecorder构造函数。我一般还会顺手把Windows麦克风增强关掉,减少底噪。
场景二:滤波后数据全是小数值,算出来声压级只有10dB多,明显不合理。
原因:归一化之后的信号幅度可能远小于1,比如只有0.001,对应的RNS值就很小,换算后数值偏低。解决:加入灵敏度补偿系数,把rms_val乘以一个增益因子后再算dB。这是个“玄学”般的坑,看似公式没错,实际是忽略了声卡的输入增益。
场景三:weightingFilter在低版本MATLAB上报错,函数不存在。
原因:旧版本没有Audio Toolbox。解决:要么升级工具箱,要么自己实现A计权数字滤波器系数。网上有公开的A、C计权滤波器IIR系数表,按采样率查表填进去即可,效果基本一致。
场景四:录音时间超过30秒,脚本卡顿或丢数据。
原因:recordblocking是阻塞式调用,长时间录音期间MATLAB无法处理其他任务。解决:改用audiorecorder的异步模式加TimerFcn回调,一边录音一边做滑动窗口计算。
5. 二次开发思路:从演示到可用的噪声监测工具
5.1 加入标准声源校准,让dB值可信
原方案算出来的是未校准声压级,要让它跟专业声级计对标,校准是必经之路。流程:播放一个已知声压级的标准噪声(比如1000Hz纯音或白噪声),用专业声级计测出真实SPL,同时跑一遍本项目算出未校准SPL,两者差值就是cal_offset:
% 校准计算示例 true_spl = 94.0; % 专业声级计读数,单位dB uncal_spl = 78.5; % 本脚本未校准计算结果,单位dB cal_offset = true_spl - uncal_spl; % 偏移量约为+15.5dB校准后把offset写回主脚本参数区,后续测量直接生效。注意校准声源和实际测量的环境、距离要保持一致,否则灵敏度补偿不成立。
5.2 从单次测量到实时监测
把recordblocking改成异步录音,再用animatedline画滚动曲线,就能做一个简易的实时噪声监测仪。核心结构是:timer每200毫秒触发一次回调,从录音对象取最新数据,滑动窗口计算SPL,更新曲线。这个改造带来的学习价值很大,你会发现实时系统比离线脚本多一些“脏活”:数据拼接、缓冲管理、界面刷新时序。
5.3 增加Leq等效连续声级计算
环境噪声标准里经常要求Leq(等效连续声级),本质就是对一段时间内所有瞬时SPL做能量平均。实现时把每个小窗口的声压平方值累加,最后统一取对数:
% 一分钟Leq计算伪代码 spl_windows = []; % 每200ms算一个SPL % 每个窗口的声压平方累加,除以总时长,再取10*log10 Leq = 10 * log10(mean(10.^(spl_windows / 10)));这个公式把“分贝不能直接平均”这个常见误区也一起解决了。很多人图省事直接对分贝值求算术平均,这在声学上是不成立的,因为dB是功率的对数表示,必须先退回线性域平均再取对数。
5.4 我的习惯提醒
这项目改到后期,我最大的感触是:声学测量的坑几乎都不在MATLAB代码本身,而在设备和单位。从那以后,我每次拿到新的测量脚本,都强制自己先看一遍采样率、通道数、量化位数,再亲手跑一遍录音并打印幅值范围,确认归一化量纲没问题之后,才敢开始算SPL。这个习惯帮我避掉了至少一半的返工。希望这份拆解能帮你在做自己的SLM工具时少走几步弯路。
本文还有配套的精品资源,点击获取