развернуть / заполнить матрицу Matlab - PullRequest
1 голос
/ 27 марта 2011

Я довольно новичок в 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 в столбце включения / выключения, то скопируйте число в столбце примечаний в «матрицу примечаний» для соответствующей строки
, если 0, то не копировать /ничего не делать.

, если число не совпадает с номером в столбце времени
скопировать предыдущую строку (которая может быть пустой, если на ней не было заметок).

для каждой строки в новой «матрице заметок» расположите цифры от низкого до высокого в разных столбцах.

Так может кто-нибудь сказать мне, что делать ??Я бьюсь головой о кирпичную стену!

1 Ответ

2 голосов
/ 27 марта 2011

Вот решение, которое будет работать, даже если список находится в совершенно случайном порядке.Он основан на следующей идее: совокупная сумма вектора [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)
...