Determining Number of Peaks in MATLAB - matlab

I'm trying to find the number of peaks in Matlab. When I plot the given wav file according to my threshold values, I can easily see that there are two distinct peaks. But how can I write "There are two peaks" on the screen? Here's my first attempt:
hfile = 'two.wav';
[stereo1, Fs, nbits, readinfo] = wavread(hfile);
mono1 = mean(stereo1,2);
M = round(0.01*Fs);
N = 2^nextpow2(4*M);
w = gausswin(M);
[S,F,T,P] = spectrogram(mono1,w,120,N,Fs);
thresh_l=1000;
thresh_h=10000000;
% take the segment of P relating to your frequencies of interest
P2 = P(F>thresh_l&F<thresh_h,:);
%show the mean power in that band over time
m = mean(P2);
[pks,loc]=findpeaks(T,'npeaks',m);
message = sprintf('The number of peaks found = %d',length(pks));
msgbox(message);

how about looking for where the first derivative switches sign? say x is your time series
dx = diff(x);
% need to add 1 b/c of the offset generated by diff
peak_loc = find((dx(1:end - 1) > 0) & (dx(2:end) <= 0)) + 1;
peak_num = len(peak_loc);

Related

calculate histogram of 2 variables

I have a vector containining the speed of 200 walks:
a = 50;
b = 100;
speed = (b-a).*rand(200,1) + a;
and another vector contaning the number of steps for each walk:
a = 8;
b = 100;
steps = (b-a).*rand(200,1) + a;
I would like to create a histogram plot with on the x axis the speeds and on the y axes the sum of the steps of each speed.
What I do is the following but I guess there is a more elegant way to do this:
unique_speed = unique(speed);
y_unique_speed = zeros(size(unique_speed));
for i = 1 : numel(unique_speed)
speed_idx = unique_speed(i);
idx = speed==speed_idx ;
y_unique_speed (i) = sum(steps (idx));
end
First you need to discretize your speed variable. Unlike in the other answer, the following allows you to choose arbitrary step size, e.g. I've chosen 1.5. Note that the last bin edge should be strictly more than the maximum data point, hence I used max(speed)+binstep. You can then use histc to determine which bin each pairs falls into, and accumarray to determine total number of steps in each bin. Finally, use bar to plot.
binstep = 1.5;
binranges = (min(speed):binstep:max(speed)+binstep)';
[~, ind] = histc(speed, binranges);
bincounts = accumarray(ind, steps, size(binranges));
hFig = figure(); axh = axes('Parent', hFig); hold(axh, 'all'); grid(axh, 'on');
bar(axh, binranges, bincounts); axis(axh, 'tight');
First, bin the speed data to discrete values:
sspeed = ceil(speed);
and then accumulate the various step sizes to each bin:
numsteps = accumarray(sspeed, steps);
plot(numsteps)

How to isolate a frequency range in MATLAB using signals concepts?

I am trying to use concepts learned in signals analysis to isolate a specific frequency from a sound file. I have a short WAV file that consists of a person talking but also has other noises with unknown frequencies both above and below the desired signal. I have an upper and lower bound for the frequency range that should contain the part of the sound I want.
I think I should be able to do this without using the signals analysis toolbox or the butter filter.
so far I have this code which plots the power spectrum for the signal:
[y, Fs] = audioread('filename.wav','double');
t = 1:1:length(y);
y = transpose(y);
a = ifft(y);
a_k = abs([a((length(y)/2)+1:-1:2),a(1:1:(length(y)/2)+1)]);
bar((-length(y)/2)+1:1:(length(y)/2),a_k);
The power spectrum looks like this:
I think I should be able to use what I have to filter our anything above or below my known range, but I am not sure how to start doing that.
To apply ideal band pass filter you can use the code below. However, please note that the ideal filter may not produce the most optimal results if your signal is not periodic (see the wiki article here).
%% Initialisation
Fs = 44100;
t0 = 0;
t1 = 1;
t = t0 : 1/Fs : t1;
f1 = 10;
f2 = 40;
y = 10*cos(2*pi*f1*t) + 20*sin(2*pi*f2*t);
%% Cosine series
true_fft = fft(y);
nfft = length(y);
% The number of unique points
if mod(nfft, 2) == 0
num_pts = round(nfft/2) + 1;
else
num_pts = ceil(nfft/2);
end
% The vector that contains only unique points
fftT = true_fft(1 : num_pts);
% The vector that contains the unique frequencies
u_f = (0 : num_pts-1)*Fs/nfft;
%% Filtered signal
% Definition of the frequency band
f_low = 5;
f_high = 15;
[~, idx_low] = min(abs(u_f - f_low));
[~, idx_high] = min(abs(u_f - f_high));
filtFFTT = fftT;
filtFFTT([1: idx_low idx_high : end]) = 0;
if mod(nfft, 2) == 0
filtFFTT = [filtFFTT conj(filtFFTT((end - 1) : -1 : 2))];
else
filtFFTT = [filtFFTT conj(filtFFTT(end : -1 : 2))];
end
%% Data visualisation
figure;
plot(t, ifft(filtFFTT));

plotting volume-time graph of .wav file

I'm trying to get volume-time graph of .wav file. First, I recorded sound (patient exhalations) via android as .wav file, but when I read this .wav file in MATLAB it has negative values. What is the meaning of negative values? Second, MATLAB experts could you please check if the code below does the same as written in my comments? Also another question. Y = fft(WindowArray);
p = abs(Y).^2;
I took the power of values returned from fft...is that correct and what is the goal of this step??
[data, fs] = wavread('newF2');
% read exhalation audio wav file (1 channel, mono)
% frequency is 44100 HZ
% windows of 0.1 s and overlap of 0.05 seconds
WINDOW_SIZE = fs*0.1; %4410 = fs*0.1
array_size = length(data); % array size of data
numOfPeaks = (array_size/(WINDOW_SIZE/2)) - 1;
step = floor(WINDOW_SIZE/2); %step size used in loop
transformed = data;
start =1;
k = 1;
t = 1;
g = 1;
o = 1;
% performing fft on each window and finding the peak of windows
while(((start+WINDOW_SIZE)-1)<=array_size)
j=1;
i =start;
while(j<=WINDOW_SIZE)
WindowArray(j) = transformed(i);
j = j+1;
i = i +1;
end
Y = fft(WindowArray);
p = abs(Y).^2; %power
[a, b] = max(abs(Y)); % find max a and its indices b
[m, i] = max(p); %the maximum of the power m and its indices i
maximum(g) = m;
index(t) = i;
power(o) = a;
indexP(g) = b;
start = start + step;
k = k+1;
t = t+1;
g = g+1;
o=o+1;
end
% low pass filter
% filtering noise: ignor frequencies that are less than 5% of maximum frequency
for u=1:length(maximum)
M = max(maximum); %highest value in the array
Accept = 0.05* M;
if(maximum(u) > Accept)
maximum = maximum(u:length(maximum));
break;
end
end
% preparing the time of the graph,
% Location of the Peak flow rates are estimated
TotalTime = (numOfPeaks * 0.1);
time1 = [0:0.1:TotalTime];
if(length(maximum) > ceil(numOfPeaks));
maximum = maximum(1:ceil(numOfPeaks));
end
time = time1(1:length(maximum));
% plotting frequency-time graph
figure(1);
plot(time, maximum);
ylabel('Frequency');
xlabel('Time (in seconds)');
% plotting volume-time graph
figure(2);
plot(time, cumsum(maximum)); % integration over time to get volume
ylabel('Volume');
xlabel('Time (in seconds)');
(I only answer the part of the question which I understood)
Per default Matlab normalizes the audio wave to - 1...1 range. Use the native option if you want the integer data.
First, in your code it should be p = abs(Y)**2, this is the proper way to square the values returned from the FFT. The reason why you take the absolute value of the FFT return values is because those number are complex numbers with a Real and Imaginary part, therefore the absolute value (or modulus) of an imaginary number is the magnitude of that number. The goal of taking the power could be for potentially obtaining an RMS value (root mean squared) of your overall amplitude values, but you could also have something else in mind. When you say volume-time I assume you want decibels, so try something like this:
def plot_signal(file_name):
sampFreq, snd = wavfile.read(file_name)
snd = snd / (2.**15) #Convert sound array to floating point values
#Floating point values range from -1 to 1
s1 = snd[:,0] #left channel
s2 = snd[:,1] #right channel
timeArray = arange(0, len(snd), 1)
timeArray = timeArray / sampFreq
timeArray = timeArray * 1000 #scale to milliseconds
timeArray2 = arange(0, len(snd), 1)
timeArray2 = timeArray2 / sampFreq
timeArray2 = timeArray2 * 1000 #scale to milliseconds
n = len(s1)
p = fft(s1) # take the fourier transform
m = len(s2)
p2 = fft(s2)
nUniquePts = ceil((n+1)/2.0)
p = p[0:nUniquePts]
p = abs(p)
mUniquePts = ceil((m+1)/2.0)
p2 = p2[0:mUniquePts]
p2 = abs(p2)
'''
Left Channel
'''
p = p / float(n) # scale by the number of points so that
# the magnitude does not depend on the length
# of the signal or on its sampling frequency
p = p**2 # square it to get the power
# multiply by two (see technical document for details)
# odd nfft excludes Nyquist point
if n % 2 > 0: # we've got odd number of points fft
p[1:len(p)] = p[1:len(p)] * 2
else:
p[1:len(p) -1] = p[1:len(p) - 1] * 2 # we've got even number of points fft
plt.plot(timeArray, 10*log10(p), color='k')
plt.xlabel('Time (ms)')
plt.ylabel('LeftChannel_Power (dB)')
plt.show()
'''
Right Channel
'''
p2 = p2 / float(m) # scale by the number of points so that
# the magnitude does not depend on the length
# of the signal or on its sampling frequency
p2 = p2**2 # square it to get the power
# multiply by two (see technical document for details)
# odd nfft excludes Nyquist point
if m % 2 > 0: # we've got odd number of points fft
p2[1:len(p2)] = p2[1:len(p2)] * 2
else:
p2[1:len(p2) -1] = p2[1:len(p2) - 1] * 2 # we've got even number of points fft
plt.plot(timeArray2, 10*log10(p2), color='k')
plt.xlabel('Time (ms)')
plt.ylabel('RightChannel_Power (dB)')
plt.show()
I hope this helps.

How to vectorize sum of vector functions

I have a for loop in MATLAB which calculates sum of sine functions as follows:
% preliminary constants, etc.
tTot = 2;
fS = 10000;dt = 1/fS; % total time, sampling rate
Npts = tTot * fS; %number of points
t = dt:dt:tTot;
c1 = 2*pi/tTot;
c2 = pi/fS;
s = zeros(1,Npts)
% loop to optimize:
for(k=1:Npts/2)
s = s + sin(c1*k*t - c2*k*(k-1))
end
Basically, a one-liner for loop that becomes really slow as Npts becomes large. The difficulty comes in the fact that I am summing vectors which are defined by a parameter k, over k.
Is there a way to make this more efficient by vectorizing? One approach I've taken so far is defining a matrix and summing out the result, but this gives me an out of memory error for larger vectors:
[K,T] = meshgrid(1:1:Npts,t);
s = sum(sin(c1*K.*T - c2*K.*(K-1)),2);
Approach #1
Using sine of difference formula: sin(A-B) = sin A cos B - cos A sin B that enables us to leverage fast matrix multiplication -
K = 1:Npts/2;
p1 = bsxfun(#times,c1*K(:),t(:).');
p2 = c2*K(:).*(K(:)-1);
s = cos(p2).'*sin(p1) - sin(p2).'*cos(p1);
Approach #2
With bsxfun -
K = 1:Npts/2;
p1 = bsxfun(#times,c1*K(:),t(:).');
p2 = c2*K(:).*(K(:)-1);
s = sum(sin(bsxfun(#minus, p1,p2)),1);
Approach #1 can be modified to bring in a smaller sized loop to accommodate for problems that have large data arrays as shown next -
num_blks = 100;%// Edit this based on how much RAM can handle workspace data
intv_len = Npts/(2*num_blks); %// Interval length based on number of blocks
KP = 1:Npts/2;
P2 = c2*KP(:).*(KP(:)-1);
sin_P2 = sin(P2);
cos_P2 = cos(P2);
s = zeros(1,Npts);
for iter = 1:intv_len:Npts/2
K = iter:iter+intv_len-1;
p1 = bsxfun(#times,c1*K(:),t(:).');
s = s + (cos_P2(K).'*sin(p1) - sin_P2(K).'*cos(p1));
end

chirp phase alteration

I am trying to create a swept-frequency cosine, and I want to be able to set the phase as I please. I tried that code, but I get an error. I want to create a vector mat(1:40), where I can manually set its phase.
Fs = 32000; %Sampling Frequency
t = 0: 1/Fs: 10 -1/Fs; %Time
tt = 10; %Time when the chance occurs
f1 = 20; %Starting Frequency
f2 = 250; %Ending Frequency
cosineph = zeros(1,40); %Phase of cosines
for iMat= 1:40
k=iMat/2;
mat(iMat) = chirp(t,k*f1,tt,k*f2,'linear',cosineph(iMat));
end
The error that I am getting is " In an assignment A(I) = B, the number of elements in B and I must be the same."
Now, I am guessing it refers to variable t, so I tried implementing that into an embedded for, but didn't get the results I wanted.
Any advice?
Thanks
You are attempting to assign a vector (the output of chirp) to a single element of a matrix (mat). This won't work. You could use a cell array instead. In the example below I've replaced mat with a cell array, outArray.
Fs = 32000; %Sampling Frequency
t = 0: 1/Fs: 10 -1/Fs; %Time
tt = 10; %Time when the chance occurs
f1 = 20; %Starting Frequency
f2 = 250; %Ending Frequency
cosineph = zeros(1,40); %Phase of cosines
for iMat= 1:40
k=iMat/2;
outArray{iMat} = chirp(t,k*f1,tt,k*f2,'linear',cosineph(iMat));
end