Home > chronux_0.5 > specscope > specscope.m

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: This function is called by:

SUBFUNCTIONS ^

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

Generated on Tue 16-Aug-2005 21:33:45 by m2html © 2003