我在Matlab上很新,所以需要一个婴儿步骤解释。
我有一些看起来像这样的MIDI数据:
时间开/关注意事项
10 1 61
90 0 61
90 1 72
92 1 87
100 0 72
我想要做的是扩展或“填充”这些空白,以便我每个时刻都有一行,并且我有列显示哪些注释在哪些(通常有多个注释)同时)。
最终目标是在给定时间对音符(谐波失调)之间的整体关系进行一些计算。
所以我在想,也许我需要为每一个可能的音符(有127个)添加一个新列,然后每次都需要一个1或0。或者我可以只有一个矩阵,它只是告诉我哪些音符在开(因此列数会有所不同)。
我编写了自己的伪代码,但不知道如何实现它。我怀疑有一个简单的功能可以做到这一点。这是我的伪代码:
从0开始,在时间0开始新的'音符矩阵'
对于数字:0到n
如果数字与时间列中的数字匹配,请转到该行的开/关列
如果开/关列为1,则将备注栏中的数字复制到相应行的'notes-on matrix'
如果为0则不要复制/什么也不做。
如果数字与时间列中的数字不匹配
复制上一行(如果没有注释,则可以为空白)。
对于新的'音符矩阵'中的每一行,在不同列中排列从低到高的数字。
所以有人能告诉我该怎么做?我在这里碰到一堵砖墙!
答案 0 :(得分:2)
这是一个即使列表完全随机顺序也能正常工作的解决方案。它基于以下想法:向量[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)