function md(action)
%MD Demonstration of missing data continuous digit recognition using HMMs.
%
% To start the process, load a speech file via the LOAD menu.
%
% Recognition takes place on the fly, and ought to take no longer than a
% few seconds on most computers.
%
% The default is to recognise without missing data techniques. Use the menus
% to use MD methods.
% 
%
% Martin Cooke, January 1998
%   Revised March 98 to use structures rather than objects (which are 
%      too slow
%   Updated June 98 to use one 64'th of the memory!
%      (no more visualising output probabilities at the individual channel
%       level)
%   updated for v2.1
%   NOTE: this reflects MD processing c. 1998 and not the latest version
%         which will one day become a demo

if nargin < 1
  action='init';
end

switch action

case 'init'
  % does main figure window exist? if so, bring it to front
  f=findobj('Tag','md_fig');
  if ~isempty(f)
    figure(f);
  else
    md_gui;		% create one
    ud=get(gcf,'UserData');
    udlf=get(ud.lp_fig,'UserData'); hmmmenu1=udlf.menu;
    udhf=get(ud.hmm_fig,'UserData'); hmmmenu2=udhf.menu;
    [p,n,e,v]=fileparts(which('md'));
    hmmfiles=dir(fullfile(p ,'digithmms',''));
    hmmfiles=hmmfiles([hmmfiles.isdir]==0);	% remove directories  
    ud.hmms(1)=emptyhmm;
    ud.numHmms=length(hmmfiles);
    for i=1:ud.numHmms
      ud.hmms(i)=hmmFromFile(fullfile(p ,'digithmms',hmmfiles(i).name));
      uimenu('Parent',hmmmenu1,'Label',ud.hmms(i).label,'Callback','md HMMChanged','Tag','HMMmenu');
      uimenu('Parent',hmmmenu2,'Label',ud.hmms(i).label,'Callback','md HMMChanged2','Tag','HMMmenu');
      if ud.hmms(i).numStates==1
        ud.silmod=i;
      end
    end
    ud.testName='';
    ud.nozzleWidth=5;
    set(findobj('Tag','NozzleWidth','Label','5'),'Checked','on');
    ud.nozzleHeight=5;
    set(findobj('Tag','NozzleHeight','Label','5'),'Checked','on');
    ud.numFrames=0;
    ud.numChans=0;
    ud.missing=0;
    ud.recompute=0;			% indicate whether to recalc probs or not
    ud.im=[];
    ud.mask=[];
    ud.md_mask=[];
    ud.peaksmask=[];
    ud.data=[];
    ud.noise=[];
    ud.overall=[];			% the thing which gets displayed (prior to scaling)
    ud.noiseLevel=0;
    ud.meanLevel=0;
    ud.currentHMM=2;
    ud.currentState=1;
    ud.snr=40;
    ud.use_peaks=0;
    ud.use_snr=0;
    ud.use_snr3=0;
    ud.use_Hirsch=0;
    ud.use_md=0;
    ud.use_ai=0;
    %ud.lastchans=1:ud.numChans;
    ud.lastframes=1:ud.numFrames;  % indicates which is the last region changed
    ud.maxStates=max([ud.hmms.numStates]); 
    ud.pnext=-1e+50*ones(ud.numHmms,ud.maxStates);
    ud.pself=-1e+50*ones(ud.numHmms,ud.maxStates);
    for i=1:ud.numHmms
      h=ud.hmms(i);
      ud.pnext(i,1:h.numStates)=h.pNext;
      ud.pself(i,1:h.numStates)=h.pSelf;
    end
    % deal with silence model
    ud.pnext(ud.silmod,ud.maxStates)=ud.hmms(ud.silmod).pNext(1);
    ud.pnext(ud.silmod,1)=-1e+50;   
    ud.mode='erase';   
    
    % put names of speech  and noise files on menu
    speechmenu=findobj(gcf,'Tag','loadspeech');
    files=dir(fullfile(p, 'data_digit_strings',''));
    for fnum=1:length(files)
      if ~files(fnum).isdir
        uimenu('Parent', speechmenu,'Label',files(fnum).name, 'Callback','md loadspeech');
      end
    end 
    noisemenu=findobj(gcf,'Tag','loadnoise');
    files=dir(fullfile(p, 'data_digit_strings',''));
    for fnum=1:length(files)
      if ~files(fnum).isdir
        uimenu('Parent',noisemenu,'Label',files(fnum).name, 'Callback','md loadnoise');
      end
    end
  
    set(gcf,'UserData',ud);
  end

case 'loadspeech'
  ud=get(gcf,'UserData');
  nt=get(gcbo,'Label');
  if ~strcmp(ud.testName,nt)    % data not already loaded
    ud.testName = nt;
    [p,n,e,v]=fileparts(which('md'));
    ud.data=LoadFromFile(fullfile(p, 'data_digit_strings', nt));    
    [ud.numChans,ud.numFrames]=size(ud.data);
    % clear all axes objects and set same XLim
    set(ud.testAxes,'XLim',[1 ud.numFrames],'YLim',[1 ud.numChans]);
    ud.mask=ones(size(ud.data));
    ud.md_mask=ud.mask;
    ud.noise=-100.*ones(size(ud.data));
    axes(ud.testAxes);
    ud.peaksmask=findPeaks(ud.data);
    ud.overall=dBadd(ud.data,ud.noise).*ud.mask;
    ud.im=imagesc(ud.overall);
    set(ud.im,'ButtonDownFcn','md edit','EraseMode','xor');
    set(ud.labelAxes,'XLim',[1 ud.numFrames],'NextPlot','replacechildren');
    title(['speech file: ' ud.testName]);
    ud.meanLevel=mean(ud.data(:));
    %ud.lastchans=1:ud.numChans;
    ud.lastframes=1:ud.numFrames;
    %ud.outprobs(1:ud.numHmms,1:ud.maxStates,1:ud.numChans,1:ud.numFrames)=0;
    %ud.aiprobs(1:ud.numHmms,1:ud.maxStates,1:ud.numChans,1:ud.numFrames)=0;
    ud.frameprobs(1:ud.numHmms,1:ud.maxStates,1:ud.numFrames)=0;
    % enable controls
    set(findobj(gcf,'Enable','off'),'Enable','on');
    set(findobj(gcf,'Tag','AImenu'),'Enable','off');
    set(findobj(gcf,'Tag','Hirsch'),'Enable','off');
    set(gcf,'UserData',ud);
    md 'recalculate'
    % md 'NoiseLevelChanged'
  else
    disp(['data file ' nt ' already loaded.']);
  end

case 'loadnoise'
  ud=get(gcf,'UserData');
  nt=get(gcbo,'Label');
  [p,n,e,v]=fileparts(which('md'));
  n=LoadFromFile(fullfile(p, 'data_digit_strings', nt));
  [numChans,numFrames]=size(n);
  noise=zeros(ud.numChans,ud.numFrames);
  if numFrames < ud.numFrames
    noise(:,1:numFrames)=n;
  else
    noise=n(:,1:ud.numFrames);
  end
  ud.noise=noise;
  set(gcf,'UserData',ud);
  %ud.lastchans=1:ud.numChans;
  ud.lastframes=1:ud.numFrames;
  md 'recalculate';
  
case 'recalculate'
  ud=get(gcf,'UserData');
  % First, calculate and display mask
  dmask=ud.mask;
  if ud.use_peaks                     % recognition using peaks
    dmask=and(dmask,ud.peaksmask);
  end
  if ud.use_Hirsch
    mix=dBadd(ud.data,ud.noise);
    ud.nest=Hirsch(mix);
    dmask = and(dmask,(mix-3) > ud.nest);
  elseif ud.use_snr                       % recognition using postive local SNR
    dmask = and(dmask,ud.data>ud.noise);
  elseif ud.use_snr3
    dmask = and(dmask,(ud.data-3)>ud.noise);
  end

  ud.overall=dBadd(ud.data,ud.noise).*dmask;
  set(ud.im,'CData',ud.overall);
  missing=round(100*sum(1-dmask(:))/(ud.numFrames*ud.numChans));
  set(findobj('Tag','StatusString'),'String',[num2str(round(missing)) ' % missing']);
  % calculate new SNR
  mean_s=mean(db2amp(ud.data(:),80));
  mean_n=mean(db2amp(ud.noise(:),80));
  if mean_n > 0
    ud.snr=amp2db(mean_s/mean_n,80);
  else
    ud.snr=40;
  end
  set(findobj('Tag','globalSNR'),'String',num2str(round(ud.snr)));
  %drawnow;
  set(gcf,'UserData',ud);
  
  if get(findobj(gcf,'Tag','recalcCheck'),'Value')
    % now work out masks for recognition
    if ud.use_md                      % treat masked values as missing
      ud.md_mask = dmask;
    else
      ud.md_mask = ones(size(ud.data));
    end
    if ud.use_ai                       % perform auditory induction
      ud.ai_mask = and(1-ud.md_mask,ud.peaksmask);
    end
    set(gcf,'UserData',ud);
    md 'recompute'
    if strcmp(get(ud.lp_fig,'Visible'),'on')
      md 'visualise_probs'
    end
  end  

case 'NozzleWidthChanged'
  ud=get(gcf,'UserData');
  s=get(gcbo,'Label');
  if strcmp(s,'all')
    ud.nozzleWidth=max(1,ud.numFrames);
  else
    ud.nozzleWidth=floor(str2num(get(gcbo,'Label'))/2)
  end
  set(findobj('Tag','NozzleWidth'),'Checked','off');
  set(gcbo,'Checked','on');
  set(gcf,'UserData',ud);
  
case 'NozzleHeightChanged'
  ud=get(gcf,'UserData');
  s=get(gcbo,'Label');
  if strcmp(s,'all')
    ud.nozzleHeight=max(1,ud.numChans);
  else
    ud.nozzleHeight=floor(str2num(get(gcbo,'Label'))/2);
  end
  set(findobj('Tag','NozzleHeight'),'Checked','off');
  set(gcbo,'Checked','on');  
  set(gcf,'UserData',ud);
  
case 'use_peaks'
  ud=get(gcf,'UserData');
  ud.use_peaks=toggle(gcbo);
  %ud.lastchans=1:ud.numChans;
  ud.lastframes=1:ud.numFrames;
  set(gcf,'UserData',ud);  
  md 'recalculate'
  
case 'use_snr'
  ud=get(gcf,'UserData');
  ud.use_snr=toggle(gcbo);
  if ud.use_snr
    ud.use_snr3=0; ud.use_Hirsch=0;
    set(findobj('Tag','snr3'),'Checked','off');
    set(findobj('Tag','Hirsch'),'Checked','off');
  end
  %ud.lastchans=1:ud.numChans;
  ud.lastframes=1:ud.numFrames;
  set(gcf,'UserData',ud);
  md 'recalculate'
  
case 'use_snr3'
  ud=get(gcf,'UserData');
  ud.use_snr3=toggle(gcbo);
  if ud.use_snr3
    ud.use_snr=0;  ud.use_Hirsch=0;
    set(findobj('Tag','snr0'),'Checked','off');
    set(findobj('Tag','Hirsch'),'Checked','off');
  end
  %ud.lastchans=1:ud.numChans;
  ud.lastframes=1:ud.numFrames;
  set(gcf,'UserData',ud);
  md 'recalculate'
  
case 'use_Hirsch'
  ud=get(gcf,'UserData');
  ud.use_Hirsch=toggle(gcbo);
  if ud.use_Hirsch
    ud.use_snr3=0; ud.use_snr=0;
    set(findobj('Tag','snr3'),'Checked','off');
    set(findobj('Tag','snr0'),'Checked','off');
  end
  %ud.lastchans=1:ud.numChans;
  ud.lastframes=1:ud.numFrames;
  set(gcf,'UserData',ud);
  md 'recalculate'
  
case 'use_md'
  ud=get(gcf,'UserData');
  ud.use_md=toggle(gcbo);
  %ud.lastchans=1:ud.numChans;
  ud.lastframes=1:ud.numFrames;
  set(gcf,'UserData',ud);
  md 'recalculate'
  
case 'use_ai'
  ud=get(gcf,'UserData');
  ud.use_ai=toggle(gcbo);
  %ud.lastchans=1:ud.numChans;
  ud.lastframes=1:ud.numFrames;
  set(gcf,'UserData',ud);
  md 'recalculate'
  
case 'NoiseLevelChanged'
  ud=get(gcf,'UserData');
  s=str2num(StringFromPopup('NoiseLevelPopup'));
  ud.noiseLevel=ud.meanLevel+s;
  set(gcf,'UserData',ud);  
  
case 'globalSNRChanged'
  ud=get(gcf,'UserData');
  ud.snr=min(39.9,max(-39.9,str2num(get(gcbo,'String')))); 
  mean_s=mean(db2amp(ud.data(:)),80);
  mean_n=mean(db2amp(ud.noise(:)),80);
  newmean_n=mean_s/db2amp(ud.snr,80);
  ud.noise=amp2db((newmean_n/mean_n)*db2amp(ud.noise,80),80);
  %ud.lastchans=1:ud.numChans;
  ud.lastframes=1:ud.numFrames;
  set(gcf,'UserData',ud);  
  md 'recalculate'
  
case 'HMMChanged'
  lud=get(gcf,'UserData');
  ud=get(lud.main_fig,'UserData');
  ud.currentHMM=findstr([ud.hmms.label],get(gcbo,'Label'));
  set(findobj(gcf,'Tag','HMMmenu'),'Checked','off');
  set(gcbo,'Checked','on');  
  set(gcf,'UserData',lud);
  figure(lud.main_fig);
  set(gcf,'UserData',ud);  
  md 'visualise_probs'  
  
case 'HMMChanged2'
  lud=get(gcf,'UserData');
  ud=get(lud.main_fig,'UserData');
  ud.currentHMM=findstr([ud.hmms.label],get(gcbo,'Label'));
  set(findobj(gcf,'Tag','HMMmenu'),'Checked','off');
  set(gcbo,'Checked','on');  
  set(gcf,'UserData',lud);
  figure(lud.main_fig);
  set(gcf,'UserData',ud);  
  md 'visualise_hmm'  
  
case 'numStatesChanged'
  lud=get(gcf,'UserData');
  ud=get(lud.main_fig,'UserData');
  s=get(gcbo,'Label');
  ud.currentState=str2num(s);
  set(findobj(gcf,'Tag','Statesmenu'),'Checked','off');
  set(gcbo,'Checked','on');  
  set(gcf,'UserData',lud);
  figure(lud.main_fig);
  set(gcf,'UserData',ud);  
  md 'visualise_probs'  
  
case 'restoreAll'
  ud=get(gcf,'UserData');
  ud.mask=ones(size(ud.data));
  ud.noise=-100.*ones(size(ud.data));
  %ud.lastchans=1:ud.numChans;
  ud.lastframes=1:ud.numFrames;
  set(gcf,'UserData',ud);
  md 'recalculate'
  
case 'doPresetDeletion'
  ud=get(gcf,'UserData');
  form=get(gcbo,'Label');
  ud.mask=ones(ud.numChans,ud.numFrames);
  switch(form)
  case 'random_50%'
    ud.mask(rand(ud.numChans,ud.numFrames)<0.5)=0;  
  case 'random_chans_50%'
    ud.mask(rand(1,ud.numChans)<0.5,:)=0;  
  case 'random_frames_50%'
    ud.mask(:,rand(1,ud.numFrames)<0.5)=0;
  case 'random_90%'
    ud.mask(rand(ud.numChans,ud.numFrames)<0.9)=0;
  case 'random_chans_90%'
    ud.mask(rand(1,ud.numChans)<0.9,:)=0;  
  case 'random_frames_90%'
    ud.mask(:,rand(1,ud.numFrames)<0.9)=0;
  case 'lowpass_50%'
    ud.mask(round(ud.numChans/2)+1:ud.numChans,:)=0; 
  case 'lowpass_25%'
    ud.mask(round(ud.numChans/4)+1:ud.numChans,:)=0;   
  case 'highpass_50%'
    ud.mask(1:round(ud.numChans/2),:)=0;
  case 'highpass_25%'
    ud.mask(1:round(3*ud.numChans/4),:)=0;
  case 'bandpass_50%'
    ud.mask(1:round(ud.numChans/4),:)=0;
    ud.mask(round(3*ud.numChans/4):ud.numChans,:)=0;
  case 'bandpass_25%'
    ud.mask(1:round(3*ud.numChans/8),:)=0;
    ud.mask(round(5*ud.numChans/8):ud.numChans,:)=0;
  case 'two_bands'
    ud.mask=zeros(ud.numChans,ud.numFrames);
    ud.mask(round(ud.numChans/4),:)=1;
    ud.mask(round(3*ud.numChans/4),:)=1;	
  case 'weakest_50%'
    remove=round(0.5*ud.numFrames*ud.numChans);
    [x,i]=sort(ud.data(:));
    ud.mask=ud.mask(:);
    ud.mask(i(1:remove))=0;
    ud.mask=reshape(ud.mask,ud.numChans,ud.numFrames);    
  case 'weakest_90%'
    remove=round(0.9*ud.numFrames*ud.numChans);
    [x,i]=sort(ud.data(:));
    ud.mask=ud.mask(:);
    ud.mask(i(1:remove))=0;
    ud.mask=reshape(ud.mask,ud.numChans,ud.numFrames);  
  end
  %ud.lastchans=1:ud.numChans;
  ud.lastframes=1:ud.numFrames;
  set(gcf,'UserData',ud);
  md 'recalculate'

case 'recompute'   			% recalc local output probs
  ud=get(gcf,'UserData');
  udf=ud.lastframes;
  reg=ud.overall(:,udf);
  mask=ud.md_mask(:,udf);
  if ud.use_ai
    ai_mask=ud.ai_mask(:,udf);
  end
  ss=findobj(gcf,'Tag','StatusString');
  olds=get(ss,'String');
  set(ss,'String','calc output probs...'); drawnow
  if ~isempty(reg)
    [oo,fr]=size(reg);
    o=ones(1,fr);
    for h=1:ud.numHmms
      hmm=ud.hmms(h);
      for s=1:hmm.numStates
        st=hmm.states(s);
        mu=st.mu';
        var=1./st.var';
        gconst=st.logvar2pi';
        xm=reg-mu(:,o);
        %ud.outprobs(h,s,udl,udf)=(-(xm.*xm.*var(:,o))-gconst(:,o)).*mask;
        if ud.use_ai
          sds=1./st.sd';
          counter_evid=log(min(1,max(1e-50,(xm+sds(:,o))./2.*sds(:,o))));
          ud.frameprobs(h,s,udf)=sum((-(xm.*xm.*var(:,o))-gconst(:,o)).*mask)+...     
                                 sum(counter_evid.*ai_mask);
        else
          ud.frameprobs(h,s,udf)=sum((-(xm.*xm.*var(:,o))-gconst(:,o)).*mask);     
        end
      end
    end
  set(gcf,'UserData',ud);
  end  
  set(ss,'String',olds); 
  %drawnow
  md 'recognise'
  
case 'recognise'
  ud=get(gcf,'UserData');
  nm=ud.numHmms;
  sm=ud.silmod;
  ns=ud.maxStates;
  pnext=ud.pnext;
  pself=ud.pself;
  frameptr=ones(nm,ns);
  sframeptr=ones(nm,ns);
  ptotal=zeros(nm,ns);
  ptotal(:,2:ns)=-1e+50;  % to prevent these tokens winning
  self=zeros(nm,ns);
  trans=zeros(nm,ns);
  best=zeros(1,ud.numFrames);
  origin=zeros(1,ud.numFrames);
  move=zeros(nm,ns);
  stay=zeros(nm,ns);
  o=ones(nm,1);
  for f=1:ud.numFrames
    ptotal = ptotal + reshape(ud.frameprobs(:,:,f),nm,ns);
    % move sil model
    ptotal(sm,ns)=ptotal(sm,1);
    [bestExitCost,bestModel] = max(ptotal(:,ns) + pnext(:,ns));
    best(f) = bestModel;
    origin(f) = frameptr(bestModel,ns);
    trans(:,1) = bestExitCost;
    trans(:,2:ns) = pnext(:,1:ns-1) + ptotal(:,1:ns-1);
    self = pself + ptotal;
    stay = self >= trans;
    move = ~stay;
    ptotal(stay) = self(stay);
    ptotal(move) = trans(move);
    sframeptr=[f*o frameptr(:,1:ns-1)];
    frameptr(move)=sframeptr(move);
    % finally, move silence model entry from first to last state
    frameptr(sm,ns)=frameptr(sm,1);
  end
  % traceback
  cf=ud.numFrames;
  soln=[];
  boundaries=[];
  while cf > 1
    bm=best(cf);
    soln=[bm soln];
    boundaries=[cf boundaries];
    cf=origin(cf);
  end
  ud.soln=soln;
  ud.segs=boundaries;
  if ~isempty(ud.soln)
    axes(ud.labelAxes);
    delete(get(gca,'Children'));
    %cla
    left=1;
    for i=1:length(ud.soln)  
      frm=ud.segs(i);
      l=line([frm frm],[0 1]);
      text((left+frm)/2,0.4,ud.hmms(ud.soln(i)).label,'FontSize',12,'HorizontalAlignment','right');
      left=frm;
    end
  end
  set(gcf,'UserData',ud);
  
case 'visualise_probs'
  ud=get(gcf,'UserData');
  set(ud.lp_fig,'Visible','on');
  figure(ud.lp_fig);
  lud=get(gcf,'UserData');
  [nh,ns,nf]=size(ud.frameprobs);
  probs=reshape(ud.frameprobs(ud.currentHMM,:,:),ns,nf);
  if isempty(lud.frameim)
    set(lud.frameProbAxes,'XLim',[1 ud.numFrames],'YTick',[1 ud.maxStates],...
    'YLim',[-1500*(ns+2) 0]);
    axes(lud.frameProbAxes);
    p=probs;
    for c=1:ns
      p(c,:) = p(c,:) - (c-1)*1500*ones(1,nf);
    end
    plot(p');
    %lud.frameim=imagesc(probs.*(probs>-3000)); % colorbar;
    %set(lud.frameim,'EraseMode','none');
    ylabel('HMM State');
  else
    set(lud.frameim,'CData',probs.*(probs>-3000));
  end      
  set(gcf,'UserData',lud);
  figure(lud.main_fig);
  
case 'visualise_hmm'
  ud=get(gcf,'UserData');
  set(ud.hmm_fig,'Visible','on');
  figure(ud.hmm_fig);
  set(ud.hmm_fig,'Name',['HMM means/vars for each state of model for ' ud.hmms(ud.currentHMM).label]);
  h=ud.hmms(ud.currentHMM);
  ns=h.numStates;
  for st=1:ns
    s=h.states(st);
    [numMixes,vecSize]=size(s.mu);
    subplot(2,ns,st);
    % errorbar(s.mu','b',sqrt(s.var'),'r');
    plot(s.mu');
    set(gca,'XLim',[1 vecSize],'YLim',[0 80]);  % this needs generalising!
    if st == 1
      ylabel('means');
    end
    xlabel(num2str(st));
    set(gca,'XTickLabel','','YTickLabel','');
    subplot(2,ns,st+ns);
    plot(s.var');
    set(gca,'XLim',[1 vecSize],'YLim',[0 300]);
    if st == 1
      ylabel('vars');
    end
    set(gca,'XTickLabel','','YTickLabel','');
  end;
  hud=get(gcf,'UserData');
  figure(hud.main_fig);
  
case 'hide_fig'
  set(gcf,'Visible','off');


case 'edit'
  ud=get(gcf,'UserData');
  cp=get(gca,'CurrentPoint');
  x=ceil(cp(1,1));
  y=ceil(cp(1,2));
  nw=ud.nozzleWidth;
  nh=ud.nozzleHeight;
  ud.lastchans=max(1,y-nh):min(ud.numChans,y+nh);
  ud.lastframes=max(1,x-nw):min(ud.numFrames,x+nw);
  switch StringFromPopup('ModePopup')
  case 'erase' 
    ud.mask(ud.lastchans,ud.lastframes)=0;
  case 'restore'
    ud.mask(ud.lastchans,ud.lastframes)=1;
  case 'add_noise'
    ud.noise(ud.lastchans,ud.lastframes)=ud.noiseLevel;  
  otherwise
end
set(gcf,'UserData',ud);
md 'recalculate'
  
case 'reallyquit'
  ud=get(gcf,'UserData');
  delete(ud.lp_fig);
  delete(ud.hmm_fig);
  delete(get(0,'CurrentFigure'));
  
otherwise
  maduievent('md',action);
  
end
%-----------------------------------
% HMM STUFF
%-----------------------------------
function h=emptyhmm

h.states=[];
h.pNext=[];
h.pSelf=[];
h.label='';
h.numStates=0;
%-----------------------------------
function x=LoadFromFile(f)

fid = fopen(f,'r');
if fid == -1
  error(['md: file ' f ' not found']);
end
[x,count]=fscanf(fid,'%d');
x=reshape(x,64,round(count/64));

%------------------------------------
function p=findPeaks(x)
% smooth x and find peaks
[r,c]=size(x);
d=x;
%d=conv(x',[0.25 0.5 0.25])';
d1=zeros(size(x));
d2=d1;
d1(1:r-1,:)=d(2:r,:);
d2(2:r,:)=d(1:r-1,:);
p=and(d>d1,d>d2);

%----------------------------------
function str=StringFromPopup(p)
tp=findobj('Tag',p);
val=get(tp,'Value');
str=get(tp,'String');
str=deblank(str(val,:));
%----------------------------------
function state=toggle(obj)
state=strcmp(get(obj,'Checked'),'off');
if state
  set(obj,'Checked','on');
else
  set(obj,'Checked','off');
end    

%----------------------------------
function z=dBadd(x,y)
% add x and y on a dB scale
z=amp2db(db2amp(x)+db2amp(y));

