Вот решение, которое будет работать, даже если список находится в совершенно случайном порядке.Он основан на следующей идее: совокупная сумма вектора [0 1 0 0 -1 0 0]
равна [0 1 1 1 0 0 0]
.Это соответствует «on» во время 2 и «off» во время 5. Теперь все, что нам нужно сделать, это заполнить массив 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)