/melba.m
http://github.com/smcg/MARTA · MATLAB · 1 lines · 1 code · 0 blank · 0 comment · 0 complexity · 4c7d69bf127f13f1e5bb714f79ad884a MD5 · raw file
- 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;