I want to extract local maximum from FFT frame after processing signal with spectrogram.
Specifically, said that I have a audio file which is a wav file and it's collected in the following procedure:
phone user hold the smartphone at approximately arm's length, breathe in their full lung volume, and forcefully exhale until the entire lung volume is expelled. The phone's microphone records the exhalation and store data in a wav file.
Then i process the audio with spectrogram using following procedure:
Firstly, I buffer the audio into 30ms frames (with 50% overlap between frames). Then each frame is windowed using a hamming window and the |FFT|db is taken to produce the magnitude spectrogram of the signal.
Here is how i do that using matlab:
[X, FS] = audioread('Rec_002.wav');
info = audioinfo('Rec_002.wav');
window = (30*0.001/info.Duration)*info.TotalSamples;
[s,f,t,ps]=spectrogram(X, window, [], [], FS);
Rec_002.wav is my audio file. After processing signal with spectrogram, i want to extract the resonances using local maximum in each frame, calculated over a sliding window. Then, any local maximum that is greater than 20% of the global maximum is saved.
Below shows how spectrogram looks like when using the data above:
Spectrogram image
This shows what resonances should looks like after extracting from spectrogram
After extracting resonaces from spectrogram image
By now, I use one of output argument s - Short-time Fourier transform to extract local maximum.
Here's my code:
local_max=max(abs(s));
threshold=0.2*max(local_max,[],2);
local_max=local(:,local_max>threashold);
The questions is that I am not sure whether it's correct to use s or should i use ps - Power spectral density (PSD) to achieve my goal.
Can anyone point out to me? Any help would be appreciated.
Spectrogram's output, s, is the amplitude of the frequency component in your signal between [t1, t2].
Power Spectral density, can be used to calculate the amount of power within a frequency range for the given time duration (STFT) through summation (integration if CT domain). This is somewhat intuitive from the name, "density".
So if you apply the threshold logic to ps instead of s, you will end up with highest gradient (local) instead of maximum (local).
Related
I've only used MATLAB as a calculator, so I'm not as well versed in the program. I hope a kind person may be able to guide me on the way since Google currently is not my friend.
I have a wav file in the link below, where there is a human voice and some noise in the background. I want the noise removed. Is there anyone who can tell me how to do it in MATLAB?
https://www.dropbox.com/s/3vtd5ehjt2zfuj7/Hold.wav
This is a pretty imperfect solution, especially since some of the noise is embedded in the same frequency range as the voice you hear on the file, but here goes nothing. What I was talking about with regards to the frequency spectrum is that if you hear the sound, the background noise has a very low hum. This resides in the low frequency range of the spectrum, whereas the voice has a more higher frequency. As such, we can apply a bandpass filter to get rid of the low noise, capture most of the voice, and any noisy frequencies on the higher side will get cancelled as well.
Here are the steps that I did:
Read in the audio file using audioread.
Play the original sound so I can hear what it sounds like using. Do this by creating an audioplayer object.
Plotted both the left and right channels to take a look at the sound signal in time domain... if it gives any clues. Looking at the channels, they both seem to be the same, so it looks like it was just a single microphone being mapped to both channels.
I took the Fourier Transform and saw the frequency distribution.
Using (4) I figured out the rough approximation of where I should cut off the frequencies.
Designed a bandpass filter that cuts off these frequencies.
Filtered the signal then played it by constructing another audioplayer object.
Let's go then!
Step #1
%% Read in the file
clearvars;
close all;
[f,fs] = audioread('Hold.wav');
audioread will read in an audio file for you. Just specify what file you want within the ''. Also, make sure you set your working directory to be where this file is being stored. clearvars, close all just do clean up for us. It closes all of our windows (if any are open), and clears all of our variables in the MATLAB workspace. f would be the signal read into MATLAB while fs is the sampling frequency of your signal. f here is a 2D matrix. The first column is the left channel while the second is the right channel. In general, the total number of channels in your audio file is denoted by the total number of columns in this matrix read in through audioread.
Step #2
%% Play original file
pOrig = audioplayer(f,fs);
pOrig.play;
This step will allow you to create an audioplayer object that takes the signal you read in (f), with the sampling frequency fs and outputs an object stored in pOrig. You then use pOrig.play to play the file in MATLAB so you can hear it.
Step #3
%% Plot both audio channels
N = size(f,1); % Determine total number of samples in audio file
figure;
subplot(2,1,1);
stem(1:N, f(:,1));
title('Left Channel');
subplot(2,1,2);
stem(1:N, f(:,2));
title('Right Channel');
stem is a way to plot discrete points in MATLAB. Each point in time has a circle drawn at the point with a vertical line drawn from the horizontal axis to that point in time. subplot is a way to place multiple figures in the same window. I won't get into it here, but you can read about how subplot works in detail by referencing this StackOverflow post I wrote here. The above code produces the plot shown below:
The above code is quite straight forward. I'm just plotting each channel individually in each subplot.
Step #4
%% Plot the spectrum
df = fs / N;
w = (-(N/2):(N/2)-1)*df;
y = fft(f(:,1), N) / N; % For normalizing, but not needed for our analysis
y2 = fftshift(y);
figure;
plot(w,abs(y2));
The code that will look the most frightening is the code above. If you recall from signals and systems, the maximum frequency that is represented in our signal is the sampling frequency divided by 2. This is called the Nyquist frequency. The sampling frequency of your audio file is 48000 Hz, which means that the maximum frequency represented in your audio file is 24000 Hz. fft stands for Fast Fourier Transform. Think of it as a very efficient way of computing the Fourier Transform. The traditional formula requires that you perform multiple summations for each element in your output. The FFT will compute this efficiently by requiring far less operations and still give you the same result.
We are using fft to take a look at the frequency spectrum of our signal. You call fft by specifying the input signal you want as the first parameter, followed by how many points you want to evaluate at with the second parameter. It is customary that you specify the number of points in your FFT to be the length of the signal. I do this by checking to see how many rows we have in our sound matrix. When you plot the frequency spectrum, I just took one channel to make things simple as the other channel is the same. This serves as the first input into fft. Also, bear in mind that I divided by N as it is the proper way of normalizing the signal. However, because we just want to take a snapshot of what the frequency domain looks like, you don't really need to do this. However, if you're planning on using it to compute something later, then you definitely need to.
I wrote some additional code as the spectrum by default is uncentered. I used fftshift so that the centre maps to 0 Hz, while the left spans from 0 to -24000Hz while the right spans from 0 to 24000 Hz. This is intuitively how I see the frequency spectrum. You can think of negative frequencies as frequencies that propagate in the opposite direction. Ideally, the frequency distribution for a negative frequency should equal the positive frequency. When you plot the frequency spectrum, it tells you how much contribution that frequency has to the output. That is defined by the magnitude of the signal. You find this by taking the abs function. The output that you get is shown below.
If you look at the plot, there are a lot of spikes around the low frequency range. This corresponds to your humming whereas the voice probably maps to the higher frequency range and there isn't that much of it as there isn't that much of a voice heard.
Step #5
By trial and error and looking at Step #5, I figured everything from 700 Hz and down corresponds to the humming noise while the higher noise contributions go from 12000 Hz and higher.
Step #6
You can use the butter function from the Signal Processing Toolbox to help you design a bandpass filter. However, if you don't have this toolbox, refer to this StackOverflow post on how user-made function that achieves the same thing. However, the order for that filter is only 2. Assuming you have the butter function available, you need to figure out what order you want your filter. The higher the order, the more work it'll do. I choose n = 7 to start off. You also need to normalize your frequencies so that the Nyquist frequency maps to 1, while everything else maps between 0 and 1. Once you do that, you can call butter like so:
[b,a] = butter(n, [beginFreq, endFreq], 'bandpass');
The bandpass flag means you want to design a bandpass filter, beginFreq and endFreq map to the normalized beginning and ending frequency you want to for the bandpass filter. In our case, that's beginFreq = 700 / Nyquist and endFreq = 12000 / Nyquist. b,a are the coefficients used for a filter that will help you perform this task. You'll need these for the next step.
%% Design a bandpass filter that filters out between 700 to 12000 Hz
n = 7;
beginFreq = 700 / (fs/2);
endFreq = 12000 / (fs/2);
[b,a] = butter(n, [beginFreq, endFreq], 'bandpass');
Step #7
%% Filter the signal
fOut = filter(b, a, f);
%% Construct audioplayer object and play
p = audioplayer(fOut, fs);
p.play;
You use filter to filter your signal using what you got from Step #6. fOut will be your filtered signal. If you want to hear it played, you can construct and audioplayer based on this output signal at the same sampling frequency as the input. You then use p.play to hear it in MATLAB.
Give this all a try and see how it all works. You'll probably need to play around the most in Step #6 and #7. This isn't a perfect solution, but enough to get you started I hope.
Good luck!
I am trying to plot the spectrogram of my time domain signal given:
N=5000;
phi = (rand(1,N)-0.5)*pi;
a = tan((0.5.*phi));
i = 2.*a./(1-a.^2);
plot(i);
spectrogram(i,100,1,100,1e3);
The problem is I don't understand the parameters and what values should be given. These values that I am using, I referred to MATLAB's online documentation of spectrogram. I am new to MATLAB, and I am just not getting the idea. Any help will be greatly appreciated!
Before we actually go into what that MATLAB command does, you probably want to know what a spectrogram is. That way you'll get more meaning into how each parameter works.
A spectrogram is a visual representation of the Short-Time Fourier Transform. Think of this as taking chunks of an input signal and applying a local Fourier Transform on each chunk. Each chunk has a specified width and you apply a Fourier Transform to this chunk. You should take note that each chunk has an associated frequency distribution. For each chunk that is centred at a specific time point in your time signal, you get a bunch of frequency components. The collection of all of these frequency components at each chunk and plotted all together is what is essentially a spectrogram.
The spectrogram is a 2D visual heat map where the horizontal axis represents the time of the signal and the vertical axis represents the frequency axis. What is visualized is an image where darker colours means that for a particular time point and a particular frequency, the lower in magnitude the frequency component is, the darker the colour. Similarly, the higher in magnitude the frequency component is, the lighter the colour.
Here's one perfect example of a spectrogram:
Source: Wikipedia
Therefore, for each time point, we see a distribution of frequency components. Think of each column as the frequency decomposition of a chunk centred at this time point. For each column, we see a varying spectrum of colours. The darker the colour is, the lower the magnitude component at that frequency is and vice-versa.
So!... now you're armed with that, let's go into how MATLAB works in terms of the function and its parameters. The way you are calling spectrogram conforms to this version of the function:
spectrogram(x,window,noverlap,nfft,fs)
Let's go through each parameter one by one so you can get a greater understanding of what each does:
x - This is the input time-domain signal you wish to find the spectrogram of. It can't get much simpler than that. In your case, the signal you want to find the spectrogram of is defined in the following code:
N=5000;
phi = (rand(1,N)-0.5)*pi;
a = tan((0.5.*phi));
i = 2.*a./(1-a.^2);
Here, i is the signal you want to find the spectrogram of.
window - If you recall, we decompose the image into chunks, and each chunk has a specified width. window defines the width of each chunk in terms of samples. As this is a discrete-time signal, you know that this signal was sampled with a particular sampling frequency and sampling period. You can determine how large the window is in terms of samples by:
window_samples = window_time/Ts
Ts is the sampling time of your signal. Setting the window size is actually very empirical and requires a lot of experimentation. Basically, the larger the window size, the better frequency resolution you get as you're capturing more of the frequencies, but the time localization is poor. Similarly, the smaller the window size, the better localization you have in time, but you don't get that great of a frequency decomposition. I don't have any suggestions here on what the most optimal size is... which is why wavelets are preferred when it comes to time-frequency decomposition. For each "chunk", the chunks get decomposed into smaller chunks of a dynamic width so you get a mixture of good time and frequency localization.
noverlap - Another way to ensure good frequency localization is that the chunks are overlapping. A proper spectrogram ensures that each chunk has a certain number of samples that are overlapping for each chunk and noverlap defines how many samples are overlapped in each window. The default is 50% of the width of each chunk.
nfft - You are essentially taking the FFT of each chunk. nfft tells you how many FFT points are desired to be computed per chunk. The default number of points is the largest of either 256, or floor(log2(N)) where N is the length of the signal. nfft also gives a measure of how fine-grained the frequency resolution will be. A higher number of FFT points would give higher frequency resolution and thus showing fine-grained details along the frequency axis of the spectrogram if visualised.
fs - The sampling frequency of your signal. The default is 1 Hz, but you can override this to whatever the sampling frequency your signal is at.
Therefore, what you should probably take out of this is that I can't really tell you how to set the parameters. It all depends on what signal you have, but hopefully the above explanation will give you a better idea of how to set the parameters.
Good luck!
I've only used MATLAB as a calculator, so I'm not as well versed in the program. I hope a kind person may be able to guide me on the way since Google currently is not my friend.
I have a wav file in the link below, where there is a human voice and some noise in the background. I want the noise removed. Is there anyone who can tell me how to do it in MATLAB?
https://www.dropbox.com/s/3vtd5ehjt2zfuj7/Hold.wav
This is a pretty imperfect solution, especially since some of the noise is embedded in the same frequency range as the voice you hear on the file, but here goes nothing. What I was talking about with regards to the frequency spectrum is that if you hear the sound, the background noise has a very low hum. This resides in the low frequency range of the spectrum, whereas the voice has a more higher frequency. As such, we can apply a bandpass filter to get rid of the low noise, capture most of the voice, and any noisy frequencies on the higher side will get cancelled as well.
Here are the steps that I did:
Read in the audio file using audioread.
Play the original sound so I can hear what it sounds like using. Do this by creating an audioplayer object.
Plotted both the left and right channels to take a look at the sound signal in time domain... if it gives any clues. Looking at the channels, they both seem to be the same, so it looks like it was just a single microphone being mapped to both channels.
I took the Fourier Transform and saw the frequency distribution.
Using (4) I figured out the rough approximation of where I should cut off the frequencies.
Designed a bandpass filter that cuts off these frequencies.
Filtered the signal then played it by constructing another audioplayer object.
Let's go then!
Step #1
%% Read in the file
clearvars;
close all;
[f,fs] = audioread('Hold.wav');
audioread will read in an audio file for you. Just specify what file you want within the ''. Also, make sure you set your working directory to be where this file is being stored. clearvars, close all just do clean up for us. It closes all of our windows (if any are open), and clears all of our variables in the MATLAB workspace. f would be the signal read into MATLAB while fs is the sampling frequency of your signal. f here is a 2D matrix. The first column is the left channel while the second is the right channel. In general, the total number of channels in your audio file is denoted by the total number of columns in this matrix read in through audioread.
Step #2
%% Play original file
pOrig = audioplayer(f,fs);
pOrig.play;
This step will allow you to create an audioplayer object that takes the signal you read in (f), with the sampling frequency fs and outputs an object stored in pOrig. You then use pOrig.play to play the file in MATLAB so you can hear it.
Step #3
%% Plot both audio channels
N = size(f,1); % Determine total number of samples in audio file
figure;
subplot(2,1,1);
stem(1:N, f(:,1));
title('Left Channel');
subplot(2,1,2);
stem(1:N, f(:,2));
title('Right Channel');
stem is a way to plot discrete points in MATLAB. Each point in time has a circle drawn at the point with a vertical line drawn from the horizontal axis to that point in time. subplot is a way to place multiple figures in the same window. I won't get into it here, but you can read about how subplot works in detail by referencing this StackOverflow post I wrote here. The above code produces the plot shown below:
The above code is quite straight forward. I'm just plotting each channel individually in each subplot.
Step #4
%% Plot the spectrum
df = fs / N;
w = (-(N/2):(N/2)-1)*df;
y = fft(f(:,1), N) / N; % For normalizing, but not needed for our analysis
y2 = fftshift(y);
figure;
plot(w,abs(y2));
The code that will look the most frightening is the code above. If you recall from signals and systems, the maximum frequency that is represented in our signal is the sampling frequency divided by 2. This is called the Nyquist frequency. The sampling frequency of your audio file is 48000 Hz, which means that the maximum frequency represented in your audio file is 24000 Hz. fft stands for Fast Fourier Transform. Think of it as a very efficient way of computing the Fourier Transform. The traditional formula requires that you perform multiple summations for each element in your output. The FFT will compute this efficiently by requiring far less operations and still give you the same result.
We are using fft to take a look at the frequency spectrum of our signal. You call fft by specifying the input signal you want as the first parameter, followed by how many points you want to evaluate at with the second parameter. It is customary that you specify the number of points in your FFT to be the length of the signal. I do this by checking to see how many rows we have in our sound matrix. When you plot the frequency spectrum, I just took one channel to make things simple as the other channel is the same. This serves as the first input into fft. Also, bear in mind that I divided by N as it is the proper way of normalizing the signal. However, because we just want to take a snapshot of what the frequency domain looks like, you don't really need to do this. However, if you're planning on using it to compute something later, then you definitely need to.
I wrote some additional code as the spectrum by default is uncentered. I used fftshift so that the centre maps to 0 Hz, while the left spans from 0 to -24000Hz while the right spans from 0 to 24000 Hz. This is intuitively how I see the frequency spectrum. You can think of negative frequencies as frequencies that propagate in the opposite direction. Ideally, the frequency distribution for a negative frequency should equal the positive frequency. When you plot the frequency spectrum, it tells you how much contribution that frequency has to the output. That is defined by the magnitude of the signal. You find this by taking the abs function. The output that you get is shown below.
If you look at the plot, there are a lot of spikes around the low frequency range. This corresponds to your humming whereas the voice probably maps to the higher frequency range and there isn't that much of it as there isn't that much of a voice heard.
Step #5
By trial and error and looking at Step #5, I figured everything from 700 Hz and down corresponds to the humming noise while the higher noise contributions go from 12000 Hz and higher.
Step #6
You can use the butter function from the Signal Processing Toolbox to help you design a bandpass filter. However, if you don't have this toolbox, refer to this StackOverflow post on how user-made function that achieves the same thing. However, the order for that filter is only 2. Assuming you have the butter function available, you need to figure out what order you want your filter. The higher the order, the more work it'll do. I choose n = 7 to start off. You also need to normalize your frequencies so that the Nyquist frequency maps to 1, while everything else maps between 0 and 1. Once you do that, you can call butter like so:
[b,a] = butter(n, [beginFreq, endFreq], 'bandpass');
The bandpass flag means you want to design a bandpass filter, beginFreq and endFreq map to the normalized beginning and ending frequency you want to for the bandpass filter. In our case, that's beginFreq = 700 / Nyquist and endFreq = 12000 / Nyquist. b,a are the coefficients used for a filter that will help you perform this task. You'll need these for the next step.
%% Design a bandpass filter that filters out between 700 to 12000 Hz
n = 7;
beginFreq = 700 / (fs/2);
endFreq = 12000 / (fs/2);
[b,a] = butter(n, [beginFreq, endFreq], 'bandpass');
Step #7
%% Filter the signal
fOut = filter(b, a, f);
%% Construct audioplayer object and play
p = audioplayer(fOut, fs);
p.play;
You use filter to filter your signal using what you got from Step #6. fOut will be your filtered signal. If you want to hear it played, you can construct and audioplayer based on this output signal at the same sampling frequency as the input. You then use p.play to hear it in MATLAB.
Give this all a try and see how it all works. You'll probably need to play around the most in Step #6 and #7. This isn't a perfect solution, but enough to get you started I hope.
Good luck!
I am reading a .wav file in Matlab. Then I play the read file with a specified sampling frequency 44100Hz. But when I try to play a file sampled at low sampling frequency, it gets played as if I am playing it in fast forward mod and thats because the sampling frequency at which I am playing is higher than at which the file is sampled.
So my question is How can I find the sampling frequency of a file I read using wavread() in Matlab. I tried to convert the read signal in frequency spectrum and then pass the magnitude of the fft() signal but it didn't work.
Any suggestions?
Observe that wavread can return sampling frequency Fs as follows:
[y, Fs] = wavread(filename)
First off you can find the sample frequency by using this function:
def read_samplepoints(file_name):
sampFreq, snd1 = wavfile.read(file_name)
samp_points = len(snd1)
data_type = snd1.dtype
return samp_points, data_type, sampFreq
Execute in terminal by using 'folder_name'.'class_name'.read_samplepoints(file_name). The last number in the returned sequence will be the sample Frequency.
To enhance the bass of your song you need to use a low band filter to only capture your lower frequencies and keep your higher ones. However, this will chance all the frequencies in your file, which you may not want. Another way is to take your file into audacity (or a similar program) and go to the effects section and adjust the bass and treble levels (similar to the Equalizer on iTunes). Those are two options and there may be a few more but try those to begin with and see where they lead you.
I am pretty new to Matlab and I am trying to write a simple frequency based speech detection algorithm. The end goal is to run the script on a wav file, and have it output start/end times for each speech segment. If use the code:
fr = 128;
[ audio, fs, nbits ] = wavread(audioPath);
spectrogram(audio,fr,120,fr,fs,'yaxis')
I get a useful frequency intensity vs. time graph like this:
By looking at it, it is very easy to see when speech occurs. I could write an algorithm to automate the detection process by looking at each x-axis frame, figuring out which frequencies are dominant (have the highest intensity), testing the dominant frequencies to see if enough of them are above a certain intensity threshold (the difference between yellow and red on the graph), and then labeling that frame as either speech or non-speech. Once the frames are labeled, it would be simple to get start/end times for each speech segment.
My problem is that I don't know how to access that data. I can use the code:
[S,F,T,P] = spectrogram(audio,fr,120,fr,fs);
to get all the features of the spectrogram, but the results of that code don't make any sense to me. The bounds of the S,F,T,P arrays and matrices don't correlate to anything I see on the graph. I've looked through the help files and the API, but I get confused when they start throwing around algorithm names and acronyms - my DSP background is pretty limited.
How could I get an array of the frequency intensity values for each frame of this spectrogram analysis? I can figure the rest out from there, I just need to know how to get the appropriate data.
What you are trying to do is called speech activity detection. There are many approaches to this, the simplest might be a simple band pass filter, that passes frequencies where speech is strongest, this is between 1kHz and 8kHz. You could then compare total signal energy with bandpass limited and if majority of energy is in the speech band, classify frame as speech. That's one option, but there are others too.
To get frequencies at peaks you could use FFT to get spectrum and then use peakdetect.m. But this is a very naïve approach, as you will get a lot of peaks, belonging to harmonic frequencies of a base sine.
Theoretically you should use some sort of cepstrum (also known as spectrum of spectrum), which reduces harmonics' periodicity in spectrum to base frequency and then use that with peakdetect. Or, you could use existing tools, that do that, such as praat.
Be aware, that speech analysis is usually done on a frames of around 30ms, stepping in 10ms. You could further filter out false detection by ensuring formant is detected in N sequential frames.
Why don't you use fft with `fftshift:
%% Time specifications:
Fs = 100; % samples per second
dt = 1/Fs; % seconds per sample
StopTime = 1; % seconds
t = (0:dt:StopTime-dt)';
N = size(t,1);
%% Sine wave:
Fc = 12; % hertz
x = cos(2*pi*Fc*t);
%% Fourier Transform:
X = fftshift(fft(x));
%% Frequency specifications:
dF = Fs/N; % hertz
f = -Fs/2:dF:Fs/2-dF; % hertz
%% Plot the spectrum:
figure;
plot(f,abs(X)/N);
xlabel('Frequency (in hertz)');
title('Magnitude Response');
Why do you want to use complex stuff?
a nice and full solution may found in https://dsp.stackexchange.com/questions/1522/simplest-way-of-detecting-where-audio-envelopes-start-and-stop
Have a look at the STFT (short-time fourier transform) or (even better) the DWT (discrete wavelet transform) which both will estimate the frequency content in blocks (windows) of data, which is what you need if you want to detect sudden changes in amplitude of certain ("speech") frequencies.
Don't use a FFT since it calculates the relative frequency content over the entire duration of the signal, making it impossible to determine when a certain frequency occured in the signal.
If you still use inbuilt STFT function, then to plot the maximum you can use following command
plot(T,(floor(abs(max(S,[],1)))))