Partager via


MIDI

Cet article explique comment énumérer les appareils MIDI (Musical Instrument Digital Interface) et envoyer et recevoir des messages MIDI à partir d’une application Windows universelle. Windows 10 prend en charge MIDI sur USB (pilotes conformes à la classe et la plupart des pilotes propriétaires), MIDI sur Bluetooth LE (Windows 10 Édition anniversaire et versions ultérieures) et via des produits tiers disponibles librement disponibles, MIDI sur Ethernet et midi routé.

Énumérer les appareils MIDI

Avant d’énumérer et d’utiliser des appareils MIDI, ajoutez les espaces de noms suivants à votre projet.

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

Ajoutez un contrôle ListBox à votre page XAML qui permettra à l’utilisateur de sélectionner l’un des périphériques d’entrée MIDI attachés au système. Ajoutez un autre pour répertorier les périphériques de sortie MIDI.

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

La classe DeviceInformation de la méthode FindAllAsync est utilisée pour énumérer de nombreux types d’appareils reconnus par Windows. Pour spécifier que vous souhaitez uniquement que la méthode recherche des périphériques d’entrée MIDI, utilisez la chaîne de sélecteur retournée par MidiInPort.GetDeviceSelector. FindAllAsync retourne un DeviceInformationCollection qui contient un DeviceInformation pour chaque appareil d’entrée MIDI inscrit auprès du système. Si la collection retournée ne contient aucun élément, il n’existe aucun périphérique d’entrée MIDI disponible. S’il existe des éléments dans la collection, effectuez une boucle dans les objets DeviceInformation et ajoutez le nom de chaque appareil au listBox de l’appareil d’entrée MIDI.

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;
}

L’énumération des périphériques de sortie MIDI fonctionne exactement de la même façon que l’énumération des périphériques d’entrée, sauf que vous devez spécifier la chaîne de sélecteur retournée par MidiOutPort.GetDeviceSelector lors de l’appel de FindAllAsync.

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;
}

Créer une classe d’assistance d’observateur d’appareil

L’espace de noms Windows.Devices.Enumeration fournit deviceWatcher qui peut notifier votre application si les appareils sont ajoutés ou supprimés du système, ou si les informations relatives à un appareil sont mises à jour. Étant donné que les applications compatibles MIDI sont généralement intéressées par les appareils d’entrée et de sortie, cet exemple crée une classe d’assistance qui implémente le modèle DeviceWatcher , afin que le même code puisse être utilisé pour les appareils d’entrée MIDI et de sortie MIDI, sans avoir besoin de duplication.

Ajoutez une nouvelle classe à votre projet pour servir de observateur d’appareil. Dans cet exemple, la classe est nommée MyMidiDeviceWatcher. Le reste du code de cette section est utilisé pour implémenter la classe d’assistance.

Ajoutez des variables membres à la classe :

  • Objet DeviceWatcher qui surveillera les modifications de l’appareil.
  • Chaîne de sélecteur d’appareil qui contiendra la chaîne de sélecteur de port dans la chaîne de sélecteur de port pour une instance et la chaîne de sélecteur de port MIDI out pour une autre instance.
  • Contrôle ListBox qui sera rempli avec les noms des appareils disponibles.
  • CoreDispatcher requis pour mettre à jour l’interface utilisateur à partir d’un thread autre que le thread d’interface utilisateur.
DeviceWatcher deviceWatcher;
string deviceSelectorString;
ListBox deviceListBox;
CoreDispatcher coreDispatcher;

Ajoutez une propriété DeviceInformationCollection utilisée pour accéder à la liste actuelle des appareils en dehors de la classe d’assistance.

public DeviceInformationCollection DeviceInformationCollection { get; set; }

Dans le constructeur de classe, l’appelant passe dans la chaîne de sélecteur d’appareil MIDI, listBox pour répertorier les appareils et le répartiteur nécessaire pour mettre à jour l’interface utilisateur.

Appelez DeviceInformation.CreateWatcher pour créer une instance de la classe DeviceWatcher , en passant la chaîne de sélecteur d’appareil MIDI.

Inscrivez des gestionnaires pour les gestionnaires d’événements de l’observateur.

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 a les événements suivants :

  • Ajouté : déclenché lorsqu’un nouvel appareil est ajouté au système.
  • Supprimé : déclenché lorsqu’un appareil est supprimé du système.
  • Mise à jour : déclenché lorsque les informations associées à un appareil existant sont mises à jour.
  • ÉnumérationCompleted : déclenché lorsque l’observateur a terminé son énumération du type d’appareil demandé.

Dans le gestionnaire d’événements pour chacun de ces événements, une méthode d’assistance, UpdateDevices, est appelée pour mettre à jour listBox avec la liste actuelle des appareils. Étant donné que UpdateDevices met à jour les éléments de l’interface utilisateur et que ces gestionnaires d’événements ne sont pas appelés sur le thread d’interface utilisateur, chaque appel doit être encapsulé dans un appel à RunAsync, ce qui entraîne l’exécution du code spécifié sur le thread d’interface utilisateur.

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();
    });
}

La méthode d’assistance UpdateDevices appelle DeviceInformation.FindAllAsync et met à jour ListBox avec les noms des appareils retournés, comme décrit précédemment dans cet article.

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);
    }
}

Ajoutez des méthodes pour démarrer l’observateur, à l’aide de la méthode Start de l’objet DeviceWatcher et pour arrêter l’observateur, à l’aide de la méthode Stop.

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

Fournissez un destructeur pour annuler l’inscription des gestionnaires d’événements observateur et définissez l’observateur d’appareil sur Null.

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

Créer des ports MIDI pour envoyer et recevoir des messages

Dans le code-behind de votre page, déclarez des variables membres pour contenir deux instances de la classe d’assistance MyMidiDeviceWatcher , une pour les appareils d’entrée et une pour les appareils de sortie.

MyMidiDeviceWatcher inputDeviceWatcher;
MyMidiDeviceWatcher outputDeviceWatcher;

Créez une instance des classes d’assistance de l’observateur, en passant la chaîne de sélecteur d’appareil, la Zone de liste à remplir et l’objet CoreDispatcher accessible via la propriété Dispatcher de la page. Ensuite, appelez la méthode pour démarrer DeviceWatcher de chaque objet.

Peu après le démarrage de chaque DeviceWatcher, il termine l’énumération des appareils actuels connectés au système et déclenche son événement EnumerationCompleted, ce qui entraîne la mise à jour de chaque ListBox avec les appareils MIDI actuels.

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

inputDeviceWatcher.StartWatcher();

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

outputDeviceWatcher.StartWatcher();

Lorsque l’utilisateur sélectionne un élément dans le ListBox d’entrée MIDI, l’événement SelectionChanged est déclenché. Dans le gestionnaire de cet événement, accédez à la propriété DeviceInformationCollection de la classe d’assistance pour obtenir la liste actuelle des appareils. S’il existe des entrées dans la liste, sélectionnez l’objet DeviceInformation avec l’index correspondant à SelectedIndex du contrôle ListBox.

Créez l’objet MidiInPort représentant l’appareil d’entrée sélectionné en appelant MidiInPort.FromIdAsync, en passant la propriété ID de l’appareil sélectionné.

Inscrivez un gestionnaire pour l’événement MessageReceived , qui est déclenché chaque fois qu’un message MIDI est reçu via l’appareil spécifié.

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;
}

Lorsque le gestionnaire MessageReceived est appelé, le message est contenu dans la propriété Message du MidiMessageReceivedEventArgs. Le type de l’objet de message est une valeur de l’énumération MidiMessageType indiquant le type de message reçu. Les données du message dépendent du type du message. Cet exemple vérifie si le message est une note sur le message et, le cas échéant, génère le canal midi, la note et la vitesse du message.

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);
    }
}

Le gestionnaire SelectionChanged pour le listBox de périphérique de sortie fonctionne de la même façon que le gestionnaire pour les appareils d’entrée, sauf qu’aucun gestionnaire d’événements n’est inscrit.

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;
    }

}

Une fois l’appareil de sortie créé, vous pouvez envoyer un message en créant un IMidiMessage pour le type de message que vous souhaitez envoyer. Dans cet exemple, le message est un MessageOnMessage. La méthode SendMessage de l’objet IMidiOutPort est appelée pour envoyer le message.

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

midiOutPort.SendMessage(midiMessageToSend);

Lorsque votre application est désactivée, veillez à nettoyer vos ressources d’applications. Annulez l’inscription de vos gestionnaires d’événements et définissez les objets midi dans les objets de port et de sortie sur Null. Arrêtez les observateur d’appareils et définissez-les sur Null.

inputDeviceWatcher.StopWatcher();
inputDeviceWatcher = null;

outputDeviceWatcher.StopWatcher();
outputDeviceWatcher = null;

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

midiOutPort.Dispose();
midiOutPort = null;

Utilisation de la synthèse MIDI générale Windows intégrée

Lorsque vous énumérez des appareils MIDI de sortie à l’aide de la technique décrite ci-dessus, votre application découvre un appareil MIDI appelé « Microsoft GS Wavetable Synth ». Il s’agit d’un synthétiseur Général MIDI intégré que vous pouvez lire à partir de votre application. Toutefois, la tentative de création d’une sortie MIDI sur cet appareil échoue, sauf si vous avez inclus l’extension sdk pour le synthé intégré dans votre projet.

Pour inclure l’extension du Kit de développement logiciel (SDK) Synth MIDI général dans votre projet d’application

  1. Dans Explorateur de solutions, sous votre projet, cliquez avec le bouton droit sur Références et sélectionnez Ajouter une référence...
  2. Développez le nœud Windows universel.
  3. Sélectionnez Extensions.
  4. Dans la liste des extensions, sélectionnez Microsoft General MIDI DLS for Universal Windows Apps.

    Remarque

    S’il existe plusieurs versions de l’extension, veillez à sélectionner la version qui correspond à la cible de votre application. Vous pouvez voir la version du SDK que votre application cible sous l’onglet Application des propriétés du projet.