


V_FILTBANKM determine matrix for a linear/mel/erb/bark-spaced v_filterbank [X,IL,IH]=(P,N,FS,FL,FH,W)
Usage:
(1) Calcuate the Mel-frequency Cepstral Coefficients
f=v_rfft(s); % v_rfft() returns only 1+floor(n/2) coefficients
x=v_filtbankm(p,n,fs,0,fs/2,'m'); % n is the fft length, p is the number of filters wanted
z=log(x*abs(f).^2); % multiply x by the power spectrum
c=dct(z); % take the DCT
(2) Calcuate the Mel-frequency Cepstral Coefficients efficiently
f=fft(s); % n is the fft length, p is the number of filters wanted
[x,cf,na,nb]=v_filtbankm(p,n,fs,0,fs/2,'m'); % na:nb gives the fft bins that are needed
z=log(x*(f(na:nb)).*conj(f(na:nb))); % multiply x by the power spectrum
c=dct(z); % take the DCT
(3) Plot the calculated filterbanks as a graph
plot((0:floor(n/2))*fs/n,v_filtbankm(p,n,fs,0,fs/2,'m')') % fs=sample frequency
(4) Plot the calculated filterbanks as a spectrogram
v_filtbankm(p,n,fs,0,fs/2,'m');
Inputs:
p number of filters in v_filterbank or the filter spacing in k-mel/bark/erb (see 'p' and 'P' options) [ceil(4.6*log10(fs))]
n length of dft
or [nfrq dfrq frq1] nfrq=number of input frequency bins, dfrq=frequency increment (Hz), frq1=first bin freq (Hz)
fs sample rate in Hz
fl low end of the lowest filter in Hz (or in mel/erb/bark/log10 with 'h' option) [default = 0 or 30Hz for 'l' option]
fh high end of highest filter in Hz (or in mel/erb/bark/log10 with 'h' option) [default = fs/2]
w any sensible combination of the following:
'b' = bark scale instead of mel
'e' = erb-rate scale
'l' = log10 Hz frequency scale
'f' = linear frequency scale [default]
'm' = mel frequency scale
'n' - round to the nearest FFT bin so each row of x contains only one non-zero entry
'c' = fl & fh specify centre of low and high filters instead of edges
'h' = fl & fh are in mel/erb/bark/log10 instead of Hz
'H' = give cf outputs in mel/erb/bark/log10 instead of Hz
'y' = lowest filter remains at 1 down to 0 frequency and highest filter remains at 1 up to nyquist freqency
The total power in the fft is preserved (unless 'u' is specified).
'Y' = extend only at low frequency end (or high end if 'y' also specified)
'p' = input P specifies the number of filters [default if P>=1]
'P' = input P specifies the filter spacing [default if P<1]
'u' = input and output are power per Hz instead of power.
'U' = input is power but output is power per Hz.
's' = single-sided input: do not include symmetric negative frequencies (i.e. non-DC inputs have already been doubled)
'S' = single-sided output: do not mirror the non-DC filter characteristics (i.e. double non-DC outputs)
'g' = plot filter coefficients as graph
'G' = plot filter coefficients as spectrogram image [default if no output arguments present]
Outputs: x(p,k) a sparse matrix containing the v_filterbank amplitudes
If the il and ih outputs are included then k=ih-il+1 otherwise k=1+floor(n/2)
Note that the peak filter values equal 2 to account for the power in the negative FFT frequencies.
cf(p) the v_filterbank centre frequencies in Hz (or in mel/erb/bark/log10 with 'H' option)
il the lowest fft bin with a non-zero coefficient
ih the highest fft bin with a non-zero coefficient
The routine performs interpolation of the input spectrum by convolving the power spectrum
with a triangular filter and then simulates a v_filterbank with asymetric triangular filters.

0001 function [x,cf,il,ih]=v_filtbankm(p,n,fs,fl,fh,w) 0002 %V_FILTBANKM determine matrix for a linear/mel/erb/bark-spaced v_filterbank [X,IL,IH]=(P,N,FS,FL,FH,W) 0003 % 0004 % Usage: 0005 % (1) Calcuate the Mel-frequency Cepstral Coefficients 0006 % 0007 % f=v_rfft(s); % v_rfft() returns only 1+floor(n/2) coefficients 0008 % x=v_filtbankm(p,n,fs,0,fs/2,'m'); % n is the fft length, p is the number of filters wanted 0009 % z=log(x*abs(f).^2); % multiply x by the power spectrum 0010 % c=dct(z); % take the DCT 0011 % 0012 % (2) Calcuate the Mel-frequency Cepstral Coefficients efficiently 0013 % 0014 % f=fft(s); % n is the fft length, p is the number of filters wanted 0015 % [x,cf,na,nb]=v_filtbankm(p,n,fs,0,fs/2,'m'); % na:nb gives the fft bins that are needed 0016 % z=log(x*(f(na:nb)).*conj(f(na:nb))); % multiply x by the power spectrum 0017 % c=dct(z); % take the DCT 0018 % 0019 % (3) Plot the calculated filterbanks as a graph 0020 % 0021 % plot((0:floor(n/2))*fs/n,v_filtbankm(p,n,fs,0,fs/2,'m')') % fs=sample frequency 0022 % 0023 % (4) Plot the calculated filterbanks as a spectrogram 0024 % 0025 % v_filtbankm(p,n,fs,0,fs/2,'m'); 0026 % 0027 % Inputs: 0028 % p number of filters in v_filterbank or the filter spacing in k-mel/bark/erb (see 'p' and 'P' options) [ceil(4.6*log10(fs))] 0029 % n length of dft 0030 % or [nfrq dfrq frq1] nfrq=number of input frequency bins, dfrq=frequency increment (Hz), frq1=first bin freq (Hz) 0031 % fs sample rate in Hz 0032 % fl low end of the lowest filter in Hz (or in mel/erb/bark/log10 with 'h' option) [default = 0 or 30Hz for 'l' option] 0033 % fh high end of highest filter in Hz (or in mel/erb/bark/log10 with 'h' option) [default = fs/2] 0034 % w any sensible combination of the following: 0035 % 0036 % 'b' = bark scale instead of mel 0037 % 'e' = erb-rate scale 0038 % 'l' = log10 Hz frequency scale 0039 % 'f' = linear frequency scale [default] 0040 % 'm' = mel frequency scale 0041 % 0042 % 'n' - round to the nearest FFT bin so each row of x contains only one non-zero entry 0043 % 0044 % 'c' = fl & fh specify centre of low and high filters instead of edges 0045 % 'h' = fl & fh are in mel/erb/bark/log10 instead of Hz 0046 % 'H' = give cf outputs in mel/erb/bark/log10 instead of Hz 0047 % 0048 % 'y' = lowest filter remains at 1 down to 0 frequency and highest filter remains at 1 up to nyquist freqency 0049 % The total power in the fft is preserved (unless 'u' is specified). 0050 % 'Y' = extend only at low frequency end (or high end if 'y' also specified) 0051 % 0052 % 'p' = input P specifies the number of filters [default if P>=1] 0053 % 'P' = input P specifies the filter spacing [default if P<1] 0054 % 0055 % 'u' = input and output are power per Hz instead of power. 0056 % 'U' = input is power but output is power per Hz. 0057 % 0058 % 's' = single-sided input: do not include symmetric negative frequencies (i.e. non-DC inputs have already been doubled) 0059 % 'S' = single-sided output: do not mirror the non-DC filter characteristics (i.e. double non-DC outputs) 0060 % 0061 % 'g' = plot filter coefficients as graph 0062 % 'G' = plot filter coefficients as spectrogram image [default if no output arguments present] 0063 % 0064 % 0065 % Outputs: x(p,k) a sparse matrix containing the v_filterbank amplitudes 0066 % If the il and ih outputs are included then k=ih-il+1 otherwise k=1+floor(n/2) 0067 % Note that the peak filter values equal 2 to account for the power in the negative FFT frequencies. 0068 % cf(p) the v_filterbank centre frequencies in Hz (or in mel/erb/bark/log10 with 'H' option) 0069 % il the lowest fft bin with a non-zero coefficient 0070 % ih the highest fft bin with a non-zero coefficient 0071 % 0072 % The routine performs interpolation of the input spectrum by convolving the power spectrum 0073 % with a triangular filter and then simulates a v_filterbank with asymetric triangular filters. 0074 0075 % 0076 % References: 0077 % 0078 % [1] S. S. Stevens, J. Volkman, and E. B. Newman. A scale for the measurement 0079 % of the psychological magnitude of pitch. J. Acoust Soc Amer, 8: 185-190, 1937. 0080 % [2] S. Davis and P. Mermelstein. Comparison of parametric representations for 0081 % monosyllabic word recognition in continuously spoken sentences. 0082 % IEEE Trans Acoustics Speech and Signal Processing, 28 (4): 357-366, Aug. 1980. 0083 0084 % Bugs/Suggestions 0085 % (1) default frequencies won't work if the h option is specified 0086 % (2) low default frequency is invalid if the 'l' option is specified 0087 % (3) Add 'z' option to include a DC output as the first coefficient 0088 % (4) Add 'Z' option to ignore the DC input component 0089 % (5) Add 'i' option to calculate the inverse of x instead 0090 0091 % Copyright (C) Mike Brookes 1997-2009 0092 % Version: $Id: v_filtbankm.m 10865 2018-09-21 17:22:45Z dmb $ 0093 % 0094 % VOICEBOX is a MATLAB toolbox for speech processing. 0095 % Home page: http://www.ee.ic.ac.uk/hp/staff/dmb/voicebox/voicebox.html 0096 % 0097 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 0098 % This program is free software; you can redistribute it and/or modify 0099 % it under the terms of the GNU General Public License as published by 0100 % the Free Software Foundation; either version 2 of the License, or 0101 % (at your option) any later version. 0102 % 0103 % This program is distributed in the hope that it will be useful, 0104 % but WITHOUT ANY WARRANTY; without even the implied warranty of 0105 % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0106 % GNU General Public License for more details. 0107 % 0108 % You can obtain a copy of the GNU General Public License from 0109 % http://www.gnu.org/copyleft/gpl.html or by writing to 0110 % Free Software Foundation, Inc.,675 Mass Ave, Cambridge, MA 02139, USA. 0111 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 0112 0113 % Notes: 0114 % (1) In the comments, "FFT bin_0" assumes DC = bin 0 whereas "FFT bin_1" means DC = bin 1 0115 % (2) "input" and "output" need to be interchanged if the 'i' option is given 0116 0117 if nargin < 6 % if no mode option, w, is specified 0118 w='f'; % default mode option: 'f' = linear output frequency scale 0119 elseif isempty(w) 0120 w=' '; % ensure mode, w, is not empty 0121 end 0122 wr=max(any(repmat('lebm',length(w),1)==repmat(w',1,4),1).*(1:4)); % output warping: 0=linear,1=log,2=erbrate,3=bark,4=mel 0123 if nargin < 5 || ~numel(fh) 0124 fh=0.5*fs; % max freq is the nyquist 0125 end 0126 if nargin < 4 || ~numel(fl) 0127 fl=30*(wr==1); % lower limit is 0 Hz unless 'l' option specified, in which case it is 30 Hz 0128 end 0129 fa=0; % first input frequency bin defaults to 0 0130 if numel(n)>1 0131 nf=n(1); % number of input frequency bins 0132 df=n(2); % input frequency bin spacing 0133 if numel(n)>2 0134 fa=n(3); % frequency of first bin 0135 end 0136 else % n gives dft length 0137 nf=1+floor(n/2); % number of input frequency bins 0138 df=fs/n; % input frequency bin spacing 0139 end 0140 fin0=fa+(0:nf-1)*df; % nf input frequency bins linearly spaced from fa to fa+(nf-1)*df 0141 mflh=[fl fh]; % low and high limits of filterbank triangular filters 0142 if ~any(w=='h') % convert mflh from Hz to mel/erb/... unless already converted via 'h' option 0143 switch wr 0144 case 1 % 'l' = log scaled 0145 if fl<=0 0146 error('Low frequency limit must be >0 for ''l'' option'); 0147 end 0148 mflh=log10(mflh); % convert frequency limits into log10 Hz 0149 case 2 % 'e' = erb-rate scaled 0150 mflh=v_frq2erb(mflh); % convert frequency limits into erb-rate 0151 case 3 % 'b' = bark scaled 0152 mflh=v_frq2bark(mflh); % convert frequency limits into bark 0153 case 4 % 'm' = mel scaled 0154 mflh=v_frq2mel(mflh); % convert frequency limits into mel 0155 end 0156 end 0157 melrng=mflh(2)-mflh(1); % mel/erb/... range 0158 if isempty(p) 0159 p=ceil(4.6*log10(2*(fa+(nf-1)*df))); % default number of output filters 0160 end 0161 puc=any(w=='P') || (p<1) && ~any(w=='p'); % input p specifies the filter spacing rather than the number of filters 0162 if any(w=='c') % 'c' option: melrng excludes outer halves of the first and last filters 0163 if puc % the p input specifies the filter spacing 0164 p=round(melrng/(p*1000))+1; % p now gives the number of filters 0165 end 0166 melinc=melrng/(p-1); % inter-filter increment in mel 0167 mflh=mflh+(-1:2:1)*melinc; % update mflh to include the full width of all filters 0168 else % melrng includes full width of all filters 0169 if puc % the p input specifies the filter spacing 0170 p=round(melrng/(p*1000))-1; % p now gives the number of filters 0171 end 0172 melinc=melrng/(p+1); % inter-filter increment in mel 0173 end 0174 % 0175 % Calculate the FFT bins0 corresponding to the filters 0176 % 0177 cf=mflh(1)+(0:p+1)*melinc; % centre frequencies in mel/erb/... including dummy ends 0178 cf(2:end)=max(cf(2:end),0); % only the first point can be negative 0179 switch wr % convert centre frequencies from mel/erb/... to Hz 0180 case 1 % 'l' = log scaled 0181 mb=10.^(cf); 0182 case 2 % 'e' = erb-rate scaled 0183 mb=v_erb2frq(cf); 0184 case 3 % 'b' = bark scaled 0185 mb=v_bark2frq(cf); 0186 case 4 % 'm' = mel scaled 0187 mb=v_mel2frq(cf); 0188 otherwise % [default] = linear scaled 0189 mb=cf; 0190 end 0191 % 0192 % first sort out 2-sided input frequencies 0193 % 0194 fin=fin0; % input frequency bin values 0195 fin(nf+1)=fin(nf)+df; % add on a dummy point at the high end 0196 if fin(1)==0 0197 fin=[-fin(nf+1:-1:2) fin]; 0198 elseif fin(1)<=df/2 0199 fin=[-fin(nf+1:-1:1) fin]; 0200 elseif fin(1)<df 0201 fin=[-fin(nf+1:-1:1) fin(1)-df df-fin(1) fin]; 0202 elseif fin(1)==df 0203 fin=[-fin(nf+1:-1:1) 0 fin]; 0204 else 0205 fin=[-fin(nf+1:-1:1) df-fin(1) fin(1)-df fin]; 0206 end 0207 nfin=length(fin); % length of extended input frequency list 0208 % 0209 % now sort out the interleaving 0210 % 0211 fout=mb; % output frequencies in Hz 0212 lowex=any(w=='y')~=any(w=='Y'); % extend to 0 Hz 0213 highex=any(w=='y') && (fout(end-1)<fin(end)); % extend at high end 0214 if lowex 0215 fout=[0 0 fout(2:end)]; 0216 end 0217 if highex 0218 fout=[fout(1:end-1) fin(end) fin(end)]; 0219 end 0220 mfout=length(fout); 0221 if any(w=='u') || any(w=='U') 0222 gout=fout(3:mfout)-fout(1:mfout-2); 0223 gout=2*(gout+(gout==0)).^(-1); % Gain of output triangles 0224 else 0225 gout=ones(1,mfout-2); 0226 end 0227 if any(w=='S') 0228 msk=fout(2:mfout-1)~=0; 0229 gout(msk)=2*gout(msk); % double non-DC outputs for a 1-sided output spectrum 0230 end 0231 if any(w=='u') 0232 gin=ones(1,nfin-2); 0233 else 0234 gin=fin(3:nfin)-fin(1:nfin-2); 0235 gin=2*(gin+(gin==0)).^(-1); % Gain of input triangles 0236 end 0237 msk=fin(2:end-1)==0; 0238 if any(w=='s') 0239 gin(~msk)=0.5*gin(~msk); % halve non-DC inputs to change back to a 2-sided spectrum 0240 end 0241 if lowex 0242 gin(msk)=2*gin(msk); % double DC input to preserve its power 0243 end 0244 foutin=[fout fin]; 0245 nfall=length(foutin); 0246 wleft=[0 fout(2:mfout)-fout(1:mfout-1) 0 fin(2:nfin)-fin(1:nfin-1)]; % left width 0247 wright=[wleft(2:end) 0]; % right width 0248 ffact=[0 gout 0 0 gin(1:min(nf,nfin-nf-2)) zeros(1,max(nfin-2*nf-2,0)) gin(nfin-nf-1:nfin-2) 0]; % gain of triangle posts 0249 % ffact(wleft+wright==0)=0; % disable null width triangles shouldn't need this if all frequencies are distinct 0250 [fall,ifall]=sort(foutin); 0251 jfall=zeros(1,nfall); 0252 infall=1:nfall; 0253 jfall(ifall)=infall; % unsort->sort index 0254 ffact(ifall([1:max(jfall(1),jfall(mfout+1))-2 min(jfall(mfout),jfall(nfall))+2:nfall]))=0; % zap nodes that are much too small/big 0255 nxto=cumsum(ifall<=mfout); 0256 nxti=cumsum(ifall>mfout); 0257 nxtr=min(nxti+1+mfout,nfall); % next input node to the right of each value (or nfall if none) 0258 nxtr(ifall>mfout)=1+nxto(ifall>mfout); % next post to the right of opposite type (unsorted indexes) 0259 nxtr=nxtr(jfall); % next post to the right of opposite type (converted to unsorted indices) or if none: nfall/(mfout+1) 0260 % 0261 % Each triangle is "attached" to the node at its extreme right end. 0262 % The general result for integrating the product of two trapesiums with 0263 % heights (a,b) and (c,d) over a width x is (ad+bc+2bd+2ac)*x/6 0264 % 0265 % integrate product of lower triangles 0266 % 0267 msk0=(ffact>0); 0268 msk=msk0 & (ffact(nxtr)>0); % select appropriate triangle pairs (unsorted indices) 0269 ix1=infall(msk); % unsorted indices of leftmost post of pair 0270 jx1=nxtr(msk); % unsorted indices of rightmost post of pair 0271 vfgx=foutin(ix1)-foutin(jx1-1); % length of right triangle to the left of the left post 0272 yx=min(wleft(ix1),vfgx); % integration length 0273 wx1=ffact(ix1).*ffact(jx1).*yx.*(wleft(ix1).*vfgx-yx.*(0.5*(wleft(ix1)+vfgx)-yx/3))./(wleft(ix1).*wleft(jx1)+(yx==0)); 0274 0275 % integrate product of upper triangles 0276 0277 nxtu=max([nxtr(2:end)-1 0],1); 0278 msk=msk0 & (ffact(nxtu)>0); 0279 ix2=infall(msk); % unsorted indices of leftmost post of pair 0280 jx2=nxtu(msk); % unsorted indices of rightmost post of pair 0281 vfgx=foutin(ix2+1)-foutin(jx2); % length of left triangle to the right of the right post 0282 yx=min(wright(ix2),vfgx); % integration length 0283 yx(foutin(jx2+1)<foutin(ix2+1))=0; % zap invalid triangles 0284 wx2=ffact(ix2).*ffact(jx2).*yx.^2.*((0.5*(wright(jx2)-vfgx)+yx/3))./(wright(ix2).*wright(jx2)+(yx==0)); 0285 0286 % integrate lower triangle and upper triangle that ends to its right 0287 0288 nxtu=max(nxtr-1,1); 0289 msk=msk0 & (ffact(nxtu)>0); 0290 ix3=infall(msk); % unsorted indices of leftmost post of pair 0291 jx3=nxtu(msk); % unsorted indices of rightmost post of pair 0292 vfgx=foutin(ix3)-foutin(jx3); % length of upper triangle to the left of the lower post 0293 yx=min(wleft(ix3),vfgx); % integration length 0294 yx(foutin(jx3+1)<foutin(ix3))=0; % zap invalid triangles 0295 wx3=ffact(ix3).*ffact(jx3).*yx.*(wleft(ix3).*(wright(jx3)-vfgx)+yx.*(0.5*(wleft(ix3)-wright(jx3)+vfgx)-yx/3))./(wleft(ix3).*wright(jx3)+(yx==0)); 0296 0297 % integrate upper triangle and lower triangle that starts to its right 0298 0299 nxtu=[nxtr(2:end) 1]; 0300 msk=msk0 & (ffact(nxtu)>0); 0301 ix4=infall(msk); % unsorted indices of leftmost post of pair 0302 jx4=nxtu(msk); % unsorted indices of rightmost post of pair 0303 vfgx=foutin(ix4+1)-foutin(jx4-1); % length of upper triangle to the left of the lower post 0304 yx=min(wright(ix4),vfgx); % integration length 0305 wx4=ffact(ix4).*ffact(jx4).*yx.^2.*(0.5*vfgx-yx/3)./(wright(ix4).*wleft(jx4)+(yx==0)); 0306 0307 % now create the matrix 0308 0309 iox=sort([ix1 ix2 ix3 ix4;jx1 jx2 jx3 jx4]); 0310 % [iox;[wx1 wx2 wx3 wx4]>0 ] 0311 msk=iox(2,:)<=(nfall+mfout)/2; 0312 iox(2,msk)=(nfall+mfout+1)-iox(2,msk); % convert negative frequencies to positive 0313 if highex 0314 iox(1,iox(1,:)==mfout-1)=mfout-2; % merge highest two output nodes 0315 end 0316 if lowex 0317 iox(1,iox(1,:)==2)=3; % merge lowest two output nodes 0318 end 0319 0320 x=sparse(iox(1,:)-1-lowex,max(iox(2,:)-nfall+nf+1,1),[wx1 wx2 wx3 wx4],p,nf); 0321 % 0322 % sort out the output argument options 0323 % 0324 if ~any(w=='H') 0325 cf=mb; % output Hz instead of mel/erb/... 0326 end 0327 cf=cf(2:p+1); % remove dummy end frequencies 0328 il=1; 0329 ih=nf; 0330 if any(w=='n') % round outputs to the centre of gravity bin 0331 sx2=sum(x,2); 0332 msk=full(sx2~=0); 0333 vxc=zeros(p,1); 0334 vxc(msk)=round((x(msk,:)*(1:nf)')./sx2(msk)); 0335 x=sparse(1:p,vxc,sx2,p,nf); 0336 end 0337 if nargout > 2 0338 msk=full(any(x>0,1)); 0339 il=find(msk,1); 0340 if ~numel(il) 0341 ih=1; 0342 elseif nargout >3 0343 ih=find(msk,1,'last'); 0344 end 0345 x=x(:,il:ih); 0346 end 0347 if any(w=='u') 0348 sx=sum(x,2); 0349 x=x./repmat(sx+(sx==0),1,size(x,2)); 0350 end 0351 % 0352 % plot results if no output arguments or g option given 0353 % 0354 if ~nargout || any(w=='g') || any(w=='G') % plot idealized filters 0355 if ~any(w=='g') && ~any(w=='G') 0356 w=[w 'G']; 0357 end 0358 newfig=0; 0359 if any(w=='g') 0360 plot(fa-df+(il:ih)*df,x'); 0361 title(['filtbankm: mode = ' w]); 0362 xlabel(['Frequency (' v_xticksi 'Hz)']); 0363 ylabel('Weight'); 0364 newfig=1; 0365 end 0366 0367 if any(w=='G') 0368 if newfig 0369 figure; 0370 end 0371 imagesc(fa-df+(il:ih)*df,1:p,x); 0372 axis 'xy' 0373 colorbar; 0374 v_cblabel('Weight'); 0375 switch wr 0376 case 1 0377 type='Log-spaced'; 0378 case 2 0379 type='Erb-spaced'; 0380 case 3 0381 type='Bark-spaced'; 0382 case 4 0383 type='Mel-spaced'; 0384 otherwise 0385 type='Linear-spaced'; 0386 end 0387 ylabel([type ' Filter']); 0388 xlabel(['Frequency (' v_xticksi 'Hz)']); 0389 title(['filtbankm: mode = ' w]); 0390 end 0391 0392 end