0001 function outdata=specscope(indata)
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019 close all;
0020
0021
0022
0023
0024
0025
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
0037
0038
0039 global acq;
0040
0041
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
0057
0058
0059 if nargout
0060
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
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
0097
0098
0099 acq.t0=clock;
0100 acq.tn=clock;
0101
0102
0103 while 1;
0104
0105
0106 if acq.stop;
0107 break;
0108 end
0109
0110
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
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
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
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
0157 while acq.times(1,end) - acq.times(1,1) > acq.display_size;
0158
0159
0160 y = length(t);
0161 acq.times(:,1:y) = [];
0162 acq.spectra(:,1:y) = [];
0163
0164 end
0165
0166
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
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
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
0208 image( acq.times, f, scaled_spectra ); axis xy;
0209 drawnow;
0210
0211 else
0212
0213 acq.skips = acq.skips + 1;
0214 end
0215
0216 end
0217
0218
0219
0220
0221
0222 acq.t1=clock;
0223 elapsed = etime(acq.t1,acq.t0);
0224 fprintf( 'Elapsed time %f seconds\n', elapsed );
0225
0226
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
0234 if acq.live
0235 stop(acq.ai);delete( acq.ai );clear acq.ai;
0236 end
0237
0238
0239 delete(fig);
0240 delete(gcf);
0241
0242 if nargout
0243
0244 fprintf('Saving output data\n');
0245 outdata=outdata(1:floor(acq.samples_acquired));
0246 end
0247
0248 return;
0249
0250
0251
0252
0253
0254
0255
0256
0257
0258
0259
0260
0261
0262
0263
0264 function keypress(varargin)
0265
0266 global acq;
0267 keypressed=get(gcf,'CurrentCharacter');
0268
0269
0270 if keypressed;
0271
0272
0273 if strcmp( keypressed, 'g');
0274 saveas( acq.fig, sprintf( 'frame%d.png',acq.times(length(acq.times)) ) )
0275 end
0276
0277
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
0285 elseif strcmp( keypressed, 'x');
0286 acq.scale = acq.scale - increment;
0287 elseif strcmp( keypressed, 's');
0288 acq.scale = acq.scale + increment;
0289
0290
0291 elseif strcmp( keypressed, 'd');
0292 defaults
0293 acq.restart=1;
0294
0295 elseif strcmp( keypressed, 'n');
0296 request_normalize
0297
0298
0299 elseif strcmp( keypressed, 'q');
0300 request_quit
0301
0302
0303 elseif strcmp( keypressed, 'p');
0304 request_pause
0305
0306
0307 elseif strcmp( keypressed, 'h');
0308 audio_instr
0309
0310
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
0346
0347
0348
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
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);
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
0458
0459
0460 function fig=create_ui()
0461 global acq;
0462
0463 bgcolor = [1 1 1];
0464
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
0605
0606
0607 function pos = centerfig(width,height)
0608
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
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