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 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

Generated on Fri 12-Aug-2011 11:36:15 by m2html © 2005