/melba.m

http://github.com/smcg/MARTA · MATLAB · 1 lines · 1 code · 0 blank · 0 comment · 0 complexity · 4c7d69bf127f13f1e5bb714f79ad884a MD5 · raw file

  1. function mOut = melba(varargin) %MELBA - Matlab Event Labelling By Acoustics % % usage: melba data % melba(data) % melba(data, params) % % melba abort % close all MELBA viewers % % where DATA is one of % MAVIS-compatible struct % AUDIO object % signal, sRate pair (where signal can be a [nSamps x nSignals] aggregate) % string evaluating to a (possibly wild-carded) MS WAV filename (include extension) % string evaluating to a (possibly wild-carded) variable name % cell string array of variable names % (if variables not found in workspace loaded from mat or wav files on path) % % and PARAMS is one of the following NAME, VALUE pairs: % CLOSE - user-specified function to call when window closes % CONFIG - specify a previously saved configuration variable % LABELS - specify a set of previously defined labels % LPROC - specify a user labelling procedure % PALATE - specify a [nPoints x X,Y] palate trace % SEX - specify subject gender ({'M'}|'F'; affects F0 heuristics) % SPLINE - specify indices into data of (tongue) points to connect by spline % SPREAD - specify signal scaling (use 'AUTO' for self-normalization) % HEAD/TAIL - specify display offsets in msecs % CHANNEL - specify {'B'}|'L'|'R' for stereo input signals ('B' for both) % % entering melba alone will pop any active viewers forward, else display help % % mark tiede fecit vers = 'mkt 01/10/08 v4.0'; %----------------------------------------------------------------------------- % no args: if any MELBA viewers active, pop 'em forward, else show help if nargin < 1, set(0,'ShowHiddenHandles', 'on'); h = findobj('Tag', 'MELBA'); set(0,'ShowHiddenHandles', 'off'); if isempty(h), eval('help melba'); else, for i = 1 : length(h), figure(h(i)); end; end; return; end; %----------------------------------------------------------------------------- % parse args if ischar(varargin{1}), % command line argument action = upper(varargin{1}); elseif isstruct(varargin{1}), % mavis-compatible data action = 'INIT'; data = varargin{1}; labels = []; for i = 1 : length(data), % use 1st monodimensional vector if size(data(i).SIGNAL,2) == 1, signal = double(data(i).SIGNAL); sRate = data(i).SRATE; if isstruct(data) & isfield(data,'LABELS'), labels = data(i).LABELS; if isfield(labels,'NOTE'), % bw compatibility lx = rmfield(labels, 'NOTE'); for li = 1 : length(labels), lx(li).HOOK = labels(li).NOTE; end; labels = lx; end; end; break; end; end; names = {data.NAME}; k = strmatch('FORMANTS',names,'exact'); if ~isempty(k), formants = data(k).SIGNAL; data(k) = []; else, formants = []; end; k = strmatch('F0',names,'exact'); if ~isempty(k), f0 = data(k).SIGNAL; data(k) = []; else, f0 = []; end; if length(data) > 1, data = 1; % flag mavis data available but don't clone it (avoid memory whack) else, data = 0; end; elseif isa(varargin{1}, 'audio'), % audio object action = 'INIT'; signal = varargin{1}; sRate = signal.SRATE; signal = double(signal); labels = []; data = 0; formants = []; f0 = []; elseif nargin>1 & all([isnumeric(varargin{1}) isnumeric(varargin{2})]), action = 'INIT'; % signal vector, srate signal = double(varargin{1}); sRate = varargin{2}; labels = []; data = 0; formants = []; f0 = []; elseif iscellstr(varargin{1}), vList = varargin{1}; if nargin > 1, args = varargin(2:end); else, args = []; end; args{end+1} = 'VLIST'; args{end+1} = vList; melba(vList{1}, args{:}); return; elseif iscell(varargin{1}), action = 'INIT'; sRate = varargin{1}; signal = double(sRate{1}); sRate = sRate{2}; labels = []; data = 0; formants = []; f0 = []; else, error('MELBA: unrecognized data format'); end; %----------------------------------------------------------------------------- % branch by action switch action, %----------------------------------------------------------------------------- % ABORT: force exit case 'ABORT', set(0,'ShowHiddenHandles', 'on'); delete(findobj('Tag', 'MELBA')); set(0,'ShowHiddenHandles', 'off'); %----------------------------------------------------------------------------- % ABOUT: display help case 'ABOUT', s = {'MELBA - Matlab Event Labelling By Acoustics'; '' [' ' vers]}; helpwin(s, 'About MELBA'); %----------------------------------------------------------------------------- % AUTO: toggle auto update status case 'AUTO', state = get(gcbf, 'userdata'); s = get(gcbo,'checked'); state.AUTO = strcmp(s,'off'); set(gcbf, 'userdata', state); if state.AUTO, s = 'on'; else, s = 'off'; end; set(state.AUTOMENU, 'checked', s); if state.AUTO, SetBounds(state); end; %----------------------------------------------------------------------------- % CFGSPEC: configure spectra analysis case 'CFGSPEC', state = get(gcbf, 'userdata'); cfg = struct('NUDGE', state.NUDGE, ... 'WSIZE', state.WSIZE, ... 'ORDER', state.ORDER, ... 'FRAME', state.FRAME, ... 'AVGW', state.AVGW, ... 'OLAP', state.OLAP, ... 'BWCLIP', state.BWCLIP, ... 'PREEMP', state.PREEMP, ... 'SOFF', state.SOFF, ... 'SPECLIM', state.SPECLIM, ... 'ANAL', state.ANAL, ... 'F0THR', state.F0THR, ... 'ISF', state.ISF); cfg = ConfigSpectra(cfg); if isempty(cfg), return; end; state.FRAME = cfg.FRAME; state.ORDER = cfg.ORDER; state.WSIZE = cfg.WSIZE; state.NUDGE = cfg.NUDGE; state.AVGW = cfg.AVGW; state.OLAP = cfg.OLAP; state.BWCLIP= cfg.BWCLIP; state.PREEMP= cfg.PREEMP; state.SOFF = cfg.SOFF; state.F0THR = cfg.F0THR; % RECOMPUTE F0 if state.SOFF<=0, state.SOFF = 1; end; if state.SPECLIM ~= cfg.SPECLIM, specLim = cfg.SPECLIM; if specLim > state.SRATE/2, specLim = state.SRATE/2; end; state.SPECLIM = specLim; set(state.SPECTRA, 'xlim', [1 specLim]); end; state.ANAL = cfg.ANAL; state.ISF = cfg.ISF; set(gcbf, 'userdata', state); SetCursor(state); SetBounds(state, 0); %----------------------------------------------------------------------------- % CLONE: duplicate window for copy, print visibility case 'CLONE', state = get(gcbf, 'userdata'); h = gcbf; ch = figure('colormap', get(h, 'colormap'), 'name', state.NAME); copyobj(flipud(findobj(h,'type','axes')), ch); set(findobj(ch,'type','patch'), 'edgeColor', [1 1 1], 'faceColor', 'none', 'eraseMode', 'normal'); uicontrol(ch, ... % head 'style', 'text', ... 'horizontalAlignment', 'left', ... 'string', sprintf('Head: %.1f', state.HEAD), ... 'units', 'characters', ... 'backgroundColor', get(ch, 'color'), ... 'foregroundColor', [1 1 1], ... 'position', [4 3.0 15 1]); uicontrol(ch, ... % tail 'style', 'text', ... 'horizontalAlignment', 'left', ... 'string', sprintf('Tail: %.1f', state.TAIL), ... 'units', 'characters', ... 'backgroundColor', get(ch, 'color'), ... 'foregroundColor', [1 1 1], ... 'position', [4 1.0 15 1]); uicontrol(ch, ... % cursor 'style', 'text', ... 'horizontalAlignment', 'right', ... 'string', sprintf('Cursor: %.1f', state.CURSOR), ... 'units', 'characters', ... 'backgroundColor', get(ch, 'color'), ... 'foregroundColor', [1 1 1], ... 'position', [18 3.0 15 1]); %----------------------------------------------------------------------------- % COG: display spectral center-of-gravity measures case 'COG', state = get(gcbf, 'userdata'); [L1, skew, kurt] = cog(state.SIGNAL, state.SRATE, state.CURSOR); fprintf('Cursor @ %d (%g ms), \tL1: %g skew: %g kurt: %g\n', ... floor(state.CURSOR*state.SRATE/1000)+1, state.CURSOR, ... L1, skew, kurt); %----------------------------------------------------------------------------- % CONTRAST: adjust spectrogram contrast case 'CONTRAST', state = get(gcbf, 'userdata'); colormap(flipud(gray(256).^get(gcbo,'value'))); ih = findobj(state.SPECGRAM, 'type', 'image'); if ~isempty(ih), set(ih,'cdata', get(ih, 'cdata')); end; %----------------------------------------------------------------------------- % CURCHG: manual cursor entry case 'CURCHG', if nargin > 2, fh = varargin{3}; else, fh = gcbf; end; state = get(fh, 'userdata'); if nargin > 1, c = varargin{2}; else, c = str2num(get(state.CURSORF, 'string')); end; if c < 0, c = 0; elseif c > state.DUR, c = state.DUR; end; state.CURSOR = c; set(fh, 'userdata', state); SetCursor(state); %----------------------------------------------------------------------------- % DOWN: mouse down case 'DOWN', state = get(gcbf, 'userdata'); curPt = get(gca, 'CurrentPoint'); mod = get(gcbf, 'selectionType'); x = curPt(1,1); y = curPt(1,2); switch varargin{2}, % click in framing panel is bounds adjust case 'FRAME', if strcmp(mod, 'open'), state.HEAD = 0; % double click sets full selection state.TAIL = state.DUR; set(gcbf, 'userData', state); SetBounds(state); return; end; slop = state.DUR * .008; % grabbing tolerance if state.HEAD>x-slop & state.HEAD<x+slop, state.MOVEMODE = 'HEAD'; % move head elseif state.TAIL>x-slop & state.TAIL<x+slop, state.MOVEMODE = 'TAIL'; % move tail elseif x>state.HEAD & x<state.TAIL, state.MOVEMODE = -x; % move selection (save starting point) else, return; end; state.MOVED = 0; set(gcbf, 'userdata', state, ... 'windowButtonMotionFcn', 'melba MOVESEL', ... 'windowButtonUpFcn', 'melba UP'); % click in temporal panel sets cursor/label case 'CURSOR', if strcmp(mod, 'open'), return; end; % ignore double click % set(state.CURSORL, 'xData', [x x]); % update cursor position state.CURSOR = x; SetCursor(state); switch mod, case 'normal', % (left button/unmodified click) fall thru state.MOVEMODE = 'CURSOR'; % cursor update case 'open', % (double-click) return; otherwise, % (right button/modified click) if strcmp(mod, 'extend'), state.MOVEMODE = 'LBL_SILENT'; % shift: silent labelling else, state.MOVEMODE = 'LBL_NOISY'; % ctl/alt: annotated labelling end; if ~isempty(state.LPROC), % user proc labelling set(gcbf, 'userdata', state); % update mode lpState = feval(state.LPROC, state.LPSTATE, 'DOWN', state.CURSOR); if ~isempty(lpState), state = get(gcbf, 'userData'); state.LPSTATE = lpState; set(gcbf, 'userdata', state); % allow state update end; return; end; end; xy = get(state.SPECGRAM, 'currentPoint'); xy = round(xy(1,2)); if xy>0 & xy <state.SRATE/2, % click within spectrogram set(state.HEADFL, 'string', 'Hz'); set(state.HEADF, 'string', [' ',int2str(xy)]); line([xy xy], get(state.SPECTRA, 'ylim'), ... 'parent', state.SPECTRA, 'tag', 'CURSOR', ... 'color', 'c', 'eraseMode','xor'); end; set(gcbf, 'userdata', state, ... 'windowButtonMotionFcn', 'melba MOVECUR', ... 'windowButtonUpFcn', 'melba UP', ... 'pointer', 'crosshair'); % click in spectrum panel reports location case 'SPECTRA', if ~strcmp(mod, 'normal'), % modified click - plot spectrum in external window PlotSpectra(state); return; end; set(state.HEADFL, 'string', 'Hz'); set(state.TAILFL, 'string', 'dB'); set(state.HEADF, 'string', [' ',round(x)]); set(state.TAILF, 'string', sprintf(' %.1f',y)); state.MOVEMODE = 'SPECTRA'; set(gcbf, 'userData', state, ... 'windowButtonMotionFcn', 'melba MOVESPEC', ... 'windowButtonUpFcn', 'melba UP', ... 'pointer', 'crosshair'); end; %----------------------------------------------------------------------------- % FORMANTS: list formant peaks case 'FORMANTS', state = get(gcbf, 'userdata'); % compute F0 c = floor(state.CURSOR*state.SRATE/1000)+1; % msecs->samples ns = round(40/1000*state.SRATE); % frame (40 msecs->samples) F0 = zeros(1,3); v = F0; for fi = 1 : 3, % compute for 3 buffers centered on cursor h = c - round(ns/2)*(3-fi); if h < 1, h = 1; end; t = h + ns - 1; if t > length(state.SIGNAL), t = length(state.SIGNAL); h = t - ns + 1; end; [F0(fi),v,R] = ComputeF01(state.SIGNAL(h:t), state.SRATE, state.ISF); end; if length(find(F0)) < 2, % if any two buffers 0 F0 = NaN; % bogus elseif std(F0(find(F0))) > 15, % if std dev > 15 Hz F0 = F0(find(F0)); if min(F0) < .67*max(F0), % check for pitch doubling F0 = min(F0); else, F0 = NaN; end; elseif F0(2), % center result available F0 = F0(2); % cursor-centered result else, % otherwise F0 = mean(F0(find(F0))); % mean of non-zero results end; % compute zero crossings, RMS for WSIZE centered on cursor ht = floor((state.CURSOR+[-state.WSIZE state.WSIZE]*.5)*state.SRATE/1000)+1; % msecs -> samps if ht(1)<1, ht(1) = 1; end; if ht(2)>length(state.SIGNAL), ht(2) = length(state.SIGNAL); end; s = state.SIGNAL(ht(1):ht(2)); zc = sum(abs(diff(s>=0))); rms = sqrt(mean(s.^2)); % compute formants [p,f,formants,bws,amps] = ComputeSpectra(state); fprintf('\n\tCursor @ %g ms\nWindow %.1f ms: %d zero crossings, RMS = %.1f, F0 = %d Hz\nFormants (Bandwidths, Amplitudes):\n [', ... state.CURSOR, state.WSIZE, zc, rms, F0); nf = min([length(formants) state.NFMTS]); for i = 1 : nf, fprintf(' %d (%d, %.1f) ', formants(i), bws(i), amps(i)); end; fprintf(']\n'); % show canned values, if any if ~isempty(state.F0), k = round(c/length(state.SIGNAL) * length(state.F0)); if k < 1, k = 1; elseif k > length(state.F0), k = length(state.F0); end; fprintf('Stored F0 == %.0f Hz\n', state.F0(k)); end; if ~isempty(state.FORMANTS), lf = size(state.FORMANTS,1); k = round(c/length(state.SIGNAL) * lf); if k < 1, k = 1; elseif k > lf, k = lf; end; fprintf('Stored Formants:'); fprintf(' %.0f', state.FORMANTS(k,1:nf)); fprintf('\n'); end; % selection head = floor(state.HEAD*state.SRATE/1000)+1; % msecs -> samps tail = floor(state.TAIL*state.SRATE/1000)+1; fprintf('Selection is %.1f : %.1f (%.1f) ms\n', ... state.HEAD, state.TAIL, state.TAIL-state.HEAD); %----------------------------------------------------------------------------- % GETCFG: return current configuration case 'GETCFG', state = varargin{2}; mOut = struct('FIGPOS', get(gcbf, 'position'), ... % figure position 'FRAME', state.FRAME, ... % # FFT evaluation points 'ORDER', state.ORDER, ... % LPC order 'WSIZE', state.WSIZE, ... % LPC evaulation window (msecs) 'NFMTS', state.NFMTS, ... % # recorded formants 'NUDGE', state.NUDGE, ... % nudge length (msecs) 'AVGW', state.AVGW, ... % averaging window (msecs) 'OLAP', state.OLAP, ... % averaging overlap (msecs) 'ZOOMW', state.ZOOMW, ... % zoomed waveform window (msecs) 'BWCLIP', state.BWCLIP, ... % formant bandwidth clipping (Hz) 'PREEMP', state.PREEMP, ... % pre-emphasis (negated is adaptive) 'SOFF', state.SOFF, ... % SPL spectral offset 'SPECLIM', state.SPECLIM, ... % spectral display limit (Hz) 'SGWIN', get(state.SGWIN,'value'), ... % spectrogram window multiplier 'CONTRAST', get(state.CONTRAST, 'value'), ... % spectrogram contrast value 'AUTO', state.AUTO, ... % auto update 'FMTS', state.FMTS, ... % formants display 'LPROC', state.LPROC, ... % default label proc 'LPSTATE', state.LPSTATE, ... % label proc data 'TARGETS', state.TARGETS, ... % export targets 'PALATE', state.PALATE, ... % palate trace 'SPLINE', state.SPLINE, ... % tongue spline data indices 'ANAL', state.ANAL, ... % spectra analysis 'SPREAD', state.SPREAD, ... % signal scaling (negative flags auto) 'CLOSEFCN', state.CLOSEFCN, ... % close function 'SLIST', state.SLIST, ... % superimposed labels 'CHANNEL', state.CHANNEL, ... % stereo handling 'F0THR', state.F0THR, ... % F0 trim thresh 'ISF', state.ISF); % true if female subject %----------------------------------------------------------------------------- % INITialize case 'INIT', % parse PARAMS if any cfg = struct('FIGPOS', [], ... % default figure position 'FRAME', 256, ... % # FFT evaluation points 'ORDER', [], ... % LPC order 'WSIZE', 30, ... % analysis window (msecs) 'NFMTS', 3, ... % # recorded formants 'NUDGE', 5, ... % nudge length (msecs) 'AVGW', 6, ... % averaging window (msecs) 'OLAP', 1, ... % averaging overlap (msecs) 'ZOOMW', 30, ... % zoomed waveform window (msecs) 'BWCLIP', 300, ... % formant bandwidth clipping (Hz) 'PREEMP', .98, ... % pre-emphasis (negated is adaptive) 'SOFF', 20, ... % SPL spectral offset (dB) 'SPECLIM', [], ... % spectral display limit (Hz) 'SGWIN', 1, ... % spectrogram window multiplier 'CONTRAST', 4, ... % spectrogram contrast factor 'AUTO', [], ... % auto update 'FMTS', [], ... % formants display 'LPROC', [], ... % default label proc 'LPSTATE', [], ... % label proc data 'TARGETS', [], ... % export targets 'PALATE', [], ... % palate trace 'SPLINE', [], ... % tongue spline data indices 'ANAL', 3, ... % spectra analysis (LPC, DFT) 'SPREAD', 'AUTO', ... % signal scaling (auto) 'CLOSEFCN', [], ... % close function 'SLIST', [], ... % superimposed labels 'CHANNEL', 'B', ... % use both if available 'F0THR', .4, ... % F0 trim thresh 'ISF', 0); % true if female subject vList = []; vListSel = []; head = 0; tail = []; name = inputname(1); signals = []; closefcn = []; if nargin > 2, i = 2; if isnumeric(varargin{2}), i = i + 1; end; while i < nargin, if ~ischar(varargin{i}), error(sprintf('argument error (arg %d)', i)); end; switch upper(varargin{i}), case 'CHANNEL', cfg.CHANNEL = upper(varargin{i+1}(1)); case 'CLOSE', closefcn = varargin{i+1}; case 'CONFIG', cfg = varargin{i+1}; case 'LABELS', labels = varargin{i+1}; case 'LPROC', cfg.LPROC = varargin{i+1}; case 'PALATE', cfg.PALATE = varargin{i+1}; case 'VLIST', % variable list vList = varargin{i+1}; case 'VLSEL', % selection within it vListSel = varargin{i+1}; case 'SLIST', % labels for superimposition cfg.SLIST = varargin{i+1}; case 'HEAD', head = varargin{i+1}; case 'TAIL', tail = varargin{i+1}; case 'NAME', name = varargin{i+1}; case 'SPLINE', cfg.SPLINE = varargin{i+1}; case 'SPREAD', cfg.SPREAD = varargin{i+1}; case 'HEAD', head = varargin{i+1}; case 'TAIL', tail = varargin{i+1}; case 'SEX', cfg.ISF = (upper(varargin{i+1}(1)) == 'F'); case 'SIGNALS', signals = varargin{i+1}; otherwise, error(sprintf('unknown argument: %s', varargin{i})); end; i = i + 2; end; end; if isempty(cfg.ORDER), % Markel & Gray heuristic if cfg.ISF, cfg.ORDER = round(sRate/1000)+8; % female else, cfg.ORDER = round(sRate/1000)+4; % male end; end; if length(cfg.SLIST) ~= length(vList), cfg.SLIST = []; end; % fail silently on length mismatch % set up window if isempty(cfg.FIGPOS), figPos = round(get(0, 'ScreenSize')/2); set(0,'ShowHiddenHandles', 'on'); h = findobj('Tag', 'MELBA'); set(0,'ShowHiddenHandles', 'off'); figPos = [10+length(h)*10 , figPos(4)-200-65-length(h)*25 , figPos(3)+100 , figPos(4)+200]; if figPos(2) < 0, % slide over figPos(1) = figPos(1) + 50; figPos(2) = figPos(4) - 200 - 65; end; else, figPos = cfg.FIGPOS; end; fh = colordef('new', 'black'); % figure handle set(fh, ... 'tag', 'MELBA', ... 'numberTitle', 'off', ... 'position', figPos, ... 'menubar', 'none', ... 'resizeFcn', 'melba RESIZE', ... 'visible', 'off'); % framing panel fa = axes('position', [.05 .87 .94 .1], ... % framing axis 'box', 'on', 'hitTest', 'off'); [nSamps,n] = size(signal); if n>nSamps && nSamps==1, signal = signal'; nSamps = n; elseif n>1, % [nSamps x nSignals] aggregate switch cfg.CHANNEL, case 'B', signals = signal; signal = signal(:,1); for k = 1 : size(signals,2), vList{k} = sprintf('%s(:,%d)',name,k); end; case 'L', signal = signal(:,1); case 'R', signal = signal(:,2); end; end; if n>nSamps, signal = signal'; nSamps = n; end; % force [nSamps x 1] dur = 1000*(nSamps-1)/sRate; % msecs if isempty(tail), tail = min([dur 3000]); % limit displayed portion to 3 secs elseif tail > dur, tail = dur; end; maxV = max(abs(signal)); if isempty(cfg.SPREAD), if maxV <= 1, cfg.SPREAD = 1; elseif maxV <= 2048, cfg.SPREAD = 2048; elseif maxV <= 32768, cfg.SPREAD = 32768; else, cfg.SPREAD = maxV; end; elseif ischar(cfg.SPREAD), % 'AUTO' (max range) cfg.SPREAD = -maxV; elseif cfg.SPREAD < 0, % AUTO range (from saved cfg) cfg.SPREAD = -maxV; end; spread = abs(cfg.SPREAD) * 1.1; % pad for display plot(signal, 'w'); set(fa, 'xtick', [], 'ytick', [], ... 'xlim', [1 nSamps], 'ylim', [-1 1]*spread); hold on; ec = [.25 .25 .25]; sh = patch([1 1 nSamps nSamps], [-1 1 1 -1]*spread, ec, ... 'EdgeColor', ec, ... 'EraseMode', 'xor'); hold off; axes('position', [.05 .87 .94 .1],'color','none', ... 'xlim', [0 dur], 'ylim', [0 1], ... 'xtick', [], 'ytick', [], ... 'buttonDownFcn', 'melba(''DOWN'',''FRAME'')'); % selection panel sa = axes('position', [.05 .7 .94 .167], ... % selection axis 'box', 'on', 'hitTest', 'off'); plot(signal, 'w'); set(sa, 'xticklabel', [], 'ytick', [], ... 'xlim', [1 nSamps], 'ylim', [-1 1]*spread); % spectrogram panel ka = axes('position', [.05 .4 .94 .305], ... % spectrogram axis 'box', 'on', 'hitTest', 'off'); % F0 panel f0a = axes('position', [.05 .3 .94 .1], ... % F0 axes 'box','on', 'hitTest','off'); try, if isempty(f0), f0 = ComputeF0({signal(:,1), sRate},'F0THR',cfg.F0THR); end; idx = isnan(f0); f0(idx) = 0; k = linspace(1,length(f0),size(signal,1)); f0 = interp1(f0,k); idx = interp1(idx,k); f0(find(idx>0)) = NaN; plot(f0, 'c'); set(f0a, 'xticklabel',[],'xlim',[1 length(f0)],'ylim',[nanmin(f0)-10 nanmax(f0)+20]); catch, f0 = []; set(f0a, 'xtick',[],'ytick',[],'box','on'); fprintf('error attempting F0 estimation\n'); end; % RMS panel rmsa = axes('position', [.05 .2 .94 .1], ... % RMS axes 'box','on', 'hitTest','off'); wl = round(20*sRate/1000); rms = sqrt(filtfilt(rectwin(wl)/wl,1,signal(:,1).^2)); plot(rms, 'color', [1 1 .8]); set(rmsa, 'xticklabel', [], 'ytick', [], ... 'xlim', [1 nSamps], 'ylim', [0 max(rms)*1.1]); % cursor axis hold on; % cursor axis ca = axes('position', [.05 .2 .94 .665], ... 'xlim', [0 dur], 'ylim', [0 1], ... 'ytick', [], 'color', 'none', ... 'buttonDownFcn', 'melba(''DOWN'',''CURSOR'')'); cl = line([0 0], [0 1], 'color', 'white', ... % cursor line handle 'eraseMode', 'xor', ... 'buttonDownFcn', 'melba(''DOWN'',''CURSOR'')'); hold off; % spectra axis if isempty(cfg.SPECLIM), specLim = sRate/2; else, specLim = cfg.SPECLIM; end; % ra = axes('position', [.575 .01 .42 .15], ... % spectra axis ra = axes('position', [.625 .01 .365 .13], ... % spectra axis 'xaxislocation', 'top', 'yticklabel', [], ... 'xlim', [1 specLim], 'ylim', [5 100], ... 'box', 'on', 'buttonDownFcn', 'melba(''DOWN'',''SPECTRA'')'); rl = line(0, 0, 'color', 'w', 'eraseMode', 'xor', ... 'buttonDownFcn', 'melba(''DOWN'',''SPECTRA'')'); % zoom axis menu = uicontextmenu; uimenu(menu, 'label', 'Plot', 'callback', 'melba(''ZOOM'',''PLOT'')'); uimenu(menu, 'label', 'Shrink', 'separator', 'on', 'callback', 'melba(''ZOOM'',''SHRINK'')'); uimenu(menu, 'label', 'Expand', 'callback', 'melba(''ZOOM'',''EXPAND'')'); uimenu(menu, 'label', 'Zoom', 'separator', 'on', 'callback', 'melba(''ZOOM'',''ZOOM'')'); uimenu(menu, 'label', 'Unzoom', 'callback', 'melba(''ZOOM'',''UNZOOM'')'); uimenu(menu, 'label', '5 ms', 'separator', 'on', 'callback', 'melba(''ZOOM'',''5'')'); uimenu(menu, 'label', '10 ms', 'callback', 'melba(''ZOOM'',''10'')'); uimenu(menu, 'label', '20 ms', 'callback', 'melba(''ZOOM'',''20'')'); uimenu(menu, 'label', '30 ms', 'callback', 'melba(''ZOOM'',''30'')'); uimenu(menu, 'label', '50 ms', 'callback', 'melba(''ZOOM'',''50'')'); uimenu(menu, 'label', '75 ms', 'callback', 'melba(''ZOOM'',''75'')'); uimenu(menu, 'label', '100 ms', 'callback', 'melba(''ZOOM'',''100'')'); ns = floor(cfg.ZOOMW*sRate/1000); % za = axes('position', [.42 .01 .149 .15], ... za = axes('position', [.4 .01 .219 .13], ... 'xtick', [], 'ytick', [], ... 'xlim', [1 ns], 'ylim', [-maxV maxV], ... 'box', 'on', 'uicontextmenu', menu); line(round(ns/2)*[1 1],[-maxV maxV],'color','g','linestyle',':','tag','CURSOR', 'hitTest','off'); zoom = line(0,0,'color','w', 'eraseMode','xor', 'userData', [-maxV maxV], 'buttonDownFcn', 'melba(''ZOOM'',''PLOT'')'); % controls hfl = uicontrol(fh, ... % head field 'style', 'text', ... 'horizontalAlignment', 'right', ... 'string', 'Head', ... 'units', 'characters', ... 'backgroundColor', get(fh, 'color'), ... 'foregroundColor', [1 1 1], ... 'position', [1 3.0 6 1]); hf = uicontrol(fh, ... 'style', 'edit', ... 'horizontalAlignment', 'left', ... 'units', 'characters', ... 'string', ' 0', ... 'position', [8 2.8 12 1.8], ... 'callback', 'melba(''SELCHG'',1)'); tfl = uicontrol(fh, ... % tail field 'style', 'text', ... 'horizontalAlignment', 'right', ... 'string', 'Tail', ... 'units', 'characters', ... 'backgroundColor', get(fh, 'color'), ... 'foregroundColor', [1 1 1], ... 'position', [1 1.0 6 1]); tf = uicontrol(fh, ... 'style', 'edit', ... 'horizontalAlignment', 'left', ... 'units', 'characters', ... 'string', sprintf(' %.1f', dur), ... 'position', [8 0.8 12 1.8], ... 'callback', 'melba(''SELCHG'',2)'); uicontrol(fh, ... % cursor field 'style', 'text', ... 'horizontalAlignment', 'right', ... 'string', 'Cursor', ... 'units', 'characters', ... 'backgroundColor', get(fh, 'color'), ... 'foregroundColor', [1 1 1], ... 'position', [21 3.0 9 1]); cf = uicontrol(fh, ... 'style', 'edit', ... 'horizontalAlignment', 'left', ... 'units', 'characters', ... 'string', ' 0', ... 'position', [31 2.8 12 1.8], ... 'callback', 'melba CURCHG'); uicontrol(fh, ... % spectrogram window popup 'style', 'text', ... 'horizontalAlignment', 'right', ... 'string', 'Sgram', ... 'units', 'characters', ... 'backgroundColor', get(fh, 'color'), ... 'foregroundColor', [1 1 1], ... 'position', [21 1.0 9 1]); wsm = uicontrol(fh, ... 'style', 'popupmenu', ... 'horizontalAlignment', 'left', ... 'units', 'characters', ... 'position', [31 0.8 12 1.3], ... 'string', 'wideband|mid 1|mid 2|narrow', ... 'value', cfg.SGWIN, ... 'HorizontalAlignment', 'left', ... 'callback', 'melba SPECUD'); % set(cf, 'units', 'pixels'); % p = get(cf, 'position'); set(wsm, 'units', 'pixels'); p = get(wsm, 'position'); p = [p(1)+p(3)+10 , p(2) , 10 , 70-p(2)]; slider = uicontrol(fh, ... % spectrogram slider 'style', 'slider', ... 'min', 1, 'max', 20, ... 'value', cfg.CONTRAST, ... 'position', p, ... 'callback', 'melba CONTRAST'); % MELBA menu menu = uimenu(fh, 'label', 'MELBA', 'HandleVisibility', 'Callback'); uimenu(menu, 'label', 'About MELBA...', ... 'callback', 'melba ABOUT'); if isempty(cfg.AUTO), if nSamps > 50000, auto=0; cs='off'; else, auto=1; cs='on'; end; else, auto = cfg.AUTO; if auto, cs = 'on'; else, cs = 'off'; end; end; autoMenu = uimenu(menu, 'label', 'Auto Update', ... 'separator', 'on', ... 'checked', cs, ... 'callback', 'melba AUTO'); uimenu(menu, 'label', 'Update', ... 'accelerator', 'U', ... 'callback', 'melba UPDATE'); uimenu(menu, 'label', 'Set Head', ... 'separator', 'on', ... 'accelerator', 'D', ... 'callback', 'melba(''SELCHG'',-1)'); uimenu(menu, 'label', 'Set Tail', ... 'accelerator', 'T', ... 'callback', 'melba(''SELCHG'',-2)'); uimenu(menu, 'label', 'Shrink Selection', ... 'accelerator', '[', ... 'callback', 'melba(''SELCHG'',-3)'); uimenu(menu, 'label', 'Expand Selection', ... 'accelerator', ']', ... 'callback', 'melba(''SELCHG'',-4)'); h = uimenu(menu, 'label', 'Play'); ph(1) = uimenu(h, 'label', 'Selection', ... 'accelerator', 'P', ... 'callback', 'melba(''PLAY'',1)'); ph(2) = uimenu(h, 'label', 'Entire File', ... 'callback', 'melba(''PLAY'',2)'); ph(3) = uimenu(h, 'label', 'To Cursor', ... 'callback', 'melba(''PLAY'',3)'); ph(4) = uimenu(h, 'label', 'From Cursor', ... 'callback', 'melba(''PLAY'',4)'); ph(5) = uimenu(h, 'label', '150ms @ Cursor', ... 'callback', 'melba(''PLAY'',5)'); set(ph, 'userData', ph); uimenu(menu, 'label', 'Save Selection...', ... 'accelerator', 'S', ... 'callback', 'melba SAVESEL'); uimenu(menu, 'label', 'Export Selection...', ... 'accelerator', 'X', ... 'callback', 'melba SAVESELX'); uimenu(menu, 'label', 'Save Configuration...', ... 'separator', 'on', ... 'callback', 'melba SAVECFG'); uimenu(menu, 'label', 'Duplicate Window', ... 'callback', 'melba CLONE'); uimenu(menu, 'label', 'Close Window', ... 'accelerator', 'W', ... 'callback', 'close'); uimenu(menu, 'label', 'Close All', ... 'callback', 'melba ABORT'); % Spectra menu menu = uimenu(fh, 'label', 'Spectra', 'HandleVisibility', 'Callback'); uimenu(menu, 'label', 'Step Forward', ... 'accelerator', 'F', ... 'callback', 'melba(''NUDGE'',1)'); uimenu(menu, 'label', 'Step Backward', ... 'accelerator', 'B', ... 'callback', 'melba(''NUDGE'',-1)'); uimenu(menu, 'label', 'List Formants', ... 'accelerator', 'R', ... 'callback', 'melba FORMANTS'); uimenu(menu, 'label', 'List COG Measures', ... 'accelerator', 'G', ... 'callback', 'melba COG'); uimenu(menu, 'label', 'Config Spectral Anal', ... 'separator', 'on', ... 'accelerator', 'A', ... 'callback', 'melba CFGSPEC'); if isempty(cfg.FMTS), if nSamps > 50000, fmts=0; cs='off'; else, fmts=1; cs='on'; end; else, fmts = cfg.FMTS; if fmts, cs = 'on'; else, cs = 'off'; end; end; fmtsMenu = uimenu(menu, 'label', 'Track Formants', ... 'accelerator', 'J', ... 'checked', cs, ... 'callback', 'melba TRACKFMTS'); uimenu(menu, 'label', 'Waterfall Plot', ... 'callback', 'melba WATERFALL'); % Labels menu menu = uimenu(fh, 'label', 'Labels', 'HandleVisibility', 'Callback'); uimenu(menu, 'label', 'Make Label...', ... 'accelerator', 'M', ... 'callback', 'melba LMAKE'); uimenu(menu, 'label', 'Edit Labels...', ... 'accelerator', 'E', ... 'callback', 'melba LEDIT'); uimenu(menu, 'label', 'Save Labels...', ... 'accelerator', 'V', ... 'callback', 'melba LSAVE'); uimenu(menu, 'label', 'Export Labels...', ... 'callback', 'melba LEXPORT'); uimenu(menu, 'label', 'Import Labels...', ... 'callback', 'melba LIMPORT'); uimenu(menu, 'label', 'Clear All Labels', ... 'callback', 'melba LCLEAR'); h = uimenu(menu, 'label', 'Labelling Behavior', ... 'separator', 'on'); if isempty(cfg.LPROC), s = '<Default>'; else, s = cfg.LPROC; end; lblMenu = uimenu(h, 'label', s); uimenu(h, 'label', 'Clear', ... 'separator', 'on', ... 'callback', 'melba(''LSETPROC'',0);'); uimenu(h, 'Label', 'Select...', ... 'accelerator', 'L', ... 'callback', 'melba(''LSETPROC'',1);'); uimenu(h, 'Label', 'Configure...', ... 'accelerator', 'K', ... 'callback', 'melba(''LSETPROC'',2);'); % Variable List menu if ~isempty(vList), menu = uimenu(fh, 'label', 'Variables', 'HandleVisibility', 'Callback'); cs = {'off','on'}; if isempty(vListSel), vListSel = 1; end; if isempty(name), name = vList{vListSel}; end; uimenu(menu, 'label', 'Previous', ... 'accelerator', '1', ... 'callback', 'melba(''VARLIST'',-1);'); uimenu(menu, 'label', 'Next', ... 'accelerator', '2', ... 'callback', 'melba(''VARLIST'',1);'); uimenu(menu, 'label', 'Next; close current', ... 'accelerator', '3', ... 'callback', 'melba(''VARLIST'',1);close(gcbf);'); uimenu(menu, 'label', 'Next plus export', ... 'accelerator', '4', ... 'callback', 'melba(''LEXPORT'');melba(''VARLIST'',1);close(gcbf);'); uimenu(menu, 'label', 'Next; export, close current', ... 'accelerator', '5', ... 'callback', 'melba(''LEXPORT'');melba(''VARLIST'',1);close(gcbf);'); uimenu(menu, 'label', 'Next; save labs, close current', ... 'accelerator', '6', ... 'callback', 'melba(''LSAVE'',1);melba(''VARLIST'',1);close(gcbf);'); uimenu(menu, 'label', vList{1}, ... 'separator', 'on', ... 'checked', cs{(vListSel==1)+1}, ... 'userData', 1, ... 'callback', 'melba(''VARLIST'',0)'); for i = 2 : length(vList), uimenu(menu, 'label', vList{i}, ... 'checked', cs{(vListSel==i)+1}, ... 'userData', i, ... 'callback', 'melba(''VARLIST'',0)'); end; end; % Movement menu if data, menu = uimenu(fh, 'label', 'Movement', 'HandleVisibility', 'Callback'); uimenu(menu, 'label', 'Temporal Plot', ... 'accelerator', '3', ... 'callback', 'melba PLOTTRAJ'); uimenu(menu, 'label', 'Spatial Plot', ... 'accelerator', '4', ... 'callback', 'melba PLOTSPAT'); uimenu(menu, 'label', 'Display 2D Plot', ... 'accelerator', '5', ... 'callback', 'melba(''SHOWSPAT'',gcbo);'); uimenu(menu, 'label', 'Select Targets...', ... 'separator', 'on', ... 'accelerator', '6', ... 'callback', 'melba LSELECT'); uimenu(menu, 'label', 'Apply Labels...', ... 'accelerator', '7', ... 'callback', 'melba LAPPLY'); end; % init internal state state = struct('FH', fh, ... % figure handle 'FPANEL', fa, ... % framing panel handle 'BOUNDS', sh, ... % selector bounds handle 'SELECTION', sa, ... % selection panel handle 'SPECGRAM', ka, ... % spectrogram panel handle 'SPECTRA', ra, ... % spectra panel handle 'SPECTRAL', rl, ... % spectra line handle 'ZOOMA', za, ... % zoom panel handle 'ZOOM', zoom, ... % zoom line handle 'CURSORH', ca, ... % cursor axis handle 'CURSORL', cl, ... % cursor line handle 'F0A', f0a, ... % F0 panel handle 'F0', f0, ... % F0 'F0THR', cfg.F0THR, ... % F0 trim thresh 'FORMANTS', formants, ... % formants 'RMSA', rmsa, ... % RMS panel handle 'RMS', rms, ... % RMS 'HEADF', hf, ... % head field handle 'TAILF', tf, ... % tail field handle 'HEADFL', hfl, ... % head field label handle 'TAILFL', tfl, ... % tail field label handle 'CURSORF', cf, ... % cursor field handle 'HEAD', head, ... % head (msecs) 'TAIL', tail, ... % tail 'CURSOR', 0, ... % cursor 'DUR', dur, ... % signal duration (msecs) 'NUDGE', cfg.NUDGE, ... % nudge length (msecs) 'FRAME', cfg.FRAME, ... % # FFT evaluation points 'ORDER', cfg.ORDER, ... % LPC order 'WSIZE', cfg.WSIZE, ... % analysis window size (msecs) 'NFMTS', cfg.NFMTS, ... % # formants recorded 'AVGW', cfg.AVGW, ... % averaging window (msecs) 'OLAP', cfg.OLAP, ... % averaging overlap (msecs) 'ZOOMW', cfg.ZOOMW, ... % zoomed waveform window (msecs) 'BWCLIP', cfg.BWCLIP, ... % formant bandwidth clipping (Hz) 'PREEMP', cfg.PREEMP, ... % pre-emphasis (negated is adaptive) 'SOFF', cfg.SOFF, ... % spectral offset 'SPECLIM', specLim, ... % spectral display limit (Hz) 'ANAL', cfg.ANAL, ... % spectra analysis 'ISF', cfg.ISF, ... % true if female subject 'SGWIN', wsm, ... % sgram window size popup menu handle 'CONTRAST', slider, ... % spectrogram contrast slider 'SIGNAL', signal, ... % signal data 'SRATE', sRate, ... % sampling rate 'SPREAD', cfg.SPREAD, ... % signal scaling 'MOVEMODE', '', ... % movement mode 'MOVED', 0, ... % movement flag 'AUTOMENU', autoMenu, ... % auto update menu handle 'AUTO', auto, ... % auto update enabled 'FMTSMENU', fmtsMenu, ... % formant display menu handle 'FMTS', fmts, ... % formant display enabled 'LBLMENU', lblMenu, ... % labelling behavior menu handle 'LPROC', cfg.LPROC, ... % labelling procedure 'LPSTATE', cfg.LPSTATE, ... % labelling procedure data 'LABELS', labels, ... % label list 'NAME', name, ... % input data name 'PALATE', cfg.PALATE, ... % palate trace 'SPLINE', cfg.SPLINE, ... % tongue spline data indices 'DATA', [], ... % mavis trajectory data 'TARGETS', cfg.TARGETS, ... % export targets 'SPATWIN', [], ... % spatial display 'VLIST', [], ... % variable list 'VLISTSEL', vListSel, ... % current varlist selection 'SLIST', cfg.SLIST, ... % superimposed label list 'CLOSEFCN', closefcn, ... % function called at window close 'CHANNEL', cfg.CHANNEL, ... % stereo handling 'SIGNALS', signals); % signals aggregate if data, % retain movement data if available state.DATA = varargin{1}; if isempty(state.TARGETS), loaded = {}; exported = {}; for i = 1 : length(state.DATA), if size(state.DATA(i).SIGNAL,2) > 1, exported{end+1} = state.DATA(i).NAME; end; end; targets = struct('LOADED', [], ... 'EXPORTED', [], ... 'COMPONENTS', [1 1 0 0 0]); % X, Y; no tilt, vel, acc targets.LOADED = loaded; targets.EXPORTED = exported; state.TARGETS = targets; end; end; % this assignment must be done separately to avoid cloning state if ~isempty(vList), state.VLIST = vList; end; % update selection SetBounds(state, 2); SetCursor(state); % plot any labels if ~isempty(labels), axes(state.CURSORH); y = get(state.CURSORH, 'yLim') * .98; labels(1).HANDS = []; for i = 1 : length(labels), if isempty(cfg.LPROC), x = labels(i).OFFSET; labels(i).HANDS = line([x x], y, 'tag', 'LABEL', 'eraseMode', 'xor', ... 'buttonDownFcn', 'melba(''LMOVE'',''DOWN'');'); if ~isempty(labels(i).NAME), labels(i).HANDS(end+1) = text(x, y(2), [' ', labels(i).NAME], ... 'verticalAlignment', 'top', ... 'eraseMode', 'xor', ... 'fontname', 'geneva', ... 'fontsize', 9); end; else, labels(i) = feval(cfg.LPROC, cfg.LPSTATE, 'PLOT', labels(i), y); end; end; state.LABELS = labels; end; % 'superimposed' labels (SLIST): must be a matrix of msec offsets [length(vList) x nLabs] % created as immoveable anonymous labels if ~isempty(cfg.SLIST), axes(state.CURSORH); y = get(state.CURSORH, 'yLim') * .98; x = [1;1] * cfg.SLIST(vListSel,:); line(x,y,'tag','LABEL','color','g'); end; % turn on visibility if ~isempty(state.SIGNALS), name = vList{vListSel}; end; if isempty(closefcn), closefcn = 'closereq'; end; set(fh, 'userdata', state, ... 'name', ['MELBA: ' name], ... 'handleVisibility', 'callback', ... 'closeRequestFcn', closefcn, ... 'visible', 'on'); % config label proc if necessary if ~isempty(cfg.LPROC) & isempty(cfg.LPSTATE), lpState = feval(cfg.LPROC, [], 'CONFIG', state); % call user proc config handler if ~isempty(lpState), if isnumeric(lpState) & lpState == -1, % call label procedure immediately set(fh, 'handleVisibility', 'on'); feval(state.LPROC, [], 'DOWN', 0, 1, fh); if ~state.AUTO, SetBounds(get(fh, 'userData'),0); end; set(fh, 'handleVisibility', 'callback'); else, state.LPSTATE = lpState; % store configured label state set(fh, 'userdata', state); end; end; end; if strncmp(computer, 'MAC' ,3), % focus X [s,r] = unix('osascript -e ''tell application "MATLAB" to activate'''); end; %----------------------------------------------------------------------------- % LAPPLY: apply labels to trajectory data, output as spreadsheet file case 'LAPPLY', state = get(gcbf, 'userdata'); if isempty(state.LABELS), return; end; if isempty(state.DATA) | isempty(state.TARGETS.EXPORTED), error('no MAVIS-compatible trajectory data available'); end; trajList = state.TARGETS.EXPORTED; valList = {}; c = state.TARGETS.COMPONENTS; if c(1), valList{end+1} = 'X'; end; if c(2), valList{end+1} = 'Y'; end; if c(3), valList{end+1} = 'T'; end; if c(4), valList{end+1} = 'vX'; valList{end+1} = 'vY'; valList{end+1} = 'V'; end; if c(5), valList{end+1} = 'aX'; valList{end+1} = 'aY'; valList{end+1} = 'A'; end; eval([state.NAME '=state.DATA;ApplyLabels(' state.NAME ',state.LABELS,[],trajList,valList)']); %----------------------------------------------------------------------------- % LCLEAR: clear all labels case 'LCLEAR', if strcmp(questdlg('Clear all labels...', 'Verify...', 'Yes', 'No', 'Yes'), 'Yes'), state = get(gcbf, 'userdata'); state.LABELS = []; set(gcbf, 'userdata', state); delete(findobj(state.CURSORH, 'tag', 'LABEL')); end; %----------------------------------------------------------------------------- % LCLRSEL: clear selected labels case 'LCLRSEL', cfgState = get(gcbf, 'UserData'); state = get(cfgState.GUI, 'UserData'); killList = get(cfgState.LISTBOX, 'Value'); if isempty(killList), return; end; curLabs = size(get(cfgState.LISTBOX, 'String'),1); for i = killList, delete(state.LABELS(i).HANDS); end; state.LABELS(killList) = []; set(cfgState.GUI, 'UserData', state); set(cfgState.LISTBOX, 'Value', [], 'String', MakeList(state)); %----------------------------------------------------------------------------- % LCONFIG: configure labelling case 'LCONFIG', state = get(gcbf, 'userdata'); figPos = get(0, 'ScreenSize'); width = 280; height = 220; macpc = strcmp(computer, 'MAC2') | strcmp(computer, 'PCWIN'); if macpc, height = height - 30; end; figPos = [figPos(1)+(figPos(3)-width)/2, figPos(2)+(figPos(4)-height)/2, width, height]; cfg = dialog('Name', 'Default Labelling Behavior', ... 'Tag', 'MELBA', ... 'menubar', 'none', ... 'Position', figPos, ... 'KeyPressFcn', 'set(gcbf,''UserData'',1);uiresume', ... 'UserData', 0); blurb = ['A label is set at cursor offset on modified click release. ' ... 'Use shift-click to create label without annotation; ctl/alt-click ' ... 'to create an annotated label. Select number of output formants below.']; if macpc, h = 20; else, h = 0; end; uicontrol(cfg, ... 'Style', 'frame', ... 'Position', [10 height-90+h width-20 80-h]); uicontrol(cfg, ... 'Style', 'text', ... 'HorizontalAlignment', 'left', ... 'String', blurb, ... 'Position', [13 height-87+h width-26 74-h]); v = state.LPSTATE; if isstruct(v) | isempty(v) | v<1, v = 0; else, v = 1; end; propLab = uicontrol(cfg, ... % propagate labels checkbox 'Style', 'checkbox', ... 'String', 'Propagate labels', ... 'Value', v, ... 'Units', 'characters', ... 'Position', [8 7 30 1.5]); p = [4 4 26 1.5]; if macpc, p(3) = p(3) + 4; end; uicontrol(cfg, ... % # formants popup 'Style','text', ... 'HorizontalAlignment', 'right', ... 'String','Number of output formants:', ... 'Units', 'characters', ... 'Position', p); p = [32 4.2 12 1.5]; if macpc, p(1)=p(1)+4; p(2)=p(2)-.1; p(3)=p(3)+2; end; nf = uicontrol(cfg, ... 'Style', 'popupmenu', ... 'HorizontalAlignment', 'left', ... 'String', '0|1|2|3|4|5', ... 'value', state.NFMTS+1, ... 'Units', 'characters', ... 'Position', p); uicontrol(cfg, ... % buttons 'Position',[width/2-70 15 60 25], ... 'String','OK', ... 'Callback','set(gcbf,''UserData'',1);uiresume'); uicontrol(cfg, ... 'Position',[width/2+10 15 60 25], ... 'String','Cancel', ... 'Callback','uiresume'); % wait for input uiwait(cfg); if ~ishandle(cfg), return; end; if get(cfg, 'userData'), state.NFMTS = get(nf, 'value') - 1; state.LPSTATE = get(propLab, 'value'); set(gcbf, 'userData', state); end; delete(cfg); %----------------------------------------------------------------------------- % LEDIT: edit labels case 'LEDIT', state = get(gcbf, 'userdata'); figPos = get(0, 'ScreenSize'); width = 350; height = 250; figPos = [figPos(1)+(figPos(3)-width)/2, figPos(2)+(figPos(4)-height)/2, width, height]; cfg = dialog('Name', 'Edit Labels', ... 'Position', figPos, ... 'keyPressFcn', 'uiresume'); h = 19; w = 0; pc = strcmp(computer, 'PCWIN'); if pc, w = 9; h = 17; end; uicontrol(cfg, ... 'HorizontalAlignment', 'center', ... 'units', 'characters', ... 'Position', [2 h 7 1], ... 'String','Label', ... 'Style','text'); uicontrol(cfg, ... 'HorizontalAlignment', 'center', ... 'units', 'characters', ... 'Position', [18+w h 8 1], ... 'String','Offset', ... 'Style','text'); uicontrol(cfg, ... 'HorizontalAlignment', 'center', ... 'units', 'characters', ... 'Position', [27+w h 10 1], ... 'String','Comments', ... 'Style','text'); lb = uicontrol(cfg, ... % listbox 'Position',[10 60 width-20 height-90], ... 'FontName', 'Courier', ... 'String', MakeList(state), ... 'Style','listbox', ... 'Max', 2, ... 'Value', [], ... 'UserData', [], ... 'Callback','melba LEDSEL;'); uicontrol(cfg, ... % buttons 'Position',[width/2-120 15 60 25], ... 'String', 'Done', ... 'callback', 'uiresume'); uicontrol(cfg, ... 'Position',[width/2-30 15 60 25], ... 'String', 'Edit', ... 'callback', 'melba LEDSEL'); uicontrol(cfg, ... 'Position',[width/2+60 15 60 25], ... 'String', 'Clear', ... 'callback', 'melba LCLRSEL'); cfgState = struct('GUI', gcbf, 'LISTBOX', lb); set(cfg, 'userdata', cfgState); % wait for input uiwait(cfg); if ishandle(cfg), delete(cfg); end; %----------------------------------------------------------------------------- % LEDSEL: edit selected label case 'LEDSEL', cfgState = get(gcbf, 'userData'); state = get(cfgState.GUI, 'userData'); if gcbo == cfgState.LISTBOX & ~strcmp(get(gcbf, 'selectionType'), 'open'), return; % ignore single listbox clicks end; i = get(cfgState.LISTBOX, 'Value'); % index of label to edit if size(i,2) ~= 1, return; end; % edit single selection only label = EditLabel(state.LABELS(i), 'Edit'); if isempty(label), return; end; % cancel set(state.CURSORL, 'visible', 'off'); delete(state.LABELS(i).HANDS); axes(state.CURSORH); y = get(state.CURSORH, 'yLim'); if isempty(state.LPROC), % default plotting label = melba('LPLOT', label, y); set(label.HANDS(1), 'buttonDownFcn', 'melba(''LMOVE'',''DOWN'');'); else, % user proc label = feval(state.LPROC, state.LPSTATE, 'PLOT', label, y); end; set(state.CURSORL, 'visible', 'on'); state.LABELS(i) = label; set(cfgState.GUI, 'userData', state); set(cfgState.LISTBOX, 'String', MakeList(state)); %----------------------------------------------------------------------------- % LEXPORT: default label export (write formants values to spreadsheet file) case 'LEXPORT', state = get(gcbf, 'userdata'); if ~isempty(state.LPROC), feval(state.LPROC, state.LPSTATE, 'EXPORT', state.LABELS, state.NAME); return; end; if isempty(state.LABELS), return; end; [fileName, pathName] = uiputfile([state.NAME '.lab'], 'Save labels as'); if fileName == 0, return; end; % cancelled fileName = [pathName, fileName]; cd(pathName); % change working directory to save destination % open the file [fid, msg] = fopen(fileName, 'wt'); if fid == -1 error(sprintf('error attempting to open %s', fileName)); end; % write headers, data fprintf(fid, 'LABEL\tOFFSET\tNOTE'); nf = state.NFMTS; for i = 1 : nf, fprintf(fid, '\tF%d', i); end; for i = 1 : nf, fprintf(fid, '\tBW%d', i); end; fprintf(fid, '\n'); for i = 1 : length(state.LABELS), fprintf(fid, '%s\t%g\t', state.LABELS(i).NAME, state.LABELS(i).OFFSET); if ischar(state.LABELS(i).HOOK), fprintf(fid, '%s', state.LABELS(i).HOOK); end; [p,f,formants,bws] = ComputeSpectra(state, state.LABELS(i).OFFSET); val = repmat(NaN, 2, nf); val(:,1:length(formants)) = [formants;bws]; fprintf(fid, '\t%g', val(:,1:nf)'); fprintf(fid, '\n'); end; % clean up fclose(fid); fprintf('Labels written to %s\n', fileName); %----------------------------------------------------------------------------- % LIMPORT: import labels from previously exported spreadsheet file (.lab) % from landmark label file (.lm) % from LEX style label files (.label) % % note: recreated as default labels case 'LIMPORT', [f, p] = uigetfile('*.*', 'Select label file'); if f == 0, return; end; % cancelled fileName = [p f]; [p,f,ext] = fileparts(fileName); fid = fopen(fileName, 'rt'); if fid == -1, error(['error attempting to open ' fileName]); end; switch upper(ext), % headers case '.LM', % landmark labels while 1, lin = fgetl(fid); if lin(1) == '#', break; end; end; case '.LABEL', % LEX style labels ; case '.TEXTGRID', % PRAAT style ; otherwise, % assume melba tab-delimited format lin = fgetl(fid); [label,lin] = strtok(lin); [offset,lin] = strtok(lin); if ~all([strcmp(label,'LABEL') strcmp(offset,'OFFSET')]), fclose(fid); error([fileName ' is not in recognized label format']); end; end; state = get(gcbf, 'userdata'); if strcmpi('.TEXTGRID',ext), [ht,lbl] = ReadPraatInt(fileName); for k = 1 : length(lbl), if ~isempty(lbl), label = struct('NAME',lbl{k},'OFFSET',ht(k,1)*1000,'VALUE',[],'HOOK',[]); melba('MAKELBL',label,[]); end; end; else, while 1, lin = fgetl(fid); if ~ischar(lin), break; end; if isempty(lin), continue; end; switch upper(ext), case '.LM', % landmark labels [offset,lin] = strtok(lin); offset = str2num(offset) * 1000; % -> msecs [lin,name] = strtok(lin); case '.LABEL', % LEX style labels while 1, lin = fgetl(fid); if ~ischar(lin) | isempty(lin) | lin(1) == '<', break; end; end; offset = []; if ischar(lin) & ~isempty(lin), lin = fgetl(fid); offset = str2num(lin(6:end)); lin = fgetl(fid); name = lin(8:end); while lin(1) ~= '>', lin = fgetl(fid); end; end; otherwise, % melba tab-delimited format tabs = findstr(lin, ' '); if isempty(tabs), fclose(fid); error([fileName ' is not in recognized label format']); end; if tabs(1) == 1, name = ''; else, name = lin(1:tabs(1)-1); end; if length(tabs) < 2, tabs(2) = length(lin); end; offset = str2num(lin(tabs(1):tabs(2))); end; if ~ischar(lin), break; end; if ~isempty(offset), label = struct('NAME', name, ... 'OFFSET', offset, ... 'VALUE', [], ... 'HOOK', ''); melba('MAKELBL', label, []); end; end; end; %----------------------------------------------------------------------------- % LMAKE: menu-triggered label creation case 'LMAKE', state = get(gcbf, 'userdata'); if isempty(state.LPROC), % default labelling melba('MAKELBL'); else, % user proc labelling feval(state.LPROC, state.LPSTATE, 'DOWN', state.CURSOR, 1); end; %----------------------------------------------------------------------------- % LMOVE: move existing label % % varargin{3} may hold name of lproc caller for setting mouseUp handler case 'LMOVE', state = get(gcbf, 'userdata'); curPt = get(gca, 'currentPoint'); x = curPt(1,1); switch varargin{2}, case 'DOWN', for n = 1 : length(state.LABELS), % find clicked label if state.LABELS(n).HANDS(1) == gcbo, break; end; end; % double-click: edit this label if strcmp(get(gcbf, 'selectionType'), 'open'), fh = gcbf; label = EditLabel(state.LABELS(n), 'Edit', 1); if isempty(label), return; end; % cancel if ~isstruct(label), % delete delete(state.LABELS(n).HANDS); state.LABELS(n) = []; set(fh, 'userData', state); return; end; set(state.CURSORL, 'visible', 'off'); delete(state.LABELS(n).HANDS); axes(state.CURSORH); ylim = get(state.CURSORH, 'yLim'); if isempty(state.LPROC), % default plotting label = melba('LPLOT', label, ylim); set(label.HANDS(1), 'buttonDownFcn', 'melba(''LMOVE'',''DOWN'');'); else, % user proc label = feval(state.LPROC, state.LPSTATE, 'PLOT', label, ylim); end; set(state.CURSORL, 'visible', 'on'); state.LABELS(n) = label; set(fh, 'userData', state); return; end; % else set up for move set(state.CURSORF, 'string', sprintf(' %.1f', x)); state.LPSTATE = struct('H',gcbo, 'IDX',n, 'SAVESTATE', state.LPSTATE); if length(varargin) > 2, upS = sprintf('%s([],''LMOVE'');',varargin{3}); % LPROC will handle mouseUp else, upS = 'melba(''LMOVE'',''UP'');'; end; set(gcbf, 'windowButtonMotionFcn', 'melba(''LMOVE'',''MOVE'');', ... 'windowButtonUpFcn',upS, ... 'pointer', 'crosshair', 'userData',state); case 'MOVE', xlim = get(gca, 'xlim'); ylim = get(gca, 'ylim'); if x < xlim(1), x = xlim(1); end; if x > xlim(2), x = xlim(2); end; set(state.LPSTATE.H, 'xdata', [x x]); % move label if length(state.LABELS(state.LPSTATE.IDX).HANDS)>1, % move name h = state.LABELS(state.LPSTATE.IDX).HANDS(2); pos = get(h, 'position'); pos(1) = x; set(h, 'position', pos); end; set(state.CURSORF, 'string', sprintf(' %.1f', x)); case 'UP', state.LABELS(state.LPSTATE.IDX).OFFSET = x; state.LPSTATE = state.LPSTATE.SAVESTATE; set(gcbf, 'windowButtonMotionFcn', '', ... 'windowButtonUpFcn', '', ... 'pointer', 'arrow', 'userData', state); set(state.CURSORF, 'string', sprintf(' %.1f', state.CURSOR)); end; %----------------------------------------------------------------------------- % LPLOT: default label plotting (passed label, ylim) % % returns updated label case 'LPLOT', if nargin > 3, fh = varargin{4}; else, fh = gcbf; end; state = get(fh, 'userdata'); label = varargin{2}; ylim = varargin{3} * .98; x = [label.OFFSET label.OFFSET]; label.HANDS = line(x, ylim, 'tag', 'LABEL', 'eraseMode', 'xor'); if ~isempty(label.NAME), label.HANDS(end+1) = text(x(2), ylim(2), [' ', label.NAME], ... 'tag', 'LABEL', ... 'eraseMode', 'xor', ... 'interpreter','none', ... 'verticalAlignment', 'top', ... 'fontname', 'geneva', ... 'fontsize', 9); end; mOut = label; %----------------------------------------------------------------------------- % LSAVE: save labels to workspace case 'LSAVE', state = get(gcbf, 'userdata'); name = [state.NAME '_lbl']; if nargin < 2, name = GetName(name, 'labels'); if isempty(name) return; end; % cancel end; if isfield(state.LABELS, 'HANDS'), labels = rmfield(state.LABELS, 'HANDS'); end; for n = 1 : length(labels), if state.NFMTS > 0, [p,f,formants,bws] = ComputeSpectra(state, state.LABELS(n).OFFSET); val = repmat(NaN, 2, state.NFMTS); nf = min(state.NFMTS,length(formants)); val(:,1:nf) = [formants(1:nf);bws(1:nf)]; labels(n).VALUE = val; else, labels(n).VALUE = []; end; end; assignin('base', name, labels); evalin('base', name) % for listing %----------------------------------------------------------------------------- % LSELECT: select export targets case 'LSELECT', state = get(gcbf, 'userdata'); targets = SelOutput(state.TARGETS); if ~isempty(targets), state.TARGETS = targets; set(gcbf, 'userdata', state); end; %----------------------------------------------------------------------------- % LSETPROC: set labelling behavior case 'LSETPROC', state = get(gcbf, 'userdata'); switch varargin{2}, case 0, % clear user proc file = '<Default>'; state.LPROC = []; state.LPSTATE = []; case 1, % set user proc [file, p] = uigetfile([fileparts(which('melba')),filesep,'lp*.m'], 'Select labelling procedure', 'location', [100 100]); if file == 0, return; end; % cancelled i = findstr(file, '.'); if ~isempty(i), file = file(1:i(1)-1); end; newState = feval(file, [], 'CONFIG'); % call user proc config handler (1st time) if isempty(newState), return; end; % cancelled state.LPROC = file; state.LPSTATE = newState; case 2, % reconfigure file = state.LPROC; if isempty(file), melba('LCONFIG'); return; end; newState = feval(file, state.LPSTATE, 'CONFIG'); % call user proc config handler if isempty(newState), return; end; % cancelled state.LPSTATE = newState; end; set(gcbf, 'userdata', state); set(state.LBLMENU, 'Label', file); %----------------------------------------------------------------------------- % MAKELBL: make new label % % returns mOut = index of created label case 'MAKELBL', mOut = []; if nargin > 3, fh = varargin{4}; else, fh = gcbf; end; state = get(fh, 'userdata'); if nargin < 2, % create default label newLabel = struct('NAME', '', ... 'OFFSET', state.CURSOR, ... 'VALUE', [], ... 'HOOK', '', 'HANDS', []); else, newLabel = varargin{2}; % user-supplied label if isempty(newLabel.OFFSET), newLabel.OFFSET = state.CURSOR; end; if isempty(newLabel.VALUE) & nargin>2, newLabel.VALUE = varargin{3}; end; newLabel.HANDS = []; end; if ~strcmp(state.MOVEMODE,'LBL_SILENT') & nargin < 3, % annotate newLabel = EditLabel(newLabel, 'Create'); if isempty(newLabel), % cancel state.MOVEMODE = ''; set(gcbf, 'userdata', state); return; end; end; axes(state.CURSORH); y = get(state.CURSORH, 'yLim'); set(state.CURSORL, 'visible', 'off'); if isempty(state.LPROC), % default plotting newLabel = melba('LPLOT', newLabel, y); set(newLabel.HANDS(1), 'buttonDownFcn', 'melba(''LMOVE'',''DOWN'');'); else, % user proc newLabel = feval(state.LPROC, state.LPSTATE, 'PLOT', newLabel, y, fh); end; set(state.CURSORL, 'visible', 'on'); state.MOVEMODE = ''; mOut = 1; if isempty(state.LABELS), state.LABELS = newLabel; else, % insert new label, ordered by offset offsets = {state.LABELS.OFFSET}; if newLabel.OFFSET < offsets{1}, state.LABELS = [newLabel state.LABELS]; elseif newLabel.OFFSET > offsets{end}, state.LABELS = [state.LABELS newLabel]; mOut = length(state.LABELS); else, for i = 2 : length(state.LABELS), if newLabel.OFFSET < offsets{i}, break; end; end; state.LABELS = [state.LABELS(1:i-1) newLabel state.LABELS(i:end)]; mOut = i - 1; end; end; set(fh, 'userdata', state); %----------------------------------------------------------------------------- % MOVECUR: move cursor case 'MOVECUR', state = get(gcbf, 'userdata'); xy = get(gca, 'currentPoint'); x = xy(1,1); if x < state.HEAD, x = state.HEAD; end; if x > state.TAIL, x = state.TAIL; end; c = get(state.CURSORL, 'xData'); if x ~= c, state.CURSOR = x; set(gcbf, 'userData', state); SetCursor(state); end; lh = findobj(state.SPECTRA, 'tag', 'CURSOR'); if ~isempty(lh), xy = get(state.SPECGRAM, 'currentPoint'); x = xy(1,1); y = xy(1,2); p = [get(state.SPECGRAM,'xlim') , get(state.SPECGRAM, 'ylim')]; if all([x>=p(1) , x<=p(2) , y>=p(3) , y<=p(4)]), set(state.HEADF, 'string', [' ',int2str(y)]); set(lh, 'xdata', [y y]); else, set(state.HEADF, 'string', ''); set(lh, 'xdata', [-1 -1]); end; end; %----------------------------------------------------------------------------- % MOVESEL: move selection case 'MOVESEL', state = get(gcbf, 'userdata'); xy = get(gca, 'currentPoint'); x = xy(1,1); y = xy(1,2); sampDur = 1 / state.SRATE; switch state.MOVEMODE, case 'HEAD', if x < 0, x = 0; end; if x >= state.TAIL-sampDur, x = state.TAIL-sampDur; end; state.HEAD = x; case 'TAIL', if x > state.DUR, x = state.DUR; end; if x <= state.HEAD+sampDur, x = state.HEAD+sampDur; end; state.TAIL = x; otherwise, dx = x + state.MOVEMODE; if state.HEAD+dx < 0, dx = 0-state.HEAD; end; if state.TAIL+dx > state.DUR, dx = state.DUR-state.TAIL; end; state.HEAD = state.HEAD + dx; state.TAIL = state.TAIL + dx; state.MOVEMODE = -x; end; state.MOVED = 1; % some movement occurred set(gcbf, 'userdata', state); SetBounds(state, 1); % update selection bounds %----------------------------------------------------------------------------- % MOVESPEC: move within spectrum panel case 'MOVESPEC', state = get(gcbf, 'userdata'); xy = get(gca, 'currentPoint'); x = xy(1,1); y = xy(1,2); p = [get(gca,'xlim') , get(gca, 'ylim')]; if all([x>=p(1) , x<=p(2) , y>=p(3) , y<=p(4)]), set(state.HEADF, 'string', [' ',round(x)]); set(state.TAILF, 'string', sprintf(' %.1f',y)); else, set(state.HEADF, 'string', ''); set(state.TAILF, 'string', ''); end; %----------------------------------------------------------------------------- % NUDGE: step cursor case 'NUDGE', state = get(gcbf, 'userdata'); c = state.CURSOR + state.NUDGE * varargin{2}; % msecs if c < state.HEAD, c = state.HEAD; elseif c > state.TAIL, c = state.TAIL; end; state.CURSOR = c; set(state.CURSORL, 'xData', [c c]); set(gcbf, 'userdata', state); SetCursor(state); %----------------------------------------------------------------------------- % PLAY: play current selection case 'PLAY', state = get(gcbf, 'userdata'); hs = floor(state.HEAD*state.SRATE/1000)+1; % msecs -> samps ts = floor(state.TAIL*state.SRATE/1000)+1; cs = floor(state.CURSOR*state.SRATE/1000)+1; ns = length(state.SIGNAL); mode = varargin{2}; mh = get(gcbo, 'userData'); % play submenu handles for mi = 1 : length(mh), if mi == mode, as = 'P'; else, as = ''; end; set(mh(mi), 'accelerator', as); end; switch mode, case 1, % selection ; case 2, % file hs = 1; ts = ns; case 3, % to cursor if cs<hs, cs = hs; end; ts = cs; case 4, % from cursor if cs>ts, cs = ts; end; hs = cs; case 5, % 150ms centered on cursor t = floor(([-75 75]+state.CURSOR)*state.SRATE/1000)+1; if t(1) < 1, hs = 1; else, hs = t(1); end; if t(2) > ns, ts = ns; else, ts = t(2); end; end; soundsc(state.SIGNAL(hs:ts), state.SRATE, [-1 1]*abs(state.SPREAD)); %----------------------------------------------------------------------------- % PLOTSPAT: spatial plot of MAVIS trajectory data case 'PLOTSPAT', state = get(gcbf, 'userdata'); map = hsv; fh = colordef('new', 'white'); % figure handle set(fh, 'name', sprintf('%s @ %.1f',state.NAME,state.CURSOR), ... 'colormap', map, 'visible', 'on'); data = state.DATA; mins = [inf inf]; maxs = [-inf -inf]; firstIter = 1; if ~isempty(state.PALATE), line(state.PALATE(:,1), state.PALATE(:,2), 'color', [.5 .5 .5]); end; nc = length(state.LABELS) + 1; for t = 1 : length(data), if size(data(t).SIGNAL,2)>1, mins = min([mins ; min(data(t).SIGNAL(:,1:2))]); maxs = max([maxs ; max(data(t).SIGNAL(:,1:2))]); head = floor(state.HEAD*data(t).SRATE/1000)+1; tail = floor(state.TAIL*data(t).SRATE/1000)+1; ci = floor(state.CURSOR*data(t).SRATE/1000)+1; line(data(t).SIGNAL(head:tail,1), data(t).SIGNAL(head:tail,2), 'color', 'k'); hh = line(data(t).SIGNAL(ci,1), data(t).SIGNAL(ci,2), ... 'color', 'b', 'marker', '*'); if firstIter, h = hh; n = {sprintf('Cursor (%.1f)', state.CURSOR)}; end; for i = 1 : length(state.LABELS), li = floor(state.LABELS(i).OFFSET*data(t).SRATE/1000)+1; color = map(floor(i*size(map,1)/(nc-1)),:); hh = line(data(t).SIGNAL(li,1), data(t).SIGNAL(li,2), ... 'color', color, 'marker', '+'); if firstIter, h(end+1) = hh; n{end+1} = sprintf('%s (%.1f)', state.LABELS(i).NAME, state.LABELS(i).OFFSET); end; end; firstIter = 0; end; end; spread = max(maxs - mins); spread = spread + spread * .05; mins = mins + (maxs - mins - spread)/2; maxs = mins + spread; set(gca,'xlim', [mins(1) maxs(1)], 'ylim', [mins(2) maxs(2)], 'box', 'on'); axis equal; legend(h, n{:}); xlabel('mm'); ylabel('mm'); title(sprintf('Head: %.1f Tail: %.1f Cursor: %.1f (msecs)', ... state.HEAD, state.TAIL, state.CURSOR)); %----------------------------------------------------------------------------- % PLOTTRAJ: temporal plot of MAVIS trajectory data case 'PLOTTRAJ', state = get(gcbf, 'userdata'); data = state.DATA; n = []; for t = 1 : length(data), if size(data(t).SIGNAL,2)>1, n(end+1) = t; end; end; data = ComputeDerivedSigs(data(n)); sRate = data(1).SRATE; head = floor(state.HEAD*sRate/1000)+1; tail = floor(state.TAIL*sRate/1000)+1; nTraj = length(data); fh = colordef('new', 'white'); % figure handle set(fh, 'name', state.NAME, 'visible', 'on'); n = 1; for t = 1 : nTraj, ns = tail - head + 1; x = [1:ns]; mins = min(data(t).SIGNAL(head:tail,1:2)); maxs = max(data(t).SIGNAL(head:tail,1:2)); spread = max(max((maxs-mins)/2)); spread = spread + spread*.2; s = data(t).SIGNAL(head:tail,1:2) - repmat(mins+(maxs-mins)/2,ns,1); s = [s , data(t).SIGNAL(head:tail,3)]; h = subplot(nTraj, 3, n); n = n + 1; plot(x,s(:,1),'b',x,s(:,2),'g',x,s(:,3),'c'); set(h, 'xlim', [1 ns], 'ylim', [-1 1]*spread, ... 'userData', {data(t).NAME 'X' 'Y' 'Tilt' 'mm'}, ... 'buttonDownFcn', 'melba PLOTTRAJZ'); ylabel(data(t).NAME); if t == nTraj, xlabel('X, Y, Tilt'); end; h = subplot(nTraj, 3, n); n = n + 1; s = data(t).SIGNAL(head:tail,4:6); plot(x,s(:,1),'b',x,s(:,2),'g',x,s(:,3),'c'); set(h, 'xlim', [1 ns], ... 'userData', {data(t).NAME 'vX' 'vY' 'V' 'cm / sec'}, ... 'buttonDownFcn', 'melba PLOTTRAJZ'); if t == nTraj, xlabel('vX, vY, V'); end; h = subplot(nTraj, 3, n); n = n + 1; s = data(t).SIGNAL(head:tail,7:9); plot(x,s(:,1),'b',x,s(:,2),'g',x,s(:,3),'c'); set(h, 'xlim', [1 ns], ... 'userData', {data(t).NAME 'aX' 'aY' 'A' 'cm / sec^2'}, ... 'buttonDownFcn', 'melba PLOTTRAJZ'); if t == nTraj, xlabel('aX, aY, A'); end; for li = 1 : length(state.LABELS), so = floor(state.LABELS(li).OFFSET*sRate/1000)+1; if so >= head & so <=tail, so = so - head + 1; % label sample offset within selection for i = 1 : 3, subplot(nTraj, 3, n-i); line([so so], get(gca,'ylim'), 'color', [.5 .5 .5]); end; end; end; end; %----------------------------------------------------------------------------- % PLOTTRAJZ: zoom trajectory plot case 'PLOTTRAJZ', h = flipud(findobj(gcbo, 'type', 'line')); fh = colordef('new', 'white'); % figure handle copyobj(h, axes); x = get(gcbo, 'xlim'); set(gca, 'xlim', x, 'ylim', get(gcbo, 'ylim'), 'box', 'on'); line(x, [0 0], 'color', 'k', 'lineStyle', ':'); s = get(gcbo, 'userData'); title(s{1}); legend(s{2:end-1}); set(gcf, 'name', sprintf('%s (%s, %s %s)', s{1:4}), 'visible', 'on'); ylabel(s{5}); %----------------------------------------------------------------------------- % RESIZE: size change case 'RESIZE', state = get(gcbf, 'userdata'); if isempty(state), return; end; % adjust size of zoom, spectra panels set(state.CONTRAST, 'units', 'normal'); cp = get(state.CONTRAST, 'position'); set(state.CONTRAST, 'units', 'pixels'); zp = get(state.ZOOMA, 'position'); sp = get(state.SPECTRA, 'position'); dx = zp(1) - (cp(1) + cp(3) + .01); zp(1) = zp(1) - dx; set(state.ZOOMA, 'position', zp); sp(1) = sp(1) - dx; sp(3) = sp(3) + dx; set(state.SPECTRA, 'position', sp); %----------------------------------------------------------------------------- % SAVECFG: save current configuration to workspace case 'SAVECFG', state = get(gcbf, 'userdata'); name = GetName([state.NAME '_cfg'], 'configuration'); if isempty(name) return; end; % cancel cfg = melba('GETCFG', state); assignin('base', name, cfg); evalin('base', name) % for listing %----------------------------------------------------------------------------- % SAVESEL: save current selection to workspace case 'SAVESEL', state = get(gcbf, 'userdata'); name = GetName([state.NAME '_sel'], 'selection'); if isempty(name) return; end; % cancel % adjust label offsets to start of selection head = floor(state.HEAD*state.SRATE/1000)+1; tail = floor(state.TAIL*state.SRATE/1000)+1; labels = state.LABELS; if ~isempty(labels), for i = length(labels) : -1 : 1, if labels(i).OFFSET < state.HEAD | labels(i).OFFSET > state.TAIL, labels(i) = []; else, labels(i).OFFSET = labels(i).OFFSET - state.HEAD; labels(i).HANDS = []; end; end; end; if isempty(state.DATA), sel = struct('NAME','AUDIO', ... 'SIGNAL', state.SIGNAL(head:tail), ... 'SRATE', state.SRATE, ... 'LABELS', labels); if ~isempty(state.F0), sel(end+1) = struct('NAME','F0','SIGNAL',state.F0,'SRATE',[],'LABELS',[]); end; if ~isempty(state.FORMANTS), sel(end+1) = struct('NAME','FORMANTS','SIGNAL',state.FORMANTS,'SRATE',[],'LABELS',[]); end; else, % export mavis-data selection sel = state.DATA; needAudio = 1; for t = 1 : length(sel), if size(sel(t).SIGNAL,2) == 1 & needAudio, sel(t).LABELS = labels; needAudio = 0; end; head = floor(state.HEAD*sel(t).SRATE/1000)+1; tail = floor(state.TAIL*sel(t).SRATE/1000)+1; sel(t).SIGNAL = sel(t).SIGNAL(head:tail,:); end; end; assignin('base', name, sel); evalin('base', name) % for listing %----------------------------------------------------------------------------- % SAVESELX: export current selection as MS WAV file case 'SAVESELX', state = get(gcbf, 'userdata'); name = GetName([state.NAME '.wav'], 'selection', 'Export @ to MS WAV file'); if isempty(name) return; end; % cancel head = floor(state.HEAD*state.SRATE/1000)+1; tail = floor(state.TAIL*state.SRATE/1000)+1; s = state.SIGNAL(head:tail); WriteSound(s, state.SRATE, name, 'wav', 2048); % 12 bit data fprintf('wrote %s\n', name); %----------------------------------------------------------------------------- % SELCHG: selection change case 'SELCHG', if nargin > 3, fh = varargin{4}; else, fh = gcbf; end; state = get(fh, 'userdata'); nSamps = length(state.SIGNAL); switch varargin{2}, case -1, % set head from cursor head = state.CURSOR; if head >= state.TAIL, head = state.TAIL-1; end; state.HEAD = head; case -2, % set tail from cursor tail = state.CURSOR; if tail <= state.HEAD, tail = state.HEAD+1; end; state.TAIL = tail; case -3, % shrink selection nudge = .1 * (state.TAIL - state.HEAD); head = state.HEAD + nudge; if head >= state.TAIL, head = state.TAIL-1; tail = state.TAIL; else, tail = state.TAIL - nudge; if tail <= head, tail = head+1; end; end; state.HEAD = head; state.TAIL = tail; case -4, % expand selection nudge = .1 * (state.TAIL - state.HEAD); head = state.HEAD - nudge; if head < 0, head = 0; end; tail = state.TAIL + nudge; if tail > state.DUR, tail = state.DUR; end; if state.HEAD==head & state.TAIL==tail, return; end; state.HEAD = head; state.TAIL = tail; case 0, % reset full selection state.HEAD = 0; state.TAIL = state.DUR; case 1, % head changed head = str2num(get(state.HEADF, 'string')); if head<0, head = 0; end; if head >= state.TAIL, head = state.TAIL-1; end; state.HEAD = head; case 2, % tail changed tail = str2num(get(state.TAILF, 'string')); if tail <= state.HEAD, tail = state.HEAD+1; end; if tail > state.DUR, tail = state.DUR; end; state.TAIL = tail; case 9, % head, tail specified as varargin{3} ht = varargin{3}; state.HEAD = ht(1); state.TAIL = ht(2); end; set(fh, 'userData', state); SetBounds(state); %----------------------------------------------------------------------------- % SHOWSPAT: toggle 2D plot case 'SHOWSPAT', % vis = umtoggle(varargin{2}); % toggle menu vis = ~strcmp(get(varargin{2},'checked'),'on'); if vis, set(varargin{2},'checked','on'); else, set(varargin{2},'checked','off'); end; state = get(gcbf, 'userdata'); if isempty(state.SPATWIN) | ~ishandle(state.SPATWIN), % initialize if ~ishandle(state.SPATWIN), set(varargin{2}, 'checked', 'on'); vis = 1; end; state.SPATWIN = colordef('new', 'white'); pos = get(gcbf, 'position'); pos(1) = pos(1) + pos(3) + 5; if ispc, pos(1) = pos(1) + 5; end; pos(3) = 300; pos(2) = pos(2) + pos(4) - 272; if ispc, pos(2) = pos(2) - 8; end; pos(4) = 300; set(state.SPATWIN,'name',sprintf('SPATIAL: %s',state.NAME), 'tag', 'MELBA', ... 'menuBar','none', 'numberTitle','off', 'visible','off', 'position',pos, ... 'windowButtonDownFcn','melba SPATDOWN'); data = state.DATA; mins = [inf inf]; maxs = [-inf -inf]; if ~isempty(state.PALATE), mins = min(state.PALATE(:,1:2)); maxs = max(state.PALATE(:,1:2)); line(state.PALATE(:,1), state.PALATE(:,2), 'color', 'b'); end; spatChan = []; for t = 1 : length(data), if size(data(t).SIGNAL,2)>1, mins = min([mins ; min(data(t).SIGNAL(:,1:2))]); maxs = max([maxs ; max(data(t).SIGNAL(:,1:2))]); spatChan(end+1) = t; end; end; spread = max(maxs - mins); spread = spread + spread * .2; mins = mins + (maxs - mins - spread)/2; maxs = mins + spread; set(gca,'xlim', [mins(1) maxs(1)], 'ylim', [mins(2) maxs(2)], 'box', 'on'); axis equal; xlabel('mm'); ylabel('mm'); nTraj = length(spatChan); h = zeros(1,nTraj); col = flipud(hsv(nTraj)); x = mins(1) - 10; y = mins(2) - 10; for t = 1 : nTraj, % create points (out of axis) h(t) = line(x, y, ... 'marker', '.', ... 'markerSize', 20, ... 'eraseMode', 'xor', ... 'hitTest', 'off', ... 'color', col(t,:)); end; if ~isempty(state.SPLINE), h(nTraj+1) = line(x, y, 'eraseMode','xor','hitTest','off','color',[.5 .5 .5]); end; set(state.SPATWIN,'userData',struct('FH',gcbf,'H',h,'SPATCHAN',spatChan)); set(gcbf, 'userData', state); set(varargin{2}, 'checked', 'on'); end; if vis, set(state.SPATWIN,'visible','on'); SetCursor(state); else, set(state.SPATWIN,'visible','off'); end; %----------------------------------------------------------------------------- % SPATDOWN: handle spatial display mouseDown case 'SPATDOWN', spatState = get(gcbf, 'userdata'); if ~ishandle(spatState.FH), return; end; figure(spatState.FH); % pop mother window figure(gcbf); switch get(gcbf, 'selectionType'), case 'open', % double-click clears trails delete(findobj(gca, 'tag','TRAIL')); case 'normal', % unmodified click does nothing ; otherwise, % modified click plots trails state = get(spatState.FH, 'userData'); spatChan = spatState.SPATCHAN; cols = flipud(hsv(length(spatChan))) * .7; for n = 1 : length(spatChan), sr = state.DATA(spatChan(n)).SRATE; h = floor(state.HEAD*sr/1000) + 1; t = floor(state.TAIL*sr/1000) + 1; xy = state.DATA(spatChan(n)).SIGNAL(h:t,1:2); line(xy(:,1),xy(:,2),'color',cols(n,:),'tag','TRAIL'); end; end; %----------------------------------------------------------------------------- % SPECUD: update & replot specgram case 'SPECUD', state = get(gcbf, 'userdata'); SetBounds(state, 0); %----------------------------------------------------------------------------- % TRACKFMTS: track formants case 'TRACKFMTS', state = get(gcbf, 'userdata'); s = get(gcbo,'checked'); state.FMTS = strcmp(s,'off'); set(gcbf, 'userdata', state); if state.FMTS, s = 'on'; else, s = 'off'; end; set(state.FMTSMENU, 'checked', s); SetBounds(state); % ht = floor([state.HEAD,state.TAIL]*state.SRATE/1000)+1; % TrackFmts(state.SIGNAL(ht(1):ht(2)), state.SRATE); %----------------------------------------------------------------------------- % UP: handle mouseUp (complete move-in-progress) case 'UP', set(gcbf, 'windowButtonMotionFcn', '', ... 'windowButtonUpFcn', '', ... 'pointer', 'arrow'); state = get(gcbf, 'userdata'); xy = get(gca, 'currentPoint'); x = xy(1,1); y = xy(1,2); switch state.MOVEMODE, case {'CURSOR','LBL_SILENT','LBL_NOISY'}, if state.CURSOR ~= x, if x < 0, x = 0; elseif x > state.TAIL, x = state.TAIL; end; state.CURSOR = x; set(gcbf, 'userData', state); SetCursor(state); end; if isempty(state.LPROC) & strncmp(state.MOVEMODE, 'LBL', 3), melba('MAKELBL'); % create new label state = get(gcbf, 'userData'); end; lh = findobj(state.SPECTRA, 'tag', 'CURSOR'); if ~isempty(lh), set(state.HEADFL, 'string', 'Head'); set(state.HEADF, 'string', sprintf(' %.1f',state.HEAD)); delete(lh); end; case 'SPECTRA', set(state.HEADFL, 'string', 'Head'); set(state.TAILFL, 'string', 'Tail'); set(state.HEADF, 'string', sprintf(' %.1f',state.HEAD)); set(state.TAILF, 'String', sprintf(' %.1f',state.TAIL)); otherwise, % selection change if state.MOVED > 0, SetBounds(state); end; end; state.MOVED = 0; state.MOVEMODE = ''; set(gcbf, 'userdata', state); %----------------------------------------------------------------------------- % UPDATE: manual update case 'UPDATE', state = get(gcbf, 'userdata'); SetBounds(state, 0); %----------------------------------------------------------------------------- % VARLIST: init new melba viewer from varlist selection % % arg: -1 previous % 1 next % 0 menu selected % % if viewer of that name already open just pop it case 'VARLIST', state = get(gcbf, 'userdata'); switch varargin{2}, case -1, i = state.VLISTSEL - 1; case 1, i = state.VLISTSEL + 1; case 0, i = get(gcbo, 'userdata'); end; if i < 1, return; end; if i > length(state.VLIST), return; end; name = state.VLIST{i}; set(0,'ShowHiddenHandles', 'on'); h = findobj('Tag', 'MELBA'); wList = []; for j = 1 : length(h), fn = get(h(j), 'name'); wList{end+1} = fn(9:end); end; j = strmatch(name, wList, 'exact'); % test open viewers if ~isempty(j), figure(h(j)); end set(0,'ShowHiddenHandles', 'off'); if isempty(j), % open new viewer cfg = melba('GETCFG', state); cfg.FIGPOS = []; % use default position v = state.LPSTATE; if ~isempty(state.LPROC) | isstruct(v) | isempty(v), v = 0; end; if isempty(state.SIGNALS), if v > 0, melba(state.VLIST{i}, 'CONFIG', cfg, ... 'LABELS', state.LABELS, ... 'VLIST', state.VLIST, 'VLSEL', i, ... 'HEAD', state.HEAD, 'TAIL', state.TAIL); else, melba(state.VLIST{i}, 'CONFIG', cfg, ... 'VLIST', state.VLIST, 'VLSEL', i, ... 'HEAD', state.HEAD, 'TAIL', state.TAIL); end; else, % handle signals aggregate if v > 0, melba(state.SIGNALS(:,i),state.SRATE, 'CONFIG', cfg, 'NAME', name, ... 'LABELS', state.LABELS, ... 'VLIST', state.VLIST, 'VLSEL', i, 'SIGNALS', state.SIGNALS, ... 'HEAD', state.HEAD, 'TAIL', state.TAIL); else, melba(state.SIGNALS(:,i),state.SRATE, 'CONFIG', cfg, 'NAME', name, ... 'VLIST', state.VLIST, 'VLSEL', i, 'SIGNALS', state.SIGNALS, ... 'HEAD', state.HEAD, 'TAIL', state.TAIL); end; end; end; %----------------------------------------------------------------------------- % WATERFALL: waterfall plot of spectrum case 'WATERFALL', state = get(gcbf, 'userdata'); head = floor(state.HEAD*state.SRATE/1000)+1; tail = floor(state.TAIL*state.SRATE/1000)+1; if tail-head >= 100000, return; end; [b,f] = ComputeSpecgram(state.SIGNAL(head:tail), state.SRATE, state.FRAME, ... get(state.SGWIN,'value'), state.AVGW, state.OLAP, state.PREEMP~=0); figure; nf = size(b,2); % decimate for performance (waterfall is slow) df = round(nf/30); f = f(1:2:end); b = b(1:2:end,1:df:end); b = 20*log10(abs(b)+eps)'; waterfall(f, [1:df:nf]+state.HEAD-1, b); shading interp; rotate3d; view(-120,70); set(gca, 'xlim', [0 round(state.SRATE/2)], ... 'ydir','reverse', 'ylim', [0 nf-1]+state.HEAD); xlabel('Hz'); ylabel('msecs'); zlabel('dB'); %----------------------------------------------------------------------------- % XFER: transfer list elements (SelOutput) case 'XFER', cfgState = get(gcbf, 'UserData'); xferDir = get(gcbo, 'UserData'); if xferDir > 0, fromH = cfgState.LOADED; toH = cfgState.EXPORTED; else, fromH = cfgState.EXPORTED; toH = cfgState.LOADED; end; xi = get(fromH, 'Value'); % selected indices fromList = cellstr(get(fromH, 'String')); if isempty(xi), if xferDir > 0, s = '<'; else, s = '>'; end; set(gcbo, 'userData', -xferDir, 'string', s); return; end; toList = cellstr(get(toH, 'String')); ki = setdiff([1:length(fromList)], xi); if isempty(toList) | isempty(toList{1}), toList = fromList(xi); else, toList = [toList ; fromList(xi)]; end; if isempty(ki), fromList = []; else, fromList = fromList(ki); end; if ~isempty(toList), toList = cellstr(sortrows(char(toList))); end; set(fromH, 'string', fromList, 'Value', []); set(toH, 'string', toList, 'Value', []); %----------------------------------------------------------------------------- % ZOOM: adjust zoomed window width case 'ZOOM', if nargin > 2, fh = varargin{3}; else, fh = gcbf; end; state = get(fh, 'userdata'); if strcmp(varargin{2}, 'PLOT'), fh = colordef('new', 'black'); s = get(state.ZOOM,'ydata'); plot(s,'w'); ns = length(s); line(round(ns/2)*[1 1],get(gca,'ylim'),'color','g','linestyle',':'); line(get(gca,'xlim'),[0 0],'color',[.5 .5 .5],'linestyle','--'); set(gca, 'xticklabel',[], 'xlim', [1 ns]); xlabel(sprintf('%g msecs', state.ZOOMW)); title(sprintf('Cursor: %.1f msecs', state.CURSOR)); set(fh, 'name', sprintf('%s @ %.1f', state.NAME, state.CURSOR), 'visible', 'on'); return; end; switch varargin{2}, case {'ZOOM','UNZOOM'}, % set vertical scaling if varargin{2}(1) == 'U', ylim = get(state.ZOOM, 'userData'); % original bounds else, s = get(state.ZOOM,'ydata'); ylim = [-1.1 1.1]*max(abs(s)); end; set(state.ZOOMA, 'ylim', ylim); return; case 'SHRINK', w = state.ZOOMW + state.ZOOMW * -.2; case 'EXPAND', w = state.ZOOMW + state.ZOOMW * .2; otherwise, w = str2num(varargin{2}); end; if w < 1, w = 1; elseif w > 200, w = 200; end; state.ZOOMW = w; set(fh, 'userdata', state); ns = round(w/1000*state.SRATE); set(state.ZOOMA, 'xlim', [1 ns]); set(findobj(state.ZOOMA, 'tag', 'CURSOR'), 'xdata', round(ns/2)*[1 1]); SetCursor(state,1); %----------------------------------------------------------------------------- % UNRECOGNIZED ACTION: handle "melba <data>" form otherwise, if evalin('base', ['exist(''' varargin{1} ''',''var'')'], ['iscellstr(' varargin{1} ')']), if nargin > 1, vName = varargin{1}; data = evalin('base', vName); eval([vName '=data;clear data;varargin{1}=' vName ';']); melba(varargin{:}); else, evalin('base',['melba(' varargin{1} ');']); end; elseif length(varargin{1})>4 && strcmpi(varargin{1}(end-3:end),'.wav'), fl = dir(varargin{1}); fl = {fl.name}; if isempty(fl), error('Error: no files matching %s found', varargin{1}); end; [s,sr] = wavread(fl{1}); varargin{1} = {s,sr}; varargin{end+1} = 'NAME'; varargin{end+1} = fl{1}; if length(fl) > 1, varargin{end+1} = 'VLIST'; varargin{end+1} = fl; end; melba(varargin{:}); else, vName = varargin{1}; if findstr(vName, '*'), % handle wildcarding vl = evalin('base',['whos(''' vName ''')']); for i = length(vl):-1:1, % kill label variables matching mask if strcmp(vl(i).class,'struct') & length(vl(i).name)>4 & strcmp(vl(i).name(end-3:end),'_lbl'), vl(i) = []; end; end; vl = {vl.name}; fl = dir([vName '.mat']); % then cwd mat files fl = {fl.name}; for i = 1 : length(fl), fl(i) = {fl{i}(1:end-4)}; end; % kill '.mat' if isempty(vl), vl = {}; end; fl = [vl(:) ; fl(:)]; if isempty(fl), error(['Error: no variables or files matching ' vName ' found']); else, melba(fl, varargin{2:end}); return; end; end; if isempty(which([vName '.mat'])), error(['Error: variable ' vName ' not available on current path']); end; data = load(vName); varargin{end+1} = 'NAME'; varargin{end+1} = vName; eval([vName '=data.' vName ';clear data;varargin{1}=' vName ';'], ... ['error([''' varargin{end} '.mat does not hold a variable of that name'']);']); melba(varargin{:}); end; end; %============================================================================= %COMPUTEF01 - modified autocorrelation pitch estimator % % usage: F0 = ComputeF01(s, sr) % % Computes pitch estimation based on the filtered error signal autocorrelation % sequence to minimize formant interaction (modified autocorrelation analysis) % % S is a vector of speech sampled at SR Hz % returns F0 (Hz) and normalized amplitude % % cf. Markel & Gray (1976) Linear Prediction of Speech, pp. 203-206 function [F0,v,R] = ComputeF01(s, sr, isF); F0 = 0; order = 12; thresh = .4; % lowpass filter (800 Hz) % s = s(:); ns = length(s); [b,a] = cheby2(6,30,1600/sr); s = filtfilt(b, a, s); % compute inverse filter ds = filter([1 -1], 1, s); % 1st difference ds = hamming(ns) .* ds; % window R = flipud(fftfilt(conj(ds),flipud(ds))); % autocorrelation vector a = levinson(R,order); % inverse filter coefficients % apply it fs = filter([1 -a], 1, s); % inverse filter fs = hamming(ns) .* fs; % window R = flipud(fftfilt(conj(fs),flipud(fs))); % autocorrelation vector (applied to error) R = R / R(1); % normalize % hunt voicing peak over range from 2.5 ms to half window size - 2.5 ms ts = round(sr*.0025); % 2.5 ms in samples vp = find(diff([0 ; diff(R(ts+1:round(ns/2)-ts))] > 0) < 0) + ts; % peak indices v = R(vp); % normalized values k = find(v >= thresh); % peaks exceeding threshold if isempty(k), return; % none elseif k < 2, vp = vp(k); % one v = v(k); else, % heuristics to avoid pitch doubling/halving p = vp / vp; p = [0 , p(:,end)']; % proportional peak periods px = linspace(0,1,length(p)); % ideal proportions if all(p > px-.02) & all(p < px+.02) & v(2) >= thresh, % matches period repetition: voiced if isF, vp = vp(1); % female: 1st peak is probable pitch period else, vp = vp(2); % male: 2nd peak is probable pitch period end; else, % non-ideal pattern, result possibly bogus [v,vp] = max(R(ts+1:round(ns/2)-ts)); % use max peak vp = vp + ts; end; end; % convert pitch period to Hz F0 = round(sr / (vp-1)); %============================================================================= % COMPUTESPECGRAM - compute spectrogram % % returns [nFFTs x nFrames] image, freq axis function [b,f] = ComputeSpecgram(s, sRate, frame, mult, wSize, shift, doPremp); if doPremp, s = diff(s); end; % 1st difference unless preemphasis set to 0 wSize = wSize * mult; % window * pop-up setting (1: broadband - 4: narrow) nSamps = length(s); wSize = floor(wSize*sRate/1000); % window size (samples) wSize = wSize + mod(wSize,2); % make sure it's even shift = shift * sRate/1000; % overlap (fractional samples) nFrames = round(nSamps/shift); w = hanning(wSize); b = zeros(frame, nFrames); sx = wSize/2 + 1; % fractional sample index s = [zeros(wSize/2,1) ; s ; zeros(wSize,1)]; for fi = 1 : nFrames, si = round(sx); pf = abs(fft(w .* s(si:si+wSize-1),frame*2)); b(:,fi) = pf(2:frame+1); % drop DC, upper reflection sx = sx + shift; end; b = filter(ones(3,1)/3,1,abs(b),[],2); % clean holes f = linspace(0,sRate/2,frame); %============================================================================= % COMPUTESPECTRA - compute spectral cross-section at specified offset % % offset defaults to cursor position % returns spectral power, frequency, 1st 5 formants and associated bandwidths, amplitudes function [p,f,formants,bandwidths,amplitudes] = ComputeSpectra(state, offset) if nargin < 2, offset = state.CURSOR; end; % get analysis frame c = floor(offset*state.SRATE/1000)+1; % msecs->samples ns = round(state.WSIZE/1000*state.SRATE); % frame (msecs->samples) h = c - round(ns/2); if h < 1, h = 1; end; t = h + ns - 1; if t > length(state.SIGNAL), t = length(state.SIGNAL); h = t - ns + 1; end; signal = state.SIGNAL(h:t); % pre-emphasize, window, lpc if state.PREEMP < 0, % adaptive r0 = signal' * signal; r1 = [0;signal(1:end-1)]' * signal; mu = r1 ./ r0; else, mu = state.PREEMP; end; if mu ~= 0, signal = filter([1 -mu], 1, signal); end; signal = hanning(ns) .* signal; % [a,g] = lpcg(signal, state.ORDER); R = flipud(fftfilt(conj(signal),flipud(signal))); % unbiased autocorrelation estimate a = levinson(R, state.ORDER); % LPC g = sqrt(real(sum((a').*R(1:state.ORDER+1,:)))); % gain [p,f] = freqz(g, a, state.FRAME, state.SRATE); p = 20*log10(abs(p/state.SOFF+eps)'); % find formants if nargout > 2, r = flipud(sort(roots(a))); formants = angle(r(find(imag(r) > 0)))' * state.SRATE/(2*pi); bandwidths = -log(abs(r(find(imag(r) > 0))))' * state.SRATE/pi; [formants, idx] = sort(formants); bandwidths = bandwidths(idx); idx = find(bandwidths<state.BWCLIP); % discard peaks with >= bwclip Hz bw formants = round(formants(idx)); bandwidths = round(bandwidths(idx)); if isempty(formants), formants = NaN; bandwidths = NaN; else, while ~formants(1), % kill initial 0s formants = formants(2:end); bandwidths = bandwidths(2:end); end; n = min(length(formants), 5); formants = formants(1:n); bandwidths = bandwidths(1:n); end; if isnan(formants), amplitudes = NaN; else, amplitudes = interp1(f, p, formants, '*linear'); end; end; %============================================================================= % CONFIGSPECTRA - configure spectral analysis % % returns [] on cancel function config = ConfigSpectra(config); figPos = get(0, 'ScreenSize'); width = 290; height = 440; pc = strcmp(computer, 'PCWIN'); if pc, width = width - 40; height = height + 20; end; figPos = [figPos(1)+(figPos(3)-width)/2, figPos(2)+(figPos(4)-height)/2, width, height]; cfg = dialog('Name', 'Configure Spectral Params', ... 'Tag', 'MELBA', ... 'Position', figPos, ... 'menubar', 'none', ... 'UserData', 0, ... 'keyPressFcn', 'set(gcbf,''UserData'',1);uiresume'); set(cfg, 'units', 'characters'); figPos = get(cfg, 'position'); h = figPos(4); % height in characters % nudge step size h = h - 3; uicontrol(cfg, ... 'Style','text', ... 'HorizontalAlignment', 'right', ... 'String','Nudge stepsize (msecs):', ... 'Units', 'characters', ... 'Position', [1 h 33 1.7]); nudgeH = uicontrol(cfg, ... 'Style', 'edit', ... 'HorizontalAlignment', 'left', ... 'String', sprintf(' %g',config.NUDGE), ... 'Units', 'characters', ... 'Position', [35 h+.3 10 2]); % analysis window size h = h - 2.3; uicontrol(cfg, ... 'Style','text', ... 'HorizontalAlignment', 'right', ... 'String','Analysis window (msecs):', ... 'Units', 'characters', ... 'Position', [1 h 33 1.7]); winH = uicontrol(cfg, ... 'Style', 'edit', ... 'HorizontalAlignment', 'left', ... 'String', sprintf(' %d',config.WSIZE), ... 'Units', 'characters', ... 'Position', [35 h+.3 10 2]); % LPC order h = h - 2.3; uicontrol(cfg, ... 'Style','text', ... 'HorizontalAlignment', 'right', ... 'String','Number of LPC coeffs:', ... 'Units', 'characters', ... 'Position', [1 h 33 1.7]); orderH = uicontrol(cfg, ... 'Style', 'edit', ... 'HorizontalAlignment', 'left', ... 'String', sprintf(' %d',config.ORDER), ... 'Units', 'characters', ... 'Position', [35 h+.3 10 2]); % # FFTs h = h - 2.3; uicontrol(cfg, ... 'Style','text', ... 'HorizontalAlignment', 'right', ... 'String','Number of FFT eval points:', ... 'Units', 'characters', ... 'Position', [1 h 33 1.7]); fftH = uicontrol(cfg, ... 'Style', 'edit', ... 'HorizontalAlignment', 'left', ... 'String', sprintf(' %d',config.FRAME), ... 'Units', 'characters', ... 'Position', [35 h+.3 10 2]); % averaging window h = h - 2.3; uicontrol(cfg, ... 'Style','text', ... 'HorizontalAlignment', 'right', ... 'String','Averaging window (msecs):', ... 'Units', 'characters', ... 'Position', [1 h 33 1.7]); avgH = uicontrol(cfg, ... 'Style', 'edit', ... 'HorizontalAlignment', 'left', ... 'String', sprintf(' %d',config.AVGW), ... 'Units', 'characters', ... 'Position', [35 h+.3 10 2]); % overlap h = h - 2.3; uicontrol(cfg, ... 'Style','text', ... 'HorizontalAlignment', 'right', ... 'String','Overlap (msecs):', ... 'Units', 'characters', ... 'Position', [1 h 33 1.7]); olapH = uicontrol(cfg, ... 'Style', 'edit', ... 'HorizontalAlignment', 'left', ... 'String', sprintf(' %d',config.OLAP), ... 'Units', 'characters', ... 'Position', [35 h+.3 10 2]); % spectral offset h = h - 2.3; uicontrol(cfg, ... 'Style','text', ... 'HorizontalAlignment', 'right', ... 'String','SPL reference (dB):', ... 'Units', 'characters', ... 'Position', [1 h 33 1.7]); soffH = uicontrol(cfg, ... 'Style', 'edit', ... 'HorizontalAlignment', 'left', ... 'String', sprintf(' %d',config.SOFF), ... 'Units', 'characters', ... 'Position', [35 h+.3 10 2]); % bw clip h = h - 2.3; uicontrol(cfg, ... 'Style','text', ... 'HorizontalAlignment', 'right', ... 'String','Formant BW clipping (Hz):', ... 'Units', 'characters', ... 'Position', [1 h 33 1.7]); bwcH = uicontrol(cfg, ... 'Style', 'edit', ... 'HorizontalAlignment', 'left', ... 'String', sprintf(' %d',config.BWCLIP), ... 'Units', 'characters', ... 'Position', [35 h+.3 10 2]); % spectral display cutoff h = h - 2.3; uicontrol(cfg, ... 'Style','text', ... 'HorizontalAlignment', 'right', ... 'String','Spectral Display Cutoff (Hz):', ... 'Units', 'characters', ... 'Position', [1 h 33 1.7]); specLimH = uicontrol(cfg, ... 'Style', 'edit', ... 'HorizontalAlignment', 'left', ... 'String', sprintf(' %d',config.SPECLIM), ... 'Units', 'characters', ... 'Position', [35 h+.3 10 2]); % F0 trimming threshold h = h - 2.3; uicontrol(cfg, ... 'Style','text', ... 'HorizontalAlignment', 'right', ... 'String','F0 trimming threshold:', ... 'Units', 'characters', ... 'Position', [1 h 33 1.7]); f0thrH = uicontrol(cfg, ... 'Style', 'edit', ... 'HorizontalAlignment', 'left', ... 'String', sprintf(' %.2f',config.F0THR), ... 'Units', 'characters', ... 'Position', [35 h+.3 10 2]); % preemphasis h = h - 2.3; p = [6 h+.35 13 1.7]; adaptH = uicontrol(cfg, ... 'Style', 'checkbox', ... 'HorizontalAlignment', 'left', ... 'String', 'Adaptive', ... 'Value', (config.PREEMP<0), ... 'Units', 'characters', ... 'Position', p); uicontrol(cfg, ... 'Style','text', ... 'HorizontalAlignment', 'right', ... 'String','pre-emphasis:', ... 'Units', 'characters', ... 'Position', [18 h 15 1.7]); preempH = uicontrol(cfg, ... 'Style', 'edit', ... 'HorizontalAlignment', 'left', ... 'String', sprintf(' %g',abs(config.PREEMP)), ... 'Units', 'characters', ... 'Position', [35 h+.3 10 2]); % anal h = h - 2.3; uicontrol(cfg, ... 'Style','text', ... 'HorizontalAlignment', 'right', ... 'String','Active Analyses:', ... 'Units', 'characters', ... 'Position', [6 h-1 18 1.7]); ah(1) = uicontrol(cfg, ... 'Style', 'checkBox', ... 'String', 'LPC', ... 'Value', bitget(config.ANAL,1), ... 'Units', 'characters', ... 'Position', [25 h+.25 9 1.7]); ah(2) = uicontrol(cfg, ... 'Style', 'checkBox', ... 'String', 'DFT', ... 'Value', bitget(config.ANAL,2), ... 'Units', 'characters', ... 'Position', [35 h+.25 10 1.7]); ah(3) = uicontrol(cfg, ... 'Style', 'checkBox', ... 'String', 'AVG', ... 'Value', bitget(config.ANAL,3), ... 'Units', 'characters', ... 'Position', [25 h-1.55 9 1.7]); ah(4) = uicontrol(cfg, ... 'Style', 'checkBox', ... 'String', 'CEPS', ... 'Value', bitget(config.ANAL,4), ... 'Units', 'characters', ... 'Position', [35 h-1.55 10 1.7]); % sex h = h - 3.8; uicontrol(cfg, ... 'Style','text', ... 'HorizontalAlignment', 'right', ... 'String','Subject Gender:', ... 'Units', 'characters', ... 'Position', [6 h 18 1.7]); p = [25 h+.2 20 1.7]; sexH = uicontrol(cfg, ... 'Style', 'popupmenu', ... 'HorizontalAlignment', 'left', ... 'String', 'Male|Female', ... 'value', config.ISF+1, ... 'Units', 'characters', ... 'Position', p); % buttons uicontrol(cfg, ... 'Position',[width/2-70 15 60 25], ... 'String','OK', ... 'Callback','set(gcbf,''UserData'',1);uiresume'); uicontrol(cfg, ... 'Position',[width/2+10 15 60 25], ... 'String','Cancel', ... 'Callback','uiresume'); % wait for input uiwait(cfg); if ~ishandle(cfg), config = []; return; end; if get(cfg, 'UserData'), anal = get(ah, 'value'); anal = anal{4}*8 + anal{3}*4 + anal{2}*2 + anal{1}; soff = str2num(get(soffH, 'string')); if soff < 1, soff = 1; end; config = struct('NUDGE', str2num(get(nudgeH, 'string')), ... 'WSIZE', str2num(get(winH, 'string')), ... 'ORDER', str2num(get(orderH, 'string')), ... 'FRAME', str2num(get(fftH, 'string')), ... 'AVGW', str2num(get(avgH, 'string')), ... 'OLAP', str2num(get(olapH, 'string')), ... 'BWCLIP', str2num(get(bwcH, 'string')), ... 'PREEMP', str2num(get(preempH, 'string')), ... 'SOFF', soff, ... 'SPECLIM', str2num(get(specLimH, 'string')), ... 'ANAL', anal, ... 'F0THR', str2num(get(f0thrH, 'string')), ... 'ISF', get(sexH, 'value')-1 ); if get(adaptH, 'value'), config.PREEMP = -config.PREEMP; end; else, config = []; end; delete(cfg); %============================================================================= % EDITLABEL - edit label % % returns [] on cancel function label = EditLabel(label, id, useDel) if nargin < 3, useDel = 0; end; figPos = get(0, 'ScreenSize'); width = 280; height = 140; figPos = [figPos(1)+(figPos(3)-width)/2, figPos(2)+(figPos(4)-height)/2, width, height]; cfg = dialog('Name', [id ' Label'], ... 'Tag', 'MELBA', ... 'menubar', 'none', ... 'Position', figPos, ... 'UserData', 0, ... 'keyPressFcn', 'set(gcbf,''UserData'',1);uiresume'); uicontrol(cfg, ... % name 'Position', [10 105 40 17], ... 'HorizontalAlignment', 'right', ... 'String','Name:', ... 'Style','text'); name = uicontrol(cfg, ... 'Position',[55 105 85 20], ... 'Style','edit', ... 'String', label.NAME, ... 'HorizontalAlignment', 'left'); uicontrol(cfg, ... % offset 'Position', [145 105 50 17], ... 'HorizontalAlignment', 'right', ... 'String','Offset:', ... 'Style','text'); offset = uicontrol(cfg, ... 'Position',[200 105 60 20], ... 'Style','edit', ... 'String', sprintf('%.1f',label.OFFSET), ... 'HorizontalAlignment', 'left'); uicontrol(cfg, ... % hook 'Position', [10 70 70 17], ... 'HorizontalAlignment', 'right', ... 'String','Comments:', ... 'Style','text'); if ischar(label.HOOK) | isempty(label.HOOK), s = label.HOOK; e = 'on'; else, s = '<data>'; e = 'inactive'; end; note = uicontrol(cfg, ... 'Position',[85 70 175 20], ... 'Style','edit', ... 'Enable', e, ... 'String', s, ... 'HorizontalAlignment', 'left'); % buttons if useDel, uicontrol(cfg, ... 'Position',[width/2-100 15 60 25], ... 'String','OK', ... 'Callback','set(gcbf,''UserData'',1);uiresume'); uicontrol(cfg, ... 'Position',[width/2-30 15 60 25], ... 'String','Cancel', ... 'Callback','set(gcbf,''UserData'',0);uiresume'); uicontrol(cfg, ... 'Position',[width/2+40 15 60 25], ... 'String','Delete', ... 'Callback','set(gcbf,''UserData'',-1);uiresume'); else, uicontrol(cfg, ... 'Position',[width/2-70 15 60 25], ... 'String','OK', ... 'Callback','set(gcbf,''UserData'',1);uiresume'); uicontrol(cfg, ... 'Position',[width/2+10 15 60 25], ... 'String','Cancel', ... 'Callback','set(gcbf,''UserData'',0);uiresume'); end; % wait for input uiwait(cfg); switch get(cfg, 'userData'), case 1, % OK label.NAME = get(name, 'String'); offset = str2num(get(offset, 'String')); if ~isempty(offset) & offset>0, label.OFFSET = offset; end; if ischar(label.HOOK) | isempty(label.HOOK), label.HOOK = get(note, 'String'); end; case 0, % Cancel label = []; case -1, % Delete label = -1; end; delete(cfg); %============================================================================= % GETNAME - get export variable name function name = GetName(name, id, blurb) if nargin < 3, blurb = 'Save @ as...'; end; figPos = get(0, 'ScreenSize'); width = 200; height = 100; figPos = [figPos(1)+(figPos(3)-width)/2, figPos(2)+(figPos(4)-height)/2, width, height]; k = findstr(blurb, '@'); if ~isempty(k), blurb = [blurb(1:k-1),id,blurb(k+1:end)]; end; cfg = dialog('Name', blurb, ... 'Tag', 'MELBA', ... 'menubar', 'none', ... 'Position', figPos, ... 'KeyPressFcn', 'set(gcbf,''UserData'',1);uiresume', ... 'UserData', 0); % name field eh = uicontrol(cfg, ... 'Position', [20 60 width-40 20], ... 'Style', 'edit', ... 'HorizontalAlignment', 'left', ... 'Callback', 'set(gcbf,''UserData'',1);uiresume', ... 'String', name); % OK, cancel buttons uicontrol(cfg, ... % buttons 'Position',[width/2-70 15 60 25], ... 'String','OK', ... 'Callback','set(gcbf,''UserData'',1);uiresume'); uicontrol(cfg, ... 'Position',[width/2+10 15 60 25], ... 'String','Cancel', ... 'Callback','uiresume'); % wait for input uiwait(cfg); if get(cfg, 'UserData'), name = get(eh, 'string'); else, name = []; end; delete(cfg); %============================================================================= % MAKELIST of current labels function labels = MakeList(state) labels = {}; for i = 1 : length(state.LABELS), label = state.LABELS(i); if ischar(label.HOOK) | isempty(label.HOOK), sn = label.HOOK; else, sn = '(data)'; end; labName = label.NAME; if isempty(labName), labName = ' '; end; s = sprintf('%-12s %9.1f %s', ... labName, ... label.OFFSET, ... sn); labels(end+1) = {s}; end; %============================================================================= % PLOTSPECTRA % % uses external function SPECTRA function PlotSpectra(state) mu = state.PREEMP; if mu < 0, mu = []; end; analI = find([bitget(state.ANAL,1) bitget(state.ANAL,2) bitget(state.ANAL,3) bitget(state.ANAL,4)]); analS = {'LPC','DFT','AVG','CEPS'}; sex = 'MF'; fh = colordef('new', 'black'); a1 = subplot(3,1,1); % speech axis pos1 = get(a1, 'position'); a2 = subplot(3,1,3); % spectrum axis pos2 = get(a2, 'position'); pos2(4) = pos1(2) - pos2(2) - .1; set(a2, 'position', pos2); [h,s,mu,F0] = spectra(state.SIGNAL, state.SRATE, state.CURSOR, ... 'WSIZE',state.WSIZE, 'ANAL',analS(analI), ... 'DEST', a2, 'PREEMP',mu, 'SEX',sex(state.ISF+1), 'SOFF', state.SOFF, ... 'AVGW',state.AVGW, 'OVERLAP',state.OLAP, 'ORDER',state.ORDER, 'SPECLIM',state.SPECLIM); legend(h, char(analS(analI))); axes(a1); ht = state.CURSOR + [-state.WSIZE state.WSIZE]; dx = 0; if ht(1)<0, dx = ht(1); ht(1)=0; end; if ht(2)>state.DUR, ht(2)=state.DUR; end; hts = floor(ht*state.SRATE/1000) + 1; s = state.SIGNAL(hts(1):hts(2)); plot(s, 'w'); xlim = [1 length(s)]; ylim = get(state.FPANEL, 'ylim'); hts = floor((state.WSIZE + dx + state.WSIZE*[-.5 .5])*state.SRATE/1000) + 1; if hts(1)<1, hts(1) = 1; end; if hts(2)>length(s), hts(2) = length(s); end; w = zeros(size(s)); w(hts(1):hts(2)) = hanning(diff(hts)+1) * (ylim(2)*.9); line([1:length(w)],w,'color','g'); set(a1, 'xlim', xlim, 'ylim', ylim, ... 'xtick', [], 'ytick', []); line(xlim, [0 0], 'color', [.5 .5 .5], 'linestyle', ':'); title(sprintf('CURSOR = %.1f WSIZE = %d FRAME = %d MU = %.2f', ... state.CURSOR, state.WSIZE, state.FRAME, mu)); a3 = axes('position', pos1, 'xlim', ht, 'ytick',[], 'color','none'); line([state.CURSOR, state.CURSOR], get(a3,'ylim'), 'color','c', 'linestyle', '--'); xlabel('msecs'); uicontrol(fh, ... % cursor button 'style', 'pushbutton', ... 'units', 'characters', ... 'string', 'Cursor', ... 'position', [3 0 10 1.5], ... 'callback', 'disp(''units: Hz, dB'');round(ginput)'); if F0 > 0, % F0 uicontrol(fh, ... 'style', 'text', ... 'units', 'characters', ... 'string', sprintf('F0 = %d Hz', F0), ... 'backgroundColor', get(fh, 'color'), ... 'foregroundColor', [1 1 1], ... 'position', [13 0 25 1.2]); end; set(fh, 'name', sprintf('%s @ %.1f', state.NAME, state.CURSOR), 'visible', 'on'); %============================================================================= % SELOUTPUT - select labelling movement target outputs function targets = SelOutput(targets) figPos = get(0, 'ScreenSize'); width = 292; height = 190; figPos = [figPos(1)+(figPos(3)-width)/2, figPos(2)+(figPos(4)-height)/2, width, height]; cfg = dialog('Name', 'Select Labelling Targets', ... 'Position', figPos, ... 'UserData', 0); xfer = uicontrol(cfg, ... % xfer 'Position',[137 123 20 20], ... 'String','>', ... 'UserData', 1, ... 'Callback', 'melba XFER;'); uicontrol(cfg, ... % loaded 'Style', 'text', ... 'HorizontalAlignment','left', ... 'String', 'Loaded', ... 'Position', [30 162 70 17]); loaded = uicontrol(cfg, ... 'Position',[20 105 100 56], ... 'String', targets.LOADED, ... 'ListBoxTop', 1, ... 'Style','listbox', ... 'Max', 2, ... 'Value', [], ... 'UserData', xfer, ... 'Callback', 'if get(get(gcbo,''userData''),''userData'')<0,set(get(gcbo,''userdata''),''userData'',1,''string'',''>'');end;'); uicontrol(cfg, ... % exported trajectories 'Style', 'text', ... 'HorizontalAlignment','left', ... 'String', 'Exported', ... 'Position', [185 162 70 17]); exported = uicontrol(cfg, ... 'Position',[175 105 100 56], ... 'String', targets.EXPORTED, ... 'Style','listbox', ... 'Max', 2, ... 'Value', [], ... 'UserData', xfer, ... 'Callback', 'if get(get(gcbo,''userData''),''userData'')>0,set(get(gcbo,''userdata''),''userData'',-1,''string'',''<'');end;'); uicontrol(cfg, ... % exported components 'Style', 'text', ... 'HorizontalAlignment', 'right', ... 'String', 'Export:', ... 'Position', [15 60 40 17]); doX = uicontrol(cfg, ... 'Position', [60 60 40 20], ... 'String','X', ... 'Style','checkbox', ... 'Value', targets.COMPONENTS(1)); doY = uicontrol(cfg, ... 'Position', [105 60 40 20], ... 'String','Y', ... 'Style','checkbox', ... 'Value', targets.COMPONENTS(2)); doT = uicontrol(cfg, ... 'Position', [150 60 40 20], ... 'String','Tilt', ... 'Style','checkbox', ... 'Value', targets.COMPONENTS(3)); doV = uicontrol(cfg, ... 'Position', [195 60 40 20], ... 'String','Vel', ... 'Style','checkbox', ... 'Value', targets.COMPONENTS(4)); doA = uicontrol(cfg, ... 'Position', [240 60 40 20], ... 'String','Acc', ... 'Style','checkbox', ... 'Value', targets.COMPONENTS(5)); cfgState = struct('LOADED', loaded, ... 'EXPORTED', exported); set(cfg, 'UserData', cfgState); % OK, cancel buttons uicontrol(cfg, ... % buttons 'Position',[width/2-70 15 60 25], ... 'String','OK', ... 'Callback','uiresume'); uicontrol(cfg, ... 'Position',[width/2+10 15 60 25], ... 'String','Cancel', ... 'Callback','set(gcbf,''UserData'',[]);uiresume'); % wait for input uiwait(cfg); if isempty(get(cfg, 'UserData')), targets = []; else, targets.LOADED = cellstr(get(loaded, 'String')); targets.EXPORTED = cellstr(get(exported, 'String')); targets.COMPONENTS = [get(doX, 'value'), ... get(doY, 'value'), ... get(doT, 'value'), ... get(doV, 'value'), ... get(doA, 'value')]; end; delete(cfg); %============================================================================= % SETBOUNDS - update selection bounds % % usage: SetBounds(state, flag) % % FLAG = 0: force update % FLAG = 1: update of selection bounds only % FLAG = 2: first time initialization function SetBounds(state, flag) if nargin<2, flag = ~state.AUTO; end; % update text fields set(state.HEADF, 'string', sprintf(' %.1f',state.HEAD)); set(state.TAILF, 'string', sprintf(' %.1f',state.TAIL)); % update selection patch hs = floor(state.HEAD*state.SRATE/1000)+1; % msecs -> samps ts = floor(state.TAIL*state.SRATE/1000)+1; set(state.BOUNDS, 'xdata', [hs hs ts ts]); if flag == 1, return; end; % compute & display spectrogram delete(get(state.SPECGRAM, 'children')); if ts-hs < 100000, % if not too big [b,f] = ComputeSpecgram(state.SIGNAL(hs:ts), state.SRATE, state.FRAME, ... get(state.SGWIN,'value'), state.AVGW, state.OLAP, state.PREEMP~=0); if state.SPECLIM < state.SRATE/2, f(find(f > state.SPECLIM)) = []; b = b(1:length(f),:); end; axes(state.SPECGRAM); ns = size(b,2); set(state.SPECGRAM, 'xlim', [1 ns], 'ylim', [0 state.SPECLIM]); imagesc(1:ns, f, b); set(state.SPECGRAM, 'ydir', 'normal', 'xtick', [], 'box', 'on'); colormap(flipud(gray(256).^get(state.CONTRAST,'value'))); if state.FMTS, if isempty(state.FORMANTS), try, if isempty(state.PREEMP), pe = .98; else, pe = state.PREEMP; end; s = state.SIGNAL(hs:ts); if max(abs(s)) <= 1, s = s * 2^16; end; fmts = snackmex(s,state.SRATE,'preemp',pe,'lpcord',state.ORDER,'nform',state.NFMTS)'; catch, fprintf('error computing formants\n'); fmts = []; end; else, ht = round([hs ts]./length(state.SIGNAL) * size(state.FORMANTS,1)); if ht(1) < 1, ht(1) = 1; end; if ht(2) > size(state.FORMANTS,1), ht(2) = size(state.FORMANTS,1); end; k = min([size(state.FORMANTS,2) , state.NFMTS]); fmts = state.FORMANTS(ht(1):ht(2),1:k); end; if ~isempty(fmts), x = linspace(1,ns,size(fmts,1))'; set(gca,'colororder',[0 0 1;0 1 0;1 0 0;0 .8 .8;.8 .8 0;.8 0 .8;.75 .75 .75]); line(x,fmts,'marker','.','linestyle','none'); end; end; else, h = text(.01,.95, 'SPECTROGRAM (selection too large; suppressed)', ... 'VerticalAlignment', 'top', ... 'units', 'normal', ... 'Color', 'w', ... 'parent', state.SPECGRAM); if strcmp(computer,'MAC2'), set(h, 'fontname','geneva','fontsize',9); end; end; % set bounds set([state.SELECTION,state.RMSA], 'xlim', [hs ts]); if ~isempty(state.F0), set([state.F0A], 'xlim', [hs ts]); end; axes(state.CURSORH); set(state.CURSORH, 'xlim', [state.HEAD state.TAIL]); if flag ~= 2, drawnow; end; set([state.CURSORL;findobj(state.CURSORH, 'tag', 'LABEL')], 'visible', 'on'); %============================================================================= % SETCURSOR - update cursor location % % usage: SetCursor(state, resetZoom) % % if RESETZOOM is nonzero only zoomed waveform display is updated function SetCursor(state, resetZoom) if nargin < 2, resetZoom = 0; end; % update zoomed waveform ns = round(state.ZOOMW/1000*state.SRATE); c = floor(state.CURSOR*state.SRATE/1000)+1; % cursor msecs->samples h = c - round(ns/2); t = h + ns - 1; if h < 1, s = [zeros(-h+1,1) ; state.SIGNAL(1:t)]; elseif t > length(state.SIGNAL), s = [state.SIGNAL(h:end) ; zeros(t-length(state.SIGNAL),1)]; else, s = state.SIGNAL(h:t); end; set(state.ZOOM, 'xData', [1:ns], 'yData', s); if resetZoom, return; end; set(state.CURSORL, 'xData', [state.CURSOR state.CURSOR]); set(state.CURSORF, 'string', sprintf(' %.1f',state.CURSOR)); % update spectra [p,f] = ComputeSpectra(state); ylim = get(state.SPECTRA, 'ylim'); doChange = 0; if min(p) < ylim(1), ylim(1) = floor(min(p)/10)*10; doChange = 1; end; if max(p) > ylim(2), ylim(2) = ceil(max(p)/10)*10; doChange = 1; end; if doChange, set(state.SPECTRA, 'ylim', ylim); end; set(state.SPECTRAL, 'xdata', f, 'ydata', p); % update spatial display if isempty(state.SPATWIN) | ~ishandle(state.SPATWIN), return; end; if strcmp(get(state.SPATWIN,'visible'),'off'), return; end; sh = get(state.SPATWIN,'userData'); sr = state.DATA(sh.SPATCHAN(1)).SRATE; n = 1; d = zeros(size(sh.SPATCHAN,1),2); for t = sh.SPATCHAN, d(n,:) = state.DATA(t).SIGNAL(floor(state.CURSOR*sr/1000)+1,1:2); set(sh.H(n), 'xData', d(n,1), 'yData', d(n,2)); n = n + 1; end; if ~isempty(state.SPLINE), p = d(includes(sh.SPATCHAN,state.SPLINE),:); if size(p,1) > 2, % compute & plot 2D spline, extrapolating 3 mm beyond tip; 5 mm behind TD xx = linspace(p(1,1)+3,p(end,1)-5,21)'; yy = interp1(p(:,1),p(:,2),xx,'spline'); ip = [xx,yy]; elseif isempty(p), % degenerate ; else, % plot line ip = p; end; set(sh.H(end), 'xData', ip(:,1), 'yData', ip(:,2)); end;