I'm creating a sweep / chirp signal using matlab / octave and my ending signal seems to be ending at the wrong frequency. How can I fix it so that the signal ends at the correct frequency.
PS: I can't use the chirp command in octave because I'm creating a chirp / sweep signal using a specific equation.
Example code with simple equation. and plot of problem
%test sweep / chirp
clear all,clc
freq1=20; %start freq
freq2=200; %end freq
fs=44100;
dur=1; %duration of signal in seconds
t = linspace(0,2*pi,fs*dur);
f=freq1:(freq2-freq1)/length(t):freq2-(freq2-freq1)/length(t);
%20:(200-20)/lenght(t) :200-(200-20)/length(t)
data=sin(f.*t); %build signal
data=(data/max(abs(data))*.8); %normalize signal
wavwrite([data'] ,fs,32,strcat('/tmp/del.wav')); %export file
plot(t,data)
PS: I'm using octave 3.8.1
The following code explains how to generate a frequency-variable sin wave.
freq1=20; %start freq
freq2=200; %end freq
dur=1; %duration of signal in seconds
freq=#(t)freq1+(freq2-freq1)/dur*t;
%another example, may help to understand the code
%dur=2
%freq=#(t)heaviside(t-1)*10+heaviside(t-1.5)*-9;
%Integerate over the time-local frequency, gives the average frequency until t which later on gives the sin with the right phase
%In case you don't have symbolic toolbox, integrate manually. For the given numbers Ifreq=#(x)x.*(x.*9.0+2.0)
Ifreq=matlabFunction(int(freq(sym('x'))));
%Defining wave function based on `Ifreq`
wave=#(t)(sin(Ifreq(t)*2*pi));
t=0:.00001:dur;
plot(t,wave(t));
Following Daniel's recipe, this is a version that uses numerical integration, and consequently doesn't require the symbolic toolbox:
freq1 = 20; % start frequency
freq2 = 200; % end frequency
fs = 44100;
dur = 1; % duration of signal in seconds
t = 0:1/fs:dur;
freqt = linspace(freq1,freq2,numel(t));
ifreqt = cumsum(freqt)/fs;
data = sin(2*pi*ifreqt);
plot(t,data);
Related
How can I change the pitch of an imported signal logarithmically / exponentially over time?
Please note that the imported signals that will be used are not single frequencies so a simple sweep or a chirp command will not work since I will be importing vocal audio files, I just created the examples below so they would work and could be tested / show the issues I'm having.
I can change the pitch of a signal over time linearly which works great see part 1 of test code and frequency plot below. Thanks to Sheljohn for the code
%Sweep question part 1
clear all,clf reset,tic,clc
pkg load signal %load packages
%%%----create signal
start_freq=500;
end_freq=20;
fs=22050
len_of_sig=7; %in seconds
t=linspace(0,2*pi*len_of_sig,fs*len_of_sig);
orig_sig1=.8*sin(start_freq*t);
wavwrite([orig_sig1(:)] ,fs,16,strcat('/tmp/0_sig.wav')); % export file
%%%---import signal
[ya, fs, nbitsraw] = wavread('/tmp/0_sig.wav');
orig_total_samples=length(ya); %make this the same length as signal wave
t_import=linspace(0,2*pi*(orig_total_samples/fs),orig_total_samples);
%%%%----Begin linsweep
x = ya(:);
fac=(end_freq-start_freq)/length(x); %linear slope
n = numel(x); % number of timepoints
m = mean(x); % average of the signal
k = transpose(0:n-1); %
h = hilbert( x - m ); % analytic signal
env1 = abs(h); % envelope
sweep=fac*pi*k.^2/(fs); %linearly increasing offset original %alter curve here
p = angle(h) + sweep; % phase + linearly increasing offset original
y = m - imag(hilbert( env1 .* sin(p) )); % inverse-transform
wavwrite([y(:)] ,fs,16,strcat('/tmp/0_sweep.wav')); % export file
%%%----------Used for plotting
z = hilbert(y);
instfreq = fs/(2*pi)*diff(unwrap(angle(z))); %orginal
t_new=t_import/(2*pi); %converts it to seconds
plot(t_new(2:end),instfreq,'-r')
xlabel('Time (secnds)')
ylabel('Frequency (Hz)')
grid on
title('Instantaneous Frequency')
Issues with the code I have below are:
1) The frequency doesn't start or end at the correct frequency.
2) It doesn't have the correct slopes
I believe it has to do with the variables fac and sweep I'm just not sure how to calculate them correctly.
fac=log(start_freq/end_freq)/length(x); %slope
sweep=-(start_freq)*exp(fac*k); %alter curve here
-
%-----------------Sweep question part 2
clear all,clf reset,tic,clc
pkg load signal %load packages
%%%----create signal
start_freq=500;
end_freq=20;
fs=22050
len_of_sig=7; %in seconds
t=linspace(0,2*pi*len_of_sig,fs*len_of_sig);
orig_sig1=.8*sin(start_freq*t);
wavwrite([orig_sig1(:)] ,fs,16,strcat('/tmp/0_sig.wav')); % export file
%%%---import signal
[ya, fs, nbitsraw] = wavread('/tmp/0_sig.wav');
orig_total_samples=length(ya); %make this the same length as signal wave
t_import=linspace(0,2*pi*(orig_total_samples/fs),orig_total_samples);
%%%%----Begin linsweep
x = ya(:);
fac=log(start_freq/end_freq)/length(x); %slope
n = numel(x); % number of timepoints
m = mean(x); % average of the signal
k = transpose(0:n-1); %
h = hilbert( x - m ); % analytic signal
env1 = abs(h); % envelope
sweep=-(start_freq)*exp(fac*k); %alter curve here
p = angle(h) + sweep; % phase + increasing offset
y = m - imag(hilbert( env1 .* sin(p) )); % inverse-transform
wavwrite([y(:)] ,fs,16,strcat('/tmp/0_sweep.wav')); % export file
%%%----------Used for plotting
z = hilbert(y);
instfreq = fs/(2*pi)*diff(unwrap(angle(z))); %orginal
t_new=t_import/(2*pi); %converts it to seconds
plot(t_new(2:end),instfreq,'-r')
xlabel('Time (seconds)')
ylabel('Frequency (Hz)')
grid on
title('Instantaneous Frequency')
The slopes I'm trying to get are when the start frequency starts at 500hz and goes to 20hz. And when the start frequency starts at 20hz and it goes to 500hz. See plots below: Note: These frequency will change so I'm trying to get the correct formula / equation that will calculate these slopes when needed.
Ps: I'm using Octave 4.0 which is similar to Matlab.
Please note that the imported signals that will be used are not single frequencies so a simple sweep or a chirp command will not work since I will be importing vocal audio files, I just created the examples below so they would work and could be tested / show the issues I'm having.
I can get the sweep to look like the plot you are interested in by making the following changes to your code. Some of them are just cosmetic for my sake (e.g. I like my time variables to remain in units of seconds throughout).
Relevant changes:
From:
t=linspace(0,2*pi*len_of_sig,fs*len_of_sig);
orig_sig1=.8*sin(start_freq*t);
fac=log(start_freq/end_freq)/length(x); %slope
To:
t=linspace(0,len_of_sig,fs*len_of_sig);
orig_sig1=0.8*sin(start_freq*t*2*pi);
fac=log(end_freq/start_freq)/length(x);
sweep=(start_freq*2*pi/fs)*exp(fac*k); %alter curve here
Here was some other changes I made,
y = env1.*sin(p);
% and later for consistency
t_import=linspace(0,orig_total_samples/fs,orig_total_samples);
t_new=t_import; %t is seconds
The fac, in my mind, is going to be the difference from your start and end, so it would be: log(endFreq)-log(startFreq) or log(endFreq/startFreq) with the additional normalization for the length. This can be flipped with a negative sign in front.
One issue with the sweep may be happening when you use it to calculate p=angle(h)+sweep; where angle(h) is in radians.
The radians vs Hz units issue may be causing some of the difficulty.
I am trying to implement an octave and 1/3-Octave-Band filters in Matlab. Unfortunately, I do not have access to the Acoustic toolbox but have tried to use the fdesign.octave in Matlab, but am not sure if I am going down the right path or not - and would appreciate any help/advice.
The following is my signal (Fs=10,000Hz) taken across 70 seconds:
Fs = 10000; % Sampling frequency
T = 1/Fs; % Sampling period
L = 700000; % Length of signal
t = (0:L-1)*T; % Time vector
S = 0.7*sin(2*pi*50*t) + sin(2*pi*120*t);
x = S + 2*randn(size(t));
Implementation of one-third octave:
d = fdesign.octave(3,'Class 1','N,F0',6,11000,44100);
Hd = design(d);
y= filter(Hd,x);
Figure of original and filtered signals:
At this point, I have a question - How would one plot the data produced by fdesign.octave in octave or one-third-octave bands?
For a complete implementation, use this from the Matlab file exchange. No acoustics toolbox required. Included demos should solve your plotting question.
Here in this code i am doing a stft on my wav-file. There is no problem with that. At the beginning, i am defining my parameter, afterwards using my wav file and then applying the stft. Basically what i am doing is a real-time spectral analysis. Anyway my question is, how do i a frequency band? I want my signal to be separated in LOW/MEDIUM/HIGH. I want my vector to be saved, from 0-250 Hz in the LOW-Band, 250-5000 Hz in the MEDIUM-Band, 5-22.05k Hz in the HIGH-Band. I advise you, to try my code in Matlab, if you don't understand it. Just take any wav-file. Btw my signal is plotted in the variable "Yres". Any solution is appreciated!
NFA=2; % Number is used for plotting every 2nd picture
t_seg=0.05; % Length of segment in ms
fftlen = 4096;
% Lenght of "fft",because our segment contains 2205 points
[y,fs]=audioread('UnchainMyHeart.wav');
% audioread = functions reads WAV-file
% y = A vector which contains my audio signal
% fs = sample frequency (44100)
% 'UnchainMyHeart' = WAV-file
t=linspace(0,length(y)/fs,length(y));
% linspace = Creating time vector
% 0 = Start time
% length(y)/fs = End time
% length(y) = Number of samples in y
plot(t,y)
% plotting signal in the time domain
segl =floor(t_seg*fs);
% Applying fft function on the variable "segl"
windowshift=segl/2;
% Defining the size of the window, which goes to the next "segl"
window=hann(segl);
% hann function
window=window.';
si=1;
%Start index
ei=segl;
%End index
AOS= length(y)/windowshift - 1;
% AOS is the number of "segl" we use (About 433)
f1=figure;
% Opening new window
f=0:1:fftlen-1;
f=f/(fftlen-1)*fs;
% Defining frequency vector
Ya=zeros(1,fftlen);
plot(f,Ya),axis([0 fs -90 50])
grid on
n=0;
%start variable
for m= 1:1:AOS
y_a = y(si:ei);
y_a= y_a.*window;
Ya=fft(y_a, fftlen);
n=n+1;
if n==1
Yres=abs(Ya);
else
Yres=Yres+abs(Ya);
end
if n==NFA
Yres=Yres/NFA;
n=0;
drawnow;
%Tut die Grafikobjekte immer auf den neuesten Stand updaten
figure(f1);
plot(f(1:end/2), 20*log10(abs(Yres(1:end/2))));
ylim([-90 50]);
title('Spektrum eines Audiosignal');
xlabel('f(Hz)');
ylabel('dB');
grid on;
end
si=si+windowshift;
% Updating start index
ei=ei+windowshift;
% Updating end index
end
This may not be the best answer! But this may help you get started on something. You can use spectrogram() function from MATLAB's Signal Processing Toolbox.
Let's suppose you have an audio file named ''UnchainMyHeart.wav'(in your case) with one channel. The code goes as follows:
% Reading the audio file
[y1,fs] = audioread('UnchainMyHeart.wav');
% Parameters for STFT (or spectrogram)
windowDuration = 30e-3; overlapDuration = 15e-3;
windowLength = round(windowDuration*fs); % window length
overlapLength = round(overlapDuration*fs); % overlapping of windows
nfft = 1024;
% Executing STFT for the signal
[S1,F1,T1,P1] = spectrogram(x1,hanning(windowLength), ...
overlapLength, nfft, fs, 'yaxis');
S1 and P1 contain STFT and Power Spectrum Density(PSD) of the signal for a time interval of each section with a time interval whose estimations are contained in T1.
For your question, you are looking for F1 which is a vector of cyclical frequencies expressed in terms of sampling frequency, fs. For example: if you have a sampling frequency of 48 kHz (fs) and nfft of 1024, then you will have 513 [(1024/2) +1)] frequency values spaced by (fs/nfft). i.e. 46.875. So your frequency components will be 0, 46.875, 46.875*2, ..., 46.875*512. The maximum you will have is 24 kHz due to Nyquist criterion.
Now, you can easily write a simple routine specifying the ranges as you said. The same technique can be used in your code which is an implementation of stft. I would suggest using MATLAB's built-in function unless your problem requires an implementation. Hope this helps!
If needed, I can answer why the parameters for STFT are chosen as included in the code.
I'm creating a sweep / chirp signal using matlab / octave and my ending signal seems to be ending at the wrong frequency. How can I fix it so that the signal ends at the correct frequency.
PS: I can't use the chirp command in octave because I'm creating a chirp / sweep signal using a specific equation.
Example code with simple equation. and plot of problem
%test sweep / chirp
clear all,clc
freq1=20; %start freq
freq2=200; %end freq
fs=44100;
dur=1; %duration of signal in seconds
t = linspace(0,2*pi,fs*dur);
f=freq1:(freq2-freq1)/length(t):freq2-(freq2-freq1)/length(t);
%20:(200-20)/lenght(t) :200-(200-20)/length(t)
data=sin(f.*t); %build signal
data=(data/max(abs(data))*.8); %normalize signal
wavwrite([data'] ,fs,32,strcat('/tmp/del.wav')); %export file
plot(t,data)
PS: I'm using octave 3.8.1
The following code explains how to generate a frequency-variable sin wave.
freq1=20; %start freq
freq2=200; %end freq
dur=1; %duration of signal in seconds
freq=#(t)freq1+(freq2-freq1)/dur*t;
%another example, may help to understand the code
%dur=2
%freq=#(t)heaviside(t-1)*10+heaviside(t-1.5)*-9;
%Integerate over the time-local frequency, gives the average frequency until t which later on gives the sin with the right phase
%In case you don't have symbolic toolbox, integrate manually. For the given numbers Ifreq=#(x)x.*(x.*9.0+2.0)
Ifreq=matlabFunction(int(freq(sym('x'))));
%Defining wave function based on `Ifreq`
wave=#(t)(sin(Ifreq(t)*2*pi));
t=0:.00001:dur;
plot(t,wave(t));
Following Daniel's recipe, this is a version that uses numerical integration, and consequently doesn't require the symbolic toolbox:
freq1 = 20; % start frequency
freq2 = 200; % end frequency
fs = 44100;
dur = 1; % duration of signal in seconds
t = 0:1/fs:dur;
freqt = linspace(freq1,freq2,numel(t));
ifreqt = cumsum(freqt)/fs;
data = sin(2*pi*ifreqt);
plot(t,data);
I have a question while computing the spectrum of a time series in Matlab. I have read the documentations concerning 'fft' function. However I have seen two ways of implementation and both wgive me different results. I would appreciate to have some answer about this difference:
1st Method:
nPoints=length(timeSeries);
Time specifications:
Fs = 1; % samples per second
Fs = 50;
freq = 0:nPoints-1; %Numerators of frequency series
freq = freq.*Fs./nPoints;
% Fourier Transform:
X = fft(timeSeries)/nPoints; % normalize the data
% find find nuquist frequency
cutOff = ceil(nPoints./2);
% take only the first half of the spectrum
X = abs(X(1:cutOff));
% Frequency specifications:
freq = freq(1:cutOff);
%Plot spectrum
semilogy(handles.plotLoadSeries,freq,X);
2nd Method:
NFFT = 2^nextpow2(nPoints); % Next power of 2 from length of y
Y = fft(timeSeries,NFFT)/nPoints;
f = 1/2*linspace(0,1,NFFT/2+1);
% % Plot single-sided amplitude spectrum.
% plot(handles.plotLoadSeries, f,2*abs(Y(1:NFFT/2+1)))
semilogy(handles.plotLoadSeries,f,2*abs(Y(1:NFFT/2+1)));
I thought that it is not necessary to use 'nextpow' function in 'fft' function in Matlab. Finally, which is the good one?
THanks
The short answer: you need windowing for spectrum analysis.
Now for the long answer... In the second approach, you are using an optimised FFT algorithm useful when the length of the input vector is a power of two. Let's assume that your original signal has 401 samples (as in my example below) from an infinitely long signal; nextpow2() will give you NFFT=512 samples. When you feed the shorter, 401-sample signal into the fft() function, it is implicitly zero-padded to match the requested length of 512 (NFFT). But (here comes the tricky part): zero-padding your signal is equivalent to multiplying an infinitely long signal by a rectangular function, an operation that in the frequency domain translates to a convolution with a sinc function. This would be the reason behind the increased noise floor at the bottom of your semilogarithmic plot.
A way to avoid this noise increase is to create manually the 512-sample signal you want to feed into fft(), using a smoother window function instead of the default rectangular one. Windowing means just multiplying your signal by a tapered, symmetric one. There are tons of literature on choosing a good windowing function, but a typically accurate one with low sidelobes (low noise increase) is the Hamming function, implemented in MATLAB as hamming().
Here is a figure illustrating the issue (in the frequency domain and time domain):
...and the code to generate this figure:
clear
% Create signal
fs = 40; % sampling freq.
Ts = 1/fs; % sampling period
t = 0:Ts:10; % time vector
s = sin(2*pi*3*t); % original signal
N = length(s);
% FFT (length not power of 2)
S = abs(fft(s)/N);
freq = fs*(0:N-1)/N;
% FFT (length power of 2)
N2 = 2^nextpow2(N);
S2 = abs(fft(s, N2)/N2);
freq2 = fs*(0:N2-1)/N2;
t2 = (0:N2-1)*Ts; % longer time vector
s2 = [s,zeros(1,N2-N)]; % signal that was implicitly created for this FFT
% FFT (windowing before FFT)
s3 = [s.*hamming(N).',zeros(1,N2-N)];
S3 = abs(fft(s3, N2)/N2);
% Frequency-domain plot
figure(1)
subplot(211)
cla
semilogy(freq,S);
hold on
semilogy(freq2,S2,'r');
semilogy(freq2,S3,'g');
xlabel('Frequency [Hz]')
ylabel('FFT')
grid on
legend( 'FFT[401]', 'FFT[512]', 'FFT[512] with windowing' )
% Time-domain plot
subplot(212)
cla
plot(s)
hold on
plot(s3,'g')
xlabel('Index')
ylabel('Amplitude')
grid on
legend( 'Original samples', 'Windowed samples' )