Java Midi 定序器计时已关闭

Posted

技术标签:

【中文标题】Java Midi 定序器计时已关闭【英文标题】:Java Midi sequencer timing is off 【发布时间】:2020-08-31 12:29:30 【问题描述】:

在过去的几天里,我一直在尝试用 Java 编写节拍器来进行练习。我制作了一个简单的 4/4 MIDI 节拍,程序使用 javax.sound.midi 库播放该节拍。 我的主要问题是音序器似乎播放了第一个节拍关闭时间。如果我将序列设置为循环,则这只发生在第一个循环上。附带说明一下,如果我更改曲目的 bpm,它会在第一个循环后重置。另外,我尝试了多个 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】:

您的代码看起来不错。

不定时的初始节拍通常是由于连接了 Midi 设备(在我使用外部 USB Midi 声卡时)。如果可以,请尝试使用其他 MidiDevice。

如果不能,解决方法是将创建的Sequence 的所有MidiEvents 移动4 个节拍,然后使用Sequencer.setLoopEndPoint(long tick)Sequencer.setLoopStartPoint(long tick) 使循环从新起点开始。

对于启动后的节奏变化,这是一个 JDK 错误。解决方法是在Sequencer.start() 之后立即调用Sequencer.setTempoInBPM()

【讨论】:

感谢您的回复,我设法通过将所有 midi 事件向前移动 100 个刻度来克服时间问题(似乎足够了)。我还设法通过调用sequencer.setTempoFactor() 来解决节奏问题,因为sequencer.setTempoInBPM() 对我不起作用。为了计算新的速度因子,我将 bpm 的新值除以 120(因为我的 midi 文件的基本速度为 120)。似乎工作正常。 好。如果您需要读取各种 Midi 文件,您需要通过在读取序列中搜索 Midi Set Tempo 元消息来获取速度。

以上是关于Java Midi 定序器计时已关闭的主要内容,如果未能解决你的问题,请参考以下文章

定序器接收器时间戳始终输出 -1

.NET 中的精确计时

ALSA 音序器:使用高速 MIDI 避免输入缓冲区溢出

Java MIDI 音序器永无止境

BurpSuite—-Sequencer模块(定序器)

BurpSuite系列----Sequencer模块(定序器)