簡體   English   中英

使用MATLAB繪制諧波產物譜

[英]Plotting Harmonic Product Spectrum using MATLAB

當出現大量諧波時,我使用了諧波乘積譜來找到基本音符。 這是我實現的代碼;

[song,FS] = wavread('C major.wav');
%sound(song,FS);

P = 20000;
N=length(song);                     % length of song
t=0:1/FS:(N-1)/FS;                  % define time period

song = sum(song,2);
song=abs(song);

%----------------------Finding the envelope of the signal-----------------%
% Gaussian Filter
w = linspace( -1, 1, P);                      % create a vector of P values between -1 and 1 inclusive
sigma = 0.335;                                % standard deviation used in Gaussian formula
myFilter = -w .* exp( -(w.^2)/(2*sigma.^2));  % compute first derivative, but leave constants out
myFilter = myFilter / sum( abs( myFilter ) ); % normalize

% fft convolution
myFilter = myFilter(:);                         % create a column vector
song(length(song)+length(myFilter)-1) = 0;      %zero pad song
myFilter(length(song)) = 0;                     %zero pad myFilter

edges =ifft(fft(song).*fft(myFilter));
tedges=edges(P:N+P-1);                      % shift by P/2 so peaks line up w/ edges
tedges=tedges/max(abs(tedges));                 % normalize

%---------------------------Onset Detection-------------------------------%
% Finding peaks
maxtab = [];
mintab = [];
x = (1:length(tedges));
min1 = Inf;
max1 = -Inf;
min_pos = NaN; 
max_pos = NaN;

lookformax = 1;
for i=1:length(tedges)

    peak = tedges(i:i);
  if peak > max1, 
      max1 = peak;
      max_pos = x(i); 
  end
  if peak < min1, 
      min1 = peak;
      min_pos = x(i); 
  end

  if lookformax
    if peak < max1-0.07
      maxtab = [maxtab ; max_pos max1];
      min1 = peak; 
      min_pos = x(i);
      lookformax = 0;
    end  
  else
    if peak > min1+0.08
      mintab = [mintab ; min_pos min1];
      max1 = peak; 
      max_pos = x(i);
      lookformax = 1;
    end
  end
end


max_col = maxtab(:,1);
peaks_det = max_col/FS;
No_of_peaks = length(peaks_det);

[song,FS] = wavread('C major.wav');
song = sum(song,2);

%---------------------------Performing STFT--------------------------------%
h = 1;
%for i = 2:No_of_peaks

    song_seg = song(max_col(7-1):max_col(7)-1);
    L = length(song_seg); 
    NFFT = 2^nextpow2(L); % Next power of 2 from length of y
    seg_fft = fft(song_seg,NFFT);%/L;

    f = FS/2*linspace(0,1,NFFT/2+1);
    seg_fft_2 = 2*abs(seg_fft(1:NFFT/2+1));
    L5 = length(song_seg);

    figure(6)
    plot(f,seg_fft_2)
    %plot(1:L/2,seg_fft(1:L/2))
    title('Frequency spectrum of signal (seg_fft)')
    xlabel('Frequency (Hz)')
    xlim([0 2500])
    ylabel('|Y(f)|')
    ylim([0 500])

%----------------Performing Harmonic Product Spectrum---------------------%

   % In harmonic prodcut spectrum, you downsample the fft data several times and multiply all those with the original fft data to get the maximum peak. 
    %HPS
    seg_fft = seg_fft(1 : size(seg_fft,1)/2 ); 
    seg_fft = abs(seg_fft);
    a = length(seg_fft);


    seg_fft2 = ones(size(seg_fft));
    seg_fft3 = ones(size(seg_fft));
    seg_fft4 = ones(size(seg_fft));
    seg_fft5 = ones(size(seg_fft));



    for i = 1:((length(seg_fft)-1)/2)
        seg_fft2(i,1) = seg_fft(2*i,1);%(seg_fft(2*i,1) + seg_fft((2*i)+1,1))/2;
    end

     %b= size(seg_fft2)

    L1 = length(seg_fft2); 
    NFFT1 = 2^nextpow2(L1); % Next power of 2 from length of y


    f1 = FS/2*linspace(0,1,NFFT1/2+1);
    seg_fft12 = 2*abs(seg_fft2(1:NFFT1/2+1));

    figure(7);
    plot(f1,seg_fft12)
    title('Frequency spectrum of signal (seg_fft2)')
    xlabel('Frequency (Hz)')
    xlim([0 2500])
    ylabel('|Y(f)|')
    ylim([0 500])

這是圖6的圖 在此處輸入圖片說明

因此,在實際情況下,一旦執行HPS(按2倍下采樣),則440.1的峰值應向下移動至220,而881的峰值應向下移動至大約440。但是當我繪制該圖時,並不是我所能得到的。 Insetad這是我得到的圖, 在此處輸入圖片說明

為什么我沒有得到正確的圖形???? 我似乎不明白我在這里做錯了什么...可以有人請讓我知道..謝謝.....

下采樣的問題是在進行下采樣之前(而不是之后)將矢量修整2倍。 你做

seg_fft = seg_fft(1 : size(seg_fft,1)/2 ); 

% [... other stuff ...]
for i = 1:((length(seg_fft)-1)/2)
    seg_fft2(i,1) = seg_fft(2*i,1);%(seg_fft(2*i,1) + seg_fft((2*i)+1,1))/2;
end

相反,您需要先降低采樣率,然后修剪:

for i = 1:((length(seg_fft)-1)/2)
    seg_fft2(i,1) = seg_fft(2*i,1);%(seg_fft(2*i,1) + seg_fft((2*i)+1,1))/2;
end

seg_fft = seg_fft(1 : size(seg_fft,1)/2 ); 

您詢問UPDATE ,為什么它不保留峰。 簡短的答案是您可能不會“看”峰。 如果你想通過下采樣過程中保持(最近)峰n ,你可以做到以下幾點:

n = 3; % degree of decimation or downsampling we want to do
N = size(seg_fft, 1); % number of samples in original FFT
Nn = n * floor(N/n);  % number of samples that can be divided by n

fftBlock = reshape(seg_fft(1:Nn, 1), n, N);
fftResampled = max(fftBlock);

這是如何運作的? 讓我們使用10 x 1點的簡單示例:

seg_fft = [0 1 10 5 4 3 6 12 4 3];

我們想要“每三”。 天真的算法會給

fftResampled = [2 3 7];

但是我們本來希望“峰值” [10 3 12] -不幸的是它們不在正確的位置。

在重塑數組(並丟失最后一個元素;如果它可能是一個有趣的值,我們可以追加並填充零)之后,我們得到:

fftBlock = [0  5  6;
            1  4  3;
           10  3  4];

(請記住,Matlab矩陣是行優先的)

現在取max (除非另有說明,否則函數將沿第一維運行),您將得到

fftResampled = [10 5 6];

即總是高峰。 盡管這保留了峰,但確實意味着您的“山谷”已被填滿。

最重要的是:在縮減采樣的過程中,沒有辦法不破壞“某些”信息-畢竟,您會將一半的采樣扔掉了。 保留的內容以及如何處理丟棄的數據中的信息內容是您只能決定的事情,因為這取決於您的應用程序以及對您而言重要的內容。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM