How can I plot the spectrogram of a sound file without using predefined functions? - matlab

How can I plot the spectrogram of a WAV file without using predefined function? i mean i dont want use FFT or spectrogram functions .
my windows i want to take over the whole signal should be Hamming windows with size 256 and overlap 50%.
could you please give me some advice ?
best regards .
i tried the following code for DFT . but i got error :
??? Error using ==> times Matrix dimensions must agree.
the code :
n=256;
wholeFile=wavread('C:\Users\alireza\Desktop\alireza bahrami\New folder\Sound clip 32.wav');
for ii = 1:4096
waveform = wholeFile(128*ii + (1:256)); % a bit of background noise...
alpha = 0.54; beta = 1 - alpha; % hamming coefficients
hwin = alpha - beta * cos(2 * pi * (1:n) / (n - 1)); % from http://en.wikipedia.org /wiki/Window_function#Hamming_window
fSample = 8192; % sampling frequency
t = (0.5:1:n) / fSample; % actual time
waveformHW = waveform .* hwin; % the windowed form of the waveform
frequencies = (0:(n/2)-1) * fSample / n;
myFFT = zeros(size(frequencies));
for fi = 1:n/2
c = cos(2*pi*frequencies(fi) * t);
s = sin(2*pi*frequencies(fi) * t);
myFFT(fi) = (sum(c .* waveformHW) + 1i * sum(s .* waveformHW))/sum(c.*c.*hwin);
end
end
figure; semilogy(frequencies, abs(myFFT))
xlim([0 1000])
xlabel 'frequency'
ylabel 'amplitude'
title 'two tones plus noise'

For my example to work, your t vector must be a column vector, change this line
t = (0.5:1:n) / fSample; % actual time
to
t = (0.5:1:n)' / fSample; % actual time
You can use regress to get the amplitude of the sine and cosine components. Delete this line of your code:
myFFT(fi) = (sum(c .* waveformHW) + 1i * sum(s .* waveformHW))/sum(c.*c.*hwin);
Replace it with
b = regress(waveformHW, [c, s]; % c and s must be column vectors
myFFT(fi) = b(1) + 1i * b(2);

Related

Phase is not linear for the time domain signal of a Gaussian spectrum

As far as I understand from mathematics, the Inverse Fourier Transform (IFFT) of a Gaussian is a Gaussian. If I have a Gaussian with a mean (mu) and variance (sigma^2), I can write it as,
S(f) = 1/sqrt(2 * pi * sigma^2) .* exp(-(f - mu).^2/(2 * sigma^2))
Where f is the frequency axis. From Matematica, I found that the analytical expression for IFFT of the square root of the Gaussian pulse as,
s(t) = (2 / pi)^(1/4) .* sqrt(sigma) .* exp(-t .* 2 .* pi .* ((sigma)^2 .* 2 .* pi .* t + 1j .* mu)))
I used 2 * pi * t instead of t from Mathematica because they use angular terms. I don't use this expression in MATLAB anyway. I use IFFT to compare with this expression.
If we closely examine this time-domain s(t), we notice that the phase is linear. I define the frequency axis the following way,
f = linspace(-1/(2 * PRT), 1/(2 * PRT), N);
Therefore, after doing an IFFT, if I divide 2 * pi * mu from the phase of the signal, I should see a linearly varying thing from 0 to (N - 1)*PRT.
I have N = 60000 and PRT = 1e-3 so, I should see a plot from 0 till 60 seconds linearly. However, I see a nonlinear plot that ends before 60 seconds.
If I do the same exercise with a Dirac delta instead of a Gaussian spectrum, I see the linear phase correctly.
Picture for Gaussian:
Code for Gaussian:
clear;
% close all;
lambda = 0.03;
mu = 4 * 2 / lambda; % Mean Doppler
sigma = 0.2 * 2 / lambda;
PRT = 1e-3;
f_amb = 1/(2 * PRT);
N = 60000; % Total number of points in time axis
f = linspace(-f_amb, f_amb, N); % velocity axis for the entire rotation
S_ = gaussmf(f, [sigma, mu]);
s_num = ifft(ifftshift(sqrt(S_)));
figure; plot(unwrap(angle(s_num))/(2*pi*mu));
Picture for Dirac Spectrum:
Code for Dirac spectrum: You can add this one just below the above code for Gaussian:
[~, idx1] = (min(abs(f - mu)));
S_Dirac_f = dirac(f - f(idx1));
idx = S_Dirac_f == Inf;
S_Dirac_f(idx) = 1;
s_num_dirac = ifft(ifftshift(sqrt(S_Dirac_f)));
figure; plot(unwrap(angle(s_num_dirac))/(2*pi*mu));

Solving the Damped Harmonic Oscillator ODE as a first order system using midpoint methods

The exact solution of the damped harmonic oscillator
$$x'' + 2\gamma x' + \omega^2 x = 0, \quad x(0)=x_0, \quad x'(0)=-\gamma x_0$$
with $0 < \gamma < \omega$ is
$$x(t)= x_0 e^{-\gamma t} \cos(\beta t) \quad \text{where} \quad \beta:=\sqrt{\omega^2 - \gamma^2}$$
Notice that this second order ODE can be written as a first order system by making the substitutions:
$x' = y$ and,
$y' = -2\gamma y - \omega^2 x$
I want to solve the system using the method:
$$\dfrac{ x_{n+1} - x_{n-1} }{2h} = y_n \quad \quad \dfrac{y_{n+1} - y_{n-1}}{2h} = -2\gamma y_n - \omega^2 x_n.$$
which is an explicit midpoint rule. This is the code that I constructed for the problem, but it is not giving me the correct result. My plot has no harmonic behavior as I would anticipate.
function resonance
omega = 1; % resonant frequency = sqrt(k/m)
a = 0.2; % drag coeficient per unit mass
b = 0.1; % driving amplitude per unit mass
omega0 = 1.2; % driving frequency
tBegin = 0; % time begin
tEnd = 80; % time end
x0 = 0.2; % initial position
v0 = 0.8; % initial velocity
a = omega^2; % calculate a coeficient from resonant frequency
% Use Runge-Kutta 45 integrator to solve the ODE
[t,w] = ode45(#derivatives, [tBegin tEnd], [x0 v0]);
x = w(:,1); % extract positions from first column of w matrix
v = w(:,2); % extract velocities from second column of w matrix
plot(t,x);
title('Damped, Driven Harmonic Oscillator');
ylabel('position (m)');
xlabel('time (s)');
% Function defining derivatives dx/dt and dv/dt
% uses the parameters a, b, A, omega0 in main program but changeth them not
function derivs = derivatives(tf,wf)
xf = wf(1); % wf(1) stores x
vf = wf(2); % wf(2) stores v
dxdt = vf; % set dx/dt = velocity
dvdt = xf + 2 * b * vf + a * tf; % set dv/dt = acceleration
derivs = [dxdt; dvdt]; % return the derivatives
end
end
Also, my apologies about the formatting. I am use to math stackexchange, and the LaTeX style formatting doesn't seem to be applicable here and I do not know how to put my math in the math environment.
You missed a sign, it should be
dvdt = - ( xf + 2 * b * vf + a * tf ); % set dv/dt = acceleration
However, the whole expression is at odds with the previously stated equation,
x'' + 2*b*x' * a*x = 0
should result in
dvdt = - ( 2*b*vf + a*xf ); % set dv/dt = acceleration
But then again you have defined a twice, so change w2=omega^2 to get
dvdt = - ( 2*b*vf + w2*xf + a ); % set dv/dt = acceleration

Selection of sigma variable for gaussian function

I am new to signal processing and would like to apply low pass filter using fft.
I found this post which answers my question. While using it, I have one question:
given that my cutoff frequency is 3500Hz and sampling rate is 25600Hz,
what will be the value of sigma to use while generating Gaussian curve, as given by code from eigenchris' answer below?
gauss = zeros(size(Y));
sigma = 8; % just a guess for a range of 20
gauss(1:r+1) = exp(-(1:r+1).^ 2 / (2 * sigma ^ 2)); % +ve frequencies
gauss(end-r+1:end) = fliplr(gauss(2:r+1)); % -ve frequencies
y_gauss = ifft(Y.*gauss,1024);
Below is function code I am using:
clf; clc;
Fs = 25600;
file = '01cKhaitan181015M4_Opp_LeftS1H8_a.dat';
signal = dlmread(file); % read file from specified location
signal = signal - mean(signal);
N = size(signal, 1);
time = 1000*(0 : N-1)/Fs; % in msec
freq = (-Fs/2 : Fs/N : Fs/2-Fs/N)';
Y = fft(signal, 1024);
r = 141; % range of frequencies we want to preserve
gauss = zeros(size(Y));
sigma = 119.75;
gauss(1:r+1) = exp(-(1:r+1).^ 2 / (2 * sigma ^ 2)); % +ve frequencies
gauss(end-r+1:end) = fliplr(gauss(2:r+1)); % -ve frequencies
y_gauss = ifft(Y.*gauss,1024);
hold on;
plot(time, signal, 'k'); plot(time, abs(y_gauss), 'c');
legend('signal', 'gaussian', 'Location', 'southwest')
hold off;
Below is link of data file
https://www.dropbox.com/s/edb8g43j4a54jvq/01cKhaitan181015M4_Opp_LeftS1H8_a.dat?dl=0
The cutoff frequency is defined as the frequency where the attenuation is 0.5 or ~6dB. You already know the desired cutoff frequency to be 3500Hz. The next step would be to get the corresponding index with:
cutoff_frequency = 3500;
sampling_rate = 25600;
cutoff_index = 1 + cutoff_frequency/sampling_rate * length(Y);
At that cutoff_index you would want to have the desired 0.5 attenuation. Solving for sigma which yields an attenuation of 0.5 at the cutoff_index in your Gaussian formula:
%% Derivation of sigma value
% 0.5 = exp(-cutoff_index^2 / (2 * sigma ^ 2));
% log(0.5) = -cutoff_index^2 / (2 * sigma ^ 2));
% sigma^2 = -cutoff_index^2 / (2 * log(0.5));
% sigma^2 = cutoff_index^2 / (2 * log(2));
yielding:
sigma = cutoff_index / sqrt(2 * log(2));
So, if for example length(Y) is 1024, you would get
sigma = (3500/25600 * 1024) / sqrt(2 * log(2)); % approx. 119.75

How can I plot the spectrogram of a wav file without using predefined functions?

How can I plot the spectrogram of a WAV file without using predefined function?
i mean i dont want use FFT or spectrogram functions .
my windows i want to take over the whole signal should be Hamming windows .
could you please give me some advice ?
best regards .
i tried it for DFT . but i got error
n=256;
wholeFile=wavread('C:\Users\alireza\Desktop\alireza bahrami\New folder\Sound clip 32.wav');
for ii = 1:4096
waveform = wholeFile(128*ii + (1:256)); % a bit of background noise...
alpha = 0.54; beta = 1 - alpha; % hamming coefficients
hwin = alpha - beta * cos(2 * pi * (1:n) / (n - 1)); % from http://en.wikipedia.org /wiki/Window_function#Hamming_window
fSample = 8192; % sampling frequency
t = (0.5:1:n) / fSample; % actual time
waveformHW = waveform .* hwin; % the windowed form of the waveform
frequencies = (0:(n/2)-1) * fSample / n;
myFFT = zeros(size(frequencies));
for fi = 1:n/2
c = cos(2*pi*frequencies(fi) * t);
s = sin(2*pi*frequencies(fi) * t);
myFFT(fi) = (sum(c .* waveformHW) + 1i * sum(s .* waveformHW))/sum(c.*c.*hwin);
end
end
figure; semilogy(frequencies, abs(myFFT))
xlim([0 1000])
xlabel 'frequency'
ylabel 'amplitude'
title 'two tones plus noise'
the error :
??? Error using ==> times
Matrix dimensions must agree.
Here is how you can compute the DFT for an arbitrary waveform. It's slow but it works.
I have updated it so you can even see how to normalize (both amplitude and frequency).
Note - THIS IS SLOW. The "Fast Fourier Transform" got its name for a reason... By cleverly rearranging the calculation, it performs the same calculation as the one below, but much faster.
n = 4096;
waveform = 0.2*rand(1, n); % a bit of background noise...
alpha = 0.54; beta = 1 - alpha; % hamming coefficients
hwin = alpha - beta * cos(2 * pi * (1:n) / (n - 1)); % from http://en.wikipedia.org/wiki/Window_function#Hamming_window
fSample = 8192; % sampling frequency
t = (0.5:1:n) / fSample; % actual time
% add some spectral components to the noise:
f1 = 440; % Hz - A on the piano
f2 = 261.62; % Hz - middle C on the piano
waveform = waveform + 10 * cos(2 * pi * f1 * t) + 3 * cos(2 * pi * f2 * t);
waveformHW = waveform .* hwin; % the windowed form of the waveform
frequencies = (0:(n/2)-1) * fSample / n;
myFFT = zeros(size(frequencies));
for fi = 1:n/2
c = cos(2*pi*frequencies(fi) * t);
s = sin(2*pi*frequencies(fi) * t);
myFFT(fi) = (sum(c .* waveformHW) + 1i * sum(s .* waveformHW))/sum(c.*c.*hwin);
end
figure; semilogy(frequencies, abs(myFFT))
xlim([0 1000])
xlabel 'frequency'
ylabel 'amplitude'
title 'two tones plus noise'
Now tested and (believed to be) working:
As you can see in the picture, the tone that falls exactly in a bin (440 Hz) has exactly the right amplitude. The one that falls between two bins (261.62 Hz) looks too low - this is because the energy ends up spread among several bins in the spectrogram. There is an initial fast fall-off, and then a rather broad (if very low amplitude) residual. This is called "spectral leakage" - you will notice that it doesn't occur on the 440 Hz signal which is exactly on top of a bin. This is an inevitable consequence of the way this calculation is done. One nice thing about the DFT is that you do not need the frequencies to be evenly spaced - so you could, for example, compute this with the "exact frequencies of the piano", and this "partial binning" problem would go away.
But using the FFT is much faster.
Note also that the scale is now correct - both for amplitude and frequency. You can adjust the number of samples and the sampling frequency, and the scaling will follow along.
Do check that the math is right... don't use this for "real work", but only to understand the underlying principles.
Did I mention that this is much slower than an FFT?
n=256;
wholeFile=wavread('C:\Users\alireza\Desktop\alireza bahrami\New folder\Sound clip 32.wav');
for ii = 1:4096
waveform = wholeFile(128*ii + (1:256)); % a bit of background noise...
alpha = 0.54; beta = 1 - alpha; % hamming coefficients
hwin = alpha - beta * cos(2 * pi * (1:n) / (n - 1)); % from http://en.wikipedia.org /wiki/Window_function#Hamming_window
fSample = 8192; % sampling frequency
t = (0.5:1:n) / fSample; % actual time
% add some spectral components to the noise:
%f1 = 440; % Hz - A on the piano
%f2 = 261.62; % Hz - middle C on the piano
%waveform = waveform + 10 * cos(2 * pi * f1 * t) + 3 * cos(2 * pi * f2 * t);
waveformHW = waveform .* hwin; % the windowed form of the waveform
frequencies = (0:(n/2)-1) * fSample / n;
myFFT = zeros(size(frequencies));
for fi = 1:n/2
c = cos(2*pi*frequencies(fi) * t);
s = sin(2*pi*frequencies(fi) * t);
myFFT(fi) = (sum(c .* waveformHW) + 1i * sum(s .* waveformHW))/sum(c.*c.*hwin);
end
end
figure; semilogy(frequencies, abs(myFFT))
xlim([0 1000])
xlabel 'frequency'
ylabel 'amplitude'
title 'two tones plus noise'

How to fit a curve to a damped sine wave in matlab

I have some measurements done and It should be a damped sine wave but I can't find any information on how to make (if possible) a good damped sine wave with Matlab's curve fitting tool.
Here's what I get using a "Smoothing spline":
Image http://s21.postimg.org/yznumla1h/damped.png.
Edit 1:
Here's what I got using the "custom equation" option:
Edit 2:
I've uploaded the data to pastebin in csv format where the first column is the amplitude and the second is the time.
The damped sin function can be created using the following code:
f=f*2*pi;
t=0:.001:1;
y=A*sin(f*t + phi).*exp(-a*t);
plot(t,y);
axis([0 1 -2.2 2.2]);
Now you can use "cftool" from matlab and load your data then set the equation type to custom and enter the formula of the damped sin function. Here you can see what I found so far...
I think the distribution of the data makes it hard for the fitting tool to do a good fit. It may need more data or more distributed data. In addition, there are other tools and methods as well, for instance check this for now: docstoc.com/docs/74524947/Mathcad-Damped-sine-fit-mcd
For all these methods that actually trying to search and find a local optimum (for each fitting parameters), the most important thing is the initial condition. I believe if you choose a good initial condition (initial guess), the fitting tool works well.
Good Luck
I wouldn't use the curve fitting toolbox for this, I'd use a curve-fitting function, e.g. lsqcurvefit. Here is an example taken from something I did a while back:
% Define curve model functions
expsin = #(a, f, phi, tau, t)a * sin(omega * t + phi) .* exp(-tau * t);
lsqexpsin = #(p, t)expsin(p(1), p(2), p(3), p(4), t);
% Setup data params
a = 1; % gain
f = 10; % frequency
phi = pi/2; % phase angle
tau = 0.9252523;% time constant
fs = 100; % sample rate
N = fs; % length
SNR = 10; % signal to noise ratio
% Generate time vector
dt = 1/fs;
t = (0:N-1)*dt;
omega = 2 * pi * f; % angular freq
noiseGain = 10^(-SNR/20); % gain for given SNR
% Generate dummy data: decaying sinusoid plus noise
x = expsin(a, omega, phi, tau, t);
noise = noiseGain * rand(size(x));
noise = noise - mean(noise);
x = x + noise;
close all; figure; hold on;
plot(t, x, 'k-', 'LineWidth', 2);
% Count zero crossings to find frequency
zCross = find(x(1:end-1) .* x(2:end) < 0);
T = mean(diff(zCross) * dt) * 2;
fEstimate = 1 / T;
omegaEstimate = 2 * pi * fEstimate;
% Fit model to data
init = [0.5, omegaEstimate, 0, 0.5];
[newparams, err] = lsqcurvefit(lsqexpsin, init, t, x);
plot(t, lsqexpsin(newparams, t))
Here some data with known parameters is generated, and some random noise added; the data is plotted. The parameters [a, phi, tau] are estimated from the data and a curve with the estimated parameters plotted on top.