How to store values in an array in MATLAB - matlab

Say I have performed FFT on a set of data and i have the frequency and amplitude values.
I want to find the highest amplitude in each FFT block. The I need to find the frequency and amplitude of the points that are atleast greater than 0.4 times the max amplitude and save them in an array.. How can i do this??
I tried the following method by I keep getting an empty matrix....
% the code after FFT
peak_points = [];
fmin = 60;
fmax = 1000;
region_of_interest = fmax>f & f>fmin;
froi = f(region_of_interest);
[p_max,loc] = max(seg_fft2(region_of_interest));
p_max;
fpeaks = froi(loc);
[points, locatn] = findpeaks(seg_fft2(region_of_interest));
if points > 0.4*p_max
peak_points = [peak_points ; points locatn]
end
Im bad with arrays.. So I cant seem to figure this out. Would appreciate someone's help in this... Thanks in advance...

Are you intending to do the seg_ffr2 on the region of interest logical array or on froi?
or maybe points is vector and you should have:
aboveMax = points > 0.4*p_max;
if any(aboveMax)
peak_points = [peak_points ; points(aboveMax) locatn(aboveMax)]
end

Related

Finding fundamental frequency .wav file Matlab

I have to extract the fundamental frequency from a .wav file of 1 second long.
I have looked over several options like finding the peaks or doing a complex cepstrum but no luck so far. I have read over the last couple of weeks a lot about it but still i cannot find a viable and optimal way of doing it. I am new to Matlab and Signal Processing.
filedir = dir('*.wav');
[y, Fs] = audioread(filedir.name);
x = y(y ~= 0) % removing the zeroes from the array
psdest = psd(spectrum.periodogram,x,'Fs',Fs,'NFFT',length(x));
[~,I] = max(psdest.Data);
fprintf('Maximum occurs at %d Hz.\n',psdest.Frequencies(I));
The output is for example: Maximum occurs at 5.758645e+02 Hz
I will probably need to actually transform that number into Hz.
For the complex cepstrum:
load mtlb
dt = 1/Fs;
I0 = round(0.1/dt);
Iend = round(1/dt);
x = mtlb(I0:Iend);
c = cceps(x);
t = 0:dt:length(x)*dt-dt;
trng = t(t>=2e-3 & t<=10e-3);
crng = c(t>=2e-3 & t<=10e-3);
[~,I] = max(crng);
fprintf('Complex cepstrum F0 estimate is %3.2f Hz.\n',1/trng(I))
clearvars
The output values is the same for all the files I load so clearly something is wrong.
I have been struggling for a while and I really need to figure this out. To extract that fundamental frequency. I use Matlab 2016b.
Thanks a lot!

How do I create a dynamic average? (Don't know how else to call it)

I have a 10000x1 matrix. I need to find the percentage of information contained in each case. The method of doing so is to make another matrix that contains the sum of remaining cells.
Example:
% info is a 10000x1
% info_n contains percentages of information.
info_n = zeros([10000 1]);
info_n(1)= info(1) /sum(info);
info_n(2)=(info(1)+info(2)) /sum(info);
info_n(3)=(info(1)+info(2)+info(3))/sum(info);
I need to do this but all the way to info_n(10000).
So far, I tried this:
for i = 1:10000
info_n(i)=(info(i))/sum(info);
end
but I can't think of a way to add previous information. Thank you for the help!
You can use cumsum ("cumulative sum") for this task:
info_n = cumsum(info)/sum(info);
EDIT: #LuisMendo's comment sparked my curiosity. It seems that you actually gain something by using his method if the length N of the vector is below about 2^15 but the advantage after that drops. This is because cumsum probably needs O(N^2) but sum only needs O(N) time.
On the left you see the times as well as the ratio of the times of the two methods plotted against the logarithm of the size. On the right you see the actual absolute time differences, which varies a lot depending on what else is currently running:
Testing script:
N = 2.^(1:28);
y1 = zeros(size(N));
y2 = zeros(size(N));
vec = 1:numel(N);
for k=vec;
disp(k)
info = zeros(N(k),1);
% flawr's suggestion
tic
info_n = cumsum(info);
info_n = info_n / info_n(end);
y1(k) = toc;
% LuisMendo's suggestion
tic
info_m = cumsum(info)/sum(info);
y2(k) = toc;
end
subplot(1,2,1)
semilogy(vec,y1,vec,y2,vec,y1./y2,vec,vec.^0);
legend('LuisMendo','flawr','LM/f','Location','SouthEast');
subplot(1,2,2)
plot(vec,y1-y2)

Reverse-calculating original data from a known moving average

I'm trying to estimate the (unknown) original datapoints that went into calculating a (known) moving average. However, I do know some of the original datapoints, and I'm not sure how to use that information.
I am using the method given in the answers here: https://stats.stackexchange.com/questions/67907/extract-data-points-from-moving-average, but in MATLAB (my code below). This method works quite well for large numbers of data points (>1000), but less well with fewer data points, as you'd expect.
window = 3;
datapoints = 150;
data = 3*rand(1,datapoints)+50;
moving_averages = [];
for i = window:size(data,2)
moving_averages(i) = mean(data(i+1-window:i));
end
length = size(moving_averages,2)+(window-1);
a = (tril(ones(length,length),window-1) - tril(ones(length,length),-1))/window;
a = a(1:length-(window-1),:);
ai = pinv(a);
daily = mtimes(ai,moving_averages');
x = 1:size(data,2);
figure(1)
hold on
plot(x,data,'Color','b');
plot(x(window:end),moving_averages(window:end),'Linewidth',2,'Color','r');
plot(x,daily(window:end),'Color','g');
hold off
axis([0 size(x,2) min(daily(window:end))-1 max(daily(window:end))+1])
legend('original data','moving average','back-calculated')
Now, say I know a smattering of the original data points. I'm having trouble figuring how might I use that information to more accurately calculate the rest. Thank you for any assistance.
You should be able to calculate the original data exactly if you at any time can exactly determine one window's worth of data, i.e. in this case n-1 samples in a window of length n. (In your case) if you know A,B and (A+B+C)/3, you can solve now and know C. Now when you have (B+C+D)/3 (your moving average) you can exactly solve for D. Rinse and repeat. This logic works going backwards too.
Here is an example with the same idea:
% the actual vector of values
a = cumsum(rand(150,1) - 0.5);
% compute moving average
win = 3; % sliding window length
idx = hankel(1:win, win:numel(a));
m = mean(a(idx));
% coefficient matrix: m(i) = sum(a(i:i+win-1))/win
A = repmat([ones(1,win) zeros(1,numel(a)-win)], numel(a)-win+1, 1);
for i=2:size(A,1)
A(i,:) = circshift(A(i-1,:), [0 1]);
end
A = A / win;
% solve linear system
%x = A \ m(:);
x = pinv(A) * m(:);
% plot and compare
subplot(211), plot(1:numel(a),a, 1:numel(m),m)
legend({'original','moving average'})
title(sprintf('length = %d, window = %d',numel(a),win))
subplot(212), plot(1:numel(a),a, 1:numel(a),x)
legend({'original','reconstructed'})
title(sprintf('error = %f',norm(x(:)-a(:))))
You can see the reconstruction error is very small, even using the data sizes in your example (150 samples with a 3-samples moving average).

Calculate area under power spectrum for certain frequency range

Below I present my code how I calculate power spectrum for my signal:
Fs=100;
n = length(x) (in my example always n=160);
whann = window(#hann,n).';
x = whann.*x';
xdft = fft(x);
xdft = (2/length(x))*xdft(1:length(x)/2+1);
xdft = abs(xdft);
xdft=xdft.^2;
freq = 0:Fs/length(x):Fs/2;
Now, I would like to calculate area under power spectrum but only for frequency range 4-6 Hz. The 32 first elements of vector freq look like this:
freq = [0,00 0,28 0,56 0,83 1,11 1,39 1,67 1,94 2,22 2,50 2,78 3,06 3,33 3,61 3,89 4,17 4,44 4,72 5,00 5,28 5,56 5,83 6,11 6,39 6,67 6,94 7,22 7,50 7,78 8,06 8,33]
So, I can find only area between 4,17 Hz- 6,11 Hz.
Could you advice me, how to calculate area under spectrum for specific frequency range (as I mentioned above for example 4-6 Hz)?
Thanks in advance for any help
I would proceed as follows:
idx = find(freq>=4 & freq<=6);
trapz(freq(idx),spectrum(idx))
If I understood your question right, what stated above should lead you to the results you wanna estimate.
EDIT
Since you don't have spectrum values for freq=4Hz and freq=6Hz, I would suggest to interpolate values like this:
int_spec = exp(interp1(log(freq),log(spec),log(4:.1:6),'linear','extrap'))
and then call
trapz(4:.1:6,int_spec)

auto-correlation in matlab

i want to ask about how to do auto-correlation function to every frame in a sound wave by my hand not built in function i do a code and i don't know if it right or wrong so please help
also i want to get the pitch period
[sound, Fs]= wavread('aa.wav');
subplot(5,1,1);plot (sound);title('The Orignal Wave')
frameSize= 10/10^3;
overlapSize= 5/10^3;
frameSize_samples = round(Fs*frameSize);
overlapSize_samples= round(overlapSize* Fs);
shift = frameSize_samples-overlapSize_samples;
nframes=ceil((length(sound)-frameSize_samples)/shift);
frames = zeros(nframes, frameSize_samples);
for i=1:nframes
frames(i,:) = sound( ((i-1)*shift+ 1):((i-1)*shift+ frameSize_samples) )';
end
subplot(5,1,2);plot (frames(:));title(' Wave after framing')
w = rectwin(frameSize_samples)';
frames1 = zeros(nframes, frameSize_samples);
for i=1:nframes
frames1(i,:)=(frames(i,:).*w);
end
%/////////////////////////////////////////////////////////
% calc energy
numSamples = length(sound);
energy = zeros(1,nframes);
for frame = 1:nframes
energy(frame) = sum(frames1(frame).^2); %# Calculate frame energy
end
subplot(5,1,3);plot (energy(:));title(' Energy')
% calc autocorrelation
autocorrelation = zeros(1,nframes);
for i=1:nframes
for k = 0:frameSize_samples
autocorrelation(i,:)= sum(frames(i)*frames(i+k));
end
end
subplot(5,1,4);plot (autocorrelation(:));title(' autocorrelation')
Based on the equation here. The autocorrelation equation looks fine. But I am confused by frames. You define it as a 2-D matrix, but then index it linearly (frames(i)).
Also, the for loop for k=0:frameSize_samples is not doing anything; you are assigning the same variable autocorrelation(i, :) in every iteration, so it will be overwritten.