Freigeben über


MIDI

In diesem Artikel erfahren Sie, wie Sie MIDI-Geräte (Musical Instrument Digital Interface) aufzählen und MIDI-Nachrichten von einer universellen Windows-App senden und empfangen. Windows 10 unterstützt MIDI über USB (klassenkonforme und proprietäre Treiber), MIDI über Bluetooth LE (Windows 10 Anniversary Edition und höher) und über frei verfügbare Drittanbieterprodukte, MIDI über Ethernet und routingfähiges MIDI.

Aufzählen von MIDI-Geräten

Fügen Sie vor dem Aufzählen und Verwenden von MIDI-Geräten dem Projekt die folgenden Namespaces hinzu.

using Windows.Devices.Enumeration;
using Windows.Devices.Midi;
using System.Threading.Tasks;

Fügen Sie Ihrer XAML-Seite ein ListBox-Steuerelement hinzu, mit dem der Benutzer eines der an das System angeschlossenen MIDI-Eingabegeräte auswählen kann. Fügen Sie eine weitere hinzu, um die MIDI-Ausgabegeräte auflisten zu können.

<ListBox x:Name="midiInPortListBox" SelectionChanged="midiInPortListBox_SelectionChanged"/>
<ListBox x:Name="midiOutPortListBox" SelectionChanged="midiOutPortListBox_SelectionChanged"/>

Die DeviceInformation-Klasse der FindAllAsync-Methode wird verwendet, um viele verschiedene Gerätetypen auflisten, die von Windows erkannt werden. Um anzugeben, dass die Methode nur MIDI-Eingabegeräte finden soll, verwenden Sie die von MidiInPort.GetDeviceSelector zurückgegebene Auswahlzeichenfolge. FindAllAsync gibt eine DeviceInformationCollection zurück, die ein DeviceInformation für jedes MIDI-Eingabegerät enthält, das beim System registriert ist. Wenn die zurückgegebene Auflistung keine Elemente enthält, sind keine MIDI-Eingabegeräte verfügbar. Wenn in der Auflistung Elemente vorhanden sind, durchlaufen Sie die DeviceInformation-Objekte, und fügen Sie dem ListBox-Listenfeld des MIDI-Eingabegeräts den Namen jedes Geräts hinzu.

private async Task EnumerateMidiInputDevices()
{
    // Find all input MIDI devices
    string midiInputQueryString = MidiInPort.GetDeviceSelector();
    DeviceInformationCollection midiInputDevices = await DeviceInformation.FindAllAsync(midiInputQueryString);

    midiInPortListBox.Items.Clear();

    // Return if no external devices are connected
    if (midiInputDevices.Count == 0)
    {
        this.midiInPortListBox.Items.Add("No MIDI input devices found!");
        this.midiInPortListBox.IsEnabled = false;
        return;
    }

    // Else, add each connected input device to the list
    foreach (DeviceInformation deviceInfo in midiInputDevices)
    {
        this.midiInPortListBox.Items.Add(deviceInfo.Name);
    }
    this.midiInPortListBox.IsEnabled = true;
}

Das Aufzählen von MIDI-Ausgabegeräten funktioniert genauso wie das Aufzählen von Eingabegeräten, mit der Ausnahme, dass Sie beim Aufrufen von FindAllAsync die von MidiOutPort.GetDeviceSelector zurückgegebene Auswahlzeichenfolge angeben sollten.

private async Task EnumerateMidiOutputDevices()
{

    // Find all output MIDI devices
    string midiOutportQueryString = MidiOutPort.GetDeviceSelector();
    DeviceInformationCollection midiOutputDevices = await DeviceInformation.FindAllAsync(midiOutportQueryString);

    midiOutPortListBox.Items.Clear();

    // Return if no external devices are connected
    if (midiOutputDevices.Count == 0)
    {
        this.midiOutPortListBox.Items.Add("No MIDI output devices found!");
        this.midiOutPortListBox.IsEnabled = false;
        return;
    }

    // Else, add each connected input device to the list
    foreach (DeviceInformation deviceInfo in midiOutputDevices)
    {
        this.midiOutPortListBox.Items.Add(deviceInfo.Name);
    }
    this.midiOutPortListBox.IsEnabled = true;
}

Erstellen einer Geräteüberwachungshilfsklasse

Der Windows.Devices.Enumeration-Namespace stellt deviceWatcher bereit, der Ihre App benachrichtigen kann, wenn Geräte aus dem System hinzugefügt oder entfernt werden oder wenn die Informationen für ein Gerät aktualisiert werden. Da MIDI-fähige Apps in der Regel sowohl an Eingabe- als auch an Ausgabegeräten interessiert sind, erstellt dieses Beispiel eine Hilfsklasse, die das DeviceWatcher-Muster implementiert, sodass derselbe Code sowohl für MIDI-Eingabe- als auch für MIDI-Ausgabegeräte verwendet werden kann, ohne dass eine Duplizierung erforderlich ist.

Fügen Sie Ihrem Projekt eine neue Klasse hinzu, die als Geräteüberwachung dienen soll. In diesem Beispiel heißt die Klasse "MyMidiDeviceWatcher". Der restliche Code in diesem Abschnitt wird verwendet, um die Hilfsklasse zu implementieren.

Fügen Sie der Klasse einige Membervariablen hinzu:

  • Ein DeviceWatcher-Objekt , das auf Geräteänderungen überwacht.
  • Eine Geräteauswahlzeichenfolge, die die MIDI-Auswahlzeichenfolge für eine Instanz und die MIDI-Out-Portauswahlzeichenfolge für eine andere Instanz enthält.
  • Ein ListBox-Steuerelement , das mit den Namen der verfügbaren Geräte aufgefüllt wird.
  • Ein CoreDispatcher , der zum Aktualisieren der Benutzeroberfläche von einem anderen Thread als dem UI-Thread erforderlich ist.
DeviceWatcher deviceWatcher;
string deviceSelectorString;
ListBox deviceListBox;
CoreDispatcher coreDispatcher;

Fügen Sie eine DeviceInformationCollection-Eigenschaft hinzu, die für den Zugriff auf die aktuelle Geräteliste von außerhalb der Hilfsklasse verwendet wird.

public DeviceInformationCollection DeviceInformationCollection { get; set; }

Im Klassenkonstruktor übergibt der Aufrufer die Auswahlzeichenfolge des MIDI-Geräts, das ListBox-Element zum Auflisten der Geräte und den Dispatcher, der zum Aktualisieren der Benutzeroberfläche erforderlich ist.

Rufen Sie DeviceInformation.CreateWatcher auf, um eine neue Instanz der DeviceWatcher-Klasse zu erstellen und dabei die MIDI-Geräteauswahlzeichenfolge zu übergeben.

Registrieren Sie Handler für die Ereignishandler des Überwachungselements.

public MyMidiDeviceWatcher(string midiDeviceSelectorString, ListBox midiDeviceListBox, CoreDispatcher dispatcher)
{
    deviceListBox = midiDeviceListBox;
    coreDispatcher = dispatcher;

    deviceSelectorString = midiDeviceSelectorString;

    deviceWatcher = DeviceInformation.CreateWatcher(deviceSelectorString);
    deviceWatcher.Added += DeviceWatcher_Added;
    deviceWatcher.Removed += DeviceWatcher_Removed;
    deviceWatcher.Updated += DeviceWatcher_Updated;
    deviceWatcher.EnumerationCompleted += DeviceWatcher_EnumerationCompleted;
}

DeviceWatcher hat die folgenden Ereignisse:

  • Hinzugefügt – Wird ausgelöst, wenn dem System ein neues Gerät hinzugefügt wird.
  • Entfernt – Wird ausgelöst , wenn ein Gerät aus dem System entfernt wird.
  • Aktualisiert – Wird ausgelöst, wenn die Informationen, die einem vorhandenen Gerät zugeordnet sind, aktualisiert werden.
  • EnumerationCompleted – Wird ausgelöst, wenn der Watcher die Enumeration des angeforderten Gerätetyps abgeschlossen hat.

Im Ereignishandler für jedes dieser Ereignisse wird eine Hilfsmethode UpdateDevices aufgerufen, um das ListBox mit der aktuellen Liste der Geräte zu aktualisieren. Da UpdateDevices UI-Elemente aktualisiert und diese Ereignishandler nicht im UI-Thread aufgerufen werden, muss jeder Aufruf in einen Aufruf von RunAsync eingeschlossen werden, wodurch der angegebene Code im UI-Thread ausgeführt wird.

private async void DeviceWatcher_Removed(DeviceWatcher sender, DeviceInformationUpdate args)
{
    await coreDispatcher.RunAsync(CoreDispatcherPriority.High, () =>
    {
        // Update the device list
        UpdateDevices();
    });
}

private async void DeviceWatcher_Added(DeviceWatcher sender, DeviceInformation args)
{
    await coreDispatcher.RunAsync(CoreDispatcherPriority.High, () =>
    {
        // Update the device list
        UpdateDevices();
    });
}

private async void DeviceWatcher_EnumerationCompleted(DeviceWatcher sender, object args)
{
    await coreDispatcher.RunAsync(CoreDispatcherPriority.High, () =>
    {
        // Update the device list
        UpdateDevices();
    });
}

private async void DeviceWatcher_Updated(DeviceWatcher sender, DeviceInformationUpdate args)
{
    await coreDispatcher.RunAsync(CoreDispatcherPriority.High, () =>
    {
        // Update the device list
        UpdateDevices();
    });
}

Die UpdateDevices-Hilfsmethode ruft DeviceInformation.FindAllAsync auf und aktualisiert das ListBox-Objekt mit den Namen der zurückgegebenen Geräte, wie zuvor in diesem Artikel beschrieben.

private async void UpdateDevices()
{
    // Get a list of all MIDI devices
    this.DeviceInformationCollection = await DeviceInformation.FindAllAsync(deviceSelectorString);

    deviceListBox.Items.Clear();

    if (!this.DeviceInformationCollection.Any())
    {
        deviceListBox.Items.Add("No MIDI devices found!");
    }

    foreach (var deviceInformation in this.DeviceInformationCollection)
    {
        deviceListBox.Items.Add(deviceInformation.Name);
    }
}

Fügen Sie Methoden hinzu, um die Überwachung zu starten, indem Sie die Start-Methode des DeviceWatcher-Objekts verwenden und die Überwachung mithilfe der Stop-Methode beenden.

public void StartWatcher()
{
    deviceWatcher.Start();
}
public void StopWatcher()
{
    deviceWatcher.Stop();
}

Stellen Sie einen Destruktor bereit, um die Registrierung der Überwachungsereignishandler aufzuheben und die Geräteüberwachung auf NULL festzulegen.

~MyMidiDeviceWatcher()
{
    deviceWatcher.Added -= DeviceWatcher_Added;
    deviceWatcher.Removed -= DeviceWatcher_Removed;
    deviceWatcher.Updated -= DeviceWatcher_Updated;
    deviceWatcher.EnumerationCompleted -= DeviceWatcher_EnumerationCompleted;
    deviceWatcher = null;
}

Erstellen von MIDI-Ports zum Senden und Empfangen von Nachrichten

Deklarieren Sie im CodeBehind für Ihre Seite Membervariablen, um zwei Instanzen der MyMidiDeviceWatcher-Hilfsklasse zu speichern, eine für Eingabegeräte und eine für Ausgabegeräte.

MyMidiDeviceWatcher inputDeviceWatcher;
MyMidiDeviceWatcher outputDeviceWatcher;

Erstellen Sie eine neue Instanz der Watcher-Hilfsklassen, übergeben Sie die Geräteauswahlzeichenfolge, das aufgefüllte ListBox-Objekt und das CoreDispatcher-Objekt, auf das über die Dispatcher-Eigenschaft der Seite zugegriffen werden kann. Rufen Sie dann die Methode auf, um die DeviceWatcher-Objekte jedes Objekts zu starten.

Kurz nach dem Start jedes DeviceWatcher-Elements wird das Aufzählen der aktuellen Geräte abgeschlossen, die mit dem System verbunden sind, und das EnumerationCompleted-Ereignis auslösen, wodurch jedes ListBox-Element mit den aktuellen MIDI-Geräten aktualisiert wird.

inputDeviceWatcher =
    new MyMidiDeviceWatcher(MidiInPort.GetDeviceSelector(), midiInPortListBox, Dispatcher);

inputDeviceWatcher.StartWatcher();

outputDeviceWatcher =
    new MyMidiDeviceWatcher(MidiOutPort.GetDeviceSelector(), midiOutPortListBox, Dispatcher);

outputDeviceWatcher.StartWatcher();

Wenn der Benutzer ein Element im MIDI-Eingabelistenfeld auswählt, wird das SelectionChanged-Ereignis ausgelöst. Greifen Sie im Handler für dieses Ereignis auf die DeviceInformationCollection-Eigenschaft der Hilfsklasse zu, um die aktuelle Liste der Geräte abzurufen. Wenn die Liste Einträge enthält, wählen Sie das DeviceInformation-Objekt mit dem Index aus, der dem SelectedIndex des ListBox-Steuerelements entspricht.

Erstellen Sie das MidiInPort -Objekt, das das ausgewählte Eingabegerät darstellt, indem Sie MidiInPort.FromIdAsync aufrufen und die ID-Eigenschaft des ausgewählten Geräts übergeben.

Registrieren Sie einen Handler für das MessageReceived-Ereignis , das ausgelöst wird, wenn eine MIDI-Nachricht über das angegebene Gerät empfangen wird.

MidiInPort midiInPort;
IMidiOutPort midiOutPort;
private async void midiInPortListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    var deviceInformationCollection = inputDeviceWatcher.DeviceInformationCollection;

    if (deviceInformationCollection == null)
    {
        return;
    }

    DeviceInformation devInfo = deviceInformationCollection[midiInPortListBox.SelectedIndex];

    if (devInfo == null)
    {
        return;
    }

    midiInPort = await MidiInPort.FromIdAsync(devInfo.Id);

    if (midiInPort == null)
    {
        System.Diagnostics.Debug.WriteLine("Unable to create MidiInPort from input device");
        return;
    }
    midiInPort.MessageReceived += MidiInPort_MessageReceived;
}

Wenn der MessageReceived-Handler aufgerufen wird, ist die Nachricht in der Message-Eigenschaft der MidiMessageReceivedEventArgs enthalten. Der Typ des Nachrichtenobjekts ist ein Wert aus der MidiMessageType-Aufzählung , der den Typ der empfangenen Nachricht angibt. Die Daten der Nachricht hängen vom Typ der Nachricht ab. In diesem Beispiel wird überprüft, ob es sich bei der Nachricht um eine Notiz handelt, und gibt in diesem Fall den Midi-Kanal, die Notiz und geschwindigkeit der Nachricht aus.

private void MidiInPort_MessageReceived(MidiInPort sender, MidiMessageReceivedEventArgs args)
{
    IMidiMessage receivedMidiMessage = args.Message;

    System.Diagnostics.Debug.WriteLine(receivedMidiMessage.Timestamp.ToString());

    if (receivedMidiMessage.Type == MidiMessageType.NoteOn)
    {
        System.Diagnostics.Debug.WriteLine(((MidiNoteOnMessage)receivedMidiMessage).Channel);
        System.Diagnostics.Debug.WriteLine(((MidiNoteOnMessage)receivedMidiMessage).Note);
        System.Diagnostics.Debug.WriteLine(((MidiNoteOnMessage)receivedMidiMessage).Velocity);
    }
}

Der SelectionChanged-Handler für das Listenfeld des Ausgabegeräts funktioniert mit dem Handler für Eingabegeräte, außer dass kein Ereignishandler registriert ist.

private async void midiOutPortListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    var deviceInformationCollection = outputDeviceWatcher.DeviceInformationCollection;

    if (deviceInformationCollection == null)
    {
        return;
    }

    DeviceInformation devInfo = deviceInformationCollection[midiOutPortListBox.SelectedIndex];

    if (devInfo == null)
    {
        return;
    }

    midiOutPort = await MidiOutPort.FromIdAsync(devInfo.Id);

    if (midiOutPort == null)
    {
        System.Diagnostics.Debug.WriteLine("Unable to create MidiOutPort from output device");
        return;
    }

}

Nachdem das Ausgabegerät erstellt wurde, können Sie eine Nachricht senden, indem Sie eine neue IMidiMessage für den Typ der Nachricht erstellen, die Sie senden möchten. In diesem Beispiel ist die Nachricht eine NoteOnMessage. Die SendMessage-Methode des IMidiOutPort-Objekts wird aufgerufen, um die Nachricht zu senden.

byte channel = 0;
byte note = 60;
byte velocity = 127;
IMidiMessage midiMessageToSend = new MidiNoteOnMessage(channel, note, velocity);

midiOutPort.SendMessage(midiMessageToSend);

Wenn Ihre App deaktiviert ist, achten Sie darauf, Die App-Ressourcen zu bereinigen. Heben Sie die Registrierung der Ereignishandler auf, und legen Sie das MIDI-Port in Port- und Ausgabeportobjekten auf NULL fest. Beenden Sie die Geräteüberwachungen, und legen Sie sie auf NULL fest.

inputDeviceWatcher.StopWatcher();
inputDeviceWatcher = null;

outputDeviceWatcher.StopWatcher();
outputDeviceWatcher = null;

midiInPort.MessageReceived -= MidiInPort_MessageReceived;
midiInPort.Dispose();
midiInPort = null;

midiOutPort.Dispose();
midiOutPort = null;

Verwenden des integrierten Windows General MIDI Synth

Wenn Sie ausgabe-MIDI-Geräte mit der oben beschriebenen Technik aufzählen, ermittelt Ihre App ein MIDI-Gerät namens "Microsoft GS Wavetable Synth". Dies ist ein integrierter General MIDI-Synthesizer, den Sie über Ihre App wiedergeben können. Der Versuch, einen MIDI-Outport zu diesem Gerät zu erstellen, schlägt jedoch fehl, es sei denn, Sie haben die SDK-Erweiterung für den integrierten Synth in Ihr Projekt eingeschlossen.

So fügen Sie die General MIDI Synth SDK-Erweiterung in Ihr App-Projekt ein

  1. Klicken Sie in Projektmappen-Explorer unter Ihrem Projekt mit der rechten Maustaste auf "Verweise", und wählen Sie "Verweis hinzufügen" aus...
  2. Erweitern Sie den Universellen Windows-Knoten .
  3. Wählen Sie Erweiterungen.
  4. Wählen Sie in der Liste der Erweiterungen Microsoft General MIDI DLS für universelle Windows-Apps aus.

    Hinweis

    Wenn mehrere Versionen der Erweiterung vorhanden sind, müssen Sie unbedingt die Version auswählen, die dem Ziel für Ihre App entspricht. Sie können sehen, auf welche SDK-Version Ihre App auf der Registerkarte "Anwendung " der Projekteigenschaften ausgerichtet ist.