www.gusucode.com > sigdemos 工具箱matlab源码程序 > sigdemos/SpectrogramExample.m

    function SpectrogramExample(y,Fs)
%SpectrogramExample Spectrogram Display.
%   SpectrogramExample displays a spectrogram of the data contained in the
%   "mtlb.mat" file.
%
%   SpectrogramExample(y,Fs) displays a spectrogram of signal y, assuming
%   a sample rate of Fs Hz.  If y is specified but Fs is not,
%   a sample rate of 1 Hz is assumed.
%
%   Context menus and context-sensitive help are enabled throughout
%   the GUI.  Explore the visualization options by right-clicking
%   on various GUI items including the spectrogram, the colorbar,
%   etc.  For example, the panner may be zoomed by dragging the
%   mouse on the left- and right-hand edges of the highlighted
%   zoom region.  Right-clicking the highlighted zoom area brings
%   up a menu for focusing in on the zoom region, and provides
%   a link to context help.
%
%   Example: Load a data file containing laughter from a crowd
%   load laughter; % Variables y and Fs are loaded into the workspace
%   SpectrogramExample(y,Fs);
%
%   See also SPECTROGRAM, SPTOOL, FILTERDESIGNER.

% Author: D. Orofino
% Copyright 1988-2015 The MathWorks, Inc.

if nargin<1,
  default_file = 'mtlb.mat';
  fprintf('Loading demo file (%s).\n\n',default_file);
  s = load(default_file);
  y  = s.mtlb; % default dataset
  Fs = s.Fs;
elseif nargin<2,
  Fs=1;  % default sample rate
end

% Create and render UI Framework
hFrame = create_uiframework;

% Create body of the GUI (e.g., axes, images, etc)
create_graphics(hFrame.WidgetHandle, y, Fs);
end

% -------------- ---------------------------------------------------
% install_plugin ---------------------------------------------------
% Check if audioplayer is available and plugin if so
  function hToolbar = install_plugin(hFrame)
    hToolbar = [];
    audioplayer_enabled = IsAudioplayerAvailable;
    if audioplayer_enabled
      % Get audio UIMGR plugin
      [hMenu, hToolbar] = render_uimgraudiotoolbar;
      
      % Path to menu & toolbar plugins
      plan = {hToolbar, 'UIFrame/Toolbar';
        hMenu, 'UIFrame/Menus'};
      
      u=uimgr.Installer(plan);
      install(u,hFrame);
    end %if
    
  end % function

%----------------------------------------------------------
% render_playbutton
  function render_playbutton(hFrame)
    % Render play button to toolbar in case audio playback is not available
    htoolbarObj = hFrame.findchild('Toolbar');
    htoolbar = htoolbarObj.WidgetHandle;
    % Load toolbar icons
    icon_file = 'specgramdemoicons.mat';
    icon = load(icon_file);
    % Play icon
    uipushtool('Parent',htoolbar, ...
      'TooltipString','Play', ...
      'ClickedCallback',@play_sound, ...
      'CData',icon.playsound);
    % Reposition the playback buttons
    v = allchild(htoolbar);
    uistack(v(1), 'down', 5);
    
  end %function

%----------------------------------------------------------
% zoom_in
  function zoom_in(~,~,hfig)
    
    if nargin<3, hfig=gcbf; end
    
    ud = get(hfig,'UserData');
    
    % Get "pixel spacing" of image content
    im_xdata = get(ud.himage,'XData');
    
    % We cannot zoom in if we only have a single xdata point.
    if length(im_xdata) < 2
      return;
    end
    im_dx    = im_xdata(2)-im_xdata(1);
    
    % Get current axis limit
    xlim     = get(ud.hax(1),'XLim');
    xlim_ctr = sum(xlim)/2;
    dxlim    = diff(xlim);
    
    % If current axis limits will only show 1 pixel width,
    % don't bother zooming in any further:
    if dxlim <= im_dx, return; end
    
    % Shrink limits 50% toward center of current limits:
    new_xlim = (xlim + xlim_ctr)/2;
    % Update the spectrogram and time slice axes:
    set(ud.hax(1),'XLim',new_xlim);  % [1 3]
    
    % Update the thumbnail:
    set(ud.hthumb,'XData',new_xlim([1 2 2 1 1]));
    
    % update the audio selection to be played
    set_audio_selection(hfig, new_xlim * ud.Fs);
    
    % Update zoom signal, if enabled:
    % plot_signal_zoom(hfig);
    
  end %function

%----------------------------------------------------------
% zoom_out
  function zoom_out(~,~,hfig)
    
    % Zooming is relative to the current focus window
    % We will not zoom out beyond the current focus window
    
    if nargin<3, hfig=gcbf; end
    ud = get(hfig,'UserData');
    
    % Get current spectrogram axis limit (= thumbnail limits)
    thumb_xlim = get(ud.hax(1),'XLim'); % thumb limits = spectrogram limits
    thumb_dx = diff(thumb_xlim);
    
    % If spectrogram axis limits >= focus window limits,
    % don't bother zooming out any further:
    hax_time = ud.hax(2);
    focus_xlim = get(hax_time,'XLim');  % = panner xlim
    focus_dx = focus_xlim(2)-focus_xlim(1);
    if thumb_dx >= focus_dx, return; end
    
    % Grow limits 50% away from center of current limits:
    new_xlim = thumb_xlim + [-thumb_dx thumb_dx]/2;
    if new_xlim(1)<focus_xlim(1), new_xlim(1)=focus_xlim(1); end
    if new_xlim(2)>focus_xlim(2), new_xlim(2)=focus_xlim(2); end
    
    % Update the thumbnail:
    set(ud.hthumb,'XData',new_xlim([1 2 2 1 1]));
    
    % Sync the spectrogram and time slice axes to the thumbnail:
    set(ud.hax(1),'XLim',new_xlim); % [1 3]
    
    % update the audio selection to be played
    set_audio_selection(hfig, new_xlim * ud.Fs);
    
    % Update zoom signal, if enabled:
    % plot_signal_zoom(hfig);
    
  end %function

%----------------------------------------------------------
% zoom_full
  function zoom_full(hco,eventStruct,hfig)
    
    if nargin<3, hfig=gcbf; end
    ud = get(hfig,'UserData');
    
    focusTimeReset(hco, eventStruct, hfig);  % reset focus window to full extent
    
    % Get time range of full spectrogram
    im_xdata = get(ud.himage,'XData');
    if length(im_xdata) == 1
      new_xlim = get(ud.hax(3), 'XLim');
    else
      % Update the spectrogram and time slice axes:
      new_xlim = [min(im_xdata) max(im_xdata)];
      set(ud.hax([1 3]),'XLim',new_xlim);
    end
    
    % Update the thumbnail:
    set(ud.hthumb,'XData',new_xlim([1 2 2 1 1]));
    
    % update the audio selection to be played
    set_audio_selection(hfig, new_xlim * ud.Fs);
    
    % Update zoom signal, if enabled:
    % plot_signal_zoom(hfig);
    
  end %function

%----------------------------------------------------------
% Parameter setting
  function param_setting(~,~)
     
    hfig = gcbf; % spectogram demo figure
    ud = get(hfig, 'UserData');
        
    if isempty(ud.param_dlg)
      
      % render the dialog
      h = GenSpecParamDialog;
      ud.param_dlgobj = h;
      
      % setting dialog strings and callbacks 
      set(h.NWindow,'String', num2str(ud.Nwin),...
                    'Callback',@(src, evt) editIsModifiedCb(src, hfig));
                  
      set(h.Nlap,'String', num2str(ud.Nlap),...
                 'Callback',@(src, evt) editIsModifiedCb(src, hfig));
               
      set(h.Nfft,'String', num2str(ud.Nfft),...
                 'Callback',@(src, evt) editIsModifiedCb(src, hfig));
      
      set(h.okButton,'Callback',@(src, evt) dlg_apply(src,hfig));
      set(h.applyButton,'Callback',@(src, evt) dlg_apply(src,hfig));
      set(h.cancelButton,'Callback',@(src, evt) dlg_close(src,hfig));  
      
      % custom close function
      set(h.SpecParamDlg,'CloseRequestFcn',@(src, evt) dlg_close(src,hfig));
      
      % save the dialog object in demo figure userdata
      ud.param_dlgobj = h;
      ud.param_dlg = h.SpecParamDlg;
      set(hfig, 'UserData', ud);
      
      set(h.SpecParamDlg,'Visible','on');
      
    end %if
    
    
    
  end % function

%--------------------------------------------------------------------------
function h = GenSpecParamDialog()

h.SpecParamDlg = figure('MenuBar', 'None', ...
                      'Resize', 'Off', ...
                      'Tag', 'SpecParamDlg', ...
                      'Name', 'Spectrogram Parameters',...
                      'IntegerHandle', 'Off', ...
                      'HandleVisibility', 'Off', ...
                      'NumberTitle', 'Off', ...
                      'Color',[1 1 1],...
                      'Position', [200, 200, 400, 160], ... 
                      'Visible', 'Off');
      
h.uip = uipanel(h.SpecParamDlg,...
                'units','pixels',...
                'Position',[3 40 397 120],...
                'Title','Settings');

h.winLenTxt = uicontrol(h.uip,...
                        'Style','text','String','Window Length(Nwin):',...
                        'Position',[10 80 150 20],...
                        'HorizontalAlignment','left');
                            
h.overlapTxt = uicontrol(h.uip,'Style','text','String','Overlap(Nwin):',...
                               'Position',[10 50 150 20],...
                               'HorizontalAlignment','left');
                             
h.fftLenTxt = uicontrol(h.uip,'Style','text','String','FFT length(Nwin):',...
                              'Position',[10 20 150 20],...
                              'HorizontalAlignment','left');

set(h.SpecParamDlg,'Color',get(h.uip,'BackgroundColor'));

h.NWindow = uicontrol(h.uip,'Style','edit','String','256',...
                               'Position',[150 80 240 20],...
                               'BackgroundColor',[1 1 1],...
                               'HorizontalAlignment','left',...
                               'Callback',@(src, evt) editIsModifiedCb(src, hFig));
                             
h.Nlap = uicontrol(h.uip,'Style','edit','String','200',...
                                'Position',[150 50 240 20],...
                                'BackgroundColor',[1 1 1],...
                                'HorizontalAlignment','left',...
                                'Callback',@(src, evt) editIsModifiedCb(src, hFig));
                              
h.Nfft = uicontrol(h.uip,...
                         'Style','edit','String','256',...
                         'Position',[150 20 240 20],...
                         'BackgroundColor',[1 1 1],...
                         'HorizontalAlignment','left',...
                         'Callback',@(src, evt) editIsModifiedCb(src, hFig));

h.okButton = uicontrol(h.SpecParamDlg, ...
                       'Style', 'PushButton', ...
                       'String','OK', ...
                       'Units',get(h.SpecParamDlg,'Units'),...
                       'Position', [70 8 70 25] , ...
                       'HorizontalAlignment', 'Center',...
                       'Tag','okbtn');

h.cancelButton = uicontrol(h.SpecParamDlg, ...
                           'Style', 'PushButton', ...
                           'String','Cancel', ...
                           'Units',get(h.SpecParamDlg,'Units'),...
                           'Position', [150 8 70 25] , ...
                           'HorizontalAlignment', 'Center',...
                           'Tag','cancelbtn');
                         
h.helpButton = uicontrol(h.SpecParamDlg, ...
                           'Style', 'PushButton', ...
                           'String','Help', ...
                           'Units',get(h.SpecParamDlg,'Units'),...
                           'Position', [230 8 70 25] , ...
                           'HorizontalAlignment', 'Center',...
                           'Tag','helpbtn',...
                           'Callback',@dlg_help);                         
                         

h.applyButton = uicontrol(h.SpecParamDlg, ...
                          'Style', 'PushButton', ...
                          'String','Apply', ...
                          'Units',get(h.SpecParamDlg,'Units'),...
                          'Position', [310 8 70 25] , ...
                          'HorizontalAlignment', 'Center',...
                          'Tag','applybtn',...
                          'Enable','off');

end

%----------------------------------------------------------
% Callback for user modifying parameters
function editIsModifiedCb(~,eventdata)
    hFig = eventdata; % handle to the spectrogram figure    
    ud = get(hFig,'Userdata');    
    
    % Enable apply button when parameters modified.
    specFig = ud.param_dlgobj;    
    set(specFig.applyButton,'Enable','on');
end

%----------------------------------------------------------
% dialog help callback
  function dlg_help(~,~)
    doc spectrogram;
  end % function
  
%----------------------------------------------------------
% dialog close callback
  function dlg_close(~,hfig)
    ud = get(hfig, 'UserData');

    % delete the dialog handle
    delete(ud.param_dlgobj.SpecParamDlg);
    ud.param_dlg = [];    
    
    set(hfig, 'UserData', ud);
    update_gui(0, [], hfig);
  end % function

%----------------------------------------------------------
% dialog apply callback
  function dlg_apply(hObj,hfig)
   
    ud = get(hfig, 'UserData');
    h = ud.param_dlgobj;
    
    ud.Nwin = str2double(get(h.NWindow,'String'));
    ud.Nlap = str2double(get(h.Nlap,'String'));
    ud.Nfft = str2double(get(h.Nfft,'String'));
    
    if strcmp(get(hObj,'Tag'),'okbtn')
      % if ok button pressed, close the dialog
      ud.param_dlg = [];
      delete(ud.param_dlgobj.SpecParamDlg);
    end
    
    set(hfig, 'UserData',ud);
    update_gui(0, [], hfig);
    
  end % function

%---------------------------------------------------------------
  function left_plot_toggle
    % Define the newMode flag in ud.plot.left
    
    hfig = gcbf;
    ud = get(hfig,'UserData');
    
    if strcmp(ud.plot.left,'spectrogram_freq_slice'),
      newMode = 'signal_psd';
    else
      newMode = 'spectrogram_freq_slice';
    end
    ud.plot.left = newMode;
    set(hfig,'UserData',ud);
    
    % Update the left plot (Frequency slice and PSD)
    update_left_plot(hfig);
    
  end %function

%---------------------------------------------------------------
  function set_crosshairs(hfig,x,y)
    
    % Get current point in axis ASAP:
    %hfig   = gcbf;
    ud = get(hfig,'UserData');
    
    % Update cache
    ud.crosshair.xctr = x;
    if nargin == 3,
      ud.crosshair.yctr = y;
    else
      y = ud.crosshair.yctr;
    end
    set(hfig,'UserData',ud);
    
    % Update crosshairs
    set([ud.hspec_y ud.htime_y ud.htslice_y], 'XData',[x x]);
    set([ud.hspec_x ud.hfreq_x], 'YData',[y y]);
    
    % Update readouts
    update_time_readout(hfig);
    update_freq_readout(hfig);
    update_dB_readout(hfig);
    update_cmap_ptr(hfig);
    
    % For the VERTICAL and H/V crosshairs,
    % update the freq slice display:
    if strcmp(ud.plot.left,'spectrogram_freq_slice'),
      set(ud.hfreq_line,'XData', get_spec_freq(hfig));
    end
    
    % For the HORIZONTAL and H/V crosshairs,
    % update the time slice display:
    if strcmp(ud.plot.top,'spectrogram_time_slice'),
      set(ud.htslice_line,'YData', get_spec_tslice(hfig));
    end
    
  end %function

%---------------------------------------------------------------
  function center_cross(~,~)
    
    hfig = gcbf;
    ud = get(hfig,'UserData');
    
    % Determine center of spectrogram axis:
    xlim=get(ud.hax(1),'XLim');
    ylim=get(ud.hax(1),'YLim');
    
    set_crosshairs(hfig,mean(xlim),mean(ylim));
    update_cmap_ptr(hfig);
    
  end %function

%---------------------------------------------------------------
  function wbmotion_thumb(~,~)
    % thumbnail motion
    
    % Get current point in axis ASAP:
    hfig   = gcbf;
    ud     = get(hfig,'UserData');
    hax    = ud.hax(2);
    cp     = get(hax,'CurrentPoint');
    curr_x = cp(1,1);
    
    xmotion = curr_x - ud.thumb.origPt;
    width   = ud.thumb.width;
    xdata   = ud.thumb.xdata + xmotion;
    
    % Constrain to axis limits, so we don't lose cursor:
    xlim=get(hax,'XLim');
    min_xdata = min(xdata);
    max_xdata = max(xdata);
    if min_xdata < xlim(1),
      xdata=[xlim(1) xlim([1 1])+width xlim([1 1])];
    elseif max_xdata > xlim(2),
      xdata = [xlim(2)-width xlim([2 2]) xlim([2 2])-width];
    end
    
    % If the patch is larger than the zoom window
    if min(xdata)<=xlim(1) && max(xdata)>=xlim(2),
      % error('signal:specgramdemo:InvalidRange','wbmotion_thumb: xdata is out of bounds');
      return
    end
    %xdata = xdata(:).'; % Temporary workaround for HG2 bug g651375
    % Update the thumbnail:
    set(ud.hthumb,'XData',xdata);
    
    % Scroll the spectrogram and time-slice axes:
    set(ud.hax(1),'XLim',xdata(1:2));  % [1 3]
    
    % update the audio selection to be played
    set_audio_selection(hfig, xdata(1:2) * ud.Fs);
    
    %if strcmp(ud.plot.top,'spectrogram_time_slice'),
    %   set(ud.htslice_line,'YData', get_spec_tslice(hfig));
    %end
    
  end %function

%---------------------------------------------------------------
  function wbmotion_thumbleft(~,~)
    % thumbnail LEFT motion
    
    % Get current point in axis ASAP:
    hfig   = gcbf;
    ud     = get(hfig,'UserData');
    
    % current object may be either the patch, or the signal line
    hax    = ud.hax(2);
    cp     = get(hax,'CurrentPoint');
    curr_x = cp(1,1);
    
    xmotion = curr_x - ud.thumb.origPt;
    xdata   = ud.thumb.xdata;
    xdata([1 4 5]) = xdata([1 4 5]) + xmotion;
    
    % Constrain to axis limits, so we don't lose cursor:
    xlim = get(hax,'XLim');
    min_xdata = min(xdata);
    if min_xdata < xlim(1),
      xdata=[xlim(1) xdata([2 3])' xlim([1 1])];
    elseif min_xdata >= xdata(2),
      xdata = ud.thumb.xdata;
    end
    %xdata = xdata(:).'; % Temporary workaround for HG2 bug g651375
    % Update the thumbnail:
    set(ud.hthumb,'XData',xdata);
    
    % Scroll the spectrogram:
    set(ud.hax(1),'XLim',xdata(1:2));
    
    % update the audio selection to be played
    set_audio_selection(hfig, xdata(1:2) * ud.Fs);
    
  end %function

%---------------------------------------------------------------
  function wbmotion_thumbright(~,~)
    % thumbnail RIGHT motion
    
    % Get current point in axis ASAP:
    hfig   = gcbf;
    ud     = get(hfig,'UserData');
    
    hax    = ud.hax(2);
    cp     = get(hax,'CurrentPoint');
    curr_x = cp(1,1);
    
    xmotion = curr_x - ud.thumb.origPt;
    xdata   = ud.thumb.xdata;
    xdata([2 3]) = xdata([2 3]) + xmotion;
    
    % Constrain to axis limits, so we don't lose cursor:
    xlim = get(hax,'XLim');
    max_xdata = max(xdata);
    if max_xdata > xlim(2),
      xdata(2:3) = xlim(2);
    elseif max_xdata <= xdata(1),
      xdata = ud.thumb.xdata;
    end
    %xdata = xdata(:).'; % Temporary workaround for HG2 bug g651375
    % Update the thumbnail:
    set(ud.hthumb,'XData',xdata);
    
    % Scroll the spectrogram:
    set(ud.hax(1),'XLim',xdata(1:2));
    
    % update the audio selection to be played
    set_audio_selection(hfig, xdata(1:2) * ud.Fs);
    
  end %function

%---------------------------------------------------------------
  function wbup_thumb(~,~)
    
    % set spectrogram and time-slice xlims
    hfig = gcbf;
    ud = get(hfig,'UserData');
    
    % Commented out, due to flash:
    %     set(ud.hax([1 3]),'XLim',xlim);
    %
    % This is fine:
    %     set(ud.hax(1),'XLim',xlim);
    %
    % the following line causes flash in the spect image
    % this is the time-slice axis:
    % why does this affect the spectrogram axis? (overlap? clipping?)
    %     set(ud.hax(3),'XLim',xlim);
    
    changePtr(gcbf,'hand');
    install_cursorfcns(gcbf,'thumb');
    
    % Turn back on image axis visibility,
    % which was turned off during wbdown_thumb
    % so that it does not flash while panning:
    % Leave off!
    %set(ud.hax(1),'Visible','on');
    %
    % Turn on crosshair visibility, which was shut off
    % during thumbnail panning (wbdown_thumb):
    set([ud.hspec_y ud.hspec_x ud.htslice_y],'Visible','on');
    
  end %function

%---------------------------------------------------------------
  function wbup_thumbleft(~,~)
    % set spectrogram and time-slice xlims
    hfig = gcbf;
    ud = get(hfig,'UserData');
    xdata = get(ud.hthumb,'XData');
    xlim = [min(xdata) max(xdata)];
    
    % Commented out, due to flash:
    %set(ud.hax([1 3]),'XLim',xlim);
    %
    % This is fine:
    set(ud.hax(1),'XLim',xlim);
    %
    % the following line causes flash in the spect image
    % this is the time-slice axis:
    %set(ud.hax(3),'XLim',xlim);
    
    changePtr(gcbf,'ldrag');
    install_cursorfcns(gcbf,'thumbleft');
    
    % Turn on crosshair visibility, which was shut off
    % during thumbnail panning (wbdown_thumb):
    set([ud.hspec_y ud.hspec_x],'Visible','on');
    
  end %function

%---------------------------------------------------------------
  function wbup_thumbright(~,~)
    % set spectrogram and time-slice xlims
    hfig = gcbf;
    ud = get(hfig,'UserData');
    xdata = get(ud.hthumb,'XData');
    xlim = [min(xdata) max(xdata)];
    
    % Commented out, due to flash:
    %set(ud.hax([1 3]),'XLim',xlim);
    %
    % This is fine:
    set(ud.hax(1),'XLim',xlim);
    %
    % the following line causes flash in the spect image
    % this is the time-slice axis:
    %set(ud.hax(3),'XLim',xlim);
    
    changePtr(gcbf,'rdrag');
    install_cursorfcns(gcbf,'thumbright');
    
    % Turn on crosshair visibility, which was shut off
    % during thumbnail panning (wbdown_thumb):
    set([ud.hspec_y ud.hspec_x],'Visible','on');
    
  end %function

%---------------------------------------------------------------
  function update_status(hfig,str)
    % UPDATE_STATUS Update status text.
    
    ud = get(hfig,'UserData');
    hstatusbar = ud.huiframe.findchild('StatusBar');
    if(isa(hstatusbar, 'uimgr.uistatusbar'))
      % If str is not a string, skip
      % -1 is often used to "skip" the update
      if ischar(str),
        hstatusbar.WidgetHandle.Text = str;
      else
        strnwin = [ud.Nwintxt, num2str(ud.Nwin)];
        strnlap = [ud.Nlaptxt, num2str(ud.Nlap)];
        strnfft = [ud.Nffttxt, num2str(ud.Nfft)];
        hstatusbar.findchild('Nwin').WidgetHandle.Text = strnwin;
        hstatusbar.findchild('Nlap').WidgetHandle.Text = strnlap;
        hstatusbar.findchild('Nfft').WidgetHandle.Text = strnfft;
      end
    end
  end %function

%---------------------------------------------------------------
  function play_sound(~, ~)
    % PLAY_SOUND Play the selected sound segment
    
    hfig = gcbf;
    ud = get(hfig,'UserData');
    y = get(ud.htime_plot,'YData');
    Fs = ud.Fs;
    
    xdata=get(ud.hthumb,'XData');
    xlim=[min(xdata) max(xdata)];
    xidx=floor(xlim*Fs)+1;
    if xidx(1)<1, xidx(1)=1; end
    if xidx(2)>length(y), xidx(2)=length(y); end
    
    % Normalize sound and play:
    mx=max(abs(y));
    try
      sound(y(xidx(1):xidx(2))./mx,Fs);
    catch ME
      msg = ME.message;
      errordlg(msg,'Audio Playback Error','modal');
    end
    
  end %function

%---------------------------------------------------------------
% called by audioplayer during playback (if audioplayer enabled)
  function update_audio_position(hco, ~)
    if hco.isplaying,   % only do this if playback is in progress
      currentPosition = get(hco, 'CurrentSample') / get(hco, 'SampleRate');
      set_crosshairs(get(hco, 'UserData'), currentPosition);
    end
    
  end %function
%---------------------------------------------------------------
% utility to easily set playback boundaries
  function set_audio_selection(hfig, selectionPair)
    if ~ isempty(getappdata(hfig, 'audioSelection')), % only do this if audioplayer enabled
      selection.inPoint = round(selectionPair(1));
      if selection.inPoint < 1, selection.inPoint = 1; end
      selection.outPoint = round(selectionPair(2));
      setappdata(hfig, 'audioSelection', selection);
    end
    
  end %function

%---------------------------------------------------------------
% used to set the "put back Position" of the vertical crosshair
  function start_function(hobj, ~)
    hfig = get(hobj, 'UserData');
    ud = get(hfig,'UserData');
    hpause = ud.huiframe.findchild('Toolbar','Playbuttons', 'Pause');
    hpause.WidgetHandle.Enable = 'on';
    hstop = ud.huiframe.findchild('Toolbar','Playbuttons', 'Stop');
    hstop.WidgetHandle.Enable = 'on';
    set(hobj, 'StopFcn', {@stop_function, ud.crosshair.xctr});
    
  end %function

%---------------------------------------------------------------
% when playback has completed, puts back the vertical crosshair
% to where it was when playback was initiated
  function stop_function(hobj, ~, where)
    while isplaying(hobj), pause(0); end  % let playback complete
    if get(hobj, 'CurrentSample') == 1, % if paused, don't put it back
      hfig = get(hobj, 'UserData');
      ud = get(hfig, 'UserData');
      hplay = ud.huiframe.findchild('Toolbar','Playbuttons', 'Play');
      hplay.WidgetHandle.State = 'off';
      hpause = ud.huiframe.findchild('Toolbar','Playbuttons', 'Pause');
      hpause.WidgetHandle.Enable = 'off';
      hstop = ud.huiframe.findchild('Toolbar','Playbuttons', 'Stop');
      hstop.WidgetHandle.Enable = 'off';
      set_crosshairs(get(hobj, 'UserData'), where);
    end
    
  end %function

%---------------------------------------------------------------
  function set_cmap_limits(hfig, new_dr)
    % Set new colormap limits
    
    ud = get(hfig,'UserData');
    hax_spec = ud.hax(1);
    hax_cbar = ud.hax(5);
    himage_cbar = ud.himage_cbar;
    
    % Set new dynamic range limits into spectrogram image
    set(hax_spec,'CLim',new_dr);
    
    % colorbar is 1:256
    % actual spectrogram dynamic range is orig_dr
    % new spectrogram dynamic range is new_dr
    orig_dr = get(himage_cbar,'YData');
    diff_dr = new_dr - orig_dr;
    cmapIndices_per_dB = 256./diff(orig_dr);  % a constant
    diff_clim = diff_dr .* cmapIndices_per_dB;
    cbar_clim = [1 256] + diff_clim;
    set(himage_cbar,'CDataMapping','scaled');  % do during creation
    set(hax_cbar,'CLim',cbar_clim);
    
  end %function

%---------------------------------------------------------------
  function reset_cmap_limits(~,~)
    % Reset colormap limits to dynamic range of spectrogram data
    
    hfig = gcbf;
    ud = get(hfig,'UserData');
    orig_dr = get(ud.himage_cbar,'YData');
    set_cmap_limits(hfig, orig_dr);
    
  end %function

%---------------------------------------------------------------
  function manual_cmap_limits(~,~,hfig)
    % manual_cmap_limits Manual change to colormap dynamic range limits
    
    if nargin<3,
      hfig = gcbf;
    end
    ud = get(hfig,'UserData');
    hax_spec = ud.hax(1);
    
    % Prompt for changes to cmap limits:
    clim = get(hax_spec,'CLim');
    % 'dB value of first color in colormap:'
    % 'dB value of last color in colormap:'
    prompt={'Value of top color in colormap (dB):', ...
      'Value of bottom color in colormap (dB):'};
    def = {num2str(clim(2)), num2str(clim(1))};
    dlgTitle='Adjust dynamic range of colormap';
    lineNo=1;
    strs=inputdlg(prompt,dlgTitle,lineNo,def);
    if isempty(strs),
      return
    end
    new_dr = [str2double(strs{2}) str2double(strs{1})];
    
    set_cmap_limits(hfig,new_dr);
    
  end %function

%---------------------------------------------------------------
  function wbmotion_cmap(~,~)
    % WBMOTION_CMAP Graphical change to colormap dynamic range limits
    
    hfig = gcbf;
    ud = get(hfig,'UserData');
    hax_spec = ud.hax(1);
    hax_cbar = ud.hax(5);
    
    % Determine cursor starting and current points ASAP:
    cp    = get(hax_cbar,'CurrentPoint');
    newPt = cp(1,2);  % y-coord only
    dy    = newPt - ud.cbar.origPt;
    
    % if SelectionType was normal,
    %   update top or bottom of colorbar, only,
    %   depending on whether user started drag
    %   in the top or bottom of bar, respectively.
    % if SelectionType was extend,
    %   update both top AND bottom of bar simultaneously,
    %   translating colormap region.
    if strcmp(ud.cbar.SelectionType,'extend'),
      change_dr = [dy dy];
    else
      if ud.cbar.StartInTop,
        change_dr = [0 dy];
      else
        change_dr = [dy 0];
      end
    end
    new_dr = ud.cbar.starting_dr + change_dr;
    if diff(new_dr)<=0,
      new_dr = ud.cbar.starting_dr;
    end
    
    % Colorbar range is 1 to 256.
    % Actual spectrogram dynamic range is orig_dr
    % New spectrogram dynamic range is new_dr
    orig_dr = get(ud.himage_cbar,'YData');    % a constant
    cmapIndices_per_dB = 256./diff(orig_dr);  % a constant
    diff_dr = new_dr - orig_dr;
    diff_clim = diff_dr .* cmapIndices_per_dB;
    cbar_clim = [1 256] + diff_clim;
    
    if diff(cbar_clim)>0,
      % Protect against poor choice of values
      set(hax_cbar,'CLim',cbar_clim,'UserData',new_dr);
    end
    
    % We defer setting the new dynamic range limits
    % into the spectrogram image axis, as it will create
    % too much flash. Instead, on button-up, the new
    % limit is set.  See wbup_cmap() for details.
    
    % Set new dynamic range limits into spectrogram image
    % Note: userdata could be empty if this is the first entry...
    %set(ud.hax(1),'CLim',new_dr);
    set(hax_spec,'CLim',new_dr);
    
  end %function

%---------------------------------------------------------------
  function isChange = changePtr(hfig, newPtr)
    
    % Get current pointer name:
    ud = get(hfig,'UserData');
    
    % Is this a change in pointer type?
    isChange = ~strcmp(ud.currPtr,newPtr);
    if isChange,
      setptr(hfig, newPtr);
      ud.currPtr = newPtr;
      set(hfig,'UserData',ud);
    end
    
  end %function

%---------------------------------------------------------------
  function wbmotion_general(~,~,hfig)
    % General button motion
    %
    % Determines if cursor is over a crosshair
    % If so, changes pointer and installs crosshair buttondowns
    % If not, changes back to normal cursor and general buttondowns
    %   as necessary.
    
    if nargin<3,
      hfig = gcbf;
    end
    [isOverHV, isSegmentAxis, isCmapAxis,isTopHalfCmap, isThumb] = ...
      over_crosshair(hfig);
    
    if ~any(isOverHV),
      % Not hovering over a crosshair
      
      if isSegmentAxis,
        % Over an axis in which we can get a delta-time measurement
        if changePtr(hfig,'crosshair'),
          install_cursorfcns(hfig,'segment');
        end
        
      elseif isCmapAxis,
        % Over the colormap axes
        % Install the up/down pointer:
        if isTopHalfCmap,
          if changePtr(hfig,'udrag'),
            update_status(hfig,'Adjust upper dynamic range (shift to translate)');
            install_cursorfcns(hfig,'cmap');
          end
        else
          if changePtr(hfig,'ddrag'),
            update_status(hfig,'Adjust lower dynamic range (shift to translate)');
            install_cursorfcns(hfig,'cmap');
          end
        end
        
      elseif any(isThumb),
        % Over thumbnail - isThumb is a 3-element vector, [left center right],
        %  indicating whether cursor is over left edge, right edge, or is over
        %  the general thumbnail patch itself.
        
        % Install appropriate pointer:
        if isThumb(1),
          % Over left edge
          if changePtr(hfig,'ldrag'),
            install_cursorfcns(hfig,'thumbleft');
          end
        elseif isThumb(3),
          % Over right edge
          if changePtr(hfig,'rdrag'),
            install_cursorfcns(hfig,'thumbright');
          end
        else
          % Over general patch region
          if changePtr(hfig,'hand'),
            install_cursorfcns(hfig,'thumb');
          end
        end
        
      else
        % Not over a special axes:
        if changePtr(hfig,'arrow'),
          install_cursorfcns(hfig,'general');
        end
      end
      
    else
      % Pointer is over a crosshair (vert or horiz or both)
      if all(isOverHV),
        % Over both horiz and vert (near crosshair center):
        if changePtr(hfig,'fleur'),
          install_cursorfcns(hfig,'hvcross');
        end
      elseif isOverHV(1),
        % Over H crosshair
        if changePtr(hfig,'uddrag'),
          install_cursorfcns(hfig,'hcross');
        end
      else
        % Over V crosshair
        if changePtr(hfig,'lrdrag'),
          install_cursorfcns(hfig,'vcross');
        end
      end
    end
    
  end %function

%---------------------------------------------------------------
  function [y,isSegmentAxis,isCmapAxis,...
      isTopHalfCmap, isThumb] = over_crosshair(hfig)
    % Is the cursor hovering over the crosshairs?
    % There are two crosshairs, one an H-crosshair, the other
    % a V-crosshair.  The H and V crosshairs span several
    % different axes.
    %
    % Function returns a 2-element vector, indicating whether
    % the cursor is currently over the H- and/or V-crosshairs.
    %    y = [isOverH isOverV]
    
    y             = [0 0];
    isSegmentAxis = 0;
    isCmapAxis    = 0;
    isTopHalfCmap = 0;
    isThumb       = [0 0 0];  % left, middle, right regions
    
    % First, are we over any axes?
    hax = overAxes(hfig);
    if isempty(hax), return; end  % not over an axis
    
    % Get current point in axis:
    cp = get(hax,'CurrentPoint');
    ud = get(hfig,'UserData');
    
    % Axis which are "segmentable" have a vertical crosshair
    % e.g., spectrogram and time axes only
    isCmapAxis    = (hax==ud.hax(5));
    isSegmentAxis = (hax==ud.hax(1));
    
    % Determine if any horiz or vert crosshairs are
    % in this axis ... store as [anyHoriz anyVert]:
    hasHVCrossHairs = [any(hax==ud.hax([1 4])) ...
      any(hax==ud.hax(1:3))];
    
    % Is cursor in colormap axis?
    if (isCmapAxis),
      % is cursor in top half of colormap axis?
      orig_dr = get(ud.hax(1),'CLim');
      isTopHalfCmap = (cp(1,2) >= sum(orig_dr)/2);
    end
    
    if any(hasHVCrossHairs),
      % Get cursor & crosshair positions:
      crosshair_pos = [ud.crosshair.xctr ud.crosshair.yctr];
      cursor_delta  = abs(crosshair_pos - cp(1,1:2));
      axis_dx       = diff(get(hax,'XLim'));
      axis_dy       = diff(get(hax,'YLim'));
      axis_delta    = [axis_dx axis_dy];
      
      % Is cursor within 1 percent of crosshair centers?
      % Limit test uses the reciprocal of the percentage tolerance
      %   1-percent -> 1 / 0.01 = 100
      % 1.5-percent -> 1 / 0.015 ~= 67
      %   2-percent -> 1 / 0.02 = 50
      %
      % Finally, allow a true result only if the axis
      % has a crosshair of the corresponding type
      %
      y = fliplr(cursor_delta * 67 < axis_delta) & hasHVCrossHairs;
    end
    
    % Are we over the thumbnail patch?
    % Check if we're over the time axis:
    if (hax == ud.hax(2)),
      % Get thumb patch limits:
      xdata=get(ud.hthumb,'XData');
      xlim=[min(xdata) max(xdata)];
      
      % Is cursor over general patch area?
      thumb_delta = xlim - cp(1,1);
      isThumb(2) = thumb_delta(1)<=0 & thumb_delta(2)>=0;
      
      % Is cursor over left or right thumbnail edge?
      % Use same tolerance as crosshair test:
      axis_dx        = diff(get(hax,'XLim'));
      isThumb([1 3]) = (abs(thumb_delta) * 67 < axis_dx);
    end
    
  end %function

%---------------------------------------------------------------
  function h=overAxes(hfig)
    % overAxes Determine if pointer is currently over an
    % axis of the figure; the axis list comes from the
    % figure UserData (ud.hax).
    
    fig = get(hfig);
    ud = fig.UserData;
    
    obj = hittest(hfig);    
    
    switch obj.Tag
      case {'SpectrogramAxes','SpectrogramImage','SpectrogramCursorXLine',...
          'SpectrogramCursorYLine'}
        h = ud.hax(1);
        
      case {'TimePannerAxes','TimePannerLine','TimePannerPatch',...
          'TimePannerCursorLine'}
        
        h = ud.hax(2);
        
      case {'TimeSliceAxes','TimeSliceLine','TimeSliceCursorLine'}
        h = ud.hax(3);
        
      case {'FreqSliceAxes','FreqSliceLine','FreqSliceCursorLine'}
        h = ud.hax(4);
        
      case {'ColorBarAxes','ColorBarImage'}
        h = ud.hax(5);
        
      case {'ColorBarIndicatorAxes','ColorBarIndicatorPatch'}
        h = ud.hax(6);
        
      otherwise
        h = [];
    end
        
  end %function
%---------------------------------------------------------------
  function y=isLeftClick(hfig)
    
    % Keywords for key/button combinations:
    %         Left    Right
    %   none: normal  alt
    %  Shift: extend  alt
    %   Ctrl: alt     alt
    % Double: open    alt
    
    y=strcmp(get(hfig,'SelectionType'),'normal');
  end %function

%---------------------------------------------------------------
  function wbdown_hcross(~,~)
    % window button down in h-crosshair mode
    if ~isLeftClick(gcbf), return; end
    install_cursorfcns(gcbf,'hcross_buttondown');
    wbmotion_cross([],[],'h');
  end %function

%---------------------------------------------------------------
  function wbdown_vcross(~,~)
    % window button down in v-crosshair mode
    if ~isLeftClick(gcbf), return; end
    install_cursorfcns(gcbf,'vcross_buttondown');
    wbmotion_cross([],[],'v');
    
  end %function

%---------------------------------------------------------------
  function wbdown_hvcross(~,~)
    % window button down in hv-crosshair mode
    if ~isLeftClick(gcbf), return; end
    install_cursorfcns(gcbf,'hvcross_buttondown');
    wbmotion_cross([],[],'hv');
    
  end %function

%---------------------------------------------------------------
  function wbdown_segment(~,~)
    % window button down in segmentation mode
    if ~isLeftClick(gcbf), return; end
    install_cursorfcns(gcbf,'segment_buttondown');
    wbmotion_segment([],[],gcbf);
    
  end %function

%---------------------------------------------------------------
  function wbdown_thumb(~,~)
    % window button down in thumbnail mode
    if ~isLeftClick(gcbf), return; end
    
    % cache y-coord of pointer
    ud = get(gcbf,'UserData');
    hax_time = ud.hax(2);
    cp = get(hax_time,'CurrentPoint');
    xdata = get(ud.hthumb,'XData');
    width = max(xdata)-min(xdata);
    
    ud.thumb.origPt = cp(1,1);   % x-coord only
    ud.thumb.width  = width;
    ud.thumb.xdata  = xdata;
    set(gcbf,'UserData',ud);
    
    changePtr(gcbf,'closedhand');
    install_cursorfcns(gcbf,'thumb_buttondown');
    
    
    % Turn off image axis visibility,
    % so that it does not flash while panning:
    %
    % off permanently now:
    %set(ud.hax(1),'Visible','off');
    %
    % Turn off crosshair visibility:
    set([ud.hspec_y ud.hspec_x ud.htslice_y],'Visible','off');
    
  end %function

%---------------------------------------------------------------
  function wbdown_thumbleft(~,~)
    
    % window button down in LEFT thumbnail mode
    if ~isLeftClick(gcbf), return; end
    
    % cache y-coord of pointer
    ud = get(gcbf,'UserData');
    hax_time = ud.hax(2);
    cp = get(hax_time,'CurrentPoint');
    xdata = get(ud.hthumb,'XData');
    width = max(xdata)-min(xdata);
    
    ud.thumb.origPt = cp(1,1);   % x-coord only
    ud.thumb.width  = width;
    ud.thumb.xdata  = xdata;
    set(gcbf,'UserData',ud);
    
    install_cursorfcns(gcbf,'thumbleft_buttondown');
    
    % Turn off crosshair visibility:
    set([ud.hspec_y ud.hspec_x],'Visible','off');
    
  end %function

%---------------------------------------------------------------
  function wbdown_thumbright(~,~)
    
    % window button down in LEFT thumbnail mode
    if ~isLeftClick(gcbf), return; end
    
    % cache y-coord of pointer
    ud = get(gcbf,'UserData');
    hax_time = ud.hax(2);
    cp = get(hax_time,'CurrentPoint');
    xdata = get(ud.hthumb,'XData');
    width = max(xdata)-min(xdata);
    
    ud.thumb.origPt = cp(1,1);   % x-coord only
    ud.thumb.width  = width;
    ud.thumb.xdata  = xdata;
    set(gcbf,'UserData',ud);
    
    install_cursorfcns(gcbf,'thumbright_buttondown');
    
    % Turn off crosshair visibility:
    set([ud.hspec_y ud.hspec_x],'Visible','off');
    
  end %function

%----------------------------------------------------
  function wbdown_cmap(~,~)
    % window button down in colormap mode
    
    hfig = gcbf;
    
    % Only allow left (normal) or shift+left (extend)
    st = get(hfig,'SelectionType');
    i = find(strncmp(st,{'normal','extend','open'}, length(st)));
    if isempty(i) 
        return; 
    end
    
    if i==3,
      % open dynamic range menu
      manual_cmap_limits([],[],hfig);
      return
    elseif i==2,
      % Shift+left button = translate,
      % show up/down cursor during drag
      % NOTE: cannot update cursor when shift is pressed
      %       but no mouse button is pressed (no event!)
      changePtr(hfig,'uddrag');
    end
    
    ud = get(hfig,'UserData');
    
    % cache y-coord of pointer
    hax_cbar = ud.hax(5);
    cp = get(hax_cbar,'CurrentPoint');
    ud.cbar.origPt = cp(1,2);   % y-coord only
    ud.cbar.SelectionType = st; % normal or extend
    
    % The current clim is in the spectrogram image
    % We want to know the midpoint of this
    orig_dr = get(ud.hax(1),'CLim');
    ud.cbar.midPt = sum(orig_dr)/2;
    
    % Determine if pointer went down in top or bottom
    % half of colorbar:
    ud.cbar.StartInTop = (ud.cbar.origPt >= ud.cbar.midPt);
    
    % Cache original dynamic range:
    hax_spec = ud.hax(1);
    ud.cbar.starting_dr = get(hax_spec,'CLim');
    set(hfig,'UserData',ud);
    
    install_cursorfcns(hfig,'cmap_buttondown');
    
    % Set initial clim into userdata in case motion
    % callback not performed (motion updates userdata).
    % wbup_cmap reads the userdata
    %
    % Turn off visibility during drag to prevent flash
    set(hax_cbar, ...
      'UserData',ud.cbar.starting_dr, ...
      'Visible','off');
  end %function

%---------------------------------------------------------------
  function wbup_hcross(~,~)
    % window button up in h-crosshair mode
    install_cursorfcns(gcbf,'hcross');
    update_cmap_ptr(gcbf);
  end %function

%---------------------------------------------------------------
  function wbup_vcross(~,~)
    % window button up in v-crosshair mode
    install_cursorfcns(gcbf,'vcross');
    update_cmap_ptr(gcbf);
  end %function

%---------------------------------------------------------------
  function wbup_hvcross(~,~)
    % window button up in hv-crosshair mode
    install_cursorfcns(gcbf,'hvcross');
    update_cmap_ptr(gcbf);
  end %function

%---------------------------------------------------------------
  function wbup_segment(~,~)
    % window button up in segmentation mode
    install_cursorfcns(gcbf,'segment');
  end %function

%---------------------------------------------------------------
  function wbup_cmap(~,~)
    % window button up in colormap mode
    install_cursorfcns(gcbf,'cmap');
    
    % Set new dynamic range limits into spectrogram image
    % Note: userdata could be empty if this is the first entry...
    ud = get(gcbf,'UserData');
    hax_cbar=ud.hax(5);
    set(ud.hax(1),'CLim',get(hax_cbar,'UserData'));
    set(hax_cbar,'Visible','on'); % re-enable axis vis
    
    % Set new status msg, since it doesn't update
    % in the install_cursorfcns fcn for cmap callbacks
    % Do this by calling the general mouse-motion fcn:
    wbmotion_general([],[]);
    
  end %function

%---------------------------------------------------------------
  function update_cmap_ptr(hfig)
    % Update colormap pointer:
    
    ud = get(hfig,'UserData');
    v = get_spec_val(hfig);  % value in dB
    dy_tri = ud.crosshair.cbar.dy_tri;
    set(ud.hcmap_arrow,'YData', [v+dy_tri v-dy_tri v]);
    
  end %function

%---------------------------------------------------------------
  function [i,j] = get_adjusted_crosshair_idx(hfig)
    % Find image matrix coordinate pair (j,i) under crosshair.
    % Adjust crosshair for "half-pixel offset" implicit in image display
    
    ud=get(hfig,'UserData');
    xc=ud.crosshair.xctr;
    yc=ud.crosshair.yctr;
    himage=ud.himage;
    im=get(himage,'CData');
    
    % Get image pixel size:
    xdata=get(himage,'XData');
    if length(xdata)>1, dx = xdata(2)-xdata(1); else dx=0; end
    
    ydata=get(himage,'YData');
    if length(ydata)>1, dy = ydata(2)-ydata(1); else dy=0; end
    
    % Remove half a pixel size from apparent cursor Position:
    xc=xc-dx/2;
    yc=yc-dy/2;
    
    % Find pixel coordinate under the crosshair:
    i=find(xc>=xdata);
    if isempty(i), i=1;
    else i=i(end)+1;
    end
    j=find(yc>=ydata);
    if isempty(j), j=1;
    else j=j(end)+1;
    end
    sz=size(im);
    if i>sz(2), i=sz(2); end
    if j>sz(1), j=sz(1); end
    
  end %function

%---------------------------------------------------------------
  function v = get_spec_val(hfig)
    
    ud    = get(hfig,'UserData');
    im    = get(ud.himage,'CData');
    [i,j] = get_adjusted_crosshair_idx(hfig);
    v     = double(im(j,i));  % Get pixel value in double-precision
    
  end %function

%---------------------------------------------------------------
  function v = get_spec_freq(hfig)
    
    ud    = get(hfig,'UserData');
    im    = get(ud.himage,'CData');
    [i,~] = get_adjusted_crosshair_idx(hfig);
    v     = im(:,i);  % Get pixel row in uint8
    
  end %function

%---------------------------------------------------------------
  function v = get_spec_tslice(hfig)
    
    ud    = get(hfig,'UserData');
    im    = get(ud.himage,'CData');
    [~,j] = get_adjusted_crosshair_idx(hfig);
    v     = im(j,:);  % Get pixel column
    
  end %function

%---------------------------------------------------------------
  function update_time_readout(hfig,diffTime)
    
    ud = get(hfig,'UserData');
    if nargin<2,
      t=ud.crosshair.xctr;
      prefix='';
    else
      t=diffTime - ud.crosshair.xctr;
      prefix='\Deltat ';
    end
    
    % Update time readout
    [y,~,u] = engunits(t, 'latex','time');
    %str=[prefix num2str(y) ' ' u];
    str=[prefix sprintf('%.4f',y) ' ' u];
    set(ud.htext_time,'String',str);
    
  end %function

%---------------------------------------------------------------
  function update_freq_readout(hfig,diffFreq)
    
    ud=get(hfig,'UserData');
    if nargin<2,
      f=ud.crosshair.yctr;
      prefix='';
    else
      f=diffFreq - ud.crosshair.yctr;
      prefix='\Deltaf ';
    end
    
    % Update freq readout
    [y,~,u] = engunits(f,'latex');
    %str=[prefix num2str(y) ' ' u 'Hz'];
    str=[prefix sprintf('%.4f',y) ' ' u 'Hz'];
    set(ud.htext_freq,'String',str);
    
  end %function

%---------------------------------------------------------------
  function update_dB_readout(hfig,diffAmpl)
    
    ud = get(hfig,'UserData');
    if nargin<2,
      a=get_spec_val(hfig);
      prefix='';
    else
      a=diffAmpl - get_spec_val(hfig);
      prefix='\Deltaa=';
    end
    
    % Update mag readout
    %str=[prefix num2str(a) ' dB'];
    str=[prefix sprintf('%.4f',a) ' dB'];
    set(ud.htext_mag,'String',str);
    
  end %function

%---------------------------------------------------------------
  function clear_dB_readout(hfig)
    
    ud = get(hfig,'UserData');
    set(ud.htext_mag,'String','');
    
  end %function

%---------------------------------------------------------------
  function wbmotion_cross(~,~,sel)
    % motion callback during horiz/vert-crosshair selection
    % sel='h', 'v', or 'hv'
    
    % Get current point in axis ASAP:
    hfig = gcbf;
    hco  = gco;
    switch get(hco,'Type')
      case 'axes'
        hax=hco;
      otherwise
        hax=get(hco,'Parent');
    end
    
    cp   = get(hax,'CurrentPoint');
    ud   = get(hfig,'UserData');
    x    = cp(1,1);
    y    = cp(1,2);
    
    switch sel
      case 'h'
        x=ud.crosshair.xctr;
      case 'v'
        y=ud.crosshair.yctr;
    end
    
    % Constrain to axis limits, so we don't lose cursor:
    if any(sel=='v'),
      xlim=get(hax,'XLim');
      if x<xlim(1),
        x=xlim(1);
      elseif x>xlim(2),
        x=xlim(2);
      end
    end
    
    if any(sel=='h'),
      ylim=get(hax,'YLim');
      if y<ylim(1),
        y=ylim(1);
      elseif y>ylim(2),
        y=ylim(2);
      end
    end
    set_crosshairs(hfig,x,y);
    
  end %function

%---------------------------------------------------------------
  function wbmotion_segment(~,~,hfig)
    % motion callback during segmentation selection
    
    % Get current point in axis ASAP:
    if nargin<3,
      hfig = gcbf;
    end
    
    hax=gco;
    t=get(hax,'Type');
    if ~strcmp(t,'axes'),
      hax = get(hax,'Parent');
    end
    cp   = get(hax,'CurrentPoint');
    x    = cp(1,1);
    y    = cp(1,2);
    
    % Constrain to axis limits, so we don't lose cursor:
    xlim=get(hax,'XLim');
    if x<xlim(1),
      x=xlim(1);
    elseif x>xlim(2),
      x=xlim(2);
    end
    ylim=get(hax,'YLim');
    if y<ylim(1),
      y=ylim(1);
    elseif y>ylim(2),
      y=ylim(2);
    end
    
    update_time_readout(hfig,x);
    update_freq_readout(hfig,y);
    clear_dB_readout(hfig);
    
  end %function

%---------------------------------------------------------------
  function install_cursorfcns(hfig,cursorType)   
  
    switch lower(cursorType)
      case 'none'
        dn     = [];
        motion = [];
        up     = [];
        status = '';
        
      case 'general'
        dn     = [];
        motion = @wbmotion_general;
        up     = [];
        status = 'Ready';
        
      case 'segment'
        dn     = @wbdown_segment;
        motion = @wbmotion_general;
        up     = [];
        status = 'Ready';
        
      case 'segment_buttondown'
        dn     = [];
        motion = @wbmotion_segment;
        up     = @wbup_segment;
        status = 'Difference from crosshair';
        
      case 'thumb'
        % button not pushed, thumbnail highlighted
        dn     = @wbdown_thumb;
        motion = @wbmotion_general;
        up     = [];
        status = 'Pan zoom window';
        
      case 'thumb_buttondown'
        % button pushed, thumbnail highlighted
        dn     = [];
        motion = @wbmotion_thumb;
        up     = @wbup_thumb;
        status = 'Release to set zoom window';
        
      case 'thumbleft'
        % button not pushed, left thumbnail edge highlighted
        dn     = @wbdown_thumbleft;
        motion = @wbmotion_general;
        up     = [];
        status = 'Adjust zoom window left edge';
        
      case 'thumbleft_buttondown'
        % button pushed, thumbnail highlighted
        dn     = [];
        motion = @wbmotion_thumbleft;
        up     = @wbup_thumbleft;
        status = 'Release to set zoom window';
        
      case 'thumbright'
        % button not pushed, right thumbnail edge highlighted
        dn     = @wbdown_thumbright;
        motion = @wbmotion_general;
        up     = [];
        status = 'Adjust zoom window right edge';
        
      case 'thumbright_buttondown'
        % button pushed, right thumbnail edge highlighted
        dn     = [];
        motion = @wbmotion_thumbright;
        up     = @wbup_thumbright;
        status = 'Release to set zoom window';
        
      case 'hcross'
        % button not pushed, h-crosshair highlighted
        dn     = @wbdown_hcross;
        motion = @wbmotion_general;
        up     = [];
        status = 'Move horizontal cursor';
        
      case 'hcross_buttondown'
        % button pushed while over horiz cross-hair
        dn     = [];
        motion = {@wbmotion_cross,'h'};
        up     = @wbup_hcross;
        status = 'Release to update cursor';
        
      case 'vcross'
        dn     = @wbdown_vcross;
        motion = @wbmotion_general;
        up     = [];
        status = 'Move vertical cursor';
        
      case 'vcross_buttondown'
        dn     = [];
        motion = {@wbmotion_cross,'v'};
        up     = @wbup_vcross;
        status = 'Release to update cursor';
        
      case 'hvcross'
        dn     = @wbdown_hvcross;
        motion = @wbmotion_general;
        up     = [];
        status = 'Move crosshair cursor';
        
      case 'hvcross_buttondown'
        dn     = [];
        motion = {@wbmotion_cross,'hv'};
        up     = @wbup_hvcross;
        status = 'Release to update cursor';
        
        % Change dynamic range of colormap
      case 'cmap'
        dn     = @wbdown_cmap;
        motion = @wbmotion_general;
        up     = [];
        % Status is set in wbmotion_general function
        % since it depends on which pointer we're using
        status = -1;
        
      case 'cmap_buttondown'
        dn     = [];
        motion = @wbmotion_cmap;
        up     = @wbup_cmap;
        status = 'Release to update colormap';
        
      otherwise
        error(message('signal:specgramdemo:InvalidParamCursorFcn', 'cursorfcn'));
    end
    
    set(hfig, ...
      'WindowButtonDownFcn',  dn, ...
      'WindowButtonMotionFcn',motion, ...
      'WindowButtonUpFcn',    up)
    
    update_status(hfig,status);
    
  end %function

%---------------------------------------------------------------
  function resize_fig(~,~)
    % Callback to resize the figure
    
    update_axes_with_eng_units(gcbf);
    
  end %function

%---------------------------------------------------------------
  function update_axes_with_eng_units(hfig)
    
    % Update the tick marks for axes that are using engineering units
    % For example, a resize could have added or removed ticks, and the
    % axes would no longer have the proper tick marks
    ud     = get(hfig,'UserData');
    hFrame = getappdata(hfig, 'UIMgr');
    if strcmp(hFrame.Enable, 'on')
      hax_time = ud.hax(2);
      hax_freq = ud.hax(4);
      
      % Update freq-axis labels for engineering units, etc:
      yy=get(hax_freq,'YTick');
      [cs,eu] = convert2engstrs(yy);
      set(hax_freq,'YTickLabel',cs);
      set(get(hax_freq,'YLabel'),'String',['Frequency, ' eu 'Hz']);
      
      % Update time-axis labels for engineering units, etc:
      yy=get(hax_time,'XTick');
      [cs,eu] = convert2engstrs(yy,'time');
      set(hax_time,'XTickLabel',cs);
      set(get(hax_time,'XLabel'),'String',['Time, ' eu]);
    end
    
  end %function

%---------------------------------------------------------------
  function update_gui(~, ~, hfig)
    
    if nargin<3, hfig=gcbf; end
    
    ptr.ptr = get(hfig,'Pointer');
    ptr.shape = get(hfig,'PointerShapeCData');
    ptr.hot = get(hfig,'PointerShapeHotSpot');
    setptr(hfig,'watch');  % set user's expectations...
    
    ud = get(hfig,'UserData');
    hax_spec     = ud.hax(1);
    hax_time     = ud.hax(2);
    hax_tslice   = ud.hax(3);
    hax_freq     = ud.hax(4);
    hax_cbar     = ud.hax(5);
    hax_cbar_ind = ud.hax(6);
    
    % Get spectrogram parameters:
    Nwin = ud.Nwin;
    Nlap = ud.Nlap;
    Nfft = ud.Nfft;
    % Recompute spectrogram
    y      = ud.y;
    Fs     = ud.Fs;
    window = 'blackman';
    w = feval(window,Nwin,'periodic');
    try
      [b,f,t]=spectrogram(y,w,Nlap,Nfft,Fs);
      [Pxx, W] = pwelch(y,w,Nlap,Nfft,Fs);
      % Reset Nwin/Nlap/Nfft:
      ud.Nwinbak = Nwin;
      ud.Nlapbak = Nlap;
      ud.Nfftbak = Nfft;
      set(hfig, 'UserData', ud);
      update_status(hfig, -1);
    catch ME
      % Error occurred
      % Put up modal error display, then
      % get spectrogram params from cache (userdata)
      msg = ME.message;
      errordlg(msg,'Specgram Demo Error','modal');
      
      % Reset Nwin/Nlap/Nfft:
      ud.Nwin = ud.Nwinbak;
      ud.Nlap = ud.Nlapbak;
      ud.Nfft = ud.Nfftbak;
      return
    end
    
    ud.f = f;
    ud.t = t;
    
    % Pxx is the distribution of power per unit frequency.
    ud.Pxx = Pxx;
    
    % W is the vector of normalized frequencies at which the PSD is estimated.
    ud.w = W;
    
    % Carefully execute log10:
    wstate=warning;
    warning off; %#ok
    b = 20*log10(abs(b));
    warning(wstate);
    
    % Handle -inf's:
    i_inf = find(isinf(b(:)));
    if ~isempty(i_inf),
      % Set all -inf points to next-lowest value:
      b(i_inf)=inf;
      min_val=min(b(:));
      b(i_inf)=min_val;
    end
    
    blim = [min(b(:)) max(b(:))];
    spec_xlim = [0 max(t)];
    spec_ylim = [0 max(f)];
    
    % Update spectrogram
    set(ud.himage,'CData',b, 'XData',t, 'YData',f);
    set(hax_spec,'XLim',spec_xlim, 'YLim', spec_ylim);
    
    % Update colorbar
    set(ud.himage_cbar, 'YData',blim, 'CData', (1:256)');
    set(hax_cbar,'YLim',blim);
    set(hax_cbar_ind, 'YLim',blim);
    
    % Update time slice
    rows=size(b,1);
    bi=floor(rows/2); if bi<1, bi=1; end
    set(ud.htslice_line,'XData',t, 'YData',b(bi,:));
    set(hax_tslice, 'XLim',spec_xlim, 'YLim',blim);
    % Use 2 ticks only
    new_ticks = return2ticks(hax_tslice);
    set(hax_tslice,'YTick',new_ticks);
    
    % frequency slice
    cols=size(b,2);
    bj=floor(cols/2); if bj<1, bj=1; end
    set(ud.hfreq_line, 'XData',b(:,bj),'YData',f);
    set(hax_freq, 'YLim',spec_ylim,'XLim',blim);
    
    % Use 2 ticks only
    new_xticks = return2ticks(ud.hax(4));
    set(ud.hax(4),'XTick',new_xticks);
    
    
    % full time trace
    % this creates the signal trace that goes on the pannable time plot 
    % below the main plot
    half_nfft = ceil(Nfft/2);
    t1=(0 : length(y)-half_nfft)/Fs;
    set(ud.htime_plot,'XData',t1,'YData',y(half_nfft:end));
    set(hax_time, 'XLim',spec_xlim);
    
    update_axes_with_eng_units(hfig);
    
    % setup thumbnail patch
    axylim = get(hax_time,'YLim');
    ymax = axylim(2);
    ymin = axylim(1);
    tmax = max(t);
    tmin = min(t);
    set(ud.hthumb, ...
      'XData',[tmin tmax tmax tmin tmin], ...
      'YData',[ymin ymin ymax ymax ymin]);
    
    % Reset crosshair positions
    crosshair      = ud.crosshair;
    crosshair.xctr = mean(spec_xlim);
    crosshair.yctr = mean(spec_ylim);
    time_ylim      = get(hax_time,'YLim');
    freq_xlim      = get(hax_freq,'XLim');
    tslice_ylim    = get(hax_tslice,'YLim');
    
    % Crosshairs:
    set(ud.hspec_x, ...
      'XData',spec_xlim, ...
      'YData',[crosshair.yctr crosshair.yctr]);
    set(ud.hspec_y, ...
      'XData',[crosshair.xctr crosshair.xctr], ...
      'YData',spec_ylim);
    set(ud.htime_y, ...
      'XData',[crosshair.xctr crosshair.xctr], ...
      'YData',time_ylim);
    set(ud.htslice_y, ...
      'XData',[crosshair.xctr crosshair.xctr], ...
      'YData',tslice_ylim);
    set(ud.hfreq_x, ...
      'XData',freq_xlim, ...
      'YData',[crosshair.yctr crosshair.yctr]);
    
    % Colormap indicator triangle:
    dy_tri=.025*diff(blim);
    yp=b(bi,bj);
    ytri=[yp+dy_tri yp-dy_tri yp ];
    set(ud.hcmap_arrow, ...
      'LineStyle','none', ...
      'XData',[0 0 1], ...
      'YData',ytri);
    
    crosshair.cbar.dy_tri = dy_tri;
    
    % Update user data:
    ud.crosshair = crosshair;
    set(hfig,'UserData',ud);
    
    % Text readouts:
    update_time_readout(hfig);
    update_freq_readout(hfig);
    update_dB_readout(hfig);
    
    %str=[num2str(b(bi,bj)) ' dB'];
    %set(ud.htext_mag,'String',str);
    
    % Re-establish pointer cursor, etc:
    set(hfig,'Pointer',ptr.ptr, ...
      'PointerShapeCData',ptr.shape, ...
      'PointerShapeHotSpot',ptr.hot);
    
    %Set to normal display
    zoom_full(0, 0, hfig);
    
  end %function

%---------------------------------------------------------------
  function printdlg_cb(~,~)
    printdlg(gcbf);
  end %function

%---------------------------------------------------------------
  function printpreview_cb(~,~, ~)
    printpreview(gcbf);
  end %function

%---------------------------------------------------------------
  function close_cb(~,~)
    delete(gcbf);
  end %function

%----------------------------------------------------------
% IsAudioplayerAvailable
  function audioplayer_enabled = IsAudioplayerAvailable
    audioplayer_enabled = true;
    warning('OFF','MATLAB:audiovideo:audioplayer:noAudioOutputDevice');
    try
      audioplayer(zeros(1024,1), 44100); %make a player for the normalized signal      
    catch 
      audioplayer_enabled = false;
    end
    warning('ON','MATLAB:audiovideo:audioplayer:noAudioOutputDevice');   
  end

%---------------------------------------------------------------
  function create_graphics(hfig, y,Fs)
    %CREATE_GRAPHICS Render the graphics.
    
    hVisParent = hfig;
    if isappdata(hfig, 'UIMgr')
      hVisParent = get(getappdata(hfig, 'UIMgr'), 'hVisParent');
    end
    
    % Try to create audioplayer object for audio playback and tracking cursor
    warning('OFF','MATLAB:audiovideo:audioplayer:noAudioOutputDevice');
    player = audioplayer(y / abs(max(y)), Fs);  %make a player for the normalized signal
    set(player, 'UserData', hfig, 'TimerPeriod', 0.05, 'TimerFcn', @update_audio_position, ...
        'StartFcn', @start_function);
    % the toolbar callback fcns look for these named bits of appdata
    setappdata(hfig, 'theAudioPlayer', player);
    setappdata(hfig, 'theAudioRecorder', []);
    selection.inPoint = 1;
    selection.outPoint = length(y);
    setappdata(hfig, 'audioSelection', selection); % selection starts as "full"
    warning('ON','MATLAB:audiovideo:audioplayer:noAudioOutputDevice');
    
    % specgram
    % inputs: t, f, b
    hax_spec = axes('Parent',hVisParent, ...
      'Position',[.25 .275 .625 .525],'Tag','SpectrogramAxes');
    himage=image('Parent',hax_spec,'Tag','SpectrogramImage');
    axis xy; colormap(jet)
    
    set(himage,'CDataMapping','scaled');
    set(hax_spec, ...
      'Box','on', ...
      'XTickLabel','');
    
    % Shut off image axis visibility
    set(hax_spec, 'Visible','off');
    
    % time slice
    hax_tslice = axes('Parent',hVisParent,...
      'Position',[.25 .825 .625 .1],'Tag','TimeSliceAxes');
    htslice_line=line('Parent',hax_tslice,...
      'Color','b','Tag','TimeSliceLine');

    set(hax_tslice, ...
      'Box','on', ...
      'FontSize',8, ...
      'XTickLabel','', ...
      'XTick',[],  ...
      'YAxisLocation','right');
    ylabel('dB');
    sz=size(y);
    
    % Title of time slice plot
    [ey,~,eu]=engunits(Fs,'latex');
    str=['Data=[' num2str(sz(1)) 'x' num2str(sz(2)) '], Fs=' ...
      num2str(ey) ' ' eu 'Hz'];
    title(str);
    
    % colorbar
    cmapLen = 256;
    hax_cbar = axes('Parent',hVisParent,...
      'Position',[.91 .275 .03 .525], 'Tag', 'ColorBarAxes');
    himage_cbar = image([0 1],[0 1],(1:cmapLen)','Tag','ColorBarImage');
    set(himage_cbar,'CDataMapping','scaled');
    set(hax_cbar, ...
      'FontSize',8, ...
      'Box','on', ...
      'XTickLabel','', ...
      'YDir','normal', 'YAxisLocation','right', 'XTick',[]);

  % frequency slice
    hax_freq = axes('Parent',hVisParent,...
      'Position',[.1 .275 .125 .525],'Tag','FreqSliceAxes');
    hfreq_line=line('Parent',hax_freq,...
      'Color','b', 'Tag','FreqSliceLine');

    set(hax_freq, ...
      'FontSize',8, ...
      'Box','on',...
      'XDir','rev', ...
      'XAxisLocation','top');
    ylabel('Frequency, Hz');
    xlabel('dB');
    
    % colorbar indicator
    hax_cbar_ind = axes('Parent',hVisParent,...
      'Position',[.885+.01 .275 .015 .525],'Tag','ColorBarIndicatorAxes');
    set(hax_cbar_ind,'Visible','off','XLim',[0 1],'YLim',[0 1], ...
      'FontSize',8, ...
      'YAxisLocation','right');
    
    % full time trace
    % inputs: y, Fs
    hax_time = axes('Parent',hVisParent,...
      'Position',[.25 .15 .625 .1],'Tag','TimePannerAxes');
    htime_plot = line('Parent',hax_time,...
      'Color','b','Tag','TimePannerLine');
    set(hax_time, ...
      'Box','on',...
      'FontSize',8, ...
      'YAxisLocation','right');
    xlabel('Parent',hax_time,...
      'Time, secs');
    ylabel('Parent',hax_time,...
      'Ampl');
     
    % thumbnail patch
    %bgclr = get(0,'defaultuicontrolbackgr');
    %bgclr = get(0,'defaultfigurecolor');
    bgclr = 'b';
    hthumb = patch([0 0 1 1 0], [0 1 1 0 0], bgclr, ...
      'Parent',hax_time, 'Tag','TimePannerPatch');
    
    % Crosshairs:
    hspec_x=line('Parent',hax_spec,'Tag','SpectrogramCursorXLine');
    hspec_y=line('Parent',hax_spec,'Tag','SpectrogramCursorYLine');
    htime_y=line('Parent',hax_time, ...
      'LineWidth',2,'Tag','TimePannerCursorLine');
    htslice_y=line('Parent',hax_tslice,'Tag','TimeSliceCursorLine');
    hfreq_x=line('Parent',hax_freq,'Tag','FreqSliceCursorLine');
    
    % Colormap indicator triangle:
    hcmap_arrow=patch('Parent',hax_cbar_ind, ...
      'XData',[0 0 1], ...
      'YData',[0 0 0],'Tag','ColorBarIndicatorPatch');
    
    % Text readouts:
    
    hax_readout = axes('Parent',hVisParent,...
      'Position',[0.02 .09 .185 .15],'Visible','off');
    patch([0 1 1 0 0],[0 0 1 1 0],'w');
    htext_time = text('Parent',hax_readout, 'Position',[0.075 .8]);
    htext_freq = text('Parent',hax_readout, 'Position',[0.075 .5]);
    htext_mag = text('Parent',hax_readout, 'Position',[0.075 .2]);
    
    % due to lack of erasemode alternative, transparency of the patchs face 
    % color is increased to show through the underlying blue signal line. 
    transparency = 0.2; % [0,1] controls transparency of patch FaceColor.
    bgclr = 'b';  % FaceColor of the patch
    % Eliminate patch border by setting its edge color and transparency
    % equal to that of its face. 
    set(hthumb,'EdgeColor',bgclr,'EdgeAlpha',transparency, ...
        'FaceColor',bgclr,'FaceAlpha',transparency);
    % Drawmode set to fast in HG1 maps to SortMethod set to childorder in HG2.
    set([hax_spec hax_tslice hax_cbar hax_freq hax_cbar_ind hax_time], ...
      'SortMethod','childorder');
  
    % Spectrogram controls:
    %
    % segment length
    ylen = length(y);
    Nfft = min(256,ylen);
    Nwin = Nfft;
    % Nlap = min(Nwin,ceil(Nwin/2));
    Nlap = min(Nwin,200);
    ud = get(hfig, 'UserData');
    ud.Nwin = Nwin;
    ud.Nfft = Nfft;
    ud.Nlap = Nlap;
    ud.Nwinbak = Nwin;
    ud.Nfftbak = Nfft;
    ud.Nlapbak = Nlap;
    
    %set(hfig,'Colormap',jet(256));
    
    % Retain info in figure userdata:
    ud.hfig        = hfig;
    ud.hax         = [hax_spec hax_time hax_tslice hax_freq hax_cbar hax_cbar_ind];
    ud.hspec_x     = hspec_x;
    ud.hspec_y     = hspec_y;
    ud.htime_y     = htime_y;
    ud.htslice_y   = htslice_y;
    ud.hfreq_x     = hfreq_x;
    ud.hcmap_arrow = hcmap_arrow;
    ud.hfreq_line  = hfreq_line;
    ud.htslice_line  = htslice_line;
    ud.htime_plot  = htime_plot;
    ud.htext_time  = htext_time;
    ud.htext_freq  = htext_freq;
    ud.htext_mag   = htext_mag;
    ud.htext_status= [];%htext_status;
    ud.crosshair   = [];
    ud.himage      = himage;
    ud.himage_cbar = himage_cbar;
    ud.hthumb      = hthumb;
    ud.f=[];
    ud.t=[];
    ud.y=y;
    ud.Fs=Fs;
    ud.currPtr = '';  % current pointer
    ud.Pxx = [];
    ud.w = [];
    ud.param_dlg = [];
    
    % Set plot default modes:
    ud.plot.top  = 'spectrogram_time_slice';
    ud.plot.left = 'spectrogram_freq_slice';
    
    set(hfig,'UserData',ud);
    
    % Protect GUI from user plots, etc:
    set([hfig ud.hax],'HandleVisibility','Callback');
    
    % After GUI has all elements in it, install context help:
    install_context_help(hfig);
    install_context_menus(hfig);
    
    % Populate GUI with data, limits, etc:
    update_gui([],[],hfig);
    
    % Enable general (non-segmenting) mouse functions:
    install_cursorfcns(hfig,'general');
    set(hfig,'Visible','on');
    if isappdata(hfig, 'UIMgr')
      hFrame = getappdata(hfig, 'UIMgr');
      hFrame.Enable = 'on';
    end
    
    update_axes_with_eng_units(hfig);
    
  end %function

% ---------------------------------------------------------------
% H E L P    S Y S T E M
% --------------------------------------------------------------
%
% General rules:
%  - Context menus that launch the "What's This?" item have their
%    tag set to 'WT?...', where the '...' is the "keyword" for the
%    help lookup system.
%

%--------------------------------------------------------------
  function HelpWhatsThisBDown(~,~)
    % HelpWhatsThisBDown Button-down function called from either
    %   the menu-based "What's This?" function, or the toolbar icon.
    
    hfig = gcbf;
    hOver = gcbo; % overobj('uicontrol');  % handle to object under pointer
        
    % Shut off button-down functions for uicontrols and the figure:
    hChildren = findobj(hfig);
    set(hChildren, 'ButtonDownFcn','');
    set(hfig,'WindowButtonDownFcn','');
    
    % Restore GUI pointers, etc:
    wbmotion_general(hfig);
    
    % Dispatch to context help:
    hc = get(hOver,'UIContextMenu');
    hm = get(hc,'Children');  % menu(s) pointed to by context menu
    
    % Multiple entries (children) of context-menu may be present
    % Tag is a string, but we may get a cell-array of strings if
    % multiple context menus are present:
    % Find 'What's This?' help entry
    tag = get(hm,'Tag');
    helpIdx = find(strncmp(tag,'WT?',3));
    if ~isempty(helpIdx),
      % in case there were accidentally multiple 'WT?' entries,
      % take the first (and hopefully, the only) index:
      if iscell(tag),
        tag = tag{helpIdx(1)};
      end
      HelpGeneral([],[],tag);
    end
    
  end %function

%--------------------------------------------------------------
  function HelpWhatsThisCB(~, ~)
    % HelpWhatsThisCB Get "What's This?" help
    %   This mimics the context-menu help selection, but allows
    %   cursor-selection of the help topic
    
    % NOTE: Enabling context-help "destroys" the enable-state
    %  of all uicontrols in the GUI.  When the callback completes,
    %  we must restore the enable states.
    
    hfig = gcbf;
    
    % Change pointer icon:
    changePtr(hfig,'help');
    
    % Install button-down functions on all uicontrols,
    %  plus the figure itself:
    % uicontrol, axes, line, patch, text
    hChildren = findobj(hfig);
    % No need to set enable states, etc.
    set(hChildren, ...
      'ButtonDownFcn',@HelpWhatsThisBDown);
    set(hfig, ...
      'WindowButtonMotionFcn','', ...
      'WindowButtonUpFcn','', ...
      'WindowButtonDownFcn','');
  end %function

%--------------------------------------------------------------
  function HelpSpecgramdemoCB(~,~)
    %HELPSPECGRAMDEMO Get specgramdemo reference-page help
    
    helpwin(mfilename);
  end %function

%--------------------------------------------------------------
  function HelpProductCB(~,~)
    %HELPRPODUCTCB Opens the Help window with the online doc Roadmap
    %              page (a.k.a. "product page") displayed.
    doc signal/
  end %function

%--------------------------------------------------------------
  function HelpDemosCB(~,~)
    %HELPDEMOSCB Starts Demo window, with the appropriate product's
    %            demo highlighted in the Demo window contents pane.
    demo toolbox signal
  end %function

%--------------------------------------------------------------
  function HelpAboutCB(~,~)
    %HELPABOUTCB Displays version number of product, and copyright.
    
    aboutsignaltbx;
  end %function

%--------------------------------------------------------------
  function HelpGeneral(~,~,tag)
    % HelpGeneral Define CSH text for specgramdemo
    
    hfig = gcbf;
    hco = gcbo;
    
    if nargin<3,
      % Testing purposes only:
      tag = get(hco,'Tag');
    end
    
    % Check for legal tag string:
    if ~ischar(tag),
      error(message('signal:specgramdemo:InvalidParamHelpTag'));
    end
    
    % Remove 'WT?' prefix;
    if strncmp(tag,'WT?',3),
      tag(1:3) = [];
    else
      error(message('signal:specgramdemo:MustBeAString'));
    end
    
    ud = get(hfig,'UserData');
    
    % Define text for CSH system
    title = ['Help: ' tag];
    msg = '';
    switch tag
      case ''
        msg = {'';
          'No help available on selected item.'};
        
      case 'Spectrogram image'
        msg = {'';
          'This image displays the spectrogram for the signal currently loaded ';
          'in the viewer.  The spectrogram presents the magnitude of the short-time ';
          'Fourier transform.';
          '';
          'Calculate the spectrogram as follows:';
          '';
          '1. Split the signal into overlapping sections and apply the';
          'window specified by the window parameter to each section.';
          '';
          '2. Compute the discrete-time Fourier transform of each';
          'section with a length Nfft FFT to estimate the short-term ';
          'frequency content of the signal. These transforms ';
          'make up the columns of B. The quantity (length(Nwin) - Nlap)'
          'specifies by how many samples the window will be shifted.';
          '';
          '3. For real input, truncate the spectrogram to the';
          'first (Nfft/2 + 1) points when Nfft is even and (Nfft + 1)/2 when ';
          'Nfft is odd.'};
        
      case 'Zoom Window Panner'
        msg = {'';
          'Shows a panoramic view of the signal which is loaded in the viewer. ';
          'When you zoom in the spectrogram, the corresponding time domain ';
          'portion is highlighed.';
          '';
          'You can zoom the panner by dragging the mouse on the left- and ';
          'right-hand edges of the highlighted zoom region.  Right-click the ';
          'highlighted zoom area to bring up a menu for focusing in on the zoomed ';
          'region'};
        
      case 'Spectrogram Frequency Slice' % Left axes
        if strcmp(ud.plot.left,'spectrogram_freq_slice'),
          msg = {'';
            'This view displays a frequency slice for the current spectrogram. The ';
            'view is updated as you move the crosshair cursor along the frequency '
            'axes (horizontally).'};
        else
          % Change the helpwin title for the PSD case.
          title = 'Help: Signal Power Spectral Density';
          
          msg = {'';
            'Displays the Power Spectral Density (PSD) estimate calculated ';
            'using Welch''s averaged modified periodogram method.'};
          
        end
        
      case 'Spectrogram Time Slice', % Top axes
        msg = {'';
          'This view displays a time slice for the current spectrogram.  The';
          'view is updated as you move the crosshair cursor along the time'
          'axes (vertically).'};
        
      case 'Colorbar'
        msg = {'';
          'The colorbar shows the color scale for the current spectrogram.';
          ''};
        
      case 'Status Bar',
        msg = {'';
          'The Status Bar displays information about the state of the ';
          'Spectrogram Demo, the current operation of the tool, and operation';
          'of the crosshair cursor.'};
        
      case 'Magnitude Readout',
        msg = {'';
          'Displays the magnitude (in dB) of a spectrogram slice.';
          ''};
        
      case 'Frequency Readout',
        msg = {'';
          'Displays frequency values in Hz.';
          ''};
        
      case 'Time Readout',
        msg = {'';
          'Displays time measurements in seconds for the Time Plot ';
          'and the Time Slice'};
        
      case 'Time Plot', % Bottom axes
        msg = {'';
          'Time Plot displays the original signal in its entirety.'};
        
      case 'Colorbar Indicator',
        msg = {'';
          'The colorbar indicator points to the level of the spectrogram.'};
        
      case 'Frequency Crosshair',
        msg = {'';
          'Move the frequency crosshair cursor to pin-point a particular ';
          'frequency location on the spectrogram''s frequency slice axes.'};
        
      case 'Time Crosshair',
        msg = {'';
          'Move the time crosshair cursor to pin-point a particular ';
          'time instance on the spectrogram''s time slice axes.'};
        
      case 'Spectrogram Demo',
        msg = {'';
          'This is the Spectrogram Demo which displays a spectrogram, ';
          'a time plot, and a frequency slice of an input signal';
          '';
          'SpectrogramExample(y,Fs) displays a spectrogram of signal y, assuming a sample ';
          'rate of Fs Hz.  If y is specified but Fs is not, a sample rate of 1 ';
          'Hz is assumed.  If no input arguments are supplied, y and Fs are ';
          'taken from the default data file "mtlb.mat."'};
        
      case 'Spectrogram Window Size',
        msg = {'';
          'Nwin specifies the length of the Periodic Blackman window used in ';
          'this demo. The default value is 256.'};
        
      case 'Spectrogram FFT Size',
        msg = {'';
          'Nfft specifies the FFT length used to calculate the spectrogram. ';
          'This value determines the frequencies at which the discrete-time ';
          'Fourier transform is computed. These values are typically powers ';
          'of two, such as 256 or 512.'};
        
      case 'Spectrogram Overlap'
        msg = {'';
          'Use Nlap to specify the number of samples to overlap the windowed sections.'};
    end
    
    % If no text is defined, simply display the tag.
    if isempty(msg),
      msg = {'';
        ['This is the ' tag '.']};
    end
    
    % Put up message box for help:
    %hmsg = msgbox(msg,title, 'help','modal');
    %CenterFigOnFig(hfig, hmsg);
    
    helpwin(char(msg),title);
  end %function

%--------------------------------------------------------------
  function install_context_help(hfig)
    
    ud = get(hfig,'UserData');
    
    main = {'Label','&What''s This?',  ...
      'Callback',@HelpGeneral, 'Parent'};
    
    setWTC(hfig,main, [ud.himage ud.hax(1)], 'Spectrogram image');
    setWTC(hfig,main, ud.hthumb, 'Zoom Window Panner');
    setWTC(hfig,main, [ud.himage_cbar ud.hax(5)], 'Colorbar');
    setWTC(hfig,main, ud.htext_status, 'Status Bar');
    setWTC(hfig,main, ud.htext_mag, 'Magnitude Readout');
    setWTC(hfig,main, ud.htext_freq, 'Frequency Readout');
    setWTC(hfig,main, ud.htext_time, 'Time Readout');
    setWTC(hfig,main, [ud.htime_plot ud.hax(2)], 'Time Plot');
    setWTC(hfig,main, [ud.htslice_line ud.hax(3)], 'Spectrogram Time Slice');
    setWTC(hfig,main, [ud.hfreq_line ud.hax(4)], 'Spectrogram Frequency Slice');
    setWTC(hfig,main, [ud.hcmap_arrow ud.hax(6)], 'Colorbar Indicator');
    
    setWTC(hfig,main, [ud.hfreq_x ud.hspec_x], 'Frequency Crosshair');
    setWTC(hfig,main, [ud.htime_y ud.htslice_y ud.hspec_y], 'Time Crosshair');
    setWTC(hfig,main, ud.hfig, 'Spectrogram Demo');
    
  end %function

% set context for:
% - readout axis
% - uitoolbar

%--------------------------------------------------------------
  function setWTC(hfig,main,hItem,tagStr)
    % setWT Set the "What's This?" context menu and callback:
    hc = uicontextmenu('Parent',hfig);
    uimenu(main{:},hc, 'Tag',['WT?' tagStr]);
    set(hItem,'UIContextMenu',hc);
    
  end %function

% ---------------------------------------------------------------
% C O N T E X T   M E N U S
% --------------------------------------------------------------

%-----------------------------------------------------------------
  function install_context_menus(hfig)
    
    install_specgram_mode_menus(hfig);
    install_colorbar_menus(hfig);
    install_freq_slice_menus(hfig);
    install_time_slice_menus(hfig);
    install_time_panner_menus(hfig);
    
  end %function

%-----------------------------------------------------------------
  function install_specgram_mode_menus(hfig)
    
    % Additional menus to prepend to the spectrogram context menu:
    
    ud = get(hfig,'UserData');
    hc = get(ud.himage,'UIContextMenu');  % ud.hax(1) also?
    
    hEntry=[];  % holds handles to each colormap menu item
    opts={hc,'2-D Image',@changeSpecgramMode, 'Checked','on'};
    hEntry(end+1) = createContext(opts);
    opts={hc,'3-D Magnitude Plot',@changeSpecgramMode};
    hEntry(end+1) = createContext(opts);
    % disable last menu until feature implemented:
    set(hEntry(end),'Enable','off');
    opts={hc,'3-D dB Plot',@changeSpecgramMode};
    hEntry(end+1) = createContext(opts);
    % disable last menu until feature implemented:
    set(hEntry(end),'Enable','off');
    
    % Give each menu item a vector of handles to all peer menus
    set(hEntry,'UserData',hEntry);
    
    fixup_context_order(hc);
    
  end %function

%-----------------------------------------------------------------
  function install_colorbar_menus(hfig)
    % Additional menus to prepend to the colorbar context menu:
    
    ud = get(hfig,'UserData');
    hc = get(ud.himage_cbar,'UIContextMenu');  % ud.hax(1) also?
    
    opts={hc,'Colormap',''};
    hCmap = createContext(opts);
    
    hEntry=[];  % holds handles to each colormap menu item
    opts={hCmap,'Jet',@changeCMap, 'Checked','on'};
    hEntry(end+1) = createContext(opts);
    opts={hCmap,'Hot',@changeCMap};
    hEntry(end+1) = createContext(opts);
    opts={hCmap,'Gray',@changeCMap};
    hEntry(end+1) = createContext(opts);
    opts={hCmap,'Bone',@changeCMap};
    hEntry(end+1) = createContext(opts);
    opts={hCmap,'Copper',@changeCMap};
    hEntry(end+1) = createContext(opts);
    opts={hCmap,'Pink',@changeCMap};
    hEntry(end+1) = createContext(opts);
    
    opts={hc,'Set Limits',@manual_cmap_limits, 'Separator','on'};
    createContext(opts);
    
    opts={hc,'Reset Limits',@reset_cmap_limits};
    createContext(opts);
    
    % Give each menu item a vector of handles to all peer menus
    set(hEntry,'UserData',hEntry);
    
    fixup_context_order(hc);
    
  end %function

%-----------------------------------------------------------------
  function install_freq_slice_menus(hfig)
    
    % Additional menus to prepend to the spectrogram context menu:
    
    ud = get(hfig,'UserData');
    hax_freq = ud.hax(4);
    hc = get(hax_freq,'UIContextMenu');  % ud.hax(1) also?
    
    hEntry=[];  % holds handles to each colormap menu item
    opts={hc,'Marginal (specgram slice)',@changeFreqSliceMode, 'Checked','on'};
    hEntry(end+1) = createContext(opts);
    %opts={hc,'Integrated (freq PSD)',@changeFreqSliceMode};
    opts={hc,'Power Spectral Density',@changeFreqSliceMode};
    hEntry(end+1) = createContext(opts);
    set(hEntry(end),'Enable','on');
    
    % Give each menu item a vector of handles to all peer menus
    set(hEntry,'UserData',hEntry);
    
    fixup_context_order(hc);
    
  end %function

%-----------------------------------------------------------------
  function install_time_slice_menus(hfig)
    
    % Additional menus to prepend to the spectrogram context menu:
    
    ud = get(hfig,'UserData');
    hax_tslice   = ud.hax(3);
    hc = get(hax_tslice,'UIContextMenu');  % ud.hax(1) also?
    
    hEntry=[];  % holds handles to each colormap menu item
    opts={hc,'Marginal (specgram slice)',@changeTimeSliceMode, 'Checked','on'};
    hEntry(end+1) = createContext(opts);
    opts={hc,'Integrated (time zoom)',@changeTimeSliceMode};
    hEntry(end+1) = createContext(opts);
    
    % disable last menu until feature implemented:
    set(hEntry(end),'Enable','off');
    
    % Give each menu item a vector of handles to all peer menus
    set(hEntry,'UserData',hEntry);
    
    fixup_context_order(hc);
    
  end %function

%-----------------------------------------------------------------
  function install_time_panner_menus(hfig)
    
    % Additional menus to prepend to the time-panner context menu:
    
    ud = get(hfig,'UserData');
    hthumb = ud.hthumb;  % add to time axis as well?
    hc = get(hthumb, 'UIContextMenu');
    
    % Update the menu on-the-fly:
    set(hc,'Callback', @focus_menu_render_callback);
    
    hEntry=[];  % holds handles to each colormap menu item
    
    opts={hc,'Focus In',@focusTimeIn};
    hEntry(end+1) = createContext(opts);
    
    opts={hc,'Previous Focus',@focusTimePrev};
    hEntry(end+1) = createContext(opts);
    
    opts={hc,'Reset Focus',@focusTimeReset};
    hEntry(end+1) = createContext(opts);
    
    % Give each menu item a vector of handles to all peer menus
    set(hEntry,'UserData',hEntry);
    
    fixup_context_order(hc);
    
    update_focus_history_menu(hfig); % pass any focus context menu
    
  end %function

%-----------------------------------------------------------------
  function hMenu=createContext(opts)
    % Helper function to append additional context menus
    args = {'Parent',opts{1}, 'Tag',opts{2}, 'Label',opts{2}, ...
        'Callback',opts{3:end}};
    hMenu=uimenu(args{:});
  end %function

%-----------------------------------------------------------------
  function fixup_context_order(hContext)
    % Put the first context menu entry (the "What's This?" entry)
    %  last in the context menu list, and turn on the separator
    %  for the "What's This?" entry
    childList = get(hContext,'Children');
    childList = childList([end 1:end-1]);
    set(hContext,'Children',childList);
    set(childList(1),'Separator','on');
    
  end %function

%---------------------------------------------------------------
  function changeCMap(~,~)
    
    hco=gcbo; hfig=gcbf;
    % Reset checks on all colormap menu items:
    set(get(hco,'UserData'),'Checked','off');
    set(hco,'Checked','on');
    
    % Update figure colormap:
    cmapStr = lower(get(hco,'Label'));
    cmap = feval(cmapStr);
    set(hfig,'Colormap',cmap);
    
  end %function

%---------------------------------------------------------------
  function changeSpecgramMode(~,~)
    
    hco=gcbo;
    % Reset checks on all menu items:
    set(get(hco,'UserData'),'Checked','off');
    set(hco,'Checked','on');
    
    % Update userdata cache:
    % Update display:
  end %function

%---------------------------------------------------------------
  function changeFreqSliceMode(~,~)
    
    hco=gcbo;
    % Reset checks on all menu items
    set(get(hco,'UserData'),'Checked','off');
    set(hco,'Checked','on');
    
    % Update userdata cache:
    % Update display:
    left_plot_toggle;
    
  end %function

%---------------------------------------------------------------
  function changeTimeSliceMode(~,~)
    
    hco=gcbo;
    % Reset checks on all menu items
    set(get(hco,'UserData'),'Checked','off');
    set(hco,'Checked','on');
    
    % Update userdata cache:
    % Update display:
  end %function


% ---------------------------------------------------------------
% F O C U S    S Y S T E M
% --------------------------------------------------------------

%---------------------------------------------------------------
  function push_curr_to_focus_history(hfig)
    
    ud = get(hfig,'UserData');
    hax_time = ud.hax(2);
    
    % focus history is stored in userdata of time-panner axis
    % as either an empty vector or cell, or as
    % a cell-array of 2-element x-lim vector.
    
    % get current time-axis limits
    curr_xlim = get(hax_time,'XLim');
    
    curr_history = get(hax_time,'UserData');
    if isempty(curr_history),
      updated_focus_history = {curr_xlim};
    else
      updated_focus_history = [curr_history {curr_xlim}];
    end
    set(hax_time,'UserData',updated_focus_history);
    
    update_focus_history_menu(hfig);
    
  end %function

%---------------------------------------------------------------
  function hist_xlim = pop_from_focus_history(hfig)
    
    ud = get(hfig,'UserData');
    hax_time = ud.hax(2);
    curr_xlim = get(hax_time,'XLim'); % get current time-axis limits
    
    curr_history = get(hax_time,'UserData');
    if isempty(curr_history),
      % no prev focus info recorded
      warning(message('signal:specgramdemo:Empty'));
      hist_xlim = curr_xlim;
      
      %im_xdata = get(ud.himage,'XData');
      %hist_xlim = [min(im_xdata) max(im_xdata)];
    else
      % Pop last history xlim
      hist_xlim = curr_history{end};
      curr_history(end) = [];
      set(hax_time,'UserData',curr_history);
    end
    
    update_focus_history_menu(hfig);
    
  end %function

%---------------------------------------------------------------
  function clear_focus_history(hfig)
    % Remove all previous focus entries
    
    ud = get(hfig,'UserData');
    hax_time = ud.hax(2);
    set(hax_time,'UserData',[]);
    
    update_focus_history_menu(hfig);
    
  end %function

%---------------------------------------------------------------
  function update_focus_history_menu(hfig)
    
    ud = get(hfig,'UserData');
    hax_time = ud.hax(2);
    
    % Update 'Previous Focus' context menu label:
    %
    curr_history = get(hax_time,'UserData');
    histLen = length(curr_history);
    str = 'Previous Focus';
    if histLen>0,
      str = [str ' (' num2str(histLen) ')'];
      ena = 'on';
    else
      ena = 'off';
    end
    
    % Get panner context menu handle:
    hmenu = findobj( get(get(ud.hthumb, 'UIContextMenu'),'Children'),'Tag','Focus In');
    hAllMenus = get(hmenu,'UserData'); % vector of handles to context menus
    hFocusPrev = hAllMenus(2);
    set(hFocusPrev, 'Label',str);
    set(hAllMenus(2:3), 'Enable',ena);  % Prev and Reset Focus menus
    
  end %function

%---------------------------------------------------------------
  function focus_menu_render_callback(~, ~)
    % Used to update the enable of the "Focus In" menu item
    % Only enabled if thumb_xlim ~= curr_xlim
    
    hfig=gcbf; hparent=gcbo;
    ud = get(hfig,'UserData');
    hAllMenus = get(hparent,'Children'); % vector of handles to context menus
    
    % Enable 'Focus on Window' if zoom window is less than entire panner
    %
    hFocusIn = hAllMenus(end);  % 'Focus on Zoom' entry
    hax_time = ud.hax(2);
    curr_xlim = get(hax_time,'XLim'); % get current time-axis limits
    % Get thumbnail xlim vector:
    thumb_xdata = get(ud.hthumb,'XData');  % current thumbnail patch coords
    thumb_xlim  = [min(thumb_xdata) max(thumb_xdata)]; % convert to xlim
    if ~isequal(curr_xlim, thumb_xlim),
      ena='on';
    else
      ena='off';
    end
    set(hFocusIn,'Enable',ena);
    
  end %function

%---------------------------------------------------------------
  function focusTimeIn(~,~)
    
    hfig=gcbf;
    
    % get current time-axis (panner) limits
    ud = get(hfig,'UserData');
    hax_time = ud.hax(2);
    curr_xlim = get(hax_time,'XLim');
    
    % Get thumbnail xlim vector:
    thumb_xdata = get(ud.hthumb,'XData');  % current thumbnail patch coords
    thumb_xlim  = [min(thumb_xdata) max(thumb_xdata)]; % convert to xlim
    
    if ~isequal(curr_xlim, thumb_xlim),
      push_curr_to_focus_history(hfig);
      
      % Zoom in to thumb limits
      hax_time = ud.hax(2);
      
      set(hax_time,'XLim', thumb_xlim);
      update_axes_with_eng_units(gcbf);
    end
    
  end %function

%---------------------------------------------------------------
  function focusTimePrev(~,~)
    
    hfig=gcbf;
    ud = get(hfig,'UserData');
    hax_time = ud.hax(2);
    
    % Reset to last focus
    xlim = pop_from_focus_history(hfig);
  
    set(hax_time, 'XLim',xlim);
    update_axes_with_eng_units(gcbf);
    
  end %function

%---------------------------------------------------------------
  function focusTimeReset(~,~,hfig)
    % Remove all previous focus entries
    
    if nargin<3, hfig=gcbf; end
    clear_focus_history(hfig);
    
    % Reset focus zoom:
    ud = get(hfig,'UserData');
    hax_time = ud.hax(2);
    im_xdata = get(ud.himage,'XData');
    
    if length(im_xdata) == 1
      set(hax_time, 'XLim', [0 im_xdata]);
    else
      set(hax_time,'XLim',[min(im_xdata) max(im_xdata)]);
    end
    update_axes_with_eng_units(hfig);
    
  end %function

% ---------------------------------------------------------------
% PARAMETER WINDOW
% --------------------------------------------------------------
% function create_param_gui

% ---------------------------------------------------------------
% AXES UPDATE FUNCTIONS
% --------------------------------------------------------------
  function update_left_plot(hfig)
    % UPDATE_LEFT_PLOT Updates the frequency plot with the appropriate analysis
    
    ud = get(hfig,'UserData');
    mode = ud.plot.left;
    if strcmp(mode,'spectrogram_freq_slice'),
      update_freqslice(hfig);
    else
      update_psdplot(hfig);
    end
    
  end %function

% --------------------------------------------------------------
  function update_freqslice(hfig)
    % UPDATE_FREQSLICE Update the Frequency Slice (on the left axes)
    
    ud = get(hfig,'UserData');
    set(ud.hfreq_line, 'XData',get_spec_freq(hfig),'YData',ud.f);
    hax_freq = ud.hax(4);
    
    b = get(ud.himage,'CData');
    blim = [min(b(:)) max(b(:))];
    spec_ylim = [0 max(ud.f)];
    xlabel('dB');
    set(hax_freq, ...
      'YLim',spec_ylim, ...
      'XLim',blim,...
      'XTickMode','auto');
    set(hax_freq, 'XTick', return2ticks(hax_freq));
    
    % Update extent of horizontal crosshair:
    set(ud.hfreq_x, 'XData',blim);
    
  end %function

% --------------------------------------------------------------
  function update_psdplot(hfig)
    % UPDATE_PSDPLOT Update the PSD plot (on the left axes)
    
    ud = get(hfig,'UserData');
    wstate = warning;
    warning off; %#ok
    density = 10*log10(ud.Pxx);
    warning(wstate);
    
    hax_freq = ud.hax(4);
    
    % Update the PSD plot with data and limits
    set(ud.hfreq_line,'XData',density,'YData',ud.w);
    xlim = [min(density(:)) max(density(:))];
    xlabel('dB/Hz');
    set(hax_freq, ...
      'YLim',     [0 ud.Fs/2],'XLim',xlim,...
      'XTickMode','auto');
    set(hax_freq, 'XTick', return2ticks(hax_freq));
    
    % Update extent of horizontal crosshair:
    set(ud.hfreq_x, 'XData',xlim);
    
  end %function

% ---------------------------------------------------------------
% UTILITY FUNCTIONS
% --------------------------------------------------------------
  function new_xtick = return2ticks(haxes)
    % RETURN2TICKS Utility to return two tick marks
    x = get(haxes,'XTick');
    if length(x)>2,
      new_xtick = [x(1) x(end)];
    else
      new_xtick = x;
    end
    
  end %function

%----------------------------------------------------------
% create_uiframework
  function  hFrame = create_uiframework
    %CREATE_UIFRAMEWORK Create the uiframework.
    
    % Create uimgr UI frame
    hFrame = CreateUIFrame(...
      CreateBaseMenus,...
      CreateBaseToolbar,...
      CreateBaseStatusbar);
    
    % Install plugins
    hPToolbar = install_plugin(hFrame);
    
    % Render uimgr ui objets
    hFrame.render;
    
    % Create play button without audioplayer
    if isempty(hPToolbar) && ispc
      render_playbutton(hFrame);
    end
    
    % Top of the uimgr tree
    % technically, this is the only handle we really need to keep
    % all others could be found from this, using hFrame.findchild(...)
    ud.huiframe = hFrame;
    
    % Other params in the userdata:
    ud.Nwintxt = 'Nwin:';
    ud.Nlaptxt = 'Nlap:';
    ud.Nffttxt = 'Nfft:';
    ud.bGraphic = false;
    ud.param_dlg = [];
    
    set(hFrame.WidgetHandle,...
      'CloseRequestFcn', @close_cb, ...
      'UserData', ud);
    
    % fix g323879
    hPrintBehavior = hggetbehavior(hFrame.WidgetHandle,'Print');
    set(hPrintBehavior,'WarnOnCustomResizeFcn','off');
    
  end %create_uiframework

%----------------------------------------------------------
% CreateBaseMenus
  function hm = CreateBaseMenus
    
    % Menus group
    hm = uimgr.uimenugroup('Menus');
    
    % Files
    mFile = uimgr.uimenugroup('File', '&File');
    
    mFilePrint = uimgr.uimenugroup('PrintOpt');
    mPrint = uimgr.uimenu('Print', '&Print');
    mPrint.setWidgetPropertyDefault('Callback',@printdlg_cb);
    mPrintview = uimgr.uimenu('Printview', 'Print Pre&view');
    mPrintview.setWidgetPropertyDefault('Callback',@printpreview_cb);
    mFilePrint.add(mPrint, mPrintview);
    
    mFileOpt = uimgr.uimenugroup('FileOpt');
    mClose = uimgr.uimenu('Close', '&Close');
    mClose.setWidgetPropertyDefault('Callback', @close_cb);
    mFileOpt.add(mClose);
    mFile.add(mFilePrint, mFileOpt);
    
    % Tools
    mTool = uimgr.uimenugroup('Zoom', '&Tools');
    
    mZoomfull = uimgr.uimenu('Zoomfull', 'Zoom &Full');
    mZoomfull.setWidgetPropertyDefault('Callback', @zoom_full);
    
    mZoomin = uimgr.uimenu('Zoomin', '&Zoom In');
    mZoomin.setWidgetPropertyDefault('Callback', @zoom_in);
    
    mZoomout = uimgr.uimenu('Zoomout', 'Zoom &Out');
    mZoomout.setWidgetPropertyDefault('Callback', @zoom_out);
    
    mZoomgroup = uimgr.uimenugroup('Zoomgroup',mZoomfull, mZoomin, mZoomout);
    
    mParam = uimgr.uimenu('Param', '&Spectrogram Parameters ...');
    mParam.setWidgetPropertyDefault('Callback', @param_setting);
    
    mTool.add(mZoomgroup, mParam);
    
    % Windows
    mWin = uimgr.uimenugroup('Windows', '&Windows');
    mWin.setWidgetPropertyDefault('Tag','winmenu', ...
      'Callback', winmenu('callback'));
    
    % Help
    mHelp = uimgr.uimenugroup('Help', '&Help');
    
    mHelpST = uimgr.uimenugroup('HelpST');
    mSPdemo = uimgr.uimenu('spgdemo', 'Spectrogram Demo &Help');
    mSPdemo.setWidgetPropertyDefault('Callback', @HelpSpecgramdemoCB);
    
    mSPCToolbox = uimgr.uimenu('spctoolbox','Signal Processing &Toolbox Help');
    mSPCToolbox.setWidgetPropertyDefault('Callback', @HelpProductCB);
    
    mHelpST.add(mSPdemo, mSPCToolbox);
    
    mHelpWs = uimgr.uimenugroup('HelpWs');
    mWhats = uimgr.uimenu('whats', '&What''s This?');
    mWhats.setWidgetPropertyDefault('Callback', @HelpWhatsThisCB);
    mHelpWs.add(mWhats);
    
    mHelpDemo = uimgr.uimenugroup('Demo');
    mDemo = uimgr.uimenu('Demos',  '&Demos');
    mDemo.setWidgetPropertyDefault('Callback', @HelpDemosCB);
    mHelpDemo.add(mDemo);
    
    mHelpAbout = uimgr.uimenugroup('About');
    mAbout = uimgr.uimenu('abouthelp', '&About Signal Processing Toolbox');
    mAbout.setWidgetPropertyDefault('Callback',  @HelpAboutCB);
    mHelpAbout.add(mAbout);
    mHelp.add(mHelpST, mHelpWs, mHelpDemo, mHelpAbout);
    
    hm.add(mFile, mTool, mWin, mHelp);
  end % CreateBaseMenus

%----------------------------------------------------------
% CreateBaesToolbar
  function ht = CreateBaseToolbar
    
    icon_file = 'specgramdemoicons.mat';
    icons = spcwidgets.LoadIconFiles(icon_file);
    
    bPrint = uimgr.uipushtool('Print');
    bPrint.IconAppData = 'print';
    bPrint.setWidgetPropertyDefault(...
      'TooltipString','Print', ...
      'ClickedCallback',@printdlg_cb);
    
    bPrintPre = uimgr.uipushtool('PrintPre');
    bPrintPre.IconAppData = 'printpreview';
    bPrintPre.setWidgetPropertyDefault(...
      'TooltipString','Print Preview', ...
      'ClickedCallback',@printpreview_cb);
    
    bPrintgroup = uimgr.uibuttongroup('Printgroup',bPrint, bPrintPre);
    
    bCenter = uimgr.uipushtool('Center');
    bCenter.IconAppData = 'center_crosshair';
    bCenter.setWidgetPropertyDefault(...
      'TooltipString','Center Crosshair', ...
      'ClickedCallback',@center_cross);
    bCentergroup = uimgr.uibuttongroup('Centergroup', bCenter);
    
    bWhats = uimgr.uipushtool('Whats');
    bWhats.IconAppData = 'whatsthis';
    bWhats.setWidgetPropertyDefault(...
      'TooltipString','What''s This?', ...
      'ClickedCallback',@HelpWhatsThisCB);
    bWhatsgroup = uimgr.uibuttongroup('Whatsgroup', bWhats);
    
    bNormal = uimgr.uipushtool('normal');
    bNormal.IconAppData = 'fullview';
    bNormal.setWidgetPropertyDefault(...
      'TooltipString','Zoom 100%', ...
      'ClickedCallback', @zoom_full);
    
    bZoomin = uimgr.uipushtool('zoomin');
    bZoomin.IconAppData = 'zoominx';
    bZoomin.setWidgetPropertyDefault(...
      'TooltipString','Zoom In', ...
      'ClickedCallback',@zoom_in);
    
    bZoomout = uimgr.uipushtool('zoomout');
    bZoomout.IconAppData = 'zoomoutx';
    bZoomout.setWidgetPropertyDefault(...
      'TooltipString','Zoom Out', ...
      'ClickedCallback',@zoom_out);
    
    bZoomgroup = uimgr.uibuttongroup('zoomgroup', bNormal, bZoomin, bZoomout);
    
    ht = uimgr.uitoolbar('Toolbar',bPrintgroup, bZoomgroup, bCentergroup, bWhatsgroup);
    setappdata(ht, icons);
    
  end % CreateBaseToolbars

%----------------------------------------------------------
% CreateBaseStatusbar
  function hs = CreateBaseStatusbar
    
    hs = uimgr.uistatusbar('StatusBar');
    hs.setWidgetPropertyDefault('Text', 'Ready');
    hsNwin = uimgr.uistatus('Nwin');
    hsNwin.setWidgetPropertyDefault(...
        'Text', 'Nwin: 256', ...
        'Tooltip', 'Spectrogram Window Size', ...
        'Width', 80);
    hsNlap = uimgr.uistatus('Nlap');
    hsNlap.setWidgetPropertyDefault(...
        'Text', 'Nlap: 200', ...
        'Tooltip', 'Spectrogram Overlap', ...
        'Width', 80);
    hsNfft = uimgr.uistatus('Nfft');
    hsNfft.setWidgetPropertyDefault(...
        'Text', 'Nfft: 256', ...
        'Tooltip', 'Spectrogram FFT Size', ...
        'Width', 80);
    hs.add(hsNwin, hsNlap, hsNfft);
    
  end % function

%----------------------------------------------------------
% CreateUIFrame
  function hFrame = CreateUIFrame(hMenu, hToolbar, hStatusbar)
    
    hFrame = uimgr.uifigure('UIFrame',...
      hMenu,...
      hToolbar,...
      hStatusbar);
    
    hFrame.Visible = 'off';
    hFrame.setWidgetPropertyDefault('NumberTitle','off', ...
        'Name','Spectrogram Demo', ...
        'MenuBar','none', ...
        'ToolBar','none', ...
        'ResizeFcn',@resize_fig, ...
        'Position',[50 15 550 450],...
        'PaperPositionMode','auto',...
        'DockControls','Off');

    hFrame.Enable = 'off';
  end % function

% %--------------------------------------------------------------
% % [EOF] specgramdemo.m