Reputation: 17428
Using the NAudio framework, I have written code like this to play some midi notes:
// 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);
}
And this is working okay, but every so often there is a little skip/delay in the rendering which I am assuming is coming from the Task.Delay not always being exact. What I would like to do is just generate a midi collection and send the whole collection to the midi out device, but I can't seem to find a way to do that. I have gotten as far as generating a collection and I know how to save that to a file - so if the solution is to make a file and then somehow send the file, this is also acceptable.
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();
Upvotes: 1
Views: 743
Reputation: 2128
Using DryWetMIDI you can write this code:
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();
}
Or if you just need to play notes on single channel, you can use 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);
}
Note that Play
will block calling thread until all MIDI data played. For non-blocking playback use Start
method of the Playback
class:
var playback = pattern.GetPlayback(TempoMap.Default, (FourBitNumber)10, outputDevice);
playback.Start();
You can read more about playing MIDI data on the Playback page of the library docs.
Upvotes: 0
Reputation: 49492
There is a Windows API that lets you emit batches of MIDI events (see 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
Upvotes: 1