Java MIDI Track, воспроизводящий повторяющиеся ноты

Я создаю приложение, в котором пользователь может добавить несколько нот MIDI в коллекцию, отсюда они могут нажать кнопку «Воспроизвести», и приложение будет перебирать каждую ноту и воспроизводить их через динамики.

Я создал класс MIDIMessage, который содержит сведения о ноте, которую пользователь выбрал для добавления в список, при этом сохраненными данными являются высота тона, скорость, задержка и канал.

Каждое из этих сообщений сохраняется в ArrayList с типом MIDIMessage.

Затем я присоединяю итератор коллекции к объекту итератора и проигрываю звук, пока в коллекции есть неиспользованный элемент.

По какой-то причине, даже если я добавляю в коллекцию только одну ноту, всегда есть две ноты, играемые с одинаковой высотой, длиной и скоростью.

Кроме того, каждая нота воспроизводится одновременно, независимо от того, сколько их в коллекции, я предположил, что между ними будет какая-то задержка.

Ниже приведен код, который я использую в данный момент:

MIDI-сообщение:

package javatest;

public class MIDIMessage
{
    private int pitch;
    private int velocity;
    private int channel;

    public MIDIMessage(int p, int v, int c)
    {
        pitch = p;
        velocity = v;
        channel = c;
    }

    public int GetPitch()
    {
        return this.pitch;
    }

    public int GetVelocity()
    {
        return this.velocity;
    }

    public int GetChannel()
    {
        return this.channel;
    }
}

Добавление заметки в коллекцию:

public void AddToList()
    {
        int channel = jComboBoxChannels.getSelectedIndex();
        int pitch = jComboBoxPitch.getSelectedIndex();
        int velocity = ((Integer)jSpinnerVelocity.getValue());    
        collection.add(new MIDIMessage(pitch,velocity,channel));
    }

Воспроизведение нот:

try
        {
            jButton1.setEnabled(false);
            Sequencer sequencer = MidiSystem.getSequencer();
            sequencer.open();
            Sequence sequence = new Sequence(Sequence.PPQ,1);
            Track track = sequence.createTrack();

            Iterator itr = collection.iterator();
            int i = 0;

            while(itr.hasNext())
            {
                MIDIMessage msg = (MIDIMessage)itr.next();

                ShortMessage noteOnMsg = new ShortMessage();
                //Signal/Channel/Pitch/Velocity
                noteOnMsg.setMessage(ShortMessage.NOTE_ON, msg.GetChannel(),msg.GetPitch(),msg.GetVelocity());

                ShortMessage noteOffMsg = new ShortMessage();
                //Signal/Channel/Pitch/Velocity
                noteOffMsg.setMessage(ShortMessage.NOTE_OFF,msg.GetChannel(),msg.GetPitch(),msg.GetVelocity());

                track.add(new MidiEvent(noteOnMsg,0));
                track.add(new MidiEvent(noteOffMsg,1));
                //i = i+1;
            }

            sequencer.setSequence(sequence);
            sequencer.setTempoInBPM(120);
            sequencer.setLoopCount(1);

            sequencer.start();

            Thread.sleep(1000);
        }

person Jamie Keeling    schedule 13.01.2011    source источник
comment
Дубликаты возникают из-за того, что количество циклов равно 1 - это означает, что все повторяется один раз (после исходного воспроизведения) - установите его на ноль, чтобы вообще не зацикливаться.   -  person BarrySW19    schedule 18.07.2018
comment
Как странно. Прошло более 7 лет с тех пор, как я задал этот НАСТОЛЬКО вопрос, и причина, по которой он мне понадобился, заключалась в том, чтобы помочь получить степень там, где я сейчас нахожусь. Я рассмотрю это снова однажды.   -  person Jamie Keeling    schedule 19.07.2018


Ответы (1)


Я не уверен, почему ноты воспроизводятся дважды, но это определенно неправильно:

            track.add(new MidiEvent(noteOnMsg,0));
            track.add(new MidiEvent(noteOffMsg,1));

Как вы можете видеть здесь, MidiEvent состоит из сообщения и тика MIDI. Вы всегда добавляете noteOnMsg к такту 0 и noteOffMsg к такту 1, что объясняет, почему для нескольких нот все они воспроизводятся одновременно. Вместо этого вы хотите сделать что-то вроде этого:

startTick = 0;

while(itr.hasNext()) {
   [...]
   track.add(new MidiEvent(noteOnMsg,startTick));
   track.add(new MidiEvent(noteOffMsg,startTick + how_long_the_note_is_played));
   startTick += difference_between_this_note_and_the_next;
   [...]
}

Я не уверен, что означает ваше delay и можете ли вы использовать его здесь, поэтому вместо этого я использую описательные имена переменных.

Обратите внимание, что здесь вам придется использовать тики MIDI - если вы хотите преобразовать секунды в тики MIDI, см. это ТАК вопрос (точнее его ответ):

[Как долго длится один такт MIDI?]

Формула 60000 / (BPM * PPQ) (миллисекунд).

person schnaader    schedule 13.01.2011
comment
Извините, я не понимаю, как мне узнать, как долго играла нота, и разница между текущей нотой и следующей? Также задержка была ошибкой, я был кодом C/P из старого исходного файла. - person Jamie Keeling; 13.01.2011
comment
Вы определяете, как долго будет воспроизводиться нота и в чем разница между текущей нотой и следующей. Если вы хотите просто играть ноту каждый удар, попробуйте использовать Sequence.PPQ для обеих переменных. - person schnaader; 13.01.2011
comment
Я пытаюсь сказать, что не понимаю, как определить длину ноты, есть ли какой-то метод, который я могу использовать, чтобы получить ее длину? В качестве примера я заметил, что вы можете вызвать new MidiEvent(noteOnMsg,1).getTick); но у меня есть ощущение, что для его использования мне нужно будет создать MidiEvent отдельно, а затем добавить его на дорожку, что позволит мне установить Tick в переменную. - person Jamie Keeling; 13.01.2011
comment
Параметр тика представляет собой простой long, представьте себе временную шкалу от 0 до бесконечности, на которой вы размещаете свои MIDI-события (включение ноты, выключение ноты). В моем ответе я также сказал вам, как долго такой тик в миллисекундах, и в моем комментарии я сказал, что использование значения Sequence.PPQ будет длиной одного удара (вы используете 120 ударов в минуту). Методы getTick и setTick полезны только в том случае, если вы хотите получить/изменить тик определенного MidiEvent. - person schnaader; 13.01.2011
comment
Я до сих пор не понимаю, из вашего ответа вы указываете, как я могу указать параметр тика, используя startTick, и продолжительность воспроизведения каждой ноты. Я пытаюсь понять, что не понимаю, как я могу узнать, как долго играется каждая нота. Я не понимаю, как я могу использовать галочку, чтобы узнать это. - person Jamie Keeling; 14.01.2011
comment
Я думаю, что метод, который я использовал, вызывал какую-то ошибку, я переписал его с нуля, используя другой пример, и до сих пор у меня не было проблем. Спасибо за помощь, это очень ценится. - person Jamie Keeling; 21.01.2011