Định thời gian trình tự Java Midi đã tắt

Aug 31 2020

Trong vài ngày qua, tôi đã cố gắng viết mã một máy đếm nhịp trong Java để thực hành. Tôi đã tạo một nhịp midi 4/4 đơn giản mà chương trình phát bằng cách sử dụng thư viện javax.sound.midi .
Vấn đề chính của tôi là trình tự sắp xếp xuất hiện để chơi nhịp đầu tiên không đúng thời gian. Nếu tôi đặt trình tự thành vòng lặp, điều này chỉ xảy ra trên vòng lặp đầu tiên. Một lưu ý nhỏ là nếu tôi thay đổi bpm của bản nhạc, nó sẽ đặt lại sau vòng lặp đầu tiên.
Ngoài ra, tôi đã thử nhiều tệp midi đề phòng xảy ra sự cố với tệp midi mà tôi đã tạo nhưng tất cả các thử nghiệm của tôi đều có kết quả giống nhau.
Đây là mã của tôi xử lý phát lại 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;
    }

}

Lớp học chính của tôi

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);
        }
        
    }
}

Trả lời

1 jjazzboss Sep 01 2020 at 00:59

Mã của bạn có vẻ ổn.

Nhịp đầu tiên bị lệch thời gian thường là do thiết bị Midi được kết nối ra ngoài (trong trường hợp của tôi khi tôi sử dụng card âm thanh Midi USB bên ngoài). Hãy thử với một MidiDevice khác nếu bạn có thể.

Nếu bạn không thể, cách giải quyết là chuyển tất cả các nhịp MidiEventsđã tạo Sequencebằng 4 nhịp, sau đó sử dụng Sequencer.setLoopEndPoint(long tick)và Sequencer.setLoopStartPoint(long tick)để bắt đầu vòng lặp tại điểm bắt đầu mới.

Đối với sự thay đổi nhịp độ sau khi bắt đầu, đó là một lỗi JDK. Cách giải quyết là gọi Sequencer.setTempoInBPM()ngay sau đó Sequencer.start().