如何将从音轨 1 提取的速度应用到 MIDI 文件的其他音轨?
How to apply the tempo extracted from track 1 to other tracks of a MIDI file?
我正在尝试从 MIDI 文件的第一首曲目中提取旋律的速度,并将其应用于包含音符事件的其余曲目。
基本上我一直在尝试在 noteOn()
消息之后替换 Thread.sleep()
方法,该消息每次都以固定的时间间隔播放音符。因此我失去了整首歌曲的节奏。
我成功地从先前提出的问题 How does Midi TEMPO message apply to other tracks? 的第一首曲目中提取了速度信息,但无法将其应用于其余曲目。
是否有特定的功能可以做到这一点?我尝试搜索但找不到。
这是我的代码供参考。
int trackNumber = 0;
for (Track track : sequence.getTracks()) {
trackNumber++;
System.out.println();
for (int i=0; i < track.size(); i++) {
MidiEvent event = track.get(i);
MidiMessage message = event.getMessage();
if (message instanceof MetaMessage) {
MetaMessage mm = (MetaMessage) message;
if(mm.getType()==SET_TEMPO){
byte[] data = mm.getData();
int tempo = (data[0] & 0xff) << 16 | (data[1] & 0xff) << 8 | (data[2] & 0xff);
int bpm = 60000000 / tempo;
}
}
if (message instanceof ShortMessage) {
ShortMessage sm = (ShortMessage) message;
if (sm.getCommand() == NOTE_ON) {
int key = sm.getData1();
int velocity = sm.getData2();
channels[0].noteOn(key,velocity);
Thread.sleep(280);//Pays all the note for fixed duration
}
else if (sm.getCommand() == NOTE_OFF) {
int key = sm.getData1();
int velocity = sm.getData2();
channels[0].noteOff(key);
}
}
}
System.out.println();
}
}
多个轨道中的事件同时发生,因此您不能单独处理轨道。
你必须
- 将所有事件放入一个列表中,并按时间戳对它们进行排序(但要确保具有相同时间戳的事件保持顺序,因此使用稳定的排序算法);或
- 每个轨道都有一个当前位置,在确定下一个事件时,在所有轨道中搜索具有最小时间戳的尚未使用的事件。
请注意,播放过程中速度可能会发生变化,因此单个 bpm
值是不够的。参见 and How can I parse a tempo of midi using Java?。
我正在尝试从 MIDI 文件的第一首曲目中提取旋律的速度,并将其应用于包含音符事件的其余曲目。
基本上我一直在尝试在 noteOn()
消息之后替换 Thread.sleep()
方法,该消息每次都以固定的时间间隔播放音符。因此我失去了整首歌曲的节奏。
我成功地从先前提出的问题 How does Midi TEMPO message apply to other tracks? 的第一首曲目中提取了速度信息,但无法将其应用于其余曲目。
是否有特定的功能可以做到这一点?我尝试搜索但找不到。
这是我的代码供参考。
int trackNumber = 0;
for (Track track : sequence.getTracks()) {
trackNumber++;
System.out.println();
for (int i=0; i < track.size(); i++) {
MidiEvent event = track.get(i);
MidiMessage message = event.getMessage();
if (message instanceof MetaMessage) {
MetaMessage mm = (MetaMessage) message;
if(mm.getType()==SET_TEMPO){
byte[] data = mm.getData();
int tempo = (data[0] & 0xff) << 16 | (data[1] & 0xff) << 8 | (data[2] & 0xff);
int bpm = 60000000 / tempo;
}
}
if (message instanceof ShortMessage) {
ShortMessage sm = (ShortMessage) message;
if (sm.getCommand() == NOTE_ON) {
int key = sm.getData1();
int velocity = sm.getData2();
channels[0].noteOn(key,velocity);
Thread.sleep(280);//Pays all the note for fixed duration
}
else if (sm.getCommand() == NOTE_OFF) {
int key = sm.getData1();
int velocity = sm.getData2();
channels[0].noteOff(key);
}
}
}
System.out.println();
}
}
多个轨道中的事件同时发生,因此您不能单独处理轨道。
你必须
- 将所有事件放入一个列表中,并按时间戳对它们进行排序(但要确保具有相同时间戳的事件保持顺序,因此使用稳定的排序算法);或
- 每个轨道都有一个当前位置,在确定下一个事件时,在所有轨道中搜索具有最小时间戳的尚未使用的事件。
请注意,播放过程中速度可能会发生变化,因此单个 bpm
值是不够的。参见