Синхронизация секвенсора Java Midi отключена

Aug 31 2020

Последние несколько дней я пытался написать на Java метроном для практики. Я сделал простой midi бит 4/4, который программа воспроизводит с использованием библиотеки javax.sound.midi .
Моя основная проблема заключается в том, что секвенсор, кажется, проигрывает первый бит вне времени. Если я установил последовательность в цикл, это произойдет только в первом цикле. Кстати, если я изменяю скорость трека, она сбрасывается после первого цикла.
Кроме того, я пробовал несколько файлов midi на случай, если возникла проблема с файлом midi, который я создал, но все мои тесты дали одинаковые результаты.
Вот мой код, который обрабатывает воспроизведение миди:

public class MidiHandler 
{
    private Sequencer sequencer;
    private Sequence seq;
    private float newTempoFactor;
    
    public MidiHandler()
    {
        try 
        {
            sequencer = MidiSystem.getSequencer();
            if (sequencer == null)
            {
                System.err.println("Sequencer not supported");
            }
            sequencer.open();
        } 
        catch (MidiUnavailableException ex) 
        {
            Logger.getLogger(MidiHandler.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    
    public void setAudioTrack(String filePath)
    {
        try 
        {
            seq= MidiSystem.getSequence(new File(filePath));
            sequencer.setSequence(seq);
        } 
        catch (InvalidMidiDataException | IOException ex) 
        {
            Logger.getLogger(MidiHandler.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    
    public void playTrack(float bpm) throws InterruptedException
    {
        try 
        { 
            seq=editEvents();//editEvents() method pushes all midi events 100 ticks forward
            sequencer.setSequence(seq);
        } 
        catch (InvalidMidiDataException ex) 
        {
            Logger.getLogger(MidiHandler.class.getName()).log(Level.SEVERE, null, ex);
        }
        sequencer.setLoopCount(Sequencer.LOOP_CONTINUOUSLY);
        sequencer.start();
        //sequencer.setTempoInBPM(bpm);
        newTempoFactor=bpm/120;
        sequencer.setTempoFactor(newTempoFactor);//Default tempo is 120bpm --> Tempo factor =1 

         

         sequencer.setLoopStartPoint(100);//Shift the loop start/end by 100 ticks
         sequencer.setLoopEndPoint(seq.getTickLength());
    }    

    public Sequence editEvents() 
    {
        Sequence seq= this.seq;
        try 
        {
            seq = MidiSystem.getSequence(new File("res//myTrack.mid"));
            for (Track track :  seq.getTracks()) 
            {
                for (int i=0; i < track.size(); i++) 
                { 
                    MidiEvent event = track.get(i);
                    event.setTick(event.getTick()+100);  
                }
            }
        } 
        catch (InvalidMidiDataException | IOException ex) 
        {
            Logger.getLogger(MidiHandler.class.getName()).log(Level.SEVERE, null, ex);
        }
        
        return seq;
    }

}

Мой основной класс

public class main 
{
    public static void main(String[] args) 
    {
        try 
        {
            MidiHandler mh = new MidiHandler();
            mh.setAudioTrack("res//myTrack.mid");
            mh.playTrack(120f);
        } 
        catch (SecurityException | InterruptedException ex) 
        {
            Logger.getLogger(main.class.getName()).log(Level.SEVERE, null, ex);
        }
        
    }
}

Ответы

1 jjazzboss Sep 01 2020 at 00:59

Ваш код выглядит нормально.

Начальный ритм, который не синхронизирован, обычно связан с подключенным устройством Midi (в моем случае, когда я использую внешнюю звуковую карту USB Midi). Если возможно, попробуйте использовать другое MidiDevice.

Если вы не можете, обходной путь состоит в том, чтобы сдвинуть все MidiEventsсозданные Sequence, скажем, 4 удара, а затем использовать Sequencer.setLoopEndPoint(long tick)и, Sequencer.setLoopStartPoint(long tick)чтобы запустить цикл в новой начальной точке.

Для изменения темпа после запуска это ошибка JDK. Обходной путь - позвонить Sequencer.setTempoInBPM()сразу после этого Sequencer.start().