Plotting spectrograms for multiple files

I have multiple acoustic files for which I would like to plot a spectrogram on a single plot.
I know how to plot one spectrogram:
[xbit,fs]=audioread(fullfname, [nlo nup]); %read in wav
xbit=detrend(xbit); %remove DC offset
calxbit=xbit*cal; %apply calibration
%Perform FFT and plot:
[S,F,T,P] = spectrogram(calxbit,window,overlap,nfft,fs,'yaxis');
figure(2);
h = pcolor(T,F,10*log10(P));
set(h,'EdgeColor','none');
This provides one figure with time on the x axis and frequency on the y-axis, and colourplot for intensity.
However, I would like to plot multiple spectrograms and so the x-axis represents multiple files which were collected over time e.g. every 5 minutes.
I considered that I could read in the acoustic data for multiple files and concatenate it, but this would not preserve any gaps in time between the subsequent files (which may or may not be negligible). I'd like to have the x tick marks represent the start time of each separate file perhaps.

Answers (1)

Wouldn't you achieve the desired output by putting the different spectrograms in consecutive subplots?
Something like this perhaps:
nFiles = numel(fullfnames);
for iFile = 1:nFiles
fullfname = fullfnames{iFile};
t0 = Tstart(iFile);
[xbit,fs]=audioread(fullfname, [nlo nup]); %read in wav
xbit=detrend(xbit); %remove DC offset
calxbit=xbit*cal; %apply calibration
%Perform FFT and plot:
[S,F,T,P] = spectrogram(calxbit,window,overlap,nfft,fs,'yaxis');
sph(iFile) = subplot(1,nFiles,iFile);
h = pcolor(T,F,10*log10(P)); % Here you might want to add the start-time, t0, to T
% with proper conversion of time-format...
set(h,'EdgeColor','none');
end
Then you can adjust the spacing of the sub-plot-panels using sph (also consider using tile-nexttile)
HTH

8 Comments

Thanks! In my plot design I'm already using sub plots to display different data that was collected at the same time, so it's preferrable to have one plot with several spectrograms plotted within.
Then you have something like 2 choises, as far as I can think up right now.
1, just add spectrograms as you process them with gaps between them with linear time from first to last.
2, add the spectrograms with a small gap between consecutive time-periods to mark gaps, and then put the
Xticks at the beginning of each spectrogram and set the XtickLabel to match the proper start-time. Perhaps something in line with this:
nFiles = numel(fullfnames);
t0 = 0;
dt = 1; % Just a random gap to put between spectrograms
for iFile = 1:nFiles
fullfname = fullfnames{iFile};
tStartstrings{iFile} = % you'll have to set/get/extract start-time for each spectrogram
[xbit,fs]=audioread(fullfname, [nlo nup]); %read in wav
xbit=detrend(xbit); %remove DC offset
calxbit=xbit*cal; %apply calibration
%Perform FFT and plot:
[S,F,T,P] = spectrogram(calxbit,window,overlap,nfft,fs,'yaxis');
h = pcolor(T,F,10*log10(P)); % Here you might want to add the start-time, t0, to T
% with proper conversion of time-format...
set(h,'EdgeColor','none');
t0(iFile+1) = t0 + T(end)+dt;
end
set(gca,'Xtick',t0,'XtickLabel',tStartstrings)
But now this feels more like a design-discussion and you have requirements and preferences I dont know about...
HTH
Hi Bjorn, what you describe is exactly what I want to do. I was trying to explain this in my question but I guess I wasn't clear enough, sorry for the confusion. I'll try your code now and see if it can get me somewhere.
Hi Bjorn, I tried this, but I can't see how it allows me to plot two spectrograms with a space between as you describe. The loop just replaces one spectrogram over an other, rather than plotting with a space between?
I attached an image of what I am trying to achieve. The spectrogram looks really 'blocky' because the files were 2 minutes long, captured every 10 minutes.
directory={'X:\SoundTrap\Boats\wav\Noises\LTSA test\LTSA test batch'}; %folder where wav files are
folder=char(directory);
fullfnames=dir(fullfile(folder,'*.wav')); %list all .wavs in folder
nFiles=numel(fullfnames);
%get fs of first (i.e. all) files in folder
firstfile=fullfile(folder,char(fullfnames(1,1).name));
[xbit,fs]=audioread(firstfile,[1 2]);
tlo=1.0;
thi=114.0;
nlo=fs*tlo; %multiply fs by tlo to get starting point
nup=fs*thi; %do the same for end point
nfft=16384;
window=16384;
overlap=window/2; %50% overlap
cal=176.4;
cal=power(10,cal/20); %calculate calibration value
t0=0;
dt=1;
for iFile = 1:2%nFiles
fullfname = fullfnames(iFile).name;
%format is xxxx.yyMMddHHmmss.cut.wav
% you'll have to set/get/extract start-time for each spectrogram
fname=strsplit(fullfname,{'.'});
fname_st=fname(2);
fname_st=datetime(fname_st,'InputFormat','yyMMddHHmmss');
tStartstrings{iFile} = fname_st;% you'll have to set/get/extract start-time for each spectrogram
[xbit,fs]=audioread(fullfile(folder,fullfname), [nlo nup]); %read in wav
xbit=detrend(xbit); %remove DC offset
calxbit=xbit*cal; %apply calibration
%Perform FFT and plot:
[S,F,T,P] = spectrogram(calxbit,window,overlap,nfft,fs,'yaxis');
h = pcolor(T,F,10*log10(P)); % Here you might want to add the start-time, t0, to T
% with proper conversion of time-format...
set(h,'EdgeColor','none');
t0(iFile+1) = t0 + T(end)+dt;
end
Ah, my bad. Just plug a:
hold on
in after the pcolor-line, that should sort this out.
Sorry, I still can't get this to work.
directory={'X:\SoundTrap\Boats\wav\Noises\LTSA test\LTSA test batch'}; %folder where wav files are
folder=char(directory);
fullfnames=dir(fullfile(folder,'*.wav')); %list all .wavs in folder
nFiles=numel(fullfnames);
%get fs of first (i.e. all) files in folder
firstfile=fullfile(folder,char(fullfnames(1,1).name));
[xbit,fs]=audioread(firstfile,[1 2]);
tlo=1.0;
thi=114.0;
nlo=fs*tlo; %multiply fs by tlo to get starting point
nup=fs*thi; %do the same for end point
nfft=16384;
window=16384;
overlap=window/2; %50% overlap
cal=176.4;
cal=power(10,cal/20); %calculate calibration value
t0=0;
dt=1;
for iFile = 1:2%nFiles
fullfname = fullfnames(iFile).name;
%format is xxxx.yyMMddHHmmss.cut.wav
% you'll have to set/get/extract start-time for each spectrogram
fname=strsplit(fullfname,{'.'});
fname_st=fname(2);
fname_st=datetime(fname_st,'InputFormat','yyMMddHHmmss');
tStartstrings{iFile} = fname_st;% you'll have to set/get/extract start-time for each spectrogram
[xbit,fs]=audioread(fullfile(folder,fullfname), [nlo nup]); %read in wav
xbit=detrend(xbit); %remove DC offset
calxbit=xbit*cal; %apply calibration
%Perform FFT and plot:
[S,F,T,P] = spectrogram(calxbit,window,overlap,nfft,fs,'yaxis');
h = pcolor(T+t0(iFile),F,10*log10(P)); % Here you might want to add the start-time, t0, to T
% with proper conversion of time-format...
hold on
set(h,'EdgeColor','none');
t0(iFile+1) = t0 + T(end)+dt;
end
Is the purpose of
t0(iFile+1) = t0 + T(end)+dt;
to create an array, where the end time of the last spectrogram can be used to designate the amount of time needing to be added on to T for the subsequent spectrogram?
This line crashes because it produces a 2 element array to be stored in a 1 element array. It looks like T(end) and dt are added to both of the values in t0, but there isn't space for that in t0(iFile).
However, it does plot two spectrograms side by side for the first two files. The code just can't proceed because of this error. I think it would probably be better to use the time of the file as an index for the x-axis, then this array wouldn't be required?
This works, but when I try to expand the limits of the plot to see both spectrograms (when doing 2 files only) the plot goes blank.
directory={'X:\SoundTrap\Boats\wav\Noises\LTSA test\LTSA test batch'}; %folder where wav files are
folder=char(directory);
fullfnames=dir(fullfile(folder,'*.wav')); %list all .wavs in folder
nFiles=numel(fullfnames);
%get fs of first (i.e. all) files in folder
firstfile=fullfile(folder,char(fullfnames(1,1).name));
[xbit,fs]=audioread(firstfile,[1 2]);
tlo=1.0;
thi=114.0;
nlo=fs*tlo; %multiply fs by tlo to get starting point
nup=fs*thi; %do the same for end point
nfft=16384;
window=16384;
overlap=window/2; %50% overlap
cal=176.4;
cal=power(10,cal/20); %calculate calibration value
t0=0;
dt=1;
for iFile = 1:2%nFiles
fullfname = fullfnames(iFile).name;
%format is xxxx.yyMMddHHmmss.cut.wav
% you'll have to set/get/extract start-time for each spectrogram
fname=strsplit(fullfname,{'.'});
fname_st=fname(2);
fname_st=datenum(fname_st,'yyMMddHHmmss');
tStartstrings{iFile} = fname_st;% you'll have to set/get/extract start-time for each spectrogram
[xbit,fs]=audioread(fullfile(folder,fullfname), [nlo nup]); %read in wav
xbit=detrend(xbit); %remove DC offset
calxbit=xbit*cal; %apply calibration
%Perform FFT and plot:
[S,F,T,P] = spectrogram(calxbit,window,overlap,nfft,fs,'yaxis');
T=fname_st+datenum(seconds(T)); %start time of .wav file + seconds of each chunk
%to produce informative x-axis
h = pcolor(T,F,10*log10(P)); % Here you might want to add the start-time, t0, to T
% with proper conversion of time-format...
hold on
set(h,'EdgeColor','none');
end
That might be because you now add the ID-time of each file to the start-time, which meand that if you have large gaps (you mentioned something like 5 minutes appart, and then each of those files were for a couple of seconds worth of data) between the spectrograms. The idea I proposed was to plot the first spectrogram at times between
0 and "10 s" the next spectrogram between "10 s"+dt and "20 s"+dt, the third spectrogram between "20 s"+2*dt and "30 s"+2*dt and so on. The next step is then to se the tick-marks to:
xTickTimes = 0:("10 s"+dt):(N*("10 s"+dt));
set(gca,'Xtick',xTickTimes)
Which should give you tickmarks at the start of each spectra, next you'll have to set the Xticklabel. For that you should have made a cell-array of the fname_st-contents, lets say you call that variable t_filestarts_all, then this should be:
set(gca,'XtickLabel',t_filestarts_all)
This way you will have an uneven time-axis, one scale for the file-start-times and between those the time-scales of the spectrograms.
HTH

Sign in to comment.

Categories

Find more on Measurements and Spatial Audio in Help Center and File Exchange

Products

Release

R2020a

Asked:

on 6 Oct 2021

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!