v_spgrambw

PURPOSE ^

V_SPGRAMBW Draw spectrogram [T,F,B]=(s,fs,mode,bw,fmax,db,tinc,ann)

SYNOPSIS ^

function [t,f,b]=v_spgrambw(s,fs,varargin)

DESCRIPTION ^

V_SPGRAMBW Draw spectrogram [T,F,B]=(s,fs,mode,bw,fmax,db,tinc,ann)

  Usage: (1) v_spgrambw(s,fs,'pJcw')                       % Plot spectrogram with my favourite set of options

         (2) [s,fs,wrd,phn]=v_readsph(filename);           % read a TIMIT file
             v_spgrambw(s,fs,'pJcwat',[],[],[],[],wrd);    % plot spectrogram with word transcription

         (3) [s,fs,wrd,phn]=v_readsph(filename);           % read a TIMIT file
             v_spgrambw(s,fs,'pJcwaAtT',[],[],[],[],{wrd phn}); % plot spectrogram with word and phone transcriptions

         (4) v_spgrambw(s,fs,'PJcwm',50,[100 2000])        % Plot narrow-band spectrogram on mel scale
                                                             from 100 to 2000 mel in power/mel units

         (5) [t,f,b]=v_spgrambw(s,fs,'p');                 % calculate the spectrogram without plotting
             imagesc(t,f,10*log10(b'));                    % plot it manually
             axis('xy');

         (6) ninc=0.0045*fs;                               % Frame increment for BW=200 Hz (in samples)
             nwin=2*ninc;                                  % Frame length (in samples)
             win=hamming(nwin);                            % Analysis window
             k=0.5*fs*sum(win.^2);                         % Scale factor to convert to power/Hz
             sf=abs(v_rfft(v_enframe(s,win,ninc),nwin,2)).^2/k;         % Calculate spectrum array
             v_spgrambw(sf,[fs/ninc 0.5*(nwin+1)/fs fs/nwin],'Jc',bw);  % Plot spectrum array

         For examples of the many options available see:
         http://www.ee.ic.ac.uk/hp/staff/dmb/voicebox/tutorial/spgrambw/spgram_tut.pdf

  Inputs:  S         speech signal, or single-sided power spectrum array, S(NT,NF), in power per Hz
           FS        sample fequency (Hz) or [FS T1] where T1 is the time of the first sample
                     or, if s is a matrix, [FS T1 FINC F1] where FS is the frame rate, T1 is
                     the time of the first sample, FINC is the frequency increment and F1 the
                     frequency of the first column.
           MODE      optional character string specifying options (see list below)
           BW        bandwidth resolution in Hz (DFT window length = 1.81/BW)[default: 200]
           FMAX      frequency range [Fmin Fstep Fmax]. If Fstep is omitted
                     it is taken to be (Fmax-Fmin)/257, if Fmin is also omitted it is taken
                     to be 0 (or 20Hz for mode l), if all three are omitted Fmax is taken to be FS/2.
                     If modes m, b, e or l are specified then the units are in mel, bark or erb or
                     log10(Hz); this can be over-ridden by the 'h' option.
           DB        either dB-range relative to peak or [dB-min dB-max] for plotting (and B output if 'D' given [default: 40]
           TINC      output frame increment in seconds [0 or missing uses default=0.45/BW]
                     or [TFIRST TLAST] or [TFIRST TINC TLAST] where TFIRST/TLAST are the times
                     of first/last frames
           ANN       annotation cell array: each row contains either {time 'text-string' 'font'} or
                     {[t_start t_end] 'text-string' 'font'} where the time value is in seconds  with s(n) at time offset+n/fs.
                     The font column can omitted in which case the system font (Helvetica) will be used (To display: g=get(gca);g.Children(1).FontName).
                     For multiple annotation lines, ANN should be a cell  array whose elements are cell arrays as described above.

 Outputs:  T(NT)        time axis values (in seconds). Input sample s(n) is at time offset+n/fs.
           F(NF)        frequency axis values in Hz or, unless mode=H, other selected frequency units
                        according to mode: m=mel, l=log10(Hz), b=bark,e=erb-rate
           B(NT,NF)     spectrogram values in power per x where x depends on the 'pPmbel' options
                        clipped to DB range if 'D' option and in dB if 'd' option.

 MODE:  'p' = output power per decade rather than power per Hz [preemphasis]
        'P' = output power per mel/bark/erb according to y axis scaling
        'd' = output B array is in dB rather than power
        'D' = clip the output B array to the limits specified by the "db" input

        'n' = use nearest frequency bin instead of interpolating

        'm' = mel scale
        'b' = bark scale
        'e' = erb scale
        'l' = log10 Hz frequency scale
        'f' = label frequency axis in Hz rather than mel/bark/...

        'h' = units of the FMAX input are in Hz instead of mel/bark
              [in this case, the Fstep parameter is used only to determine
               the number of filters]
        'H' = express the F output in Hz instead of mel/bark/...

        'g' = draw a graph even if output arguments are present
        'j' = jet colourmap
        'J' = "thermal" colourmap that is linear in grayscale. Based on Oliver Woodford's
                 real2rgb at http://www.mathworks.com/matlabcentral/fileexchange/23342
        'i' = inverted colourmap (white background)
        'c' = include a colourbar as an intensity scale
        'w' = draw the speech waveform above the spectrogram
        'a','A' = centre-align annotations rather than left-aligning them.
                  'a' applies to the lowest annotation, 'A' to all the others.
        't','T' = add time markers with annotations.
                  't' applies to the lowest annotation, 'T' to all the others.

 The BW input gives the 6dB bandwidth of the Hamming window used in the analysis.
 Equal amplitude frequency components are guaranteed to give separate peaks if they
 are this far apart. This value also determines the time resolution: the window length is
 1.81/BW and the low-pass filter applied to amplitude modulations has a 6-dB bandwidth of
 BW/2 Hz.

 The units are power per Hz unless the 'P' option is given in which case power
 per displayed unit is used or power per decade for the 'l' option.

CROSS-REFERENCE INFORMATION ^

This function calls: This function is called by:

SUBFUNCTIONS ^

SOURCE CODE ^

0001 function [t,f,b]=v_spgrambw(s,fs,varargin)
0002 %V_SPGRAMBW Draw spectrogram [T,F,B]=(s,fs,mode,bw,fmax,db,tinc,ann)
0003 %
0004 %  Usage: (1) v_spgrambw(s,fs,'pJcw')                       % Plot spectrogram with my favourite set of options
0005 %
0006 %         (2) [s,fs,wrd,phn]=v_readsph(filename);           % read a TIMIT file
0007 %             v_spgrambw(s,fs,'pJcwat',[],[],[],[],wrd);    % plot spectrogram with word transcription
0008 %
0009 %         (3) [s,fs,wrd,phn]=v_readsph(filename);           % read a TIMIT file
0010 %             v_spgrambw(s,fs,'pJcwaAtT',[],[],[],[],{wrd phn}); % plot spectrogram with word and phone transcriptions
0011 %
0012 %         (4) v_spgrambw(s,fs,'PJcwm',50,[100 2000])        % Plot narrow-band spectrogram on mel scale
0013 %                                                             from 100 to 2000 mel in power/mel units
0014 %
0015 %         (5) [t,f,b]=v_spgrambw(s,fs,'p');                 % calculate the spectrogram without plotting
0016 %             imagesc(t,f,10*log10(b'));                    % plot it manually
0017 %             axis('xy');
0018 %
0019 %         (6) ninc=0.0045*fs;                               % Frame increment for BW=200 Hz (in samples)
0020 %             nwin=2*ninc;                                  % Frame length (in samples)
0021 %             win=hamming(nwin);                            % Analysis window
0022 %             k=0.5*fs*sum(win.^2);                         % Scale factor to convert to power/Hz
0023 %             sf=abs(v_rfft(v_enframe(s,win,ninc),nwin,2)).^2/k;         % Calculate spectrum array
0024 %             v_spgrambw(sf,[fs/ninc 0.5*(nwin+1)/fs fs/nwin],'Jc',bw);  % Plot spectrum array
0025 %
0026 %         For examples of the many options available see:
0027 %         http://www.ee.ic.ac.uk/hp/staff/dmb/voicebox/tutorial/spgrambw/spgram_tut.pdf
0028 %
0029 %  Inputs:  S         speech signal, or single-sided power spectrum array, S(NT,NF), in power per Hz
0030 %           FS        sample fequency (Hz) or [FS T1] where T1 is the time of the first sample
0031 %                     or, if s is a matrix, [FS T1 FINC F1] where FS is the frame rate, T1 is
0032 %                     the time of the first sample, FINC is the frequency increment and F1 the
0033 %                     frequency of the first column.
0034 %           MODE      optional character string specifying options (see list below)
0035 %           BW        bandwidth resolution in Hz (DFT window length = 1.81/BW)[default: 200]
0036 %           FMAX      frequency range [Fmin Fstep Fmax]. If Fstep is omitted
0037 %                     it is taken to be (Fmax-Fmin)/257, if Fmin is also omitted it is taken
0038 %                     to be 0 (or 20Hz for mode l), if all three are omitted Fmax is taken to be FS/2.
0039 %                     If modes m, b, e or l are specified then the units are in mel, bark or erb or
0040 %                     log10(Hz); this can be over-ridden by the 'h' option.
0041 %           DB        either dB-range relative to peak or [dB-min dB-max] for plotting (and B output if 'D' given [default: 40]
0042 %           TINC      output frame increment in seconds [0 or missing uses default=0.45/BW]
0043 %                     or [TFIRST TLAST] or [TFIRST TINC TLAST] where TFIRST/TLAST are the times
0044 %                     of first/last frames
0045 %           ANN       annotation cell array: each row contains either {time 'text-string' 'font'} or
0046 %                     {[t_start t_end] 'text-string' 'font'} where the time value is in seconds  with s(n) at time offset+n/fs.
0047 %                     The font column can omitted in which case the system font (Helvetica) will be used (To display: g=get(gca);g.Children(1).FontName).
0048 %                     For multiple annotation lines, ANN should be a cell  array whose elements are cell arrays as described above.
0049 %
0050 % Outputs:  T(NT)        time axis values (in seconds). Input sample s(n) is at time offset+n/fs.
0051 %           F(NF)        frequency axis values in Hz or, unless mode=H, other selected frequency units
0052 %                        according to mode: m=mel, l=log10(Hz), b=bark,e=erb-rate
0053 %           B(NT,NF)     spectrogram values in power per x where x depends on the 'pPmbel' options
0054 %                        clipped to DB range if 'D' option and in dB if 'd' option.
0055 %
0056 % MODE:  'p' = output power per decade rather than power per Hz [preemphasis]
0057 %        'P' = output power per mel/bark/erb according to y axis scaling
0058 %        'd' = output B array is in dB rather than power
0059 %        'D' = clip the output B array to the limits specified by the "db" input
0060 %
0061 %        'n' = use nearest frequency bin instead of interpolating
0062 %
0063 %        'm' = mel scale
0064 %        'b' = bark scale
0065 %        'e' = erb scale
0066 %        'l' = log10 Hz frequency scale
0067 %        'f' = label frequency axis in Hz rather than mel/bark/...
0068 %
0069 %        'h' = units of the FMAX input are in Hz instead of mel/bark
0070 %              [in this case, the Fstep parameter is used only to determine
0071 %               the number of filters]
0072 %        'H' = express the F output in Hz instead of mel/bark/...
0073 %
0074 %        'g' = draw a graph even if output arguments are present
0075 %        'j' = jet colourmap
0076 %        'J' = "thermal" colourmap that is linear in grayscale. Based on Oliver Woodford's
0077 %                 real2rgb at http://www.mathworks.com/matlabcentral/fileexchange/23342
0078 %        'i' = inverted colourmap (white background)
0079 %        'c' = include a colourbar as an intensity scale
0080 %        'w' = draw the speech waveform above the spectrogram
0081 %        'a','A' = centre-align annotations rather than left-aligning them.
0082 %                  'a' applies to the lowest annotation, 'A' to all the others.
0083 %        't','T' = add time markers with annotations.
0084 %                  't' applies to the lowest annotation, 'T' to all the others.
0085 %
0086 % The BW input gives the 6dB bandwidth of the Hamming window used in the analysis.
0087 % Equal amplitude frequency components are guaranteed to give separate peaks if they
0088 % are this far apart. This value also determines the time resolution: the window length is
0089 % 1.81/BW and the low-pass filter applied to amplitude modulations has a 6-dB bandwidth of
0090 % BW/2 Hz.
0091 %
0092 % The units are power per Hz unless the 'P' option is given in which case power
0093 % per displayed unit is used or power per decade for the 'l' option.
0094 
0095 %%%% BUGS %%%%%%
0096 % * allow ANN rows to be a mixture of intervals and instants
0097 % * allow multiple ANN rows
0098 % * Do not use triangular interpolation if the output frequencies are the same as an FFT
0099 % * Place as many subticks as will fit beyond the last tick with the 'f' option
0100 % * Use a special subtick pattern between ticks that are powers of 10 using the 'f' option
0101 % * Future options:
0102 %       ['q' = constant q transform]
0103 %       ['k' = add a piano keyboard to the frequency scale]
0104 %       ['z' = use a bipolar colourmap for a matrix input with negative values]
0105 
0106 %      Copyright (C) Mike Brookes 1997-2011
0107 %      Version: $Id: v_spgrambw.m 10865 2018-09-21 17:22:45Z dmb $
0108 %
0109 %   VOICEBOX is a MATLAB toolbox for speech processing.
0110 %   Home page: http://www.ee.ic.ac.uk/hp/staff/dmb/voicebox/voicebox.html
0111 %
0112 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
0113 %   This program is free software; you can redistribute it and/or modify
0114 %   it under the terms of the GNU General Public License as published by
0115 %   the Free Software Foundation; either version 2 of the License, or
0116 %   (at your option) any later version.
0117 %
0118 %   This program is distributed in the hope that it will be useful,
0119 %   but WITHOUT ANY WARRANTY; without even the implied warranty of
0120 %   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0121 %   GNU General Public License for more details.
0122 %
0123 %   You can obtain a copy of the GNU General Public License from
0124 %   http://www.gnu.org/copyleft/gpl.html or by writing to
0125 %   Free Software Foundation, Inc.,675 Mass Ave, Cambridge, MA 02139, USA.
0126 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
0127 persistent tcmap
0128 if isempty(tcmap)
0129     % modified thermal with better grayscale linearity
0130     tcmap=[ 0 0 0; 7 0 17; 14 0 33; 21 0 50; 29 0 67; 36 0 84; 43 0 100; 50 0 117;
0131         57 0 134; 64 0 150; 72 0 167; 80 3 164; 89 7 156; 97 11 149; 106 15 142; 114 19 134;
0132         123 23 127; 131 27 119; 140 31 112; 149 35 105; 157 39 97; 166 43 90; 174 47 82;
0133         183 51 75; 192 55 68; 200 59 60; 209 63 53; 217 67 45; 226 71 38; 234 75 31;
0134         243 79 23; 252 83 16; 255 88 12; 255 95 12; 255 102 11; 255 109 11; 255 116 10;
0135         255 123 10; 255 130 9; 255 137 9; 255 144 8; 255 151 8; 255 158 7; 255 165 7;
0136         255 172 6; 255 179 6; 255 186 5; 255 193 4; 255 200 4; 255 207 3; 255 214 3; 255 221 2;
0137         255 228 2; 255 235 1; 255 242 1; 255 249 0; 255 252 22; 255 252 55; 255 253 88;
0138         255 253 122; 255 254 155; 255 254 188; 255 255 222; 255 255 255]/255;
0139 end
0140 if nargin<2
0141     error('Usage: SPGRAMBW(s,fs,mode,bw,fmax,db,tinc)');
0142 end
0143 %
0144 % first decode the input arguments
0145 %
0146 if size(s,1)==1
0147     s=s(:);   % force to be a column vector (unless it is a matrix)
0148 end
0149 [ns1,ns2]=size(s);
0150 ap=zeros(1,6);
0151 j=2;
0152 if numel(fs)<2
0153     fs(2)=1/fs(1);  % first sample or frame is at time 1/fs
0154 end
0155 for i=1:length(varargin)
0156     if ischar(varargin{i})
0157         ap(1)=i;
0158     else
0159         ap(j)=i;
0160         j=j+1;
0161     end
0162 end
0163 if ap(1) && ~isempty(varargin{ap(1)})
0164     mode=varargin{ap(1)};
0165 else
0166     mode='';  % default mode
0167 end
0168 if ap(2) && ~isempty(varargin{ap(2)})
0169     bw=varargin{ap(2)};
0170 else
0171     bw=200;
0172 end
0173 if ap(3) && ~isempty(varargin{ap(3)})
0174     fmax=varargin{ap(3)};
0175 else
0176     fmax=[];
0177 end
0178 if ap(4) && ~isempty(varargin{ap(4)})
0179     db=varargin{ap(4)};
0180 else
0181     db=40;
0182 end
0183 if ap(5) && ~isempty(varargin{ap(5)})
0184     tinc=varargin{ap(5)};
0185 else
0186     tinc=0;
0187 end
0188 switch numel(tinc)
0189     case 1
0190         tinc=[tinc -Inf Inf];
0191     case 2
0192         tinc=[0 tinc];
0193     otherwise
0194         tinc=tinc([2 1 3]);
0195 end
0196 if tinc(1)<=0
0197     tinc(1)=1.81/(4*bw); % default frame increment
0198 end
0199 if ap(6)
0200     ann=varargin{ap(6)};
0201 else
0202     ann=[];
0203 end
0204 
0205 % now sort out the mode flags
0206 
0207 mdsw='  ';           % [yscale preemph]
0208 for i=1:length(mode)
0209     switch mode(i)
0210         case {'l','m','b','e'}
0211             mdsw(1)=mode(i);
0212         case {'p','P'}
0213             mdsw(2)=mode(i);
0214     end
0215 end
0216 if mdsw(2)=='P'
0217     mdsw(2)=mdsw(1);        % preemphasis is scaling dependent
0218 end
0219 %
0220 % sort out the frequency axis
0221 %
0222 flmin=30;                   % min frequency for 'l' option
0223 nfrq=257;                   % default number of frequency bins
0224 if ns2==1
0225     fnyq=fs(1)/2;           % default upper frequency limit is fs/2
0226 else                        % input is a power spectrum
0227     if numel(fs)<3
0228         fs(3)=fs(1)*0.25;   % default increment is 0.25 times frame increment
0229     end
0230     if numel(fs)<4
0231         fs(4)=0;            % first freq bin is DC by default
0232     end
0233     fnyq=fs(4)+(ns2-1)*fs(3);  % default upper frequency limit is highest supplied frequency
0234 end
0235 
0236 if ~numel(fmax)             % no explicit frequency range
0237     switch mdsw(1)
0238         case 'l'
0239             fx=linspace(log10(flmin),log10(fnyq),nfrq);   % 20  Hz to Nyquist
0240         case 'm'
0241             fx=linspace(0,v_frq2mel(fnyq),nfrq);   % DC to Nyquist
0242         case 'b'
0243             fx=linspace(0,v_frq2bark(fnyq),nfrq);   % DC to Nyquist
0244         case 'e'
0245             fx=linspace(0,v_frq2erb(fnyq),nfrq);   % DC to Nyquist
0246         otherwise   % linear Hz scale
0247             fx=(0:nfrq-1)*fnyq/(nfrq-1);
0248     end
0249 else
0250     if any(mode=='h')
0251         switch mdsw(1)
0252             case 'l'
0253                 fmaxu=log10(fmax);   % 20  Hz to Nyquist
0254             case 'm'
0255                 fmaxu=v_frq2mel(fmax);   % DC to Nyquist
0256             case 'b'
0257                 fmaxu=v_frq2bark(fmax);   % DC to Nyquist
0258             case 'e'
0259                 fmaxu=v_frq2erb(fmax);   % DC to Nyquist
0260             otherwise
0261                 fmaxu=fmax;  % linear Hz scale
0262         end
0263     else
0264         fmaxu=fmax;                 % already in the correct units
0265     end
0266     if numel(fmax)<2   % only max value specified
0267         if mdsw(1)=='l'
0268             fx=linspace(log10(flmin),fmaxu,nfrq);   % 20  Hz to fmax
0269         else
0270             fx=linspace(0,fmaxu,nfrq);   % DC to fmax
0271         end
0272     elseif numel(fmax)<3 % min and max values specified
0273         fx=linspace(fmaxu(1),fmaxu(2),nfrq);   % fmin to fmax
0274     else
0275         fmaxu(2)=fmax(2)*(fmaxu(3)-fmaxu(1))/(fmax(3)-fmax(1)); % scale the step size appropriately
0276         fx=fmaxu(1):fmaxu(2):fmaxu(3);   % fmin to fmax in steps of finc
0277         nfrq=length(fx);
0278     end
0279 end
0280 switch mdsw(1)          % convert the frequency range to Hz
0281     case 'l'
0282         f=10.^fx;
0283         frlab='log_{10}Hz';
0284         frlabf='log';
0285         frq2y=@log10;
0286         y2frq=@(x) 10.^x;
0287     case 'm'
0288         f=v_mel2frq(fx);
0289         frlab='Mel';
0290         frlabf='Mel';
0291         frq2y=@v_frq2mel;
0292         y2frq=@v_mel2frq;
0293     case 'b'
0294         f=v_bark2frq(fx);
0295         frlab='Bark';
0296         frlabf='Bark';
0297         frq2y=@v_frq2bark;
0298         y2frq=@v_bark2frq;
0299     case 'e'
0300         f=v_erb2frq(fx);
0301         frlab='Erb-rate';
0302         frlabf='Erb';
0303         frq2y=@v_frq2erb;
0304         y2frq=@v_erb2frq;
0305     otherwise
0306         f=fx;
0307         frlab='Hz';
0308         frq2y=@(x) x;
0309         y2frq=@(x) x;
0310 end
0311 if ~any(mode=='H')
0312     f=fx;               % give output frequencies in native units instead of Hz unless 'H' is specified
0313 end
0314 %
0315 % now calculate the spectrogram
0316 %
0317 if ns2==1   % input is a speech signal vector
0318     winlen = fix(1.81*fs(1)/bw);   % window length
0319     win=0.54+0.46*cos((1-winlen:2:winlen)*pi/winlen);  % Hamming window
0320     ninc=max(round(tinc(1)*fs(1)),1);                 % window increment in samples
0321     %  we need to take account of minimum freq increment + make it exact if possible
0322     fftlen=pow2(nextpow2(4*winlen));        % enough oversampling to get good interpolation
0323     win=win/sqrt(sum(win.^2));              % ensure window squared sums to unity
0324     ix1=max(round((tinc(2)-fs(2))*fs(1)-(winlen-3)/2),1); % first sample required
0325     ix2=min(ceil((tinc(3)-fs(2))*fs(1)+(winlen+1)/2),ns1); % last sample required
0326     [sf,t]=v_enframe(s(ix1:ix2),win,ninc);
0327     t=fs(2)+(t+ix1-2)/fs(1);                         % time axis
0328     b=v_rfft(sf,fftlen,2);
0329     b=b.*conj(b)*2/fs(1);          % Power per Hz
0330     b(:,1)=b(:,1)*0.5;   % correct for no negative zero frequency to double the power
0331     b(:,end)=b(:,end)*0.5;   % correct for no negative nyquist frequency to double the power
0332     fb=(0:fftlen/2)*fs(1)/fftlen; % fft bin frequencies
0333     fftfs=fs(1);
0334 else
0335     b=s;
0336     t=fs(2)+(0:ns1-1)/fs(1);  % frame times
0337     fb=fs(4)+(0:ns2-1)*fs(3);
0338     fftlen=[ns2 fs(3) fs(4)]; % for v_filtbankm: ns2=# input freq bins, freq increment (Hz), first bin freq (Hz)
0339     fftfs=0;
0340     %     fftlen=2*(ns2-1);  % assume an even length fft
0341     %     fftfs=fftlen*fs(3);
0342 end
0343 nfr=numel(t);                   % number of frames
0344 dblab='Power/Hz';
0345 switch mdsw(2)
0346     case {'p','l'}
0347         b=b.*repmat(fb*log(10),nfr,1);       % convert to power per decade
0348         dblab='Power/Decade';
0349     case 'm'
0350         b=b.*repmat((700+fb)*log(1+1000/700)/1000,nfr,1);       % convert to power per mel
0351         dblab='Power/Mel';
0352     case 'b'
0353         b=b.*repmat((1960+fb).^2/52547.6,nfr,1);       % convert to power per bark
0354         dblab='Power/Bark';
0355     case 'e'
0356         b=b.*repmat(6.23*fb.^2 + 93.39*fb + 28.52,nfr,1);       % convert to power per erb
0357         dblab='Power/Erb-rate';
0358 end
0359 %
0360 % Now map onto the desired frequency scale
0361 %
0362 if any(mode=='n')
0363     fbopt=['cushn' mdsw(1)];
0364 else
0365     fbopt=['cush' mdsw(1)];
0366 end
0367 b=b*filtbankm(nfrq,fftlen,fftfs,fx(1),fx(end),fbopt)';
0368 
0369 if ~nargout || any(mode=='g') ||  any(lower(mode)=='d')
0370     if numel(db)<2          % find clipping limits
0371         plim=max(b(:))*[0.1^(0.1*db) 1];
0372     else
0373         plim=10.^(0.1*db(1:2));
0374     end
0375     if plim(2)<=0
0376         plim(2)=1;
0377     end
0378     if plim(1)<=0 || plim(1)==plim(2)
0379         plim(1)=0.1*plim(2);
0380     end
0381     if ~nargout || any(mode=='g')
0382         bd=10*log10(max(b,max(b(:)*1e-30)));  % save an unclipped log version for plotting
0383     end
0384     if any(mode=='D')
0385         b=min(max(b,plim(1)),plim(2)); % clip the output
0386     end
0387     if any(mode=='d')
0388         b=10*log10(b);    % output the dB version
0389     end
0390 end
0391 % now plot things
0392 if ~nargout || any(mode=='g')
0393     cla;  % clear current axis
0394     imagesc(t,fx,bd');
0395     axis('xy');
0396     set(gca,'tickdir','out','clim',10*log10(plim));
0397     if any(mode=='j')
0398         colormap('jet');
0399         map=colormap;
0400     elseif any(mode=='J')
0401         map=tcmap;
0402     else
0403         map = repmat((0:63)'/63,1,3);
0404     end
0405     if any(mode=='i')               % 'i' option = invert the colourmap
0406         map=map(64:-1:1,:);
0407     end
0408     colormap(map);
0409     if any(mode=='c')                % 'c' option = show a colourbar
0410         colorbar;
0411         v_cblabel([dblab ' (dB)']);
0412     end
0413     %
0414     % Now check if annotations or a waveform are required
0415     %
0416     % calculate space for annotations
0417     annh=2*(any(mode=='w') && ns2==1);                              % height of waveform (2 * annotation height)
0418     nann=0;
0419     if ~isempty(ann)                                                % we also have annotations
0420         if ~iscell(ann{1})                                          % only one annotation array
0421             ann={ann};                                              % ... so make it a sub-cell
0422         end
0423         nann=length(ann);
0424         annh=repmat(annh,1,2*nann+1);                               % two possible components for each annotation + waveform
0425         modet=any(mode=='t');
0426         for i=1:nann
0427             anni=ann{nann+1-i};                                     % process in reverse order (bottom up)
0428             annh(2*i)=size(anni,2)>1;                               % annotation needed
0429             annh(2*i-1)=(modet && annh(2*i)) || size(anni,2)==1;    % time markers needed
0430             modet=any(mode=='T');                                   % 'T' applies to each annotation except for the first
0431         end
0432     end
0433     ylim=get(gca,'ylim');                                           % y-axis limits
0434     % draw annotations
0435     if any(annh>0)
0436         yrange = ylim(2)-ylim(1);                                   % y-axis range
0437         zlim=ylim;
0438         toptaw=cumsum([0 0.05*yrange*annh])+ylim(2);                % y-limits of time-markers, annotations, waveform
0439         zlim(2)=toptaw(end);                                        % new upper limit for y
0440         set(gca,'ylim',zlim,'color',map(1,:));                      % extend y limits with black background
0441         if annh(end) % draw waveform
0442             six=min(max(floor((get(gca,'xlim')-fs(2))*fs(1))+[1 2],1),ns1);
0443             smax=max(s(six(1):six(2)));
0444             smin=min(s(six(1):six(2)));
0445             if smax==smin
0446                 smax=smax+1;
0447                 smin=smin-1;
0448             end
0449             srange=smax-smin;
0450             hold on
0451             plot(fs(2)+(six(1)-1:six(2)-1)/fs(1),(s(six(1):six(2))-smin)/srange*0.9*(toptaw(end)-toptaw(end-1))+toptaw(end-1),'color',map(48,:))
0452             hold off
0453         end
0454         modet=any(mode=='t');
0455         modea=any(mode=='a');
0456         for iann=1:nann % loop thropugh each annotation cell array
0457             anni=ann{nann+1-iann}; % process in reverse order (bottom up)
0458             if annh(2*iann-1) || annh(2*iann)
0459                 tmk=cell2mat(anni(:,1));
0460                 tmksel=tmk(:,1)<=t(end) & tmk(:,end)>=t(1); % select those that lie within the time axis
0461                 yix=1+[tmk(tmksel,1)<t(1) ones(sum(tmksel),2) tmk(tmksel,end)>t(end)]';
0462                 tmk(:,1)=max(tmk(:,1),t(1));  % clip to axis limits
0463                 tmk(:,end)=min(tmk(:,end),t(end));
0464             end
0465             if annh(2*iann-1) && any(tmksel)  % draw time markers
0466                 ymk=toptaw(2*iann-1:2*iann)*[0.8 0.4;0.2 0.6];
0467                 switch size(tmk,2)
0468                     case 0
0469                     case 1      % isolated marks
0470                         hold on
0471                         plot([tmk(tmksel) tmk(tmksel)]',repmat(ymk',1,sum(tmksel)),'color',map(48,:));
0472                         hold off
0473                     otherwise % draw durations
0474                         hold on
0475                         plot(tmk(tmksel,[1 1 2 2])',ymk(yix),'color',map(48,:));
0476                         hold off
0477                 end
0478             end
0479             if annh(2*iann) && any(tmksel)  % print annotations
0480                 if modea                    % align centrally
0481                     horal='center';
0482                     tmk=(tmk(:,1)+tmk(:,end))*0.5;
0483                 else
0484                     horal='left';
0485                     tmk=tmk(:,1);
0486                 end
0487                 if size(anni,2)>2
0488                     font='Arial';
0489                     for i=1:size(anni,1)
0490                         if tmksel(i)
0491                             if ~isempty(anni{i,3})
0492                                 font = anni{i,3};
0493                             end
0494                             text(tmk(i),toptaw(2*iann),anni{i,2},'color',map(48,:),'fontname',font,'VerticalAlignment','baseline','HorizontalAlignment',horal);
0495                         end
0496                     end
0497                 else
0498                     for i=1:size(anni,1)
0499                         if tmksel(i)
0500                             text(tmk(i),toptaw(2*iann),anni{i,2},'color',map(48,:),'VerticalAlignment','baseline','HorizontalAlignment',horal);
0501                         end
0502                     end
0503                 end
0504             end
0505             modet=any(mode=='T'); % 'T' applies to each annotation except for the first
0506             modea=any(mode=='A'); % 'A' applies to each annotation except for the first
0507         end
0508     end
0509     xlabel(['Time (' v_xticksi 's)']);
0510     if any(mode=='f') && ~strcmp(frlab,'Hz')
0511         ylabel([frlabf '-scaled frequency (Hz)']);
0512         ytickhz(frq2y,y2frq);
0513     else
0514         ylabel(['Frequency (' v_yticksi frlab ')']);
0515     end
0516     ytick=get(gca,'YTick');
0517     ytickl=get(gca,'YTickLabel');
0518     msk=ytick<=ylim(2);
0519     if any(~msk)
0520         set(gca,'YTick',ytick(msk),'YTickLabel',ytickl(msk));
0521     end
0522 end
0523 
0524 function ytickhz(frq2y,y2frq)
0525 % label non linear y frequency axis
0526 %
0527 % Bugs/Suggestions:
0528 % * Add a penalty for large numbers (e.g. 94 is less "round" than 11)
0529 % * possibly add subticks at 1:2:5 if boundaries are 1 and 10
0530 % * could treat subtick allocation specially if bounding lables are both powers of 10
0531 %   and work in log spacing rather than spacing directly
0532 
0533 % algorithm constants
0534 
0535 seps=[0.4 1 3 6]; % spacings: (a) min subtick, (b) min tick, (c) min good tick, (d) max good tick
0536 ww=[0.5 0.6 0.8 0.1 0.3 0.3 0.2];  % weight for (a) last digit=5, (b) power of 10, (c) power of 1000, (d) equal spacing, (e) 1:2:5 labels (f) <seps(3) (g) >seps(4)
0537 nbest=10; % number of possibilities to track
0538 
0539 prefix={'y','z','a','f','p','n','u','m','','k','M','G','T','P','E','Z','Y'};
0540 
0541 ah=gca;
0542 getgca=get(ah);  % Get original axis properties
0543 set(ah,'Units','points','FontUnits','points');
0544 getgcac=get(ah);  % Get axis properties in points units
0545 set(ah,'Units',getgca.Units,'FontUnits',getgca.FontUnits); % return to original values
0546 ylim=getgca.YLim;
0547 yrange=ylim*[-1;1];
0548 chsz= yrange*getgcac.FontSize/getgcac.Position(4); % char height in Y-units
0549 % divide the y-axis up into bins containing at most one label each
0550 maxl=ceil(2*yrange/chsz);  % max number of labels
0551 
0552 % candidate array [cand(:,[1 2])/1000 cand(:,5) cand(:,6)/1000 cand(:,[7 8])]
0553 % 1,2=y limits, 3,4=log limits, 5=Hz, 6=cost, 7=mantissa, 8=exponent, 9=sig digits, 10=y-position
0554 cand=zeros(maxl+2,10);
0555 yinc=(yrange+chsz*0.0002)/maxl;  % bin spacing (allowing for a tiny bit to ensure the ends are included)
0556 cand(2:end-1,2)=ylim(1)+yinc*(1:maxl)'-chsz*0.0001;
0557 cand(3:end-1,1)=cand(2:end-2,2);
0558 cand(2,1)=cand(2,2)-yinc;
0559 cand(2:end-1,1:2)=y2frq(max(cand(2:end-1,1:2),0));
0560 
0561 % find the "roundest" number in each interval
0562 % first deal with intervals containing zero
0563 cand([1 maxl+2],6)=-1;
0564 cand(2,9)=(cand(2,1)<=0);  % mask out interval contaiing zero
0565 cand(2,6)=-cand(2,9);
0566 msk=cand(:,6)==0;  % find rows without a cost yet
0567 cand(msk,3:4)=log10(cand(msk,1:2));
0568 % find powers of 1000
0569 loglim=ceil(cand(:,3:4)/3);
0570 msk=loglim(:,2)>loglim(:,1);
0571 if any(msk)
0572     xp=loglim(msk,1);
0573     wuns=ones(length(xp),1);
0574     cand(msk,5:9)=[1000.^xp wuns-ww(3) wuns 3*xp wuns];
0575 end
0576 % find powers of 10
0577 loglim=ceil(cand(:,3:4));
0578 msk=~msk & (loglim(:,2)>loglim(:,1));
0579 if any(msk)
0580     xp=loglim(msk,1);
0581     wuns=ones(length(xp),1);
0582     cand(msk,5:9)=[10.^xp wuns-ww(2) wuns xp wuns];
0583 end
0584 % find value with fewest digits
0585 msk=cand(:,6)==0;  % find rows without a cost yet
0586 maxsig=1-floor(log10(10^min(cand(msk,3:4)*[-1;1])-1)); % maximum number of significant figures to consider
0587 pten=10.^(0:maxsig-1);   % row vector of powers of ten
0588 noten=10.^(-floor(cand(msk,3))); % exponent of floating point representation of lower bound
0589 sigdig=sum((ceil(cand(msk,2).*noten*pten)-ceil(cand(msk,1).*noten*pten))==0,2); % number of digits common to the interval bounds
0590 lowman=ceil(cand(msk,1).*noten.*10.^sigdig);
0591 midman=10*floor(lowman/10)+5;
0592 highman=ceil(cand(msk,2).*noten.*10.^sigdig);
0593 mskman=midman>=lowman & midman<highman;   % check if we can include a manitssa ending in 5
0594 lowman(mskman)=midman(mskman);
0595 cand(msk,6:9)=[sigdig+1 lowman floor(cand(msk,3))-sigdig sigdig+1];
0596 cand(msk,5)=cand(msk,7).*10.^cand(msk,8);
0597 cand(msk,6)=cand(msk,6)-(mod(cand(msk,7),10)==5)*ww(1);
0598 cand(2:end-1,10)=frq2y(cand(2:end-1,5));
0599 cand([1 maxl+2],10)=ylim + seps(4)*chsz*[-1 1]; % put imaginary labels at the optimum spacing beyond the axes
0600 % [cand(:,[1 2 5])/1000 cand(:,[6 7 8 9])]
0601 
0602 % Now do n-best DP to find the best sequence
0603 
0604 ratint=[8/5 25/10 0 0 4/3];
0605 costs=Inf(nbest,maxl+2); % cumulative path costs
0606 costs(1,1)=0; % starting node only has one option
0607 prev=ones(nbest,maxl+2); % previous label in path
0608 labcnt=zeros(nbest,maxl+2); % number of labels in path
0609 for i=2:maxl+2
0610     ntry=nbest*(i-1); % number of previous options
0611     prevc=reshape(repmat(1:i-1,nbest,1),ntry,1); % previous candidate
0612     prevprev=1+floor((prev(1:ntry)'-1)/nbest); % previous previous candidate
0613     msk=prevprev>1+(maxl+2)*(i==maxl+2); % mask for label triplets
0614     labcnti=labcnt(1:ntry)+1;
0615     disti=(cand(i,10)-cand(prevc,10))/chsz; % distance to previous label in characters
0616     costa=max(seps(3)-disti,0)*ww(6)+max(disti-seps(4),0)*ww(7);
0617     incri=(cand(i,5)-cand(prevc,5)); % label increment
0618     incrj=(cand(i,5)-cand(prevprev,5)); % double label increment
0619     if any(msk)
0620         costa(msk)=costa(msk)- ww(4)*(abs(incrj(msk)-2*incri(msk))<0.01*incri(msk));
0621         if cand(i,7)==1 || cand(i,7)==2 || cand(i,7)==5 % look for labels 1:2:5
0622             costa(msk)=costa(msk)- ww(5)*(abs(incrj(msk)-ratint(cand(i,7))*incri(msk))<0.01*incri(msk));
0623         end
0624     end
0625     costa(disti<seps(2))=Inf;
0626     costi=(costs(1:ntry).*max(labcnt(1:ntry),1)+costa'+cand(i,6))./labcnti;
0627     [sc,isc]=sort(costi);
0628     isc=isc(1:nbest);
0629     costs(:,i)=sc(1:nbest)';
0630     prev(:,i)=isc';
0631     labcnt(:,i)=labcnti(isc)';
0632 end
0633 
0634 % now traceback the best sequence
0635 
0636 % fprintf('Traceback\n\n');
0637 ichoose=0;
0638 labchoose=[];
0639 for i=1:nbest
0640     if labcnt(i,maxl+2)>1 && costs(i,maxl+2)<Inf
0641         lablist=zeros(labcnt(i,maxl+2)-1,1);
0642         k=prev(i,maxl+2);
0643         for j=labcnt(i,maxl+2)-1:-1:1
0644             lablist(j)=1+floor((k-1)/nbest);
0645             k=prev(k);
0646         end
0647         %         fprintf('Cost=%8.2f :',costs(i,maxl+2));
0648         %         fprintf(' %g',cand(lablist,5))
0649         %         fprintf('\n');
0650         if ~ichoose || labcnt(ichoose,maxl+2)==1
0651             ichoose=i;
0652             labchoose=lablist;
0653         end
0654     end
0655 end
0656 
0657 % now create the labels
0658 
0659 ntick=length(labchoose);
0660 % sort out the subticks
0661 subpos=[];
0662 if ntick>=2
0663     for i=1:ntick-1
0664         clj=cand(labchoose(i:i+1),:);
0665         sprec=min(clj(1,8)+100*(clj(1,7)==0),clj(2,8)); % subtick precision
0666         spos=(clj(1,7)*10^(clj(1,8)-sprec):clj(2,7)*10^(clj(2,8)-sprec))*10^sprec;
0667         nsub=length(spos);
0668         if nsub==2
0669             spos=spos*[1 0.5 0;0 0.5 1];
0670             nsub=3;
0671         end
0672         if nsub>=3
0673             yspos=frq2y(spos);
0674             for kk=1:3 % try various subdivisions: every 1, 2 or 5
0675                 k=kk+2*(kk==3);  % 1, 2 and 5
0676                 if 2*k<=nsub-1 && ~mod(nsub-1,k)  % must divide exactly into nsub
0677                     if all((yspos(1+k:k:nsub)-yspos(1:k:nsub-k))>=(seps(1)*chsz)) % check they all fit in
0678                         subpos=[subpos yspos(1+k:k:nsub-k)];
0679                         if i==1
0680                             spos=(ceil(cand(2,1)/10^sprec):clj(1,7)*10^(clj(1,8)-sprec))*10^sprec;
0681                             nsub=length(spos);
0682                             yspos=frq2y(spos);
0683                             if nsub>=k+1 && all((yspos(nsub:-k:1+k)-yspos(nsub-k:-k:1))>=(seps(1)*chsz))
0684                                 subpos=[subpos yspos(nsub-k:-k:1)];
0685                             end
0686                         elseif i==ntick-1
0687                             spos=(clj(2,7)*10^(clj(2,8)-sprec):floor(cand(end-1,2)/10^sprec))*10^sprec;
0688                             nsub=length(spos);
0689                             yspos=frq2y(spos);
0690                             if nsub>=k+1 && all((yspos(1+k:k:nsub)-yspos(1:k:nsub-k))>=(seps(1)*chsz))
0691                                 subpos=[subpos yspos(1+k:k:nsub)];
0692                             end
0693                         end
0694                         break;
0695                     end
0696                 end
0697             end
0698         end
0699     end
0700 end
0701 nsub=length(subpos);
0702 tickpos=[cand(labchoose,10); subpos'];
0703 ticklab=cell(ntick+nsub,1);
0704 sipref=min(max(floor((sum(cand(labchoose,8:9),2)-1)/3),-8),8);
0705 nzadd=cand(labchoose,8)-3*sipref;  % trailing zeros to add
0706 digzer=cand(labchoose,7).*10.^max(nzadd,0); % label digits including trailing zeros
0707 ndleft=cand(labchoose,9)+nzadd; % digits to the left of the decimal point
0708 for i=1:ntick
0709     tickint=num2str(digzer(i));
0710     if nzadd(i)<0
0711         tickint=[tickint(1:ndleft(i)) '.' tickint(1+ndleft(i):end)];
0712     end
0713     ticklab{i} = sprintf('%s%s',tickint,prefix{sipref(i)+9});
0714 end
0715 for i=ntick+1:ntick+nsub
0716     ticklab{i}='';
0717 end
0718 [tickpos,ix]=sort(tickpos);
0719 ticklab=ticklab(ix);
0720 
0721 set(ah,'YTick',tickpos','YTickLabel',ticklab);
0722

Generated by m2html © 2003