I am trying to write a MIDI file in C#. I am using Sanford MIDI Toolkit. The following is a code snippet I am using to write NoteOn
and NoteOff
events.
private static void InsertNoteOn(Track t, int pitch, int velocity, int position, int duration, int channel)
{
ChannelMessageBuilder builder = new ChannelMessageBuilder();
builder.Command = ChannelCommand.NoteOn;
builder.Data1 = pitch;
builder.Data2 = velocity;
builder.MidiChannel = channel;
builder.Build();
t.Insert(position, builder.Result);
}
private static void InsertNoteOff(Track t, int pitch, int velocity, int position, int duration, int channel)
{
ChannelMessageBuilder builder = new ChannelMessageBuilder();
builder.Command = ChannelCommand.NoteOff;
builder.Data1 = pitch;
builder.Data2 = velocity;
builder.MidiChannel = channel;
builder.Build();
t.Insert((position + duration), builder.Result);
}
First I insert all NoteOn
events for all notes of the track, then I insert all NoteOff
events of the track.
The approach works most of the time. However, sometimes the resulting MIDI file is rendered incorrectly. The problem happens sometimes when two notes of the same pitch are written after one another. The first note will render with its length equaling to both notes' lengths, and the second note will have length zero.
My assumption is that the NoteOff
event of the first note is interpreted as NoteOff
of the second note and vice versa.
I have tried the following variations:
NoteOn
and NoteOff
events in the temporal order in the track
NoteOn
events in temporal order and then add all NoteOff
events in temporal orderNoteOff
events in temporal order and then add all NoteOn
events in temporal orderOnly the last approach works, but notes have shorter length, which does not fix the problem.
Is there a fix to this? Is there a specific order which NoteOn
and NoteOff
events should take in the track? Is there a specific order in which the track insertion method should be called?
EDIT: The problem occurs in the following case:
较高的音符从C移到C#以获得可见性。这些应该是两个相同长度的音符,而是一个用两个音符和#39;长度和另一个长度为零。
答案 0 :(得分:1)
在MIDI文件中,多个事件可能具有相同的时间戳。在这种情况下,它们通过线路以与文件中写入的顺序相同的顺序发送。
Sanford MIDI工具包仅使用时间戳来指定事件的位置,而不记录如何处理具有相同时间戳的多个事件。
为确保您的音符关闭事件发生在音符开启事件之前,您必须使用不同的时间戳,即减少音符的长度。 (要降低实际差异,请增加时间戳分辨率。)
答案 1 :(得分:1)
我通过在将所有事件插入轨道之前对所有事件进行排序来解决问题。我使用了以下方法。
x = x;
该方法以下列方式使用。
private static void InsertNote(int pitch, int velocity, int position, int duration, int channel, ref List<Tuple<int, bool, ChannelMessage>> messages)
{
ChannelMessageBuilder builder = new ChannelMessageBuilder();
builder.Command = ChannelCommand.NoteOn;
builder.Data1 = pitch;
builder.Data2 = velocity;
builder.MidiChannel = channel;
builder.Build();
messages.Add(new Tuple<int, bool, ChannelMessage>(position, true, builder.Result));
builder.Command = ChannelCommand.NoteOff;
builder.Data1 = pitch;
builder.Data2 = velocity;
builder.MidiChannel = channel;
builder.Build();
messages.Add(new Tuple<int, bool, ChannelMessage>(position + duration, false, builder.Result));
}