MIDI文件记录在毫秒中多长时间?
我只是想为这两个注释定义毫秒的长度。实时的第一个音符少于一秒钟,第二个音符约为2秒。
当我迭代MIDI事件时,每次获得笔记或记名事件时,我都会在那里取tricks。我100%确定这些三角洲壁虱是正确的,因为我将它们与正常工作的算法进行了比较。
据我所知,如果该文件中的F5密钥在X时间内登录起笔记,则该事件的记录将在Note-Off事件的Deltaticks数量之后触及。因此,这意味着要找到F5密钥的长度的毫秒多长时间,我需要做的就是将Note-Off事件Delta Ticks转换为毫秒。 (我是正确的吗?)
我解析了MIDI文件,所以我有节奏和PPQ,我对此进行了很多搜索,所以直到现在我找到了这个公式:
double bpm = (60000000 / mTempo);
deltaTimesInMilliseconds = deltaTicks * (60000 / (bpm * division));
但是我可能在这里做错了什么,因为Delta时间是毫秒的时间对于F5密钥票据,大约为7000,这意味着7秒。
谢谢您的帮助!
更多文件:
public class MidiParserN {
private static double mTempo;
private static short division;
...
...
private static TrackChunkEvent
parseMTrkEvent() throws Exception {
int deltaTicks = Math.abs(calculateDeltaTicks(getVariableLengthQuantity()));
double deltaTimesInMilliseconds = 0;
int eventType = parseEventType();
int eventNum = -1;
String eventName = "";
if (eventType == EventTypes.MIDI_EVENT.ordinal()) {
eventNum = parseMidiMessage();
eventName = midiEventsTypes[eventNum].toString();
} else if (eventType == EventTypes.SYSEX_EVENT.ordinal()) {
parseSysexEvent();
} else {
eventNum = parseAndExecuteMetaEvent();
eventName = metaEventsTypes[eventNum].toString();
}
if (eventName.equals("NOTE_ON_EVENT") && deltaTimes != 0 && mTempo != 0) {
double bpm = (60000000 / mTempo);
deltaTimesInMilliseconds = deltaTicks * (60000 / (bpm * division));
log("DELTA_TIMES_MILLISECONDS", Double.toString(deltaTimesInMilliseconds));
}
else if (eventName.equals("NOTE_OFF_EVENT") && deltaTimes != 0 && mTempo != 0) {
double bpm = (60000000 / mTempo);
deltaTimesInMilliseconds = deltaTicks * (60000 / (bpm * division));
log("DELTA_TIMES_MILISECONDS", Double.toString(deltaTimesInMilliseconds));
}
return new TrackChunkEvent(eventNum, eventName);
}
...
...
}
在节奏事件上更新节奏:
if (firstByteOfMetaEvent == 0x51 && secondByteOfMetaEvent == 3) {
int tempo = byteArrayToInt(parseMetaEventSetTempo());
mTempo = tempo;
return MetaEventsTypes.SET_TEMPO.ordinal();
}
MIDI文件:
4d 54 68 64 00 00 00 06 00 01 00 01 00 02 01 80 4D 54 72 6B 00 00 00 00 00 00 13 00 ff 58 04 04 04 04 04 04 04 04 02 18 08 00 ff 51 03 08 52 00 33 00 ff 03 15 45 6C 65 63 2E 20 50 69 69 61 6E 6F 20 28 43 6C 61 73 61 73 69 69 69 63 29 00 C0 00 C0 00 82 20 90 3C 32 60 80 32 60 3C 00 83 00 83 00 83 00 83 00 90 41 32 8D 40 40 80 40 80 41 00 00 00 00 00 00 00 FF 2F 00
So I have this very simple MIDI file:
I simply want do define the length in milliseconds for these 2 notes. In real time the first note is less than a second and the second note is about 2 seconds.
As I iterate the MIDI events, every time I get a note-on or note-off event I fetch there delta ticks. I'm 100% sure these delta ticks are correct because I compared them with an algorithm that works properly.
As far as I understand, if the F5 key in this file hits note-on in an X time, the note-off of that event will hit after the number of deltaTicks of the note-off event. So that means that to find how long in milliseconds the length of the F5 key is, all I need to do is to convert the note-off event delta ticks to milliseconds. (Am I correct?)
I parsed the MIDI file so I have the tempo and the PPQ, and I searched a lot about this so till now I found this formula:
double bpm = (60000000 / mTempo);
deltaTimesInMilliseconds = deltaTicks * (60000 / (bpm * division));
but I'm probably doing something wrong here because the delta time in milliseconds for the F5 key note-off event is about 7000, which means 7 seconds.
Thank's for any help!
More of the file:
public class MidiParserN {
private static double mTempo;
private static short division;
...
...
private static TrackChunkEvent
parseMTrkEvent() throws Exception {
int deltaTicks = Math.abs(calculateDeltaTicks(getVariableLengthQuantity()));
double deltaTimesInMilliseconds = 0;
int eventType = parseEventType();
int eventNum = -1;
String eventName = "";
if (eventType == EventTypes.MIDI_EVENT.ordinal()) {
eventNum = parseMidiMessage();
eventName = midiEventsTypes[eventNum].toString();
} else if (eventType == EventTypes.SYSEX_EVENT.ordinal()) {
parseSysexEvent();
} else {
eventNum = parseAndExecuteMetaEvent();
eventName = metaEventsTypes[eventNum].toString();
}
if (eventName.equals("NOTE_ON_EVENT") && deltaTimes != 0 && mTempo != 0) {
double bpm = (60000000 / mTempo);
deltaTimesInMilliseconds = deltaTicks * (60000 / (bpm * division));
log("DELTA_TIMES_MILLISECONDS", Double.toString(deltaTimesInMilliseconds));
}
else if (eventName.equals("NOTE_OFF_EVENT") && deltaTimes != 0 && mTempo != 0) {
double bpm = (60000000 / mTempo);
deltaTimesInMilliseconds = deltaTicks * (60000 / (bpm * division));
log("DELTA_TIMES_MILISECONDS", Double.toString(deltaTimesInMilliseconds));
}
return new TrackChunkEvent(eventNum, eventName);
}
...
...
}
Updating tempo on tempo event:
if (firstByteOfMetaEvent == 0x51 && secondByteOfMetaEvent == 3) {
int tempo = byteArrayToInt(parseMetaEventSetTempo());
mTempo = tempo;
return MetaEventsTypes.SET_TEMPO.ordinal();
}
The MIDI file:
4d 54 68 64 00 00 00 06 00 01 00 02 01 80 4d 54 72 6b 00 00 00 13 00 ff 58 04 04 02 18 08 00 ff 51 03 08 52 ae 00 ff 2f 00 4d 54 72 6b 00 00 00 33 00 ff 03 15 45 6c 65 63 2e 20 50 69 61 6e 6f 20 28 43 6c 61 73 73 69 63 29 00 c0 00 82 20 90 3c 32 60 80 3c 00 83 00 90 41 32 8d 40 80 41 00 00 ff 2f 00
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
首先,您的公式应在简单的情况下起作用。请参阅我的答案 “>几秒钟内的MIDI时间戳)。那里的公式是针对微秒的,因此我们可以将其除以1000,我们将获得您的毫秒计算。
好吧,我已经用我的库检查了您的文件。我要注意的第一件事是你对了,第二个音符大约是2秒长:
现在让我们将您的公式应用于tick中的第二个音符的长度:
i.sstatic.net/q2fvw.png“ rel =“ nofollow noreferrer ...这显然是错误的数字。但是修复是超级气味的。在这一行中,您的格子问题的问题是:
您将
bpm
定义为double
,但您不使用double
。实际上,您有整数划分,因此您失去了精度,从而导致不良结果。让我们检查新代码:太好了!我们现在有正确的号码。但是我在这里看不到7000。我想您的VLQ计算是错误的。我的意思是这行:
在这里滴答滴答的第二个音符的正确时间和长度,请检查您的数字:
但在通常的情况下,您的方法是错误的。它将在大多数MIDI文件上失败。您的公式使用两个主要假设:
这两个假设在现实世界中都行不通。要正确计算MS中的注释的长度:
如果没有节奏更改,则时间的计算很简单。您只需要计算事件的绝对时间,概括了以前事件的所有时间段,然后应用您的公式即可。
但是,随着节奏变化的时间计算需要更复杂的逻辑。您需要观看每一个恒定速度的范围,沿此过程中的MS中的每一个长度概括到MIDI事件的过程中落在其中一个范围内。
您在Java上,所以我不能为该任务推荐适当的工具。对于.NET,您可以使用我的 drywetmidi 库,该库支持时间和长度的不同形式,并正确处理了操作节奏节奏的时间。
First of all, your formula should work in your simple case. Please see my answer to another question (Midi timestamp in seconds). The formula there is for microseconds, so we can divide it by 1000 and we'll get your calculations for milliseconds.
Well, I've checked your file with my library. First thing I should to notice is you're right, second note is about 2 seconds long:
Now let's apply your formula to the second note's length in ticks:
Well...it's obviously wrong number. But fix is supereasy. The problem with your formala in this line:
You defined
bpm
asdouble
, but you don't usedouble
. In fact you have integer division and thus you lose precision which leads to bad result. Let's check new code:Great! We have right number now. But I don't see 7000 here. I suppose your VLQ calculations are wrong. I mean this line:
Here correct time and length of the second note in ticks so please check your numbers:
BUT your approach is wrong in general case. It will fail on most MIDI files. Your formula use two main assumptions:
Both assumptions won't work in real world. To correctly calculate the length of a note in ms you need:
Calculation of time is simple if there are no tempo changes. You just need to calculate absolute time of an event summing all delta-times of previous events and then apply your formula.
But calculation of time with tempo changes requires more complex logic. You need to watch each range of constant tempo summing the length of each one in ms along the way until the time of a MIDI event falls in one of those ranges.
You're on Java so I can't recommend appropriate tool for that task. For .NET you can use my DryWetMIDI library which supports different formats of times and lengths correctly handling tempo changes.