所以我有這個非常簡單的 MIDI 檔案:

我只是想為這兩個音符定義以毫秒為單位的長度。實時第一個音符不到一秒,第二個音符大約是 2 秒。
當我迭代 MIDI 事件時,每次我得到一個音符開或音符關事件時,我都會在那里獲取增量滴答聲。我 100% 確定這些增量刻度是正確的,因為我將它們與正常作業的演算法進行了比較。
據我了解,如果此檔案中的 F5 鍵在 X 時間內擊中音符,則該事件的音符關閉將在音符關閉事件的 deltaTicks 數之后擊中。所以這意味著要找到 F5 鍵的長度以毫秒為單位,我需要做的就是將音符關閉事件增量刻度轉換為毫秒。(我對么?)
我決議了 MIDI 檔案,所以我有了速度和 PPQ,我搜索了很多關于這個,所以直到現在我找到了這個公式:
double bpm = (60000000 / mTempo);
deltaTimesInMilliseconds = deltaTicks * (60000 / (bpm * division));
但我可能在這里做錯了,因為 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 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 36 00 4 0 0 50 7 0 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 01 0 8d 04
作為影像:

uj5u.com熱心網友回復:
首先,您的公式應該適用于您的簡單情況。請參閱我對另一個問題的回答(
現在讓我們將您的公式應用于第二個音符的刻度長度:

嗯……這顯然是錯誤的數字。但是修復超級簡單。您在這一行中的正式問題:
double bpm = (60000000 / mTempo);
您定義bpm為double,但您不使用double. 事實上,你有整數除法,因此你失去了導致糟糕結果的精度。讓我們檢查一下新代碼:

偉大的!我們現在有正確的號碼。但是我在這里看不到7000。我想你的 VLQ 計算是錯誤的。我的意思是這一行:
int deltaTicks = Math.abs(calculateDeltaTicks(getVariableLengthQuantity()));
這里正確的時間和第二個音符的長度,所以請檢查你的數字:

但是在一般情況下,您的方法是錯誤的。它會在大多數 MIDI 檔案上失敗。您的公式使用兩個主要假設:
- 檔案中沒有速度變化;
- Note On 和相應的 Note Off 之間沒有其他 MIDI 事件。
這兩種假設在現實世界中都行不通。要正確計算以毫秒為單位的音符長度,您需要:
- 以毫秒為單位計算 Note On 事件的時間;
- 以毫秒為單位計算相應的 Note Off 事件的時間;
- 拿那些時間之間的差異。
如果沒有節奏變化,時間計算很簡單。您只需要計算事件的絕對時間,將先前事件的所有增量時間相加,然后應用您的公式。
但是計算節奏變化的時間需要更復雜的邏輯。您需要沿途觀察恒定速度的每個范圍,將每個范圍的長度(以毫秒為單位)相加,直到 MIDI 事件的時間落在其中一個范圍內。
您使用的是 Java,所以我不能為該任務推薦合適的工具。對于 .NET,您可以使用我的DryWetMIDI庫,它支持正確處理速度變化的不同時間和長度格式。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/467486.html
下一篇:如何映射物體類中的特定列
