Matlab实现倒谱法 求 基音频率和共振峰


前言

有关同态、倒谱、基音周期等概念,可参考一篇本科毕业论文,链接:link

一、倒谱

x ^ ( n ) \hat{x}(n) x^(n):复倒谱
c ( n ) c(n) c(n):倒谱
cepstrum

二、基音周期

1.流程图

pitch
在这里插入图片描述

  • 4.7中图为截取的一帧时域波形,可见清音无明显周期性,因此估计清音的pitch=0

2. 实现代码(Matlab)

function [pitchf] = getPitch(audio,Fs,time,overlap)
% 获取一段音频的 基音频率 fp(Hz) = fs/Np 
% 步骤:分帧、hamming窗、倒谱c(n)、求Np
% Np是倒谱上最大峰值和次峰值之间的采样点数
len = length(audio); 
N = time*Fs;                   %每帧N个采样点
mixN = floor(overlap*N);       %帧叠的点数
frames = floor(len/(N-mixN));  %总帧数
LNp = floor(Fs/1000);          %基音周期的范围取50~1000Hz(1ms-20ms)
HNp = floor(Fs/50);            %基音周期最多可能有多少个采样点数
pitchf = zeros([1 (frames-1)]);
start = 1;
for m=1:(frames-1)             %对每一帧求基音周期
    tail = start+N-1;          %每帧帧尾
    if tail>len                %数组越界
        tail = len;
    end
    tmp = audio(start:tail,1); %取出一帧
    
    x = tmp.*hamming(length(tmp));
    lgS = log(abs(fft(x)));    %傅里叶变换后取模,再取对数
    cn = ifft(lgS);            %得到x(n)的倒谱c(n) 
    lenc = ceil(length(cn)/2); %圆周共轭,为减少运算取一半
    if HNp > lenc              %考虑到数组越界
        HNp = lenc;
    end
    c = cn(LNp:HNp);           %在合适范围内搜索Np
    [maxcn,idx] = max(c);      %搜索出max
    if maxcn>0.08              %门限设置为0.08
        pitchN = LNp+idx;
        pitchf(m) = Fs/pitchN;
        formantcn = cn(1:pitchN); 
    end
    %%求第50帧的浊音共振峰
    if m==50
        formant = Formant(formantcn,pitchN);
        xi = (1:floor(pitchN/2))*Fs/pitchN; %实偶对实偶,取一半即可
        plot(xi,formant(1:floor(pitchN/2)));
        xlabel('频率(Hz)');ylabel('平滑对数幅度');
        t = title(["第50帧 频率-共振峰"]);
        t.FontSize = 16;
    end
    start = start + (N-mixN)-1;%下一帧帧首
end                           
end

三、共振峰

在这里插入图片描述

function [formant] = Formant(formantcn,pitchN)
%求某一帧倒谱的共振峰
%formantcn是某一帧音频的倒谱
%pitchN 是这一帧的基音周期(单位:采样点数)
%求共振峰流程:formantcn-加窗-FT-取实部-取对数-中值滤波
%完成上述过程后,峰值对应的频率就是共振峰频率
%做法参考:https://www.docin.com/p-715554902.html
xn = formantcn.*hamming(pitchN);    %加窗
tmp = 20*log(abs(fft(xn)));         %FT-取实部-取对数
formant = zeros([1 pitchN]);
formant(1:2) = tmp(1:2);   
for i = 3:(pitchN-2)                    %以下为中值滤波
    md1 = median(tmp(i-2:i));
    md2 = median(tmp(i-1:i+1));
    md3 = median(tmp(i:i+2));
    formant(i) = md1*0.25 + md2*0.5 + md3*0.25;
end
end

四、实验

  • 读取原音频
[audio,Fs] = audioread("summer.wav");
plot(audio);
xlabel('采样点数(n)');ylabel('音频采样值');
titlename = "夏天,你好";
t = title([titlename]);
t.FontSize = 16;
  • 获得基音周期和共振峰
pitchf = getPitch(audio,Fs,0.03,0.6);
stem(pitchf,'.');
xlabel('帧数(n)');ylabel('基音频率(Hz)');
t = title(["基音周期"]);
t.FontSize = 16;

在这里插入图片描述


总结

共振峰效果并不是很好,也可能和音频本身相关,不过还是只能说是将个就写了点实现

  • 7
    点赞
  • 55
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
倒谱法是一种常用的基音周期检测方法,可以借助MATLAB实现。下面提供一个简单的基音周期检测和共振检测的MATLAB代码示例,供参考。 ```matlab % 基音周期检测和共振检测 % 首先读取音频文件,使用matlab自带的audioread函数 [x, fs] = audioread('voice.wav'); % 设置分析参数 winlen = 512; % 窗口长度 overlap = 256; % 帧重叠长度 nfft = 1024; % FFT点数 preemph = 0.97; % 预加重系数 minf0 = 80; % 最小基频 maxf0 = 300; % 最大基频 voicedthresh = 0.4; % 有声门限 unvoicedthresh = 0.1; % 无声门限 % 对每一帧进行处理 frames = enframe(x, winlen, overlap); % 分帧 nframes = size(frames, 1); % 帧数 f0 = zeros(nframes, 1); % 存储基频 formants = zeros(nframes, 4); % 存储共振 for i = 1:nframes frame = frames(i, :); % 取出一帧 frame = filter([1 -preemph], 1, frame); % 预加重 spec = abs(fft(frame, nfft)); % 傅里叶变换 spec = spec(1:nfft/2); % 取一半 logspec = log(spec); % 取对数 cepstrum = ifft(logspec); % 倒谱 cepstrum = cepstrum(1:nfft/2); % 取一半 cepstrum(1:minf0/fs*nfft) = 0; % 去掉基频以下的分量 cepstrum(maxf0/fs*nfft:end) = 0; % 去掉基频以上的分量 [~, locs] = findpeaks(cepstrum); % 找值 if ~isempty(locs) f0(i) = fs/locs(1); % 基频为第一个频率倒数 formants(i, :) = locs(2:5)*fs/nfft; % 共振为2~5个频率 end end % 判断有声无声 voiced = f0 > voicedthresh*fs/winlen & f0 < maxf0; unvoiced = f0 < unvoicedthresh*fs/winlen; f0(unvoiced) = 0; % 无声部分基频设为0 % 绘制频谱、倒谱基频共振等 t = (0:nframes-1)*overlap/fs; f = (0:nfft/2-1)/nfft*fs; figure; subplot(2, 2, 1); imagesc(t, f, 20*log10(abs(spec))); axis xy; xlabel('Time (s)'); ylabel('Frequency (Hz)'); title('Spectrogram'); subplot(2, 2, 2); imagesc(t, f, 20*log10(abs(cepstrum))); axis xy; xlabel('Time (s)'); ylabel('Quefrency'); title('Cepstrum'); subplot(2, 2, 3); plot(t, f0, 'r'); axis([0 max(t) 0 maxf0]); xlabel('Time (s)'); ylabel('Frequency (Hz)'); title('Pitch'); subplot(2, 2, 4); plot(t, formants); axis([0 max(t) 0 maxf0]); xlabel('Time (s)'); ylabel('Frequency (Hz)'); title('Formants'); legend('F1', 'F2', 'F3', 'F4'); ``` 该代码读取声音文件`voice.wav`,对每一帧进行基音周期检测和共振检测,并将结果绘制成图形。其中,`enframe`是分帧函数,`findpeaks`是寻找值函数。可以根据需要自行调整参数,例如窗口长度、帧重叠长度、FFT点数等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值