# specscope

## PURPOSE

record and plot audio spectrogram

## SYNOPSIS

function outdata=specscope(indata)

## DESCRIPTION

     record and plot audio spectrogram

     Usage: outdata=specscope(indata)

      Input: indata (optional)
        Displays a recorded piece of data, if an argument is passed
        Otherwise displays audio data from an attached microphone

      Output: outdata (optional)
        If present, will return up to 10 minutes
        of captured audio data.

      Note: Parameters such as sampling frequency, number of tapers
        and display refresh rate may be set below if desired.
        You can also acquire data from a national instruments card.

## CROSS-REFERENCE INFORMATION

This function calls:

- close VR=CLOSE(VR)
- close VW=CLOSE(VW)
- close VR=CLOSE(VR)
- close VW=CLOSE(VW)
- close VR=CLOSE(VR)
- close VW=CLOSE(VW)
- mtspecgramc Multi-taper time-frequency spectrum - continuous process
- dpsschk Helper function to calculate tapers and, if precalculated tapers are supplied,

This function is called by:

## SUBFUNCTIONS

- function keypress(varargin)
- function defaults()
- function update_display()
- function request_quit(varargin)
- function request_pause(varargin)
- function request_normalize(varargin)
- function update_defaults(varargin)
- function update_tapers(varargin)
- function update_window(varargin)
- function update_offset(varargin)
- function update_scale(varargin)
- function update_display_size(varargin)
- function update_fpass(varargin)
- function update_deriv(varargin)
- function update_log(varargin)
- function update_bgsub(varargin)
- function fig=create_ui()
- function pos = centerfig(width,height)
- function audio_instr()

## SOURCE CODE

    0001 function outdata=specscope(indata)
    0002 % record and plot audio spectrogram
    0003 %
    0004 % Usage: outdata=specscope(indata)
    0005 %
    0006 %  Input: indata (optional)
    0007 %    Displays a recorded piece of data, if an argument is passed
    0008 %    Otherwise displays audio data from an attached microphone
    0009 %
    0010 %  Output: outdata (optional)
    0011 %    If present, will return up to 10 minutes
    0012 %    of captured audio data.
    0013 %
    0014 %  Note: Parameters such as sampling frequency, number of tapers
    0015 %    and display refresh rate may be set below if desired.
    0016 %    You can also acquire data from a national instruments card.
    0017 %
    0018
    0019 close all;
    0020
    0021 %=======================================================================
    0022 % Check toolboxes
    0023 %=======================================================================
    0024
    0025 % Check for toolboxes
    0026 if not(exist('analoginput','file'));
    0027     fprintf('You need to install the DAQ toolbox first\n');
    0028     return
    0029 end
    0030 if not(exist('mtspecgramc','file'));
    0031     fprintf('You need to install the Chronux toolbox first from http://chronux.org/\n');
    0032     return
    0033 end
    0034
    0035 %=======================================================================
    0036 % Set parameters
    0037 %=======================================================================
    0038
    0039 global acq;
    0040
    0041 % Set defaults
    0042 acq.params.Fs = 44100;
    0043 acq.pause = 0;
    0044 acq.skips = 0;
    0045 acq.stop = 0;
    0046 acq.restart = 0;
    0047 acq.plot_frequency = 10;
    0048 acq.samples_acquired = 0;
    0049 acq.spectra = [];
    0050 acq.times = [];
    0051 defaults
    0052 audio_instr;
    0053 fig=create_ui;
    0054
    0055 %=======================================================================
    0056 % Check arguments, start DAQ
    0057 %=======================================================================
    0058
    0059 if nargout
    0060    % save up to ten minutes data, preallocated...
    0061     fprintf('Pre-allocating memory for data save - please be patient.\n');
    0062    outdata=zeros( (acq.params.Fs * 60 * 10), 1 );
    0063 end
    0064
    0065 if nargin == 1;
    0066     acq.indata = indata;
    0067     acq.live = 0;
    0068 else
    0069     % Create and set up and start analog input daq
    0070     input=1;
    0071     if input==1;
    0072         acq.ai = analoginput('winsound');
    0073         addchannel( acq.ai, 1 );
    0074     else
    0075         acq.ai = analoginput('nidaq', 1);
    0076         addchannel(acq.ai, 0);
    0077         set(acq.ai,'InputType','SingleEnded')
    0078         set(acq.ai,'TransferMode','Interrupts')
    0079         set(acq.ai,'TriggerType','Manual');
    0080     end
    0081     set( acq.ai, 'SampleRate', acq.params.Fs )
    0082     acq.params.Fs = get( acq.ai, 'SampleRate' );
    0083     set( acq.ai, 'SamplesPerTrigger', inf )
    0084     start(acq.ai)
    0085     acq.live = 1;
    0086
    0087     if input==2;
    0088         trigger(acq.ai);
    0089     end
    0090 end
    0091
    0092 acq.samples_per_frame = acq.params.Fs / acq.plot_frequency;
    0093
    0094
    0095 %=======================================================================
    0096 % The scope main loop
    0097 %=======================================================================
    0098
    0099 acq.t0=clock;
    0100 acq.tn=clock;
    0101
    0102 % Loop over frames to acquire and display
    0103 while 1;
    0104
    0105     % Check for quit signal
    0106     if acq.stop;
    0107         break;
    0108     end
    0109
    0110     % Calculate times
    0111     calctime = acq.samples_acquired / acq.params.Fs;
    0112     acq.samples_acquired = acq.samples_acquired + acq.samples_per_frame;
    0113     acq.t1 = clock;
    0114     elapsed = etime(acq.t1,acq.t0);
    0115
    0116     % Get a small snippet of data
    0117     if ( acq.live )
    0118        data = getdata( acq.ai, acq.samples_per_frame );
    0119     else
    0120       while elapsed < acq.samples_acquired / acq.params.Fs
    0121         pause( acq.samples_acquired / (acq.params.Fs) - elapsed );
    0122         acq.t1=clock;
    0123         elapsed = etime(acq.t1,acq.t0);
    0124       end
    0125       if acq.samples_acquired + 2 * acq.samples_per_frame >= length( acq.indata )
    0126         acq.stop=1;
    0127       end
    0128       data = acq.indata(floor(acq.samples_acquired+1):floor(acq.samples_per_frame+acq.samples_acquired));
    0129     end
    0130
    0131     if nargout
    0132         outdata(floor(acq.samples_acquired+1):floor(acq.samples_acquired+length(data))) = data(:);
    0133     end
    0134
    0135     if acq.restart;
    0136        acq.restart = 0;
    0137        acq.spectra = [];
    0138        acq.times = [];
    0139     end
    0140
    0141     % Calculate spectrogram of data snippet
    0142     if acq.deriv
    0143       [s, t, f] = mtspecgramc(diff(data), acq.moving_window, acq.params );
    0144     else
    0145       [s, t, f] = mtspecgramc(data, acq.moving_window, acq.params );
    0146     end
    0147
    0148     % Add new spectra to that already calculated
    0149     acq.times = [acq.times t+calctime];
    0150     if acq.log
    0151         acq.spectra = [acq.spectra log(s')];
    0152     else
    0153         acq.spectra = [acq.spectra s'];
    0154     end
    0155
    0156     % Remove old spectra once window reaches desired size
    0157     while acq.times(1,end) - acq.times(1,1) > acq.display_size;
    0158
    0159         % Ring buffer!
    0160         y = length(t);
    0161         acq.times(:,1:y) = [];
    0162         acq.spectra(:,1:y) = [];
    0163
    0164     end
    0165
    0166     % Only plot if display is keeping up with real time and not paused
    0167     show_plot=1;
    0168     if nargin==0
    0169        if get(acq.ai, 'SamplesAvailable' ) > 10 * acq.samples_per_frame && acq.pause==0
    0170       show_plot=0;
    0171        end
    0172     else
    0173       if elapsed > calctime + 0.5
    0174     show_plot=0;
    0175       end
    0176     end
    0177
    0178     if acq.pause
    0179        show_plot=0;
    0180     end
    0181     if show_plot
    0182
    0183         if  acq.bgsub
    0184             acq.mean_spectra = mean( acq.spectra, 2 );
    0185         end
    0186
    0187         % Normalize until full screen passes by if requested
    0188         if acq.normalize>=1;
    0189             if acq.normalize==1
    0190                 acq.tn=clock;
    0191                 acq.normalize=2;
    0192             end
    0193             if etime(clock,acq.tn)>1.25*acq.display_size
    0194                 acq.normalize=0;
    0195             end
    0196             mins = min(min(acq.spectra));
    0197             maxs = max(max(acq.spectra));
    0198         end
    0199
    0200         % Scale the spectra based upon current offset and scale
    0201         if acq.bgsub
    0202            scaled_spectra = acq.offset + ( acq.scale ) * ( acq.spectra - repmat( acq.mean_spectra, [1,size(acq.spectra,2)]) ) / ( maxs - mins );
    0203         else
    0204             scaled_spectra = acq.offset + acq.scale * ( acq.spectra - mins ) / ( maxs - mins );
    0205         end
    0206
    0207         % Draw the image to the display
    0208         image( acq.times, f, scaled_spectra ); axis xy;
    0209         drawnow;
    0210
    0211     else
    0212         % Keep track of skipped displays
    0213         acq.skips = acq.skips + 1;
    0214     end
    0215
    0216 end
    0217
    0218 %=======================================================================
    0219 % Clean up
    0220 %=======================================================================
    0221
    0222 acq.t1=clock;
    0223 elapsed = etime(acq.t1,acq.t0);
    0224 fprintf( 'Elapsed time %f seconds\n', elapsed );
    0225
    0226 % Warn if many skips were encountered
    0227 if acq.skips > 5;
    0228     fprintf( '\nWARNING:\nThis program skipped plotting %d times to keep pace.\n', acq.skips )
    0229     fprintf( 'Run again without keyboard interaction or changing the figure size.\n' )
    0230     fprintf( 'If this message reappears you should reduce the plot frequency parameter.\n\n' );
    0231 end
    0232
    0233 % Clean up the analoginput object
    0234 if acq.live
    0235   stop(acq.ai);delete( acq.ai );clear acq.ai;
    0236 end
    0237
    0238 % Clean up the figure
    0239 delete(fig);
    0240 delete(gcf);
    0241
    0242 if nargout
    0243    % save up to ten minutes data, preallocated...
    0244     fprintf('Saving output data\n');
    0245    outdata=outdata(1:floor(acq.samples_acquired));
    0246 end
    0247
    0248 return;
    0249
    0250 %
    0251 %
    0252 %=======================================================================
    0253 % Functions called
    0254 %=======================================================================
    0255 %
    0256 %
    0257
    0258
    0259 %=======================================================================
    0260 % Handle Keypresses
    0261 %=======================================================================
    0262
    0263 % Handle figure window keypress events
    0264 function keypress(varargin)
    0265
    0266 global acq;
    0267 keypressed=get(gcf,'CurrentCharacter');
    0268
    0269 % ignore raw control, shift, alt keys
    0270 if keypressed;
    0271
    0272     % Save current frame as gif
    0273     if strcmp( keypressed, 'g');
    0274        saveas( acq.fig, sprintf( 'frame%d.png',acq.times(length(acq.times)) ) )
    0275     end
    0276
    0277     % Offset changes
    0278     increment=1;
    0279     if strcmp( keypressed, 'l');
    0280     acq.offset = acq.offset - increment;
    0281     elseif strcmp( keypressed, 'o');
    0282         acq.offset = acq.offset + increment;
    0283
    0284     % Scale changes
    0285     elseif strcmp( keypressed, 'x');
    0286         acq.scale = acq.scale - increment;
    0287     elseif strcmp( keypressed, 's');
    0288         acq.scale = acq.scale + increment;
    0289
    0290     % Reset defaults
    0291     elseif strcmp( keypressed, 'd');
    0292         defaults
    0293     acq.restart=1;
    0294     % Normalize spectra
    0295     elseif strcmp( keypressed, 'n');
    0296        request_normalize
    0297
    0298     % Quit
    0299     elseif strcmp( keypressed, 'q');
    0300     request_quit
    0301
    0302     % Pause
    0303     elseif strcmp( keypressed, 'p');
    0304        request_pause
    0305
    0306     % Help
    0307     elseif strcmp( keypressed, 'h');
    0308         audio_instr
    0309
    0310     % Change colormaps for 0-9,a-c
    0311     elseif strcmp( keypressed, '0' );
    0312         colormap( 'jet' );
    0313     elseif strcmp( keypressed, '1' );
    0314         colormap( 'bone' );
    0315     elseif strcmp( keypressed, '2' );
    0316         colormap( 'colorcube' );
    0317     elseif strcmp( keypressed, '3' );
    0318         colormap( 'cool' );
    0319     elseif strcmp( keypressed, '4' );
    0320         colormap( 'copper' );
    0321     elseif strcmp( keypressed, '5' );
    0322         colormap( 'gray' );
    0323     elseif strcmp( keypressed, '6' );
    0324         colormap( 'hot' );
    0325     elseif strcmp( keypressed, '7' );
    0326         colormap( 'hsv' );
    0327     elseif strcmp( keypressed, '8' );
    0328         colormap( 'autumn' );
    0329     elseif strcmp( keypressed, '9' );
    0330         colormap( 'pink' );
    0331     elseif strcmp( keypressed, 'a' );
    0332         colormap( 'spring' );
    0333     elseif strcmp( keypressed, 'b' );
    0334         colormap( 'summer' );
    0335     elseif strcmp( keypressed, 'c' );
    0336         colormap( 'winter' );
    0337     end
    0338
    0339     update_display
    0340
    0341 end
    0342 return
    0343
    0344 %=======================================================================
    0345 % Defaults
    0346 %=======================================================================
    0347
    0348 % Reset defaults
    0349 function defaults()
    0350   global acq;
    0351   acq.params.raw_tapers = [2 3];
    0352   acq.moving_window = [0.02 0.02];
    0353   acq.params.tapers=dpsschk(acq.params.raw_tapers,round(acq.params.Fs*acq.moving_window(1)),acq.params.Fs);
    0354   acq.offset = 0;
    0355   acq.scale = 64;
    0356   acq.display_size = 3;
    0357   acq.params.fpass = [50 8000];
    0358   acq.deriv=1;
    0359   acq.log=1;
    0360   acq.bgsub = 1;
    0361   acq.params.pad= 0;
    0362   acq.normalize = 2;
    0363 return
    0364
    0365 function update_display()
    0366 global acq;
    0367     set(acq.tapers_ui,'String',sprintf( '%.0f %.0f', acq.params.raw_tapers(1), acq.params.raw_tapers(2) ));
    0368     set(acq.window_ui,'String',sprintf( '%.2f %.2f', acq.moving_window(1), acq.moving_window(2) ));
    0369     set(acq.offset_ui,'String',sprintf( '%d', acq.offset ));
    0370     set(acq.scale_ui,'String',sprintf( '%d', acq.scale ));
    0371     set(acq.display_size_ui,'String',sprintf( '%.1f', acq.display_size ));
    0372     set(acq.frequency_ui,'String',sprintf( '%.1f %.1f', acq.params.fpass(1), acq.params.fpass(2)  ))
    0373     set(acq.derivative_ui,'Value',acq.deriv);
    0374     set(acq.log_ui,'Value',acq.log);
    0375     set(acq.bgsub_ui,'Value',acq.bgsub);
    0376     return
    0377
    0378
    0379 %=======================================================================
    0380 % Update ui controls
    0381 %=======================================================================
    0382
    0383 function request_quit(varargin)
    0384      global acq;
    0385      acq.stop=1;
    0386 return
    0387
    0388 function request_pause(varargin)
    0389      global acq;
    0390      acq.pause = not( acq.pause );
    0391 return
    0392
    0393 function request_normalize(varargin)
    0394     global acq;
    0395         acq.normalize = 2;
    0396 return
    0397
    0398 function update_defaults(varargin)
    0399   global acq;
    0400   defaults
    0401   update_display
    0402   acq.restart=1;
    0403 return
    0404
    0405 function update_tapers(varargin)
    0406      global acq;
    0407      acq.params.raw_tapers = sscanf(get( gco, 'string' ),'%f %d')';
    0408      acq.params.tapers=dpsschk(acq.params.raw_tapers,round(acq.params.Fs*acq.moving_window(1)),acq.params.Fs); % check tapers
    0409 return
    0410
    0411 function update_window(varargin)
    0412      global acq;
    0413      acq.moving_window = sscanf(get( gco, 'string' ),'%f %f');
    0414          acq.params.tapers=dpsschk(acq.params.raw_tapers,round(acq.params.Fs*acq.moving_window(1)),acq.params.Fs);
    0415      acq.restart = 1;
    0416 return
    0417
    0418 function update_offset(varargin)
    0419      global acq;
    0420      acq.offset = sscanf(get( gco, 'string' ),'%f');
    0421      return
    0422
    0423 function update_scale(varargin)
    0424      global acq;
    0425      acq.scale = sscanf(get( gco, 'string' ),'%f');
    0426      return
    0427
    0428 function update_display_size(varargin)
    0429      global acq;
    0430      acq.display_size = sscanf(get( gco, 'string' ),'%f');
    0431      return
    0432
    0433 function update_fpass(varargin)
    0434      global acq;
    0435      acq.params.fpass = sscanf(get( gco, 'string' ),'%f %f');
    0436          acq.restart = 1;
    0437      return
    0438
    0439 function update_deriv(varargin)
    0440      global acq;
    0441      acq.deriv=get( gco, 'Value' );
    0442      acq.normalize=1;
    0443      return
    0444
    0445 function update_log(varargin)
    0446      global acq;
    0447      acq.log=get( gco, 'Value' );
    0448      acq.normalize=1;
    0449      return
    0450
    0451 function update_bgsub(varargin)
    0452      global acq;
    0453      acq.bgsub=get( gco, 'Value' );
    0454      return
    0455
    0456 %=======================================================================
    0457 % UI display
    0458 %=======================================================================
    0459
    0460 function fig=create_ui()
    0461     global acq;
    0462
    0463     bgcolor = [1 1 1]; % .7 .7 .7
    0464     % ===Create main figure==========================
    0465     fig = figure('Position',centerfig(800,600),...
    0466         'NumberTitle','off',...
    0467         'Name','Real-time spectrogram',...
    0468         'doublebuffer','on',...
    0469         'HandleVisibility','on',...
    0470     'Renderer', 'openGL', ...
    0471     'KeyPressFcn', @keypress, ...
    0472         'Color',bgcolor);
    0473
    0474     acq.fig = fig;
    0475     offset = 80;
    0476     % ===text==========
    0477     uicontrol(gcf,'Style','text',...
    0478         'String', 'tapers',...
    0479         'Position',[offset+225 20 45 20],...
    0480         'BackgroundColor',bgcolor);
    0481     uicontrol(gcf,'Style','text',...
    0482         'String', 'moving win',...
    0483         'Position',[offset+300 20 70 20],...
    0484         'BackgroundColor',bgcolor);
    0485     uicontrol(gcf,'Style','text',...
    0486         'String', 'offset',...
    0487         'Position',[offset+375 20 30 20],...
    0488         'BackgroundColor',bgcolor);
    0489     uicontrol(gcf,'Style','text',...
    0490         'String', 'scale',...
    0491         'Position',[offset+410 20 30 20],...
    0492         'BackgroundColor',bgcolor);
    0493     uicontrol(gcf,'Style','text',...
    0494         'String', 't axis',...
    0495         'Position',[offset+445 20 30 20],...
    0496         'BackgroundColor',bgcolor);
    0497     uicontrol(gcf,'Style','text',...
    0498         'String', 'f axis',...
    0499         'Position',[offset+480 20 40 20],...
    0500         'BackgroundColor',bgcolor);
    0501     uicontrol(gcf,'Style','text',...
    0502         'String', 'deriv',...
    0503         'Position',[offset+550 20 35 20],...
    0504         'BackgroundColor',bgcolor);
    0505     uicontrol(gcf,'Style','text',...
    0506         'String', 'log',...
    0507         'Position',[offset+580 20 35 20],...
    0508         'BackgroundColor',bgcolor);
    0509     uicontrol(gcf,'Style','text',...
    0510         'String', 'bgsub',...
    0511         'Position',[offset+610 20 35 20],...
    0512         'BackgroundColor',bgcolor);
    0513
    0514     % ===The quit button===============================
    0515     uicontrol('Style','pushbutton',...
    0516         'Position',[offset+5 5 45 20],...
    0517         'String','Quit',...
    0518         'Interruptible','off',...
    0519         'BusyAction','cancel',...
    0520         'Callback',@request_quit);
    0521
    0522     % ===The pause button===============================
    0523     uicontrol('Style','pushbutton',...
    0524         'Position',[offset+55 5 45 20],...
    0525         'String','Pause',...
    0526         'Interruptible','off',...
    0527         'BusyAction','cancel',...
    0528         'Callback',@request_pause);
    0529
    0530     % ===The defaults button===============================
    0531     uicontrol('Style','pushbutton',...
    0532         'Position',[offset+105 5 50 20],...
    0533         'String','Defaults',...
    0534         'Interruptible','off',...
    0535         'BusyAction','cancel',...
    0536         'Callback',@update_defaults);
    0537
    0538     % ===The normalize button===============================
    0539     uicontrol('Style','pushbutton',...
    0540         'Position',[offset+160 5 60 20],...
    0541         'String','Normalize',...
    0542         'Interruptible','off',...
    0543         'BusyAction','cancel',...
    0544         'Callback',@request_normalize );
    0545
    0546     % ===Tapers============================================
    0547     acq.tapers_ui = uicontrol(gcf,'Style','edit',...
    0548         'String', sprintf( '%.0f %.0f', acq.params.raw_tapers(1), acq.params.raw_tapers(2) ),...
    0549         'Position',[offset+225 5 70 20],...
    0550         'CallBack', @update_tapers);
    0551
    0552     % ===Window============================================
    0553     acq.window_ui=uicontrol(gcf,'Style','edit',...
    0554         'String', sprintf( '%.2f %.2f', acq.moving_window(1), acq.moving_window(2) ),...
    0555         'Position',[offset+300 5 70 20],...
    0556         'CallBack', @update_window);
    0557
    0558     % ===Offset============================================
    0559     acq.offset_ui = uicontrol(gcf,'Style','edit',...
    0560         'String', sprintf( '%d', acq.offset ),...
    0561         'Position',[offset+375 5 30 20],...
    0562         'CallBack', @update_offset);
    0563
    0564     % ===Scale============================================
    0565     acq.scale_ui = uicontrol(gcf,'Style','edit',...
    0566         'String', sprintf( '%d', acq.scale ),...
    0567         'Position',[offset+410 5 30 20],...
    0568         'CallBack', @update_scale);
    0569
    0570     % ===display size======================================
    0571     acq.display_size_ui = uicontrol(gcf,'Style','edit',...
    0572         'String', sprintf( '%.1f', acq.display_size ),...
    0573         'Position',[offset+445 5 30 20],...
    0574         'CallBack', @update_display_size);
    0575
    0576     % ===frequency axis=====================================
    0577     acq.frequency_ui = uicontrol(gcf,'Style','edit',...
    0578         'String', sprintf( '%.1f %.1f', acq.params.fpass(1), acq.params.fpass(2)  ),...
    0579         'Position',[offset+480 5 80 20],...
    0580         'CallBack', @update_fpass);
    0581
    0582     % ===derivative=====================================
    0583     acq.derivative_ui = uicontrol(gcf,'Style','checkbox',...
    0584         'Value',acq.deriv,...
    0585         'Position',[offset+565 5 20 20],...
    0586         'CallBack', @update_deriv);
    0587
    0588     % ===log=====================================
    0589     acq.log_ui = uicontrol(gcf,'Style','checkbox',...
    0590         'Value',acq.log,...
    0591         'Position',[offset+590 5 20 20],...
    0592         'CallBack', @update_log);
    0593
    0594     % ===bgsub=====================================
    0595     acq.bgsub_ui = uicontrol(gcf,'Style','checkbox',...
    0596         'Value',acq.bgsub,...
    0597         'Position',[offset+615 5 20 20],...
    0598         'CallBack', @update_bgsub);
    0599
    0600 return
    0601
    0602
    0603 %=======================================================================
    0604 % Assorted functions
    0605 %=======================================================================
    0606
    0607 function pos = centerfig(width,height)
    0608 % Find the screen size in pixels
    0609 screen_s = get(0,'ScreenSize');
    0610 pos = [screen_s(3)/2 - width/2, screen_s(4)/2 - height/2, width, height];
    0611 return
    0612
    0613
    0614 function audio_instr()
    0615 % Show instructions
    0616
    0617   fprintf('INSTRUCTIONS:\n');
    0618   fprintf('Click on figure window first to activate controls.\n')
    0619   fprintf('Adjust tapers, windows, scales, offsets and axes using the gui\n');
    0620   fprintf('The deriv checkbox toggles derivative of the data\n');
    0621   fprintf('The log checkbox toggles a log of the spectrum\n');
    0622   fprintf('Press d or use defaults button to reset most parameters to defaults.\n')
    0623   fprintf('Press n or use normalize button to normalize spectra based upon values in current display.\n')
    0624   fprintf('Press 0-9,a-c to choose a colormap (default 0).\n')
    0625   fprintf('Press p to pause and unpause display.\n')
    0626   fprintf('Press o and l to adjust offset, or use offset textbox on gui.\n');
    0627   fprintf('Press s and x to adjust scale, or use scale textbox on gui.\n');
    0628   fprintf('Press h for this message.\n')
    0629   fprintf('Press q to quit, or use quit button on gui.\n\n')
    0630
    0631 return
    0632