/melba.m
MATLAB | 1 lines | 1 code | 0 blank | 0 comment | 0 complexity | 4c7d69bf127f13f1e5bb714f79ad884a MD5 | raw file
Large files files are truncated, but you can click here to view the full 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…
Large files files are truncated, but you can click here to view the full file