Matlab读取WAV文件:从audioread入门语音信号处理

发布时间:2026/10/6 4:48:18
Matlab读取WAV文件:从audioread入门语音信号处理 打开Matlab先给自己整杯咖啡咱们今天要玩点有意思的语音信号处理。不从那些花里胡哨的界面操作讲起直接从硬盘里拽个wav文件进来我习惯用audioread直接怼一行代码数据到手。这条路径看着简单但背后藏着一整套语音信号处理的入门逻辑——文件格式、采样率、量化精度、数据组织方式每一样都会影响你后续所有分析的结果。这篇东西就是围绕“读入wav并开启语音信号处理”这件事把思路、操作、坑点一次讲透。1. 动手之前先搞清楚wav文件到底是什么1.1 为什么语音处理首选wav而不是mp3语音信号处理的第一个门槛不是算法不是滤波器设计而是“你怎么把声音变成电脑能算的数字”。mp3、aac这类格式经过了有损压缩高频细节和瞬态信息已经被砍掉了一部分做欣赏音乐无所谓但做分析就不太合适。你分析的是压缩伪影还是真实语音特征说不清楚。wav文件Waveform Audio File Format是微软和IBM联合开发的未压缩音频格式属于RIFF文件家族。核心优势就一句话采样后的原始数据长什么样它就存成什么样。声音进入麦克风模数转换器按固定频率采样得到一串幅值序列wav基本就是把这串序列原封不动写进文件。这意味着你用audioread读出来的数据就是数模转换器输出的那个原始序列中间没有经历任何编码解码的信息损耗。打个比方mp3相当于你拍了一张照片还压了一遍发朋友圈wav相当于直接看相机RAW原图。做语音特征提取、噪声分析、端点检测这类精细活儿RAW级别保真度很重要。当然wav文件体积大一分钟立体声44.1kHz/16bit大约10MB但现代硬盘和内存早已不是瓶颈所以学术实验、算法验证、嵌入式原型开发几乎都用wav做标准输入。1.2 采样率、位深、声道读数据前你必须知道的三件事audioread返回的其实很简单一个数据矩阵加一个采样率。但想用好这两个返回值得先弄明白它们对应什么物理意义。采样率Sample Rate每秒钟对模拟声音信号采样的次数单位是Hz。常见的语音采样率有8000Hz电话质量、16000Hz语音识别标准、44100HzCD音质、48000Hz视频音轨标准。根据奈奎斯特采样定理采样率必须大于信号最高频率的两倍否则会产生混叠。人的语音能量主要集中在300Hz到3400Hz所以8kHz采样率就能保住语音可懂度但想保留更丰富的齿音、气声细节需要16kHz甚至更高。audioread返回的fs就是文件头里记录的采样率数值你可以直接拿它来构建时间轴t (0:length(y)-1) / fs;这样后续画波形图、算短时能量、做频谱分析才有正确的时间基准。千万别自己拍脑袋写死一个采样率一旦文件实际采样率和你假设的采样率不一致整个频域分析结果都会跟着跑偏。位深Bit Depth每个采样点用多少个bit来量化幅值。常见的有8bit、16bit、24bit、32bit float。位深决定了动态范围和量化信噪比。16bit是CD标准信噪比理论值约96dB对绝大多数语音处理任务足够用。8bit只有48dB左右的动态范围背景噪声容易被放大一般不建议用在严肃分析里。audioread在读入文件时会自动做一件事把所有整数PCM数据归一化到[-1, 1]区间。比如16bit的整数原始值是-32768到32767读进来以后变成-1到1的浮点数。这个细节极其重要——你用audiowrite写数据时也要把数据控制在[-1, 1]范围内否则会被截断或产生削波失真。声道数Channelsaudioread返回的数据是一个二维矩阵行数等于采样点数列数等于声道数。单声道是N×1双声道是N×25.1环绕声就是N×6。语音信号处理绝大多数情况只需要单声道所以拿到多声道数据后通常先做混单声道处理比如直接把左右声道平均或者只取一个声道。这个操作小到很多人不以为意但在后续计算过零率、短时能量时多声道和单声道的处理逻辑完全不同。注意audioread返回的矩阵永远是行向量方向排列的——每一行是一个采样时刻每一列是一个声道。这和很多Python音频库返回的形状正好相反跨语言对比结果时记得先确认数据排列方式。2. 核心实操用audioread把wav文件“怼”进来2.1 基本用法一行代码读出全部数据audioread最基础的用法就一行[y, fs] audioread(speech.wav);y是音频数据矩阵fs是采样率。如果你不是做实时流处理只是分析一段已经录好的语音这一行已经够了。但audioread还有几个容易被忽略的重要参数。比如只需要读取文件某一段时可以用[start, stop]指定采样点区间[y_seg, fs] audioread(speech.wav, [1, 16000]);这一招在调试时极为有用。一个大文件几百万个采样点每次全量读入再截取不仅慢还烧内存。限定读取区间相当于只加载你关心的那部分数据处理效率高很多。而且注意start和stop是以“采样点”为单位的不是秒。你要读第1秒到第2秒的内容需要换算成[1*fs, 2*fs]或者更稳妥地用round处理非整数边界。还有一个audioread的隐藏技巧用audioread结合audioinfo先看文件元信息再做针对性读取。info audioinfo(speech.wav); disp(info);audioinfo会返回采样率、总采样点数、位深、声道数、压缩方式、文件大小等所有元数据。我习惯先跑这个看一眼再决定是一次性读全量还是分段读。这个习惯帮我避开了不少内存溢出的问题。2.2 读进来之后先固话三件事数据成功读入不代表万事大吉我每次拿到y和fs之后都会按固定顺序做三件固话操作。第一确认数据形状size(y) % 看一眼行列数如果列数大于1说明是多声道文件。先决定要不要混成单声道。混单声道最简单的方式if size(y, 2) 1 y_mono mean(y, 2); else y_mono y; end把左右声道加起来取平均是最朴素也最稳妥的混单声道方式。第二确认数据范围。audioread理论上会返回[-1, 1]区间的浮点数但某些特殊编码的wav文件可能会出现轻微越界或者全部数据都在一个很小的范围内。用max(abs(y))看一下峰值如果峰值远小于1比如只有0.01说明这段录音音量很小后续做特征提取时可能需要先做增益归一化。第三确认采样率的合理性。语音处理里fs常见的值是8000、16000、44100。如果你的文件采样率是22050、32000这类“中间值”不影响处理但后续如果要把数据喂给语音识别模型或和别的数据做拼接最好先重采样到统一采样率。Matlab里用resample可以直接完成y_resampled resample(y_mono, 16000, fs);resample内部会自动设计抗混叠滤波器比你自己先插值再滤波可靠得多。2.3 audioread和wavread的区别旧代码迁移是否必要如果你是从2015年之前开始用Matlab的大概率写过wavread。这个老函数在R2016a之后的官方文档里就标记为“仍然可用但不推荐”后续版本中逐渐被audioread替代。最关键的区别有两个。第一wavread默认返回整数类型如果你不指定输出类型读16bit wav会得到int16数组而audioread统一返回double浮点数而且数据范围已经归一化到[-1, 1]。这意味着老代码里的double(y) / 32768这类手动归一化的写法在迁移到audioread后可以直接删掉但如果没删掉就相当于对数据做了两次归一化幅值会缩水32768倍后续分析会完全错乱。第二wavread支持的文件格式有限基本只支持标准PCM编码的wavaudioread的支持范围更广除了wav还支持ogg、flac、m4a、mp3等格式。虽然咱们这个场景主要用wav但掌握的接口能读更多格式总不是坏事。老代码迁移的建议很简单把wavread换成audioread把原来依赖整数类型数据的代码逻辑例如用int16做阈值判断全面检查一遍因为数据类型变了所有的比较运算、算术运算都需要重新验证。我见过一个同事迁移后忘记修改一个 32768的阈值判断结果所有数据都被判定为削波整整排查了半天。提示凡是看到“数据全是0”“数据全是一个常数”“音频播放没声音但波形看起来正常”这种诡异现象优先检查是不是做了两次归一化或者数据类型判断逻辑还停留在wavread的整数时代。3. 读进来只是第一步语音信号处理的标准开场动作3.1 时域先画波形图让数据“长”出样子来读入数据后第一个动作永远是可视化。你连这段语音长什么样都没见过就急着去算特征、跑模型出错了都不好定位。Matlab里画波形图极简单t (0:length(y_mono)-1) / fs; plot(t, y_mono); xlabel(时间 (s)); ylabel(幅值);画完波形图第一件事是看整体轮廓。语音信号的特点是“稀疏的”——有人说话时幅值大没人说话时幅值接近零。这个特性直接决定了语音处理里非常经典的“端点检测”策略通过短时能量判断哪里是语音、哪里是静音。从波形图里还能看出有没有削波。如果波形顶部和底部被切成一字平头说明录音时增益调太大信号超出量化范围被截断了。削波会引入大量高次谐波失真后续做频谱分析时会看到高频区域莫名其妙多了很多能量而且这些能量不是真实语音内容是削波产生的——这是削波最坑的地方它不会让你的波形“看起来不对”甚至听起来只是稍微破音但频谱上已经脏了。再看一眼包络。语音的包络可以粗略反映发音节奏。比如一段“你好”波形包络会出现两个明显的隆起。这个直觉印象对后续理解短时能量、短时过零率非常有用。3.2 频域从时域波形到功率谱时域波形只能看幅值变化看不出频率成分。语音信号里有基频决定音高、共振峰决定音色、清音摩擦噪声决定辅音清晰度这些信息全部藏在频域里。想看到频率结构用傅里叶变换。N length(y_mono); Y fft(y_mono); P abs(Y(1:N/21)).^2 / N; f (0:N/2) * fs / N; plot(f, 10*log10(P eps)); xlabel(频率 (Hz)); ylabel(功率谱密度 (dB));这里有几个细节值得说清楚。为什么只取一半因为实信号的傅里叶变换是共轭对称的后半部分是前半部分的镜像画出来没意义。为什么功率谱要除以N并取对数因为直接画幅度谱时低频部分的巨大能量会把高频细节压成一条贴近零的线取对数dB刻度能让动态范围压缩小细节才能显现出来。从功率谱里能读出的最典型特征是共振峰。比如元音/a/的频谱会出现几个明显的峰这些峰对应的频率位置就是声道共振特性决定的。你读10段不同人说同样一个“a”共振峰位置会有一个大概的范围这正是语音识别中区分元音的关键所在。3.3 语谱图把时间、频率、能量画到一张图上波形图丢了频率信息平均功率谱丢了时间信息。语音是非平稳信号——上一秒是元音下一秒是辅音频率成分随时间剧烈变化。想同时看到时间和频率就得用语谱图。spectrogram(y_mono, hamming(256), 128, 512, fs, yaxis);这里参数解释一下hamming(256)是窗函数和窗长每256个采样点作为一个短时片段128是相邻帧之间的重叠点数512是FFT点数决定频率分辨率fs是采样率。语谱图的横轴是时间纵轴是频率颜色深浅代表能量强弱。语谱图是语音信号处理里最实用的可视化工具没有之一。你能直观看到浊音段元音、浊辅音表现为颜色较深的横向条纹这些条纹就是基频及其谐波体现出明显的周期性。清音段如/s/、/sh/表现为高频区域颜色均匀的噪杂块没有竖条纹结构。语谱图上的“竖条边界”对应语音的起止时刻天然用于端点检测。我通常会把波形图、功率谱、语谱图三张图并排显示先整体扫一眼再做任何算法处理。这一步虽然朴素但能让你对数据有一个全局的直觉判断这段语音大概内容是什么、有没有噪声、有没有削波、噪声是宽带还是窄带、信噪比大概如何。后续所有处理步骤都会因此更有方向感。注意spectrogram默认返回的S矩阵是一个复矩阵如果后续你要基于语谱图做特征提取记得用abs(S).^2取能量谱不要直接用复数参与运算。4. 实战中的坑常见问题与排查技巧4.1 文件读不进来路径、格式、权限三大元凶audioread报错读不了文件我见得最多的就是三种情况。文件路径问题当前工作目录里根本没有这个文件。有人喜欢直接在命令行敲audioread(speech.wav)结果文件放在D:\data\里自然找不到。解决办法有两个一是用cd切换当前目录二是用绝对路径[y, fs] audioread(D:\data\speech.wav);注意Matlab字符串里的反斜杠需要写成双反斜杠如果直接写单反斜杠会被当成转义字符。更稳妥的写法是用正斜杠[y, fs] audioread(D:/data/speech.wav);这在Windows下完全没问题还能避免转义符引起的各种诡异报错。文件格式问题有些wav文件其实是压缩格式比如用某些录音软件导出的wav是ADPCM编码的audioread能读但读出来的数据可能与你预期不同另一些扩展名是wav但容器格式实际是别的编码直接报错。处理办法是用audioinfo查看CompressionMethod字段如果是PCM放心读如果显示ADPCM或MP3就得先转换格式或用专门的解码工具。文件权限问题文件正在被其他程序占用、只读权限受限、放在系统保护目录下都可能让audioread罢工。这种错误信息通常会明确提示权限拒绝解决办法就是先把文件复制到一个普通工作目录再重新读取。遇到权限和路径问题别在命令行里死磕直接把文件拖到当前目录往往最省事。4.2 内存不足动辄几十万采样点的处理策略一段一分钟的语音44.1kHz采样率双声道读进来大约是529万个采样点double类型每个占8字节算下来约80MB内存。单个文件还好但如果要做批处理——几十个文件依次读入并处理内存管理不好就会出问题。最经典的错误写法fileList dir(*.wav); allData []; for i 1:length(fileList) [y, ~] audioread(fileList(i).name); allData [allData; y]; % 每次循环都复制一次数据 end每次执行allData [allData; y]Matlab都要重新分配一块更大的内存然后把旧数据完整复制一遍效率极低且内存碎片化严重。更合理的方式是预先分配fileList dir(*.wav); nFiles length(fileList); allData cell(nFiles, 1); for i 1:nFiles allData{i} audioread(fileList(i).name); end再大一点的文件比如两小时的录音一次性读入不现实就应该用分段读取加逐段处理的方式。segmentLen fs * 10; % 每次读10秒 startPos 1; while startPos segmentLen - 1 totalSamples [seg, ~] audioread(filename, [startPos, startPos segmentLen - 1]); % 对seg做处理 startPos startPos segmentLen; end分段读取既可以避免内存峰值又能在每段结束后释放内存是长音频处理的常规操作。4.3 数据范围异常为什么波形看起来“不对劲”读进来的y最大值是几百甚至几千这明显不对audioread读PCM wav会归一到[-1, 1]出现几百的数值说明文件本身不是标准PCM格式或者文件是32bit float编码但内部数据本身就没归一化。数据全部是NaN这通常来自损坏的wav文件或者你在后续处理中用了不合法操作比如除以0、对负数取根号把数据污染成了NaN。NaN有个特别坑的特性——它会在数组里传播一个NaN参加任何算术运算结果都是NaN你很难追踪是哪一步引入的。排查方法是在可疑步骤前后加sum(isnan(y))检查。数据全是0如果波形图是一条水平直线但播放音频有声音说明读入环节很可能出了问题或者你的数据组织方式不对——比如y是D列C行的形状你用y(:, 1)取了一列本来不是音频数据的索引。这类“数据在手里但取错了”的问题比文件本身损坏更常见记得先size(y)看一眼。4.4 多声道文件的混乱左右声道怎么处理读入一个双声道wav后size(y)返回[N, 2]。有些人拿到数据后不管三七二十一直接做FFT结果算出来的是一个N×2矩阵的傅里叶变换每一列单独变换。这并非完全是错的但你后续的特征提取、滤波、谱分析都要对每个声道独立做一遍代码复杂度翻倍。如果这段语音是单麦克风录的只是被存成了双声道文件左右声道内容几乎一样直接取第一列就够了y_mono y(:, 1);如果左右声道内容不一样比如左声道是语音、右声道是背景音乐那你得先弄清楚任务目的。提取语音通常用左声道分析音频环境有时会用两个声道的差异信息。但说实话多数语音处理场景中把两个声道平均成单声道是最不易出错的。经验之谈处理任何wav文件第一步audioinfo第二步size(y)第三步plot(y(:,1))先看一眼波形。这三步做完90%的数据问题能被拦截在进入算法之前。5. 读完wav之后从数据到语音信号处理的进阶路线5.1 预处理三部曲去均值、增益归一化、滤波读进来的语音数据不能直接扔给算法。我建议按照固定套路做预处理。去均值y y - mean(y);。如果wav文件里有直流偏置频谱上会在0Hz附近出现一个很大的能量峰影响后续共振峰提取。去掉均值相当于去掉直流分量属于常规清洁动作。增益归一化语音信号幅值大小受录音设备灵敏度影响很大。同一句话用不同话筒录幅值差几倍很正常。如果训练模型时不做幅度归一化模型会学习到与设备相关的无关特征。最简单的归一化是让峰值等于某个固定值y y / max(abs(y)) * 0.9;乘0.9是为了留一点安全余量防止后续重采样、滤波时引入过冲导致削波。滤波语音有效信息集中在300Hz-3400Hz低于300Hz的往往是环境低频噪声和电源哼声50Hz及谐波高于3400Hz的往往是气流噪声和麦克风电路噪声。一个简单的带通滤波能有效提升信噪比f_low 300; f_high 3400; [y_filtered, ~] bandpass(y_mono, [f_low, f_high], fs);Matlab的bandpass函数会自动设计并应用带通滤波器不需要手动指定滤波器阶数。但注意bandpass要求信号数据是列向量如果你传入一个行向量函数会报错或者输出维度和预期不符。这是一个非常容易踩的坑。5.2 特征提取让机器“听懂”语音的关键一步预处理完进入语音信号处理的核心场景——特征提取。对初学者来说我建议从两个最经典的特征入手。短时能量语音的响度随时间显著变化静音段能量接近零语音段能量显著上升。计算方式是把信号分成若干帧每帧25ms帧移10ms是语音处理领域的经典配置每帧求平方和取对数frameLen round(0.025 * fs); frameShift round(0.010 * fs); nFrames floor((length(y) - frameLen) / frameShift) 1; energy zeros(nFrames, 1); for i 1:nFrames frame y((i-1)*frameShift 1 : (i-1)*frameShift frameLen); energy(i) sum(frame.^2); end过零率语音信号里浊音段的声波振动有规律波形平滑过零次数低清音段的空气摩擦产生的高频噪声波形剧烈过零次数高。定义是每帧内信号符号变化的次数zeroCrossing zeros(nFrames, 1); for i 1:nFrames frame y((i-1)*frameShift 1 : (i-1)*frameShift frameLen); signs sign(frame); zeroCrossing(i) sum(abs(diff(signs)) 0) / 2; end短时能量和过零率结合起来可以组成一个非常实用的端点检测器能量高的一般是语音能量低但过零率高的可能是清音或者噪声。这也是早期语音识别系统VADVoice Activity Detection语音活动检测的核心思想。更进一步如果要提取MFCC梅尔频率倒谱系数语音识别的事实标准特征Matlab也有现成工具R2019a之后可以用mfcc函数直接提取coeffs mfcc(y_mono, fs);MFCC的核心理念是把语音频谱按照人耳感知特性映射到梅尔刻度再取倒谱得到的系数对说话人差异、环境噪声有更强的鲁棒性。绝大多数现代语音识别模型使用的输入特征本质上都是MFCC或其变体。5.3 从单条语音到数据集处理批量化与自动化单个文件处理好了真正的工程挑战在批量处理。一个语音识别项目往往有成百上千条wav文件每条长短不一、采样率不尽相同、噪声背景各有差异。这种场景下的核心经验就是写一个标准化的数据读取与预处理流水线用统一的参数处理所有文件。我习惯把所有步骤封装成一个函数function [feat, fs] extract_feature_from_file(filename) info audioinfo(filename); [y, fs] audioread(filename); if size(y, 2) 1 y mean(y, 2); end y y - mean(y); y y / max(abs(y)) * 0.9; % 统一重采样到16kHz if fs ~ 16000 y resample(y, 16000, fs); fs 16000; end % 提取特征 feat mfcc(y, fs); end然后对整个文件集统一调用fileList dir(audio/*.wav); features cell(length(fileList), 1); for i 1:length(fileList) features{i} extract_feature_from_file(fullfile(fileList(i).folder, fileList(i).name)); end这种统一流水线思路的好处非常多你只需要在函数里修改一处比如把带通滤波器频率从[300, 3400]改成[80, 8000]所有文件都会以相同逻辑重新处理结果可对比、可复现。反之如果每个文件单独手工处理今天心情好多滤一下波明天心情差不滤波那出来的特征集根本无法用于训练一个稳定的模型。实际项目中一个让很多人崩溃的问题是不同文件的采样率不同。有的文件是16kHz有的是44.1kHz如果混在一起直接提取特征同一个语音内容在不同文件里会有不同的频率分辨率表现模型很容易学到“采样率差异的特征”而不是“语音内容的特征”。所以统一重采样到固定采样率通常是16kHz是我在流水线里强制加的一步。这个细节不算复杂但直接决定了后续模型的稳定性和泛化能力。6. 最后的实操心得与一些小建议回头看整个流程从audioread读入wav文件到波形可视化、频谱分析、语谱图再到预处理、特征提取其实构建了一条完整的语音信号处理最小工作流。很多人一开始觉得语音信号处理门槛高要懂傅里叶变换、要懂滤波器设计、要懂随机过程但实际动手做起来核心链路并不长关键是每一步都要清楚自己在做什么、为什么这么做。我自己在实际操作中养成的一个习惯是每读入一个wav文件先不急着跑任何分析而是先花几十秒看一下完整波形和语谱图。这一步不需要任何理论功底只是用眼睛观察数据。你听一下音频再看一下波形把“听到的”和“看到的”对应起来慢慢地就会对语音信号的各种形态形成直觉。这个直觉在后续调试算法时意义非凡——信号有没有异常、噪声是哪种类型、参数调得合不合理很多时候不用等算法跑完看一眼中间的可视化结果就能判断个大概。最后再分享一个小技巧如果你用的不是最新版Matlab其实也可以处理wav只是部分新函数比如mfcc、bandpass不可用需要用fft、filter自己手动实现。遇到这种情况不用慌底层原理是一样的手动实现反而能帮你理解得更透彻。等把原理摸透了再升级到新版你会发现那些封装好的函数用起来更顺手。这个内容后续还能继续扩展加上实时录音数据采集、加入各种噪声类型做鲁棒性实验、或者接上机器学习模型做语音分类、说话人识别。但无论扩展到哪里起点永远是那个简单得不能再简单的动作——用audioread把wav文件读进Matlab然后开始认真地看看你手里的数据。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询