我们正在尝试使用javax.sound.midi为大学编写一个简单的吉他英雄克隆。
目前我们可以用音序器播放midi文件,并记录当前播放的midi事件。但是,我们希望显示即将到来的事件(更具体地说,是MIDI消息中指示注释的数据字节)。
我们目前正在使用一个音序器来播放声音和一个接收器,并将声音记录到控制台。但是,它会记录声音播放的确切时刻,而不是即将到来的声音。
在Sequencer类:
public void startSequencer() {
try {
sequencer.open();
Thread.sleep(2000);
sequencer.setTempoInBPM(BPM);
InputStream is = new BufferedInputStream(new FileInputStream(song));
sequencer.setSequence(is);
sequencer.getTransmitter().setReceiver(receiver);
sequencer.start();
while (sequencer.isRunning()) {
Thread.sleep(100);
}
sequencer.close();
System.out.println(receiver.getNoteOnAmount());
} catch (MidiUnavailableException | InterruptedException | IOException | InvalidMidiDataException e) {
throw new RuntimeException(e);
}
}
In Receiver class:
public void send(MidiMessage message, long timeStamp) {
//If the Status is NOTE_ON (144), process the note
if (message.getStatus() == 144) {
System,out.println(message.getMessage()[1]);
}
使用Sequencer.getSequence()
从Sequencer
中检索Sequence
来自Midi文件的所有MidiEvents
都存储在Sequence
中的一个或多个Track
实例中。MidiEvent
是带有刻度位置的MidiMessage
。MidiEvents
按刻度位置排序。
因此,在您的Receiver.send(MidiMessage)
方法中,搜索轨道以找到与接收到的MidiMessage
对应的MidiEvent
。
如果你只有一个轨道,并找到相应的MidiEvent
在索引meIndex
/tickmeTick
,那么下一个MidiEvents
要播放的是在meIndex+1
,meIndex+2
等。如果您有多个音轨,您还需要在其他音轨中搜索标记位置正好在meTick
之后的MidiEvents
。
当然还有很多优化的空间(例如提前预处理Tracks
),这样你就不用每次收到一个通知都花时间重新解析所有的track。