为啥 MIDI 音符计时不起作用?

Posted

技术标签:

【中文标题】为啥 MIDI 音符计时不起作用?【英文标题】:Why won't midi note timings work?为什么 MIDI 音符计时不起作用? 【发布时间】:2012-12-04 18:04:24 【问题描述】:

我正在创建一个 MIDI 钢琴卷帘编辑器。 Note 类创建并包含一个 NOTE_ON 对象和一个关联的 NOTE_OFF 对象以及一个矩形,用户在屏幕上操纵它来操纵音符的音高、时间和持续时间。下面是这个类的代码减去矩形的代码。我不知道为什么它不能正常工作。测试程序会创建五个这样的 Note 对象并将它们显示在钢琴卷帘网格上,并且它们可以正常播放。当一个矩形被向上或向下拖动时,改变音高的方法也可以正常工作。但是当我调用更改时间或持续时间的方法时,笔记开始出现异常。首先,当它们被移动时,它们不会在被告知的地方演奏,然后如果音符被拖到顶部或相互延伸,移动的音符将阻止其下方的音符播放。我作为 arg 发送到这些方法的移动单位设置为 16,因此音符将始终捕捉到第 16 拍位置。谁能发现我的代码有什么问题?

public class Note 
MidiEvent noteOn;
MidiEvent noteOff;

private int channel;
private int pitch;
private int vel;

// Constructor calls methods to create NOTE_ON, NOTE_OFF, and graphical rectangle
public Note(MidiMessage on, long tickPos) 
    noteOn = createNoteOn(on, tickPos);
    ShortMessage shortMessage = (ShortMessage) on;
    noteOff = createNoteOff(shortMessage.getChannel(), shortMessage.getData1(), tickPos);


public MidiEvent createNoteOn(MidiMessage on, long tickPos) 
    noteOn = new MidiEvent(on, tickPos);
    return noteOn;


public MidiEvent createNoteOff(int chan, int pitch, long tickPos) 
    try 
        noteOff = new MidiEvent(new ShortMessage(ShortMessage.NOTE_OFF, chan, pitch, 0), tickPos + 2);
     catch (InvalidMidiDataException e) 
            e.printStackTrace();
    
    return noteOff;


// Method for moving musical pitch of this note up or down for both NOTE_ON and NOTE_OFF
public void setPitch(int pitchUpOrDown) 
    MidiMessage message = noteOn.getMessage();
    ShortMessage shortMessage = (ShortMessage) message;
        channel = shortMessage.getChannel();
        pitch = shortMessage.getData1();
        vel = shortMessage.getData2();
        try 
            shortMessage.setMessage(ShortMessage.NOTE_ON, channel, pitch + pitchUpOrDown, vel);
         catch (InvalidMidiDataException e) 
            e.printStackTrace();
        
        message = noteOff.getMessage();
        shortMessage = (ShortMessage) message;
        try 
            shortMessage.setMessage(ShortMessage.NOTE_OFF, channel, pitch + pitchUpOrDown, 0);
         catch (InvalidMidiDataException e) 
            e.printStackTrace();
        


// Moves entire note without changing duration of note
public void shiftLocation(int diff) 
    noteOn.setTick(noteOn.getTick() + diff);
    noteOff.setTick(noteOff.getTick() + diff);


// Moves start time of note while leaving end time in place, changes duration of note
public void setStartTime(long start) 
    noteOn.setTick(start);


// Moves end time of note while leaving start time in place, changes duration of note
public void setDuration(int duration) 
    noteOff.setTick(noteOff.getTick() + duration);

MIDI 音序器和合成器:

import javax.sound.midi.*;

public class MusicEngine 
Sequencer sequencer;
Sequence sequence;
Synthesizer synthesizer;
Track track;
MidiEvent event;
// PPQ, or ticks per beat
int ticksPerBeat = 16;
int tempoBPM = 120;

// Constructor
public MusicEngine()       
    createMidi();


// Get sequencer and sequence and track
public void createMidi() 
    try 
        sequencer =  MidiSystem.getSequencer();
        if (sequencer == null) 
            System.out.println("Cannot get a sequencer");
            System.exit(0);
        
        sequencer.open();

        // If sequencer is not the same as synthesizer, link the two 
        // (required in J2SE 5.0)
        if (!(sequencer instanceof Synthesizer)) 
            System.out.println("Linking the sequencer to the synthesizer");
            synthesizer = MidiSystem.getSynthesizer();
            synthesizer.open();
            Receiver synthReceiver = synthesizer.getReceiver();
            Transmitter seqTransmitter = sequencer.getTransmitter();
            seqTransmitter.setReceiver(synthReceiver);
         else 
            synthesizer = (Synthesizer)sequencer;
        sequence = new Sequence(Sequence.PPQ, ticksPerBeat);
        track = sequence.createTrack();
        sequencer.setTempoInBPM(tempoBPM);
     catch(MidiUnavailableException e) 
        System.out.println("No sequencer available");
        System.exit(0);
     catch(Exception e) 
        e.printStackTrace();
        System.exit(0);
    



// Create an individual MIDI event
public MidiEvent createEvent(int command, int channel, int one, int two, int tick) 
    event = null;
    try 
        ShortMessage a = new ShortMessage();
        a.setMessage(command, channel, one, two);
        event = new MidiEvent(a, tick);
     catch(InvalidMidiDataException e) 
        e.printStackTrace();
    
    return event;


public void add(MidiEvent event) 
    track.add(event);


public void playSong(int tempo) 
    try 
        sequencer.setSequence(sequence);
    
    catch (InvalidMidiDataException e) 
        e.printStackTrace();
    

    sequencer.start();


public void stopSong() 
    sequencer.stop();


【问题讨论】:

可能有助于发布您用于播放序列的代码?此外,您能否首先将事物隔离到一个简单的测试用例中,在该测试用例中创建带有几个音符的音轨,更改其中一个音符的时间/持续时间,然后播放序列? 我添加了合成器和音序器引擎。我得走开一会儿。当我回来时,我会尝试简化代码,并从我已经开始编写的更详细的代码中提取一个简单的测试代码。 【参考方案1】:

我可能在这里误解了您的代码,但您似乎只是在 setDuration() 方法中增加了注释的长度。不应该是这个样子吗?

public void setDuration(int duration) 
    noteOff.setTick(noteOn.getTick() + duration);

【讨论】:

是的,这就是这个方法的目的:增加音符的长度。就在它上面是一个单独的方法来实际移动音符(setStartTime)。我仍在努力提供代码的精简形式,但我也遇到了使该代码工作的问题。这是我的第一个真正的项目,所以在我弄清楚如何做这些事情的过程中,请您耐心等待。

以上是关于为啥 MIDI 音符计时不起作用?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 Access VBA 下拉方法不起作用?

为啥我的一个 segues 不起作用?

快速无效计时器不起作用

刷新 JComponent 倒计时不起作用

QWebEngineView:计时器在可见时不起作用

requirejs 计时问题 - 一些引导 js 不起作用