直接播放 MidiEventCollection

Playing MidiEventCollection directly

使用NAudio框架,我写了这样的代码来播放一些midi音符:

// Guts of a play note method which takes a cancellation token, a note
// a channel and a duration - CurrentVolume is a property of the class
// that plays the notes
midiOut.Send(MidiMessage.StartNote(note, CurrentVolume, channel).RawData);
try
{
    await Task.Delay(duration, cancellationToken);
}
finally
{
    midiOut.Send(MidiMessage.StopNote(note, CurrentVolume, channel).RawData);
}

这工作正常,但有时渲染中会有一点 skip/delay,我假设它来自 Task.Delay 并不总是准确的。我想做的只是生成一个 midi 集合并将整个集合发送到 midi 输出设备,但我似乎找不到办法做到这一点。我已经达到了生成集合的程度,并且知道如何将其保存到文件中 - 所以如果解决方案是创建一个文件然后以某种方式发送文件,这也是可以接受的。

var collection = new MidiEventCollection(0, 120);
collection.AddEvent(new NoteOnEvent(0, 1, 64, 127, 15), 1);
collection.AddEvent(new NoteOnEvent(15, 1, 65, 127, 15), 1);
collection.AddEvent(new NoteOnEvent(30, 1, 66, 127, 15), 1);
collection.AddEvent(new NoteOnEvent(45, 1, 67, 127, 15), 1);
collection.AddEvent(new NoteOnEvent(60, 1, 68, 127, 15), 1);

collection.PrepareForExport();

有一个 Windows API 可让您发出批量 MIDI 事件(请参阅 midiStreamOut for example) which would be ideal for this scenario but unfortunately NAudio does not contain wrappers for this. NAudio's MIDI capabilities are more focused on reading and writing MIDI files. Your options are either to create the p/invoke wrappers for the MIDI APIs I mentioned or to try a different audio library such as MIDI.NET

使用DryWetMIDI你可以写这个代码:

using Melanchall.DryWetMidi.Common;
using Melanchall.DryWetMidi.Devices;
using Melanchall.DryWetMidi.Core;
using Melanchall.DryWetMidi.Interaction;

// ...

var eventsToPlay = new MidiEvent[]
{
    new NoteOnEvent((SevenBitNumber)100, SevenBitNumber.MaxValue) { Channel = (FourBitNumber)10 },
    new NoteOffEvent((SevenBitNumber)100, SevenBitNumber.MinValue) { Channel = (FourBitNumber)10 },
    // ...
};

using (var outputDevice = OutputDevice.GetByName("Microsoft GS Wavetable Synth"))
using (var playback = new Playback(eventsToPlay, TempoMap.Default, outputDevice))
{
    playback.Play();
}

或者如果你只需要在单通道上播放音符,你可以使用Pattern:

using MusicTheory = Melanchall.DryWetMidi.MusicTheory;
using Melanchall.DryWetMidi.Composing;

// ...

var pattern = new PatternBuilder()
    .Note(MusicTheory.Octave.Get(3).ASharp, length: MusicalTimeSpan.Quarter)
    .Note(MusicTheory.Octave.Get(3).C, length: MusicalTimeSpan.Eighth)
    // ...
    .Build();

using (var outputDevice = OutputDevice.GetByName("Microsoft GS Wavetable Synth"))
{
    pattern.Play(TempoMap.Default, (FourBitNumber)10, outputDevice);
}

请注意,Play 将阻止调用线程,直到播放完所有 MIDI 数据。对于非阻塞播放,使用 Playback class:

Start 方法
var playback = pattern.GetPlayback(TempoMap.Default, (FourBitNumber)10, outputDevice);
playback.Start();

您可以在库文档的 Playback 页面上阅读有关播放 MIDI 数据的更多信息。