即使列表的顺序完全随机,这里也有一个可行的解决方案。它基于以下思想:向量[0 1 0 0 -1 0 0]的累积和为[0 1 1 1 0 0 0]。这对应于时间 2 的“on”和时间 5 的“off”。现在我们需要做的就是用1 和-1 填充一个数组,然后运行CUMSUM 将其转换为一个具有,在每一列中,只要声音是on,就会出现。
我假设有 128 个音符 (0-127),并且您希望在最后有一个静音时间步长(如果所有音符最终都结束)。请注意,Matlab 从 1 开始计数,因此时间 0 对应于第 1 行。
%# midiData is a n-by-3 array with [time,on/off,note]
midiData = [...
10 1 61
90 0 61
90 1 72
92 1 87
100 0 72];
%# do not call unique here, because repeated input rows are relevant
%# note values can be from 0 to 127
nNotes = 128;
%# nTimepoints: find the highest entry in midiData's time-column
%# add 2, because midiData starts counting time at 0
%# and since we want to have one additional timepoint in the end
nTimepoints = max(midiData(:,1))+2;
%# -- new solution ---
%# because the input is a bit messed up, we have to use a more complicated
%# solution. We'll use `accumarray`, with which we sum up all the
%# entries for on (+1) and off (-1) for each row(time)/column(note) pair.
%# after that, we'll apply cumsum
%# transform the input, so that 'off' is -1
%# wherever the second col of midiData is 0
%# replace it with -1
midiData(midiData(:,2)==0,2) = -1;
%# create output in one step
%# for every same coordinate (time,note), sum all the
%# on/offs (@sum). Force the output to be
%# a nTimepoints-by-nNotes array, and fill in zeros
%# where there's no information
output = accumarray(midiData(:,[1 3])+1,midiData(:,2),...
[nTimepoints,nNotes],@sum,0);
%# cumsum, and we're done
output = cumsum(output,1);
前面的解决方案,为了完整性:
%# --- old solution ---
%# create output array, which we'll first populate with 1 and -1
%# after which we transform it into an on-off array
%# rows are timepoints, columns are notes
output = zeros(nTimepoints,nNotes);
%# find all on's
%# onIdx is 1 if the second column of midiData is 1
onIdx = midiData(:,2) == 1;
%# convert time,note pairs into linear indices for
%# writing into output in one step
%# Add 1 to time and note, respectively, so that we start counting at 1
plusOneIdx = sub2ind([nTimepoints,nNotes],midiData(onIdx,1)+1,midiData(onIdx,3)+1);
%# write "1" wherever a note turns on
output(plusOneIdx) = 1;
%# now do the same for -1
offIdx = midiData(:,2) == 0;
minusOneIdx = sub2ind([nTimepoints,nNotes],midiData(offIdx,1)+1,midiData(offIdx,3)+1);
%# instead of overwrite the value in output, subtract 1
%# so that time/note that are both on and off become zeros
output(minusOneIdx) = output(minusOneIdx) - 1;
%# run cumsum on the array to transform the +1/-1 into stretches of 1 and 0
%# the 'dim' argument is 1, because we want to sum in the direction in
%# which rows are counted
output = cumsum(output,1);
%# for fun, visualize the result
%# there's white whenever a note is on
imshow(output)