Store maximum frequency - matlab

I am working on a script which performs a FFT of given short audio file in a loop. I also want to store the peak frequency but I do not know how to do that.
The code looks similar to this:
n = ...
Frequencies = zeros(1,n); % Allocating memory for the peak frequencies
for k = 1:n
str(k)
textFileName = [num2str(k) '.m4a'];
[data,fs] = audioread(textFileName);
%...
% Fast Fourier transform and plotting part works ok
%...
[peaks,frequencies] = findpeaks(abs(cutP2),cutf,'MinPeakHeight',10e-3);
% Here starts the problem
maximum_Peak = max(peaks);
Frequencies(k) = ... % I need to store the frequency which is coupled
% with the maximum amplitude but I do not know how
end
close(figure(n)) %The loop opens one redundant blank plot, I could not
%find out any other way to close it
I do not want to store the amplitudes of peak frequencies, but frequencies of peak amplitudes. If you could help me with the redundant figure, I would be happy. I tried to implement an if statement but did not work.

max contains a second output which returns the index of the maximum value. Use this second value to stores the value of interest.
[maximum_Peak,I] = max(peaks); %Note I Use 'I' for index - personal habit
Frequencies(k) = frequencies(I);
Also, if your goal is only to find the max point findpeaks may be overkill and you could potentially use:
[maximum_Peak,I] = max(abs(cutP2));
%Might want to check that max is high enough
Frequencies(k) = cutf(I);
Note although the code is similar it is not the same and depends on what you want to do.
Finally, some unsolicited advice, your use of frequencies and Frequencies is a bit of a red flag. Generally differences based on capitalization are not a good idea. Consider renaming the latter to freq_of_max_amp

Related

Any good ways to obtain zero local means in audio signals?

I have asked this question on DSP.SE before, but my question has got no attention. Maybe it was not so related to signal processing.
I needed to divide a discrete audio signal into segments to have some statistical processing and analysis on them. Therefore, segments with fixed local mean would be very helpful for my case. Length of segments are predefined, e.g. 512 samples.
I have tried several things. I do use reshape() function to divide audio signal into segments, and then calculate means of every segment as:
L = 512; % Length of segment
N = floor(length(audio(:,1))/L); % Number of segments
seg = reshape(audio(1:N*L,1), L, N); % Reshape into LxN sized matrix
x = mean(seg); % Calculate mean of each column
Subtracting x(k) from each seg(:,k) would make each local mean zero, yet it would distort audio signal a lot when segments are joined back.
So, since mean of hanning window is almost 0.5, substracting 2*x(k)*hann(L) from each seg(:,k) was the first thing I tried. But this time multiplying by 2 (to make the mean of hanning window be almost equal to 1) distorted the neighborhood of midpoints in each segments itself.
Then, I have used convolution by a smaller hanning window instead of multiplying directly, and subtracting these (as shown in figure below) from each seg(:,k).
This last step gives better results, yet it is still not very useful when segments are smaller. I have seen many amazing approaches here on this site for different problems. So I just wonder if there is any clever ways or existing methods to obtain zero local means which distorts an audio signal less. I read that, this property is useful in some decompositions such as EMD. So maybe I need such decompositions?
You can try to use a moving average filter:
x = cumsum(rand(15*512, 1)-0.5); % generate a random input signal
mean_filter = 1/512 * ones(1, 512); % generate a mean filter
mean = filtfilt(mean_filter, 1, x); % filtfilt is used instead of filter to obtain a symmetric moving average.
% plot the result
figure
subplot(2,1,1)
plot(x);
hold on
plot(mean);
subplot(2,1,2)
plot(x - mean);
You can tune the filter by changing the interval of the mean filter. Using a smaller interval, results in lower means inside each interval, but filters also more low frequencies out of your signal.

getting the delay/lag of Ultrasonic pulse velocity matlab

I am currently doing a thesis that needs Ultrasonic pulse velocity(UPV). UPV can easily be attained via the machines but the data we acquired didn't have UPV so we are tasked to get it manually.
Essentially in the data we have 2 channels, one for the transmitter transducer and another for a receiver transducer.
We need to get the time from wave from the transmitter is emitted and the time it arrives to the receiver.
Using matlab, I've tried finddelay and xcorr but doesnt quite get the right result.
Here is a picture of the points I would want to get. The plot is of the transmitter(blue) and receiver(red)
So I am trying to find the two points in the picture but with the aid of matlab. The two would determine the time and further the UPV.
I am relatively a new MATLAB user so your help would be of great assistance.
Here is the code I have tried
[cc, lags] = xcorr(signal1,signal2);
d2 = -(lags(cc == max(cc))) / Fs;
#xenoclast hi there! so far the code i used are these.
close all
clc
Fs = input('input Fs = ');
T = 1/Fs;
L = input('input L = ');
t = (0:L-1)*T;
time = transpose(t);
i = input('input number of steploads = ');
% construct test sequences
%dataupv is the signal1 & datathesis is the signal2
for m=1:i
y1 = (dataupv(:,m) - mean(dataupv(:,m))) / std(dataupv(:,m));
y2 = (datathesis(:,m) - mean(datathesis(:,m))) / std(datathesis(:,m));
offset = 166;
tt = time;
% correlate the two sequences
[cc, lags] = xcorr(y2, y1,);
% find the in4dex of the maximum
[maxval, maxI] = max(cc);
[minval, minI] = min(cc);
% use that index to obtain the lag in samples
lagsamples(m,1) = lags(maxI);
lagsamples2(m,1) = lags(minI);
% plot again without timebase to verify visually
end
the resulting value is off by 70 samples compared to when i visually inspect the waves. the lag resulted in 244 but visually it should be 176 here are the data(there are 19 sets of data but i only used the 1st column) https://www.dropbox.com/s/ng5uq8f7oyap0tq/datatrans-dec-2014.xlsx?dl=0 https://www.dropbox.com/s/1x7en0x7elnbg42/datarec-dec-2014.xlsx?dl=0
Your example code doesn't specify Fs so I don't know for sure but I'm guessing that it's an issue of sample rate(s). All the examples of cross correlation start out by constructing test sequences according to a specific sample rate that they usually call Fs, not to be confused with the frequency of the test tone, which you may see called Fc.
If you construct the test signals in terms of Fs and Fc then this works fine but when you get real data from an instrument they often give you the data and the timebase as two vectors, so you have to work out the sample rate from the timebase. This may not be the same as the operating frequency or the components of the signal, so confusion is easy here.
But the sample rate is only required in the second part of the problem, where you work out the offset in time. First you have to get the offset in samples and that's a lot easier to verify.
Your example will give you the offset in samples if you remove the '/ Fs' term and you can verify it by plotting the two signals without a timebase and inspecting the sample positions.
I'm sure you've looked at dozens of examples but here's one that attempts to not confuse the issue by tying it to sample rates - you'll note that nowhere is it specified what the 'sample rate' is, it's just tied to samples (although if you treat the 5 in the y1 definition as a frequency in Hz then you will be able to infer one).
% construct test sequences
offset = 23;
tt = 0:0.01:1;
y1 = sin(2*pi*5*tt);
y2 = 0.8 * [zeros(1, offset) y1];
figure(1); clf; hold on
plot(tt, y1)
plot(tt, y2(1:numel(tt)), 'r')
% correlate the two sequences
[cc, lags] = xcorr(y2, y1);
figure(2); clf;
plot(cc)
% find the index of the maximum
[maxval, maxI] = max(cc);
% use that index to obtain the lag in samples
lagsamples = lags(maxI);
% plot again without timebase to verify visually
figure(3); clf; hold on
plot(y1)
plot(y2, 'r')
plot([offset offset], [-1 1], 'k:')
Once you've got the offset in samples you can probably deduce the required conversion factor, but if you have timebase data from the instrument then the inverse of the diff of any two consecutive entries will give it you.
UPDATE
When you correlate the two signals you can visualise it as overlaying them and summing the product of corresponding elements. This gives you a single value. Then you move one signal by a sample and do it again. Continue until you have done it at every possible arrangement of the two signals.
The value obtained at each step is the correlation, but the 'lag' is computed starting with one signal all the way over to the left and the other overlapping by only one sample. You slide the second signal all the way over until it's only overlapping the other end by a sample. Hence the number of values returned by the correlation is related to the length of both the original signals, and relating any given point in the correlation output, such as the max value, to the arrangement of the two signals that produced it requires you to do a calculation involving those lengths. The xcorr function makes this easier by outputting the lags variable, which tracks the alignment of the two signals. People may also talk about this as an offset so watch out for that.

Recognizing the pitch of a sound with matlab

I'm trying to do a project that reads in a WAV file that has a sequence of music note in it using MATLAB. For example, my WAV file might contain a sequence of C-D-C-E. And feeding this file into my program would print out "C D C E."
I tried using WAVREAD to turn the file into a vector, then
used sampling to downsample it and make into a one-channel file.
Then I was able to come up with a spectrogram that has "peaks" at certain frequencies.
From here, I would like to get help on how to make MATLAB to recognize the frequencies at the peaks, therefore enabling me to print out the note.
Or am I on the wrong track?
Thanks in advance!
You are on the correct track but this is not a simple problem. What I would suggest looking into is something called a chromagram. This will use information that you gathered from the spectrogram and "bin" it into piano note frequencies. This will give an approximation of a songs harmonic content. This may not be entirely accurate though because of residual energy in the note's harmonics, but it is a start.
Do realize that transcription, which is what you are doing, is a very difficult task and has yet to be 100% solved. People are still researching this to today. I have code to generate chroma, but I will have to dig for it.
EDIT
Here is some code to chroma
clc; close all; clear all;
% didn't have wav file, but simply replace this with the following
% [audio,fs] = wavread('audioFile.wav')
audio = rand(1,10000);
fs = 44100; % temp sampling frequency, will depend on audio input
NFFT = 1024; % feel free to change FFT size
hamWin = hamming(NFFT); % window your audio signal to avoid fft edge effects
% get spectral content
S = spectrogram(audio,hamWin,NFFT/2,NFFT,fs);
% Start at center lowest piano note
A0 = 27.5;
% all 88 keys
keys = 0:87;
center = A0*2.^((keys)/12); % set filter center frequencies
left = A0*2.^((keys-1)/12); % define left frequency
left = (left+center)/2.0;
right = A0*2.^((keys+1)/12); % define right frequency
right = (right+center)/2;
% Construct a filter bank
filter = zeros(numel(center),NFFT/2+1); % place holder
freqs = linspace(0,fs/2,NFFT/2+1); % array of frequencies in spectrogram
for i = 1:numel(center)
xTemp = [0,left(i),center(i),right(i),fs/2]; % create points for filter bounds
yTemp = [0,0,1,0,0]; % set magnitudes at each filter point
filter(i,:) = interp1(xTemp,yTemp,freqs); % use interpolation to get values for frequencies
end
% multiply filter by spectrogram to get chroma values.
chroma = filter*abs(S);
%Put into 12 bin chroma
chroma12 = zeros(12,size(chroma,2));
for i = 1:size(chroma,1)
bin = mod(i,12)+1; % get modded index
chroma12(bin,:) = chroma12(bin,:) + chroma(i,:); % add octaves together
end
That should do the trick. It may not be the fastest solution, but it should get the job done.
Surely it can be optimized.
As MZimmerman6 this is a very complex problem. Peak to peak measuring may be successful, but will certainly not if the music gets anymore complicated. I have tackled this problem before and seen other people try it as well and the most successful projects among my peers I have seen involve the following:
1) Constrain the time. It may actually be difficult for a program to determine when a note is even changing! This is especially true if you are trying to separate vocals from instrumentals, or for example when two chords play sequentially, but they have one note that stays the same between them. So by constrain the time it is meant find out when each chunk of music happens, so in your case divide the track into four tracks, one for each note. You may be able to use the attack of each note to your advantage, to automatically detect the attack as the beginning of a new segment to test.
2) Constrain the frequencies. You have to use what you know, otherwise you will need to make eigenmode comparisons. Singular value decomposition has been effective in this arena. But if you somehow have the piano playing separate notes (individually), and you have recordings of the piano playing the song, what you can do is a fast fourier transform of each segment (see above time constraints), cut out the noise, and compare them. Then you employ a subtractive method or another metric to determine the best "fit" for each note.
This is a rough explanation of the concerns, but trust me, the more constraints you can put on this sort of analysis the better.

Given an array of data, extract possible frequencies with fft, how to?

I am fairly new to vibrations and using matalb fft.I am given a set of data (1D array) of length 15000 (not sure if this is relevant) and I am trying to figure out if there is any wave buried in this data at all. I was instructed to possibly use matlab fft. Is that the correct way to do it? what shall I expect to see? I am really not sure how to interpret any results I would get.
Please let me know what you all think.
Thanks and if more details are needed I will provide them.
Example:
% Df=[array is given to me and it is of size 15000];
% t=[time used for the above array, and if it is of the same size, also provided to me]
N_0= length(t);
fs_0=length(Dfxz);
Y_0=fft(Dfxz,N_0);
k_0=-N_0/2:N_0/2-1;
%Find the phase angle
p_0 = (angle(Y_0));
R_0 = norm(Y_0);
ff_0 = (0:length(Y_0)-1)'/length(Y_0)*100; % Frequency vector
FT_power1_0 = abs(Y_0);
plot(k_0*fs_0/N_0,fftshift(abs(Y_0)))
I only see 1 peek at frequency = 0 but I am sure that there are non zero frequencies, what am I doing wrong?
Thanks!
PS:I am not sure how to pick the sampling frequency either? any tips please (keep in mind that I do not know the original frequency)
Try my version. It looks to me like you have all the information you need to find frequency peaks in your data if they exist. If all you can see is a big peak at zero frequency, you probably have a massive DC offset that is drowning out all your other data. I have put a remedy in my code . .
x = randn(15000,1); %//This is the data you were given - I'll use noise for demonstration - replace x with your Df data
%//If youre getting a massive peak at zero that dwarfs everything else, you
%//probably have a large DC offset. Easily removed in the time domain using
%//the following ..
x = x-mean(x);
tAxis = linspace(3/15000,3,15000); %//You said you have this too - I'll make up something for demonstration - make sure you replace this with your t data
dt = diff(tAxis(1:2)); %//sample period from time axis
fs = 1/dt;%//sample rate from sample period
NFFT = numel(x); %//number of fft bins - change if you like
Y = abs(fft(x, NFFT)).^2; %power spectrum
%//Calculate frequency axis
df = fs/NFFT;
fAxis = 0:df:(fs-df);
%//Plot it all
figure; plot(fAxis(1:NFFT/2), Y(1:NFFT/2))
xlabel('Frequency in Hz')
ylabel('Power')
If that works out OK, you can go into more depth by checking out another FFT answer on stackoverflow.

Finding a certain value below the maxima in Matlab

I have 2 800x1 arrays in Matlab which contain my amplitude vs. frequency data, one array contains the magnitude, the other contains the corresponding values for frequency. I want to find the frequency at which the amplitude has reduced to half of its maximum value.
What would be the best way to do this? I suppose my two main concerns are: if the 'half amplitude' value lies between two data points, how can I find it? (e.g. if the value I'm looking for is 5, how can I "find it in my data" if it lies between two data points such as 4 and 6?)
and if I find the 'half amplitude' value, how do I then find the corresponding value for frequency?
Thanks in advance for your help!
You can find the index near your point of interest by doing
idx = magnitudes >= (max(magnitude)/2);
And then you can see all the corresponding frequencies, including the peak, by doing
disp(frequencies(idx))
You can add more conditions to the idx calculation if you want to see less extraneous stuff.
However, your concern about finding the exact frequency is harder to answer. It will depend heavily on the nature of the signal and also on the lineshape of your window function. In general, you might be better off trying to characterize your peak with a few points and then doing a curvefit of some kind. Are you trying to calculate Q of a resonant filter, by any chance?
If it's ok, you can do simple linear interpolation. Find segments where the drop occurs and calculate intermediate values. That will be no good, if you expect noise in the signal.
idx = find(magnitudes(2:end) <= (max(magnitudes)/2) & ...
magnitudes(1:end-1) >= (max(magnitudes)/2));
mag1 = magnitudes(idx); % magnitudes of points before drop
mag2 = magnitudes(idx+1); % magnitudes of points after drop below max/2
fr1 = frequencies(idx); % frequencies just before drop
fr2 = frequencies(idx+1); % frequencies after drop below max/2
magx = max(magnitudes)/2; % max/2
frx = (magx-mag2).*(fr1-fr2)./(mag1-mag2) + fr2; % estimated frequencies
You can also use INTERP1 function.