在语音与音乐处理过程中,常用到短时傅里叶变换(Short Time Fourier Transformation, STFT)。在一些学习路径中,STFT也是学习小波之前的预备知识。本文简单实现了 Matlab 中 Spectrogram 函数,在没有小波知识支撑下,讨论了参数的选择,以及分辨率相关的问题。

参考博客:

  来源:CSDN    作者:风翼冰舟
  来源:知乎       作者:咚懂咚懂咚
  来源:CSDN    作者:水可马二    来源:CSDN    作者:沈子恒    来源:CSDN    作者:gent__chen

短时傅里叶变换简介


  自己写一下对STFT的认识,不一定对。

  短时傅里叶变换,其实还是傅里叶变换,只不过把一段长信号按信号长度(nsc)、重叠点数(nov)重新采样。原始信号的每一个数据点,都有一个电压值对应,即只有一个直流自由度。重新采样之后,数据点个数变少,每一个数据点由(nsc)个点组成,即得到了其他频率的自由度。但是这个点的频谱是有上下限的——上限受采样频率界定,下限受数据时长界定,和 FFT 一样。

  这么处理数据是有缘由的。语音与音乐信号中,信号频率常常变化,而且频率成分丰富,导致简单的傅里叶变换不能很好的描述信号。然而我们人耳处理声音的能力很强,可以对很短的一段声音进行精确的频率分析。所以在语音识别以及语音信号预处理过程中,STFT 是 FFT 的仿生改良版(个人理解)。当然,在其他声学相关方向中,STFT也是蛮有用的。没查过相关文献不敢乱说,模态分析、瞬态特性等应该能用上(个人猜测)。

上图是我用本文的例程实现的语音“XX大学”的 STFT 结果。语音是自己拿录音笔录的,时间域上没有做截断,但由于自己声线太低,频率域截到 2000 Hz。可以看到,我在开始之后等了一段时间才开始说话,后面有几段“嘎嘣脆”的噪声。还可以看到,我的元音发音还是很清晰的,但声线真心低。理论上能从这张图还原出原始信号,不在话下。

Spectrogram 函数函数实现


  简单编了一个 STFT 函数如下:

function [ S , W , T ] = mf_spectrogram...
( signal , nsc , nov , fs )
%MF_SPECTROGRAM Short-time Fourier transform realization
% Input
% signal - Signal vector
% nsc - Abb. of Number SeCtion
% nov - Abb. of Number OverLap
% fs - Abb. of Frequency of Sample
% Output
% S - A matrix that each colum is a FFT for time of nsc
% W - A vector labeling frequency
% T - A vector labeling time % Signal Preprocessing
h = hamming(nsc, 'periodic'); % Hamming weight function
L = length(signal); % Length of Signal
nst = nsc-nov; % Number of STep per colum
ncl = fix( (L-nsc)/nst ) + 1; % Number of CoLum
nff = 2^nextpow2(nsc); % Number of Fast Fourier Transformation
Ps = zeros(nsc,ncl);
for n = 1:ncl % Ps means Processed Signal
Ps(:,n) = signal( (n-1)*nst+1 : (n-1)*nst+nsc ).*h';
end % Ps is a matrix % Short-time Fourier transform
STFT0 = fft(Ps,nff); % Turn into DFT in dB
STFT1 = abs(STFT0/nff);
STFT2 = STFT1(1:nff/2+1,:); % Symmetric results of FFT
STFT2(2:end-1,:) = 2*STFT2(2:end-1,:); % Add the value of the other half
STFT3 = 20*log10(STFT2); % Turn sound pressure into dB level % Axis Generating
fax = fs*(0:(nff/2))/nff; % Frequency axis setting
tax = ( .5*nsc : nst : nst*(ncl-1)+.5*nsc ) / fs; % Time axis generating
[ffax,ttax] = meshgrid(tax,fax); % Generating grid of figure % Output
W = ffax;
T = ttax;
S = STFT3;
end

  为了节省代码量,我搞了一个绘图函数:

function [  ] = my_pcolor( f , t , s )
%MY_PCOLOR 绘图函数
% Input
% f - 频率轴矩阵
% t - 时间轴矩阵
% s - 幅度轴矩阵
gca = pcolor(f,t,s); % 绘制伪彩色图
set(gca, 'LineStyle','none'); % 取消网格,避免一片黑
handl = colorbar; % 彩图坐标尺
set(handl, 'FontName', 'Times New Roman', 'FontSize', 14)
ylabel(handl, 'Magnitude, dB') % 坐标尺名称
end

  下面是实现的例程:

clc
clear
close all % 基本参数
fa = [ 50 800 ]; % 扫描频率上下限
fs = 6400; % 采样频率 % 分辨率相关设定参数
te = 1; % [s] 扫频时间长度
fre = 8; % [s] 频率分辨率
tre = 0.002; % [Hz] 时间分辨率 % Chirp 信号生成
t = 0:1/fs:te; % [s] 时间序列
sc = chirp(t,fa(1),te,fa(2)); % 信号生成 % 分辨率相关输入参数
nsc = floor(fs/fre);
% nov = floor(nsc-(fs*tre));
nov = floor(nsc*0.9);
nff = max(256,2^nextpow2(nsc)); % 计算与绘制结果
subplot(1,3,1) % 绘制自编函数结果
[S,W,T] = mf_spectrogram(sc,nsc,nov,fs);
my_pcolor(W,T,S)
caxis([-130.86,-13.667]);
title('自编函数');
xlabel('时间 second');
ylabel('频率 Hz');
subplot(1,3,2) % 绘制 Matlab 函数结果
s = spectrogram(sc,hamming(nsc),nov,nff,fs,'yaxis');
% Turn into DFT in dB
s1 = abs(s/nff);
s2 = s1(1:nff/2+1,:); % Symmetric results of FFT
s2(2:end-1,:) = 2*s2(2:end-1,:); % Add the value of the other half
s3 = 20*log10(s2); % Turn sound pressure into dB level
my_pcolor(W,T,s3 )
caxis([-130.86,-13.667]);
title('Matlab 自带函数');
xlabel('时间 second');
ylabel('频率 Hz');
subplot(1,3,3) % 两者误差
my_pcolor(W,T,20*log10(abs(10.^(s3/20)-10.^(S/20))))
caxis([-180,-13.667]);
title('error');
ylabel('频率 Hz');
xlabel('时间 second');
suptitle('Spectrogram 实现与比较');

  跑的结果我就不贴了,Demo确定是没问题的。网上也有很多相关话题的,例程都比较简单,但我非常善于把问题复杂化:te(扫描长度)、fre(频率下限)、tre(时域分辨率)、nsc(单段数据长度)、nov(重叠数据点数)、nff(FFT点数) 等参数都是为了看设定什么样的参数能够使得 STFT 结果最优。我只是科普性地了解过小波,公式推导还没展开,所以在此只能“实验性”地讨论“尺度”相关的话题。

  观察频率上限受到采样频率限制,频率下限受到nsc限制。nsc越大,单段数据时间跨度越长,在该段时间内频率如果变化快,会导致频域分辨率降低(te = 1 ; fre = 2 ; nov = 99%)。另一方面,为了保持 FFT 频率分辨率,设置 nff 不低于256,这使得当nsc较小时,单段信号中,低频成分可能也就几个周期就结束了,等同于时域加了一个矩形窗,最终造成了频域产生旁瓣。一句话,单段信号过短,低频效果不好;单段信号过长,捕捉不到频率快速变化的信号。

数据长度、FFT 点数对结果的影响


  实验性地比较了一下不同数据长度、FFT点数对结果的影响:

%  This script is demonstrating zero effects
% Basic parameter
fs = 100; % 采样频率
Ndata = [ 30 60 500 ]; % 数据长度
Nff = [ 32 64 512 ]; % FFT的数据长度 % Signal Generating
t1 = ( 0:Ndata(1)-1 )/fs;
t2 = ( 0:Ndata(2)-1 )/fs;
t3 = ( 0:Ndata(3)-1 )/fs;
x1 = 0.5*sin(2*pi*15*t1)+2*sin(2*pi*40*t1); % 时间域信号
x2 = 0.5*sin(2*pi*15*t2)+2*sin(2*pi*40*t2); % 时间域信号
x3 = 0.5*sin(2*pi*15*t3)+2*sin(2*pi*40*t3); % 时间域信号 % FFT and Plot
for n = 1:3 % Ndata sweep
xname = ['x',num2str(n)];
x = eval(xname);
for m = 1:3 % Nff sweep
name = ['y',num2str(n),num2str(m)];
eval([name '=fft(x,Nff(m));'])
y = eval(name);
Y = abs(y);
f = (0:Nff(m)-1)*fs/Nff(m); % 真实频率 subplot(3,3,(n-1)*3+m)
plot(f(1:Nff(m)/2),[Y(1),Y(2:Nff(m)/2)*2]/min(Ndata(n),Nff(m)));
xlabel('频率/Hz');
ylabel('振幅');
ylim([0,2]);
title(['Ndata=',num2str(Ndata(n)),' Nfft=',num2str(Nff(m))]);
grid on;
end
end

为了节省代码,同时运行结束后保留计算数据,使用了 eval 函数对变量和字符串进行相互转化。绘制了9个图如下:

横向是提高 FFT 点数的比较,纵向是提高数据长度的比较。可以看到,提高 FFT 点数会使得频域分辨率提高;增长数据长度,能够减少旁瓣的生成。


END

Matlab_spectrogram_短时傅里叶分析_实现与讨论的更多相关文章

  1. spectrogram函数做短时傅里叶分析

    整理自:http://blog.sina.com.cn/s/blog_6163bdeb0102dwfw.html 今天偶人发现原来matlab自带了短时傅里叶变换的分析函数,老版本的matlab是sp ...

  2. (4)_结果与讨论Result and Discussion【论文写作】

  3. Atitit 语音识别的技术原理

    Atitit 语音识别的技术原理 1.1. 语音识别技术,也被称为自动语音识别Automatic Speech Recognition,(ASR),2 1.2. 模型目前,主流的大词汇量语音识别系统多 ...

  4. 数据结构--树(遍历,红黑,B树)

    平时接触树还比较少,写一篇博文来积累一下树的相关知识. 很早之前在数据结构里面学的树的遍历. 前序遍历:根节点->左子树->右子树 中序遍历:左子树->根节点->右子树 后序遍 ...

  5. 手写一个更好用的performSelector/msgSend(详细修改版)

    这其实是一个NSInvocation练习作业 GitHub源码 vk_msgSend 引子 工作中难免会遇到一些场景,开发的时候不想引入整个头文件,但是又想调用一些方法 动态创建,动态调用看起来比较酷 ...

  6. Atitit.木马 病毒 免杀 技术 360免杀 杀毒软件免杀 原理与原则 attilax 总结

    Atitit.木马 病毒 免杀 技术 360免杀 杀毒软件免杀 原理与原则 attilax 总结 1. ,免杀技术的用途2 1.1. 病毒木马的编写2 1.2. 软件保护所用的加密产品(比如壳)中,有 ...

  7. C语言ASM汇编内嵌语法【转】

    转自:http://www.cnblogs.com/latifrons/archive/2009/09/17/1568198.html GCC 支持在C/C++代码中嵌入汇编代码,这些汇编代码被称作G ...

  8. 【DWT笔记】傅里叶变换与小波变换

    [DWT笔记]傅里叶变换与小波变换 一.前言 我们经常接触到的信号,正弦信号,余弦信号,甚至是复杂的心电图.脑电图.地震波信号都是时域上的信号,我们也成为原始信号,但是通常情况下,我们在原始信号中得到 ...

  9. 【codeforces 553E】 Kyoya and Train

    http://codeforces.com/problemset/problem/553/E (题目链接) 艹尼玛,CF还卡劳资常数w(゚Д゚)w!!系统complex被卡TLE了T_T,劳资写了一天 ...

随机推荐

  1. 阿里云yum配置

    CentOS 安装源列表见 CentOS Mirror List.本文使用阿里云安装源安装官方源和扩展源.其他安装源也可以参考. 依次执行命令. #使用 yum-config-manager 软件包命 ...

  2. 嵌套if-esle语句

    C语言自学之嵌套if-esle语句 Dome : 获奖条件为年销售业绩100万以上,并且入职满两年的员工.小明进入公司1年,销售业绩为120万. 在代码编辑器中使用嵌套if-else语句判断小明是否有 ...

  3. h5软键盘弹起 底部按钮被顶起问题解决

    解决思路: 当键盘弹起时隐藏掉按钮,键盘隐藏时按钮显示 监测键盘是否弹起(浏览器页面是否发生变化) 代码: 1.定义一个底部按钮 <div class="returnbtn" ...

  4. jq里验证插件的自定义方法Jquery.validator.addMethod()示例

    最近写验证的时候感觉原生的验证谢了一遍又一遍,就想到了“不要重复造轮子,学会管理自己的工具库”这句名言,于是尝试用jq的validator. 用过又发现需要自定义方法去验证,于是去查官网,写了Jque ...

  5. mybatis注解调用存储过程

    mapper @SelectProvider(type = RoleMenuSqlProvider.class,method = "updateRoleMenuRelation") ...

  6. vmware三种网络模式的工作原理及配置详解

    vmware为我们提供了三种网络工作模式,它们分别是:Bridged(桥接模式).NAT(网络地址转换模式).Host-Only(仅主机模式). 打开vmware虚拟机,我们可以在选项栏的“编辑”下的 ...

  7. [转]imageMagick 在nodejs中报错Error: spawn identify ENOENT的解决方案

    同时还有 Error: Could not execute GraphicsMagick/ImageMagick 这个问题, 也参考了 https://blog.csdn.net/chenxinpen ...

  8. 多线程之interrupt

    1.interrupt()作为中断程序,并不会直接终止运行,而是设置中断状态,由线程自己处理中断.可以选择终止线程.等待新任务或继续执行. 2.interrupt()经常用于中断处于堵塞状态的的线程, ...

  9. Kafka分布式消息队列

    基本架构 Kafka分布式消息队列的作用: 解耦:将消息生产阶段和处理阶段拆分开,两个阶段互相独立各自实现自己的处理逻辑,通过Kafka提供的消息写入和消费接口实现对消息的连接处理.降低开发复杂度,提 ...

  10. LoadRunner基本简介

    # LoadRunner  # ## 安装要求 ##     做性能测试的时候,电脑要是一个干净的系统.     尽量是裸装电脑纯净版,不能安装太多的浏览器,支持的有IE.Firefox.chrome ...