function [Incipits,Starts,Ends,Beats] = make_incipits(ENAstruct,NBeat,NIncip)
% [Incipits,Starts,Ends,Beats] = make_incipits(ENAstruct,NBeat,NIncip)
% Return a set of "Incipits" - beat-chroma patches that
% represent a full piece. Algorithm is to take the Bar closest
% to each section marker, then take the next NBeat beats
% (default 32) as the incipit. Do this for all Sections; if
% there are more than NIncip (default 4) of them, repeatedly
% find the two most similar (by Euclidean distance), and remove
% the chronologically later of the two.
% Return NIncips x 12 x NBeat array of Incipits, and vectors of
% the start and end times (in secs) of each selected incipit, as
% well as indices of starting beats.
% 2010-04-13 Dan Ellis dpwe@ee.columbia.edu
% Make sure args 2 and 3 are not empty
if nargin < 2; NBeat = 32; end
if nargin < 3; NIncip = 4; end
% key-normalized beat-chroma
BChroma = chromrot(en_beatchroma(ENAstruct),ENAstruct.key);
nchr = size(BChroma,1);
% Convert Bars into Beat indices, and Sections into Bar indices
BarIndex = find_nearest(ENAstruct.bar, ENAstruct.beat);
SectBarIndex = find_nearest(ENAstruct.section, ENAstruct.bar);
% Map SectBarIndex to beats
SectBeatIndex = BarIndex(SectBarIndex);
nsect = length(SectBeatIndex);
nbeats = length(ENAstruct.beat);
% Output always has NIncip entries, some may be zero
Incipits = zeros(max(NIncip,nsect),nchr,NBeat);
Starts = zeros(1,max(NIncip,nsect));
Ends = zeros(1,max(NIncip,nsect));
Beats = zeros(1,max(NIncip,nsect));
for i = 1:nsect
nbts = min(NBeat, nbeats-SectBeatIndex(i)+1);
Incipits(i,:,1:nbts) = BChroma(:,SectBeatIndex(i)-1+[1:nbts]);
Starts(i) = ENAstruct.beat(SectBeatIndex(i));
Ends(i) = ENAstruct.beat(SectBeatIndex(i)+nbts-1);
Beats(i) = SectBeatIndex(i);
end
% Now, maybe prune incipits
while size(Incipits,1) > NIncip
% Make distance matrix
currnincip = size(Incipits,1);
dists = zeros(currnincip,currnincip);
mindist = Inf;
for i = 1:currnincip-1
for j = i+1:currnincip
dist = norm(squeeze(Incipits(i,:,:)-Incipits(j,:,:)));
% disp(sprintf('%d - %d = %f',i,j,dist));
if dist < mindist
mindist = dist;
minpair = [i j];
end
end
end
% disp(sprintf('min: %d - %d = %f',minpair(1),minpair(2),mindist));
% Now remove later of min pair
inkeep = find([1:currnincip] ~= minpair(2));
Incipits = Incipits(inkeep,:,:);
Starts = Starts(inkeep);
Ends = Ends(inkeep);
Beats = Beats(inkeep);
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function I = find_nearest(times, base)
% Return I such that base(I) = times, or as close as possible
nt = length(times);
nb = length(base);
diffs = repmat(times,nb,1) - repmat(base',1,nt);
[vv,I] = min(abs(diffs));