简体   繁体   English

Java Midi 音序器定时关闭

[英]Java Midi sequencer timing is off

For the past few days, I've been trying to code a metronome in Java for practice.这几天一直在尝试在Java中编写一个节拍器进行练习。 I have made a simple 4/4 midi beat that the program plays with the use of javax.sound.midi library.我制作了一个简单的 4/4 midi 节拍,该程序使用javax.sound.midi库进行播放。
My main issue is that the sequencer appears to play the first beat off-time.我的主要问题是音序器似乎在关闭时间播放第一拍。 If I set the sequence to loop, this occurs only the on the first loop.如果我将序列设置为循环,这只会在第一个循环中发生。 On a side note if I change the track's bpm, it resets after the first loop.附带说明一下,如果我更改曲目的 bpm,它会在第一个循环后重置。
Also, I have tried multiple midi files just in case there was an issue with the midi file that I created but all my tests had the same results.此外,我尝试了多个 midi 文件,以防万一我创建的 midi 文件出现问题,但我的所有测试都有相同的结果。
Here is my code that handles the midi playback:这是我处理 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;
    }

}

My main class我的主 class

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

Your code looks OK.您的代码看起来不错。

The initial beat which is off-timing is usually due to the connected out Midi device (in my case when I use my external USB Midi soundcard).不定时的初始节拍通常是由于连接的 Midi 设备(在我的情况下,当我使用外部 USB Midi 声卡时)。 Try with a different MidiDevice if you can.如果可以,请尝试使用不同的 MidiDevice。

If you can't, the workaround is to shift all the MidiEvents of the created Sequence by say 4 beats, then use Sequencer.setLoopEndPoint(long tick) and Sequencer.setLoopStartPoint(long tick) to make loop start at the new starting point.如果不能,解决方法是将创建的Sequence的所有MidiEvents移动 4 个节拍,然后使用Sequencer.setLoopEndPoint(long tick)Sequencer.setLoopStartPoint(long tick)使循环从新的起点开始。

For the tempo change after start, it's a JDK bug.对于开始后的节奏变化,这是一个 JDK 错误。 The workaround is to call Sequencer.setTempoInBPM() right after Sequencer.start() .解决方法是在Sequencer.setTempoInBPM() Sequencer.start()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM