Analyzing wav files in MATLAB - matlab

So I have this piano recording (in .wav format). I am able to do an FFT on the whole recording and identify frequencies.
However, according to some articles I read, its best if the wav file is broken down into windows, where each window would include one particular note.
For this I need to initially plot a "power envelope" of my time domain signal (considering the note average energy concept) therefore there'll be one increase and one decrease for each note and note onsets can be determined by checking the local minima.
This is where 'windows' are introduced, where each window consists of only one onset and then FFT is performed on each window.
Im having difficulty in plotting the power envelope and moving onto breaking it down into windows. Would appreciate some help with the Matlab coding for this.
The code I've used is pretty straightforward:
[wave,fs] = wavread ('c scale fast.wav'); % read file into memory */
%sound(wave,fs); % see what it sounds like */
wave = wave.*hamming(length(wave));
t = 0:1/fs:(length(wave)-1)/fs; % and get sampling frequency */
figure(2);
subplot(2,1,1);
plot(t,wave);
title('Wave File');
ylabel('Amplitude');
xlabel('Length (in seconds)');
L = length(wave);
NFFT = 2^nextpow2(L); % Next power of 2 from length of y
Y = fft(wave,NFFT)/L;
f = fs/2*linspace(0,1,NFFT/2+1);
% Plot single-sided amplitude spectrum.
subplot(2,1,2);
plot(f,2*abs(Y(1:NFFT/2+1)))
title('Single-Sided Amplitude Spectrum of y(t)')
xlabel('Frequency (Hz)')
ylabel('|Y(f)|')
After my signal (abs value of my wav file) is convolved with the Gaussian filter i try taking the 1st and 2nd derivatives, but i don't get an output when i try to plot it.
edges=fconv(abs(song),detect);
tedges=edges(P/2:N+P/2-1);
tedges=tedges/max(abs(tedges));
W= diff(tedge);
Z= diff(W);
It is when i try to plot W and Z that I don't get the output I need. My graph is empty in other words. I can't figure out what I'm doing wrong here...

Useful: http://blogs.mathworks.com/videos/2009/12/31/basics-finding-a-subset-of-a-matrix/
Basic flow:
for v=1:window_length:length(data)
data_subsection=data(v:v+window_length);
subsection_fft = fft(data_subsection);
plot(...);
end

Related

Visualising fft-signal__Waterplot

This is the kind of plot i imagined myself.
https://de.mathworks.com/help/matlab/ref/waterfall.html
Ok, i don't want to explain too much, how my code works. It would take too much time. Just try the second code yourself. Take any small wav-file you can find. When you compile the code, you can see three frequency bands and see that many spectrums are plotted every 30ms. If you have a specifically question concerning my code, how it works, ask me in the comments.
I want every spectrum, at least from one frequency band, to plot it in a 3-dimensional plot. In short, what are the coordinates of the first spectrum and the 2nd, the 3rd, the 4th and so on.
My time segment on which is a fft applied, is 30 ms long. The first point on the x-axis is 30 ms, the next one 60ms and the next one 90ms and so on. What is the y-coordinate from the 30ms? This would be on the frequency axis or the y-axis. The z-axis would be the magnitude out of a frequency component at some point in time (or at a given sliding window frame). How can i do that? How do i write that? I am having big trouble with this matter. And since every explanation is in another language, it makes it much more harder for me.
As you may know, i have an audiofile (music) on which i compute a STFT. I want to visualise it. See the following explanation in my code. Read the comments!
My first idea to do this way, was using the function "mesh" or something similar.
Here is my mesh-code:
X=1:10;
Y=1:15;
Z = [];
% Here i would define the number of time segments
% See the next following code, to understand, what i mean.
for i = 1:length(X)
% Here in this line, i want to compute my short fft
%
% number of frequencies
for j = 1: length(Y)
Z(j,i) = 1.0/(i*j);
end
end
mesh(X,Y,Z)
This code plots me a mesh, i just wanted to know for myself, how this works. Anyway please be aware, that i am quite sure that i do not know, how the function "mesh" works to the fullest, but i think, i understood most of it.
Another thing i need to mention is, that i am defining frequency bands in my next following code. I did this, because i noticed, i have very high amplitudes in a range from 1 - 1000Hz, which is why, i defined 3 frequency bands. It is not necessary to plot all of them, but i want to visualise at least one. Not visualising the whole frequency range from the audio signal, but only the specificially chosen band.
%% MATLAB
%_________________________________________
[y,fs]=audioread('dontstopmenow.wav');
% audioread = Read WAV-file
% y = Vector, which contains audio signal
% fs = Sample Rate
% 'dontstopmenow' = WAV-file
%_________________________________________
%PARAMETER FOR STFT
%_________________________________________
t_seg=0.03; % Length of segment in ms
fftlen = 4096; %FFT-Points
%Defining the length of my frequency bands
f_LOW= 1:200; % contain lower frequencies
f_MEDIUM= 201:600; % contain medium frequencies
f_HIGH= 601:1000; % contain higher frequencies
%_______________________________________________________
segl =floor(t_seg*fs);
% Length of segment, on which we use the fft
% "floor" rounds off the result
windowshift=segl/2;
% size of window which goes to the next segment
window=hann(segl);
%hann function
window=window.';
% From a row vector to a column vector
si=1;
%Start index
ei=segl;
%End index
N= length(y)/windowshift - 1;
% Number of time segements in audio signal
f1=figure;
% New window
f=0:1:fftlen-1;
f=f/(fftlen-1)*fs;
% frequency vector
Ya=zeros(1,fftlen);
%Plotting time segments!
for m= 1:1:N
y_a = y(si:ei);
y_a= y_a.*window;
Ya=fft(y_a, fftlen);
Ya=abs(Ya(1:end/2));
%One-sided-spectrum
drawnow; %Updates graphical objects
figure(f1);
plot(f(1:end/2), 20*log10(Ya));
%STFT __ plots the whole audio signal after a stft, every 30ms
%% L,M,H - Bands
subplot(3,1,1)
y_low = Ya(f_LOW);
plot(f_LOW,y_low);
ylim([-20 60]);
title('Spektrum (LOW)');
xlabel('f(Hz)');
ylabel('dB');
grid on
subplot(3,1,2)
y_medium = Ya(f_MEDIUM);
plot(f_MEDIUM,y_medium);
ylim([-20 30]);
title('Spektrum (MEDIUM)');
xlabel('f(Hz)');
ylabel('dB');
grid on
subplot(3,1,3)
y_high = Ya(f_HIGH);
plot(f_HIGH,y_high);
ylim([-20 30]);
title('Spektrum (HIGH)');
xlabel('f(Hz)');
ylabel('dB');
grid on;
si=si+windowshift;
% start index updated
ei=ei+windowshift;
% end index updated
end
Here's the list of statements you could add to your code to generate the waterfall plot:
Let's store all STFT outputs in a matrix named Yb. Firstly, allocate the memory by adding this line before the for-loop.
Yb = NaN(N, fftlen/2);
Next, in the for-loop, save the fft result for each segment. This can be done by adding the following line after you have finished the computation of Ya (just before drawnow).
Yb(m,:) = Ya;
Now you're ready to generate the waterfall plot. This can be done by adding the following code after the end of the for-loop.
figure;
waterfall(f(f_LOW), (1:N)*windowshift/fs, Yb(:,f_LOW));
xlabel('Frequency (Hz)');
ylabel('Time (s)');
Hope this achieves what you want.
Following is not related to the main question: I also have the following suggestions to improve some other aspects of your code:
(1) The computation of the frequency grid f has a small scaling error. It should be:
f=f/fftlen*fs;
(2) Depending on the WAV file you use, your code may get fractional values in windowshift and N, but both of them need to be integers. So, use appropriate rounding methods while computing them. I'd suggest the following:
windowshift = round(segl/2);
N = floor(length(y)/windowshift);
(3) In the for-loop, you plot the whole fft only to overwrite that with the subplots immediately. That line should be removed.

Matlab: how to find fundamental frequency of a speech

I am new to Matlab and speech processing as well. I want to find the fundamental frequency of speech signal to determine the gender of the speaker. I removed the silence from the signal by analysing it within 10 msec periods.
After that I got the fft using this code :
abs(fft(input_signal_without_silences))
My plot of both the speech signal and the fft of it is below:
Now, I want to find the fundamental frequency but I could not understand which steps do I need to do this. Or do I misunderstand this concept?
As far as I have learnt, there are some methods like autocorrelation,
Since I am not familiar to both speech processing and matlab, any help and advice is very much appreciated.
The fft() help can solve most parts of your problem. I can give a brief overview of things based on the content of the help file.
At the moment what you are plotting is the two sided, unnormalized fft coefficients, which don't tell much. Use the following to get a more user informed spectral analysis of the voice signal. Using the single sided spectram you would be able to find the dominant frequency which might be the fundamental frequency of the speech signal.
y = []; %whatever your signal
T = 1e-2; % Sample time, 10 ms
Fs = 1/T; % Sampling frequency
L = length(y); % Length of signal
NFFT = 2^nextpow2(L); % Next power of 2 from length of y
Y = fft(y,NFFT)/L;
f = Fs/2*linspace(0,1,NFFT/2+1);
% Plot single-sided amplitude spectrum.
plot(f,2*abs(Y(1:NFFT/2+1)))
title('Single-Sided Amplitude Spectrum of y(t)')
xlabel('Frequency (Hz)')
ylabel('|Y(f)|')
The problem is that you have a plot of Amplitude vs Sample Number instead of a plot of Amplitude vs Frequency.In order to calculate the fundamental frequency you need to find the frequency that corresponds to the highest frequency.
Matlab returns frequencies from -fs/2 to fs/2 so the frequency at index n is
f = n * (fs/N) - (fs/2)
where f = frequency, fs = sampling frequency, N = number of points in FFT.
So basically all you need to do is get the index where the plot is highest and substitute it in the equation above to get an estimate of the fundamental frequency.Make sure n > N/2 so that your fundamental frequency is positive.

Audioread in matlab of wav file and FFT

I'm working on Matlab, I want to perform FFT on a wav file I previously recorded on Matlab as well.
fs = 44100; % Hz
t = 0:1/fs:1; % seconds
f = 600; % Hz
y = sin(2.*pi.*f.*t);
audiowrite('600freq.wav',y,fs)
This is the way I'm recording in the wav file.
Now to the reading and FFT part:
[y,Fs] = audioread('600freq.wav');
sound(y)
plot(fft(y))
This is the plot of the FFT I get:
Maybe I'm missing something about the FFT, but I expected two vertical lollipops.
Another thing I noticed that's wrong, is when I play the sound after reading it form the file it's longer and the pitch is significantly lower.
My guess is a sampling rate problem, but I really have no idea of what to do about it.
Thanks for any help in advance.
That's because you're not plotting the magnitude. What you are plotting are the coefficients, but these are complex valued. Because of that, the horizontal axis is the real component and the vertical axis is the imaginary component. Also, when you use sound by itself, the default sampling frequency is 8 kHz (8192 Hz to be exact) which explains why your sound is of a lower pitch. You need to use the sampling frequency as a second argument into sound, and that's given to you by the second output of audioread.
So, try placing abs after the fft call and also use Fs into sound:
[y,Fs] = audioread('600freq.wav');
sound(y, Fs);
plot(abs(fft(y)))
Also, the above code doesn't plot the horizontal axis properly. If you want to do that, make sure you fftshift your spectra after you take the Fourier transform, then label your axis properly. If you want to determine what each horizontal value is in terms of frequency, this awesome post by Paul R does the trick: How do I obtain the frequencies of each value in an FFT?
Basically, each horizontal value in your FFT is such that:
F = i * Fs / N
i is the bin number, Fs is the sampling frequency and N is the number of points you're using for the FFT. F is the interpreted frequency of the component you're looking at.
By default, fft assumes that N is the total number of points in your array. For the one-sided FFT, i goes from 0, 1, 2, up to floor((N-1)/2) due to the Nyquist sampling theorem.
Because what you're actually doing in the code you tried to write is displaying both sides of the spectrum, that's why it's nice to centre the spectrum so that the DC frequency is located in the middle and the left side is the negative spectra and the right side is the positive spectra.
We can incorporate that into your code here:
[y,Fs] = audioread('600freq.wav');
sound(y, Fs);
F = fftshift(abs(fft(y)));
f = linspace(-Fs/2, Fs/2, numel(y)+1);
f(end) = [];
plot(f, F);
The horizontal axis now reflects the correct frequency of each component as well as the vertical axis reflecting the magnitude of each component.
By running your audio generation code which generates a sine tone at 600 Hz, and then the above code to plot the spectra, I get this:
Note that I inserted a tool tip right at the positive side of the spectra... and it's about 600 Hz!

Frequency : Decibel plot using FFT in MATLAB

I am trying to use MATLAB to import a WAV file and create the type of diagram shown below. I am basically trying to pull frequency information and plot it according to decibels. Here is the code I am working with, but it doesn't seem to pull the frequency information correctly:
[x fs]=wavread('filename.wav');
dt=1/fs;%% time interval
X=fft(x);
df=1/(length(x)*dt); %% frequency interval
f=(1:length(X))*df;%% frequency vector
%% frequency domain plot, freq in hertz
figure
plot(f,abs(X))
Please help! Thank you!
In your code "X" contains the waveform information, not the frequency information. To get the frequency information of the soundfile you could use the FFT function. I use this (more elaborate, but still simple) code for what you want to do:
[FileName,PathName]=uigetfile('*.wav');
[y, Fs, nbits] = wavread(fullfile(PathName,FileName));
length_y=length(y);
NFFT = 2^nextpow2(length_y); % Next power of 2 from length of y
fft_y=fft(y,NFFT)/length_y;
f = Fs/2*linspace(0,1,NFFT/2+1);
semilogy(f,abs(fft_y(1:length(f))));
title('Single-Sided Amplitude Spectrum of y(t)')
xlabel('Frequency (Hz)')
ylabel('|Y(f)|')
I hope this is useful to you. The plot will not be in steps like the one you have shown, but that can also be achieved - using the "stairs" plot function.

Mean amplitude value in EMG signal

I have an EMG-signal and would like to get the mean value of the amplitude in a defined frequency space. I tried making FFT, but i don't get a vector with amplitudes and frequencies. I am new to Matlab - please help.
Here is how you would get power (i.e., amplitude) and frequency from the fft function in Matlab:
fs = 1024; % sampling rate of signal
time = 1; % time in sec of simulated signal
xn = randn (fs*time,1); % creating random signal
nfft = time*fs; % size of window on which to perform FFT
Y = fft (xn, nfft);
% Convert value to obtain the power of the signal at
% each frequency
Pyy = Y .* conj(Y) / nfft;
% Create a frequency axis for ploting
fy = fs/nfft * (0:(nfft/2) -1);
plot (fy, Pyy(1:nfft/2))
ylabel ('power (AU)^2') % arbitrary units
xlabel ('frequency (Hz)')
xlim ([0 512])
However, keep in mind that EMG is not a stationary signal. When doing spectral analysis on EMG signals we are trying to approximate the true spectrum. This is why EMG is usually seperated into windows of data. FFT is performed on each of these windows and the averaged.
Rather than using the fft function, you might want to consider using the pwelch function in the Matlab Signal Processing Toolbox. It allows you to set the window size, amount of overlap of the windows, etc.
Alternatively you could use the very popular Neurospec Matlab toolbox writen by David Halliday. It has a PDF that describes all the functions and examples you can use. It has additional functionality (e.g., coherence), but you can ignore it if you like and simply extract the spectral data.