MIDI
Artikel ini menunjukkan kepada Anda cara menghitung perangkat MIDI (Musical Instrument Digital Interface) dan mengirim dan menerima pesan MIDI dari aplikasi Universal Windows. Windows 10 mendukung MIDI melalui USB (driver yang sesuai dengan kelas dan sebagian besar kepemilikan), MIDI melalui Bluetooth LE (Windows 10 Anniversary Edition dan yang lebih baru), dan melalui produk pihak ketiga yang tersedia secara bebas, MIDI melalui Ethernet dan MIDI yang dirutekan.
Menghitung perangkat MIDI
Sebelum menghitung dan menggunakan perangkat MIDI, tambahkan namespace berikut ke proyek Anda.
using Windows.Devices.Enumeration;
using Windows.Devices.Midi;
using System.Threading.Tasks;
Tambahkan kontrol ListBox ke halaman XAML Anda yang akan memungkinkan pengguna untuk memilih salah satu perangkat input MIDI yang terpasang pada sistem. Tambahkan yang lain untuk mencantumkan perangkat output MIDI.
<ListBox x:Name="midiInPortListBox" SelectionChanged="midiInPortListBox_SelectionChanged"/>
<ListBox x:Name="midiOutPortListBox" SelectionChanged="midiOutPortListBox_SelectionChanged"/>
Kelas DeviceInformation metode FindAllAsync digunakan untuk menghitung berbagai jenis perangkat yang dikenali oleh Windows. Untuk menentukan bahwa Anda hanya ingin metode menemukan perangkat input MIDI, gunakan string pemilih yang dikembalikan oleh MidiInPort.GetDeviceSelector. FindAllAsync mengembalikan DeviceInformationCollection yang berisi DeviceInformation untuk setiap perangkat input MIDI yang terdaftar dengan sistem. Jika koleksi yang dikembalikan tidak berisi item, maka tidak ada perangkat input MIDI yang tersedia. Jika ada item dalam koleksi, ulangi objek DeviceInformation dan tambahkan nama setiap perangkat ke ListBox perangkat input 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;
}
Menghitung perangkat output MIDI bekerja dengan cara yang sama persis seperti menghitung perangkat input, kecuali bahwa Anda harus menentukan string pemilih yang dikembalikan oleh MidiOutPort.GetDeviceSelector saat memanggil 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;
}
Membuat kelas pembantu pengamat perangkat
Namespace Layanan Windows.Devices.Enumeration menyediakan DeviceWatcher yang dapat memberi tahu aplikasi Anda jika perangkat ditambahkan atau dihapus dari sistem, atau jika informasi untuk perangkat diperbarui. Karena aplikasi yang diaktifkan MIDI biasanya tertarik pada perangkat input dan output, contoh ini membuat kelas pembantu yang mengimplementasikan pola DeviceWatcher , sehingga kode yang sama dapat digunakan untuk perangkat input MIDI dan output MIDI, tanpa perlu duplikasi.
Tambahkan kelas baru ke proyek Anda untuk berfungsi sebagai pengamat perangkat Anda. Dalam contoh ini kelas diberi nama MyMidiDeviceWatcher. Sisa kode di bagian ini digunakan untuk mengimplementasikan kelas pembantu.
Tambahkan beberapa variabel anggota ke kelas :
- Objek DeviceWatcher yang akan memantau perubahan perangkat.
- String pemilih perangkat yang akan berisi MIDI dalam string pemilih port untuk satu instans dan string pemilih port MIDI out untuk instans lain.
- Kontrol ListBox yang akan diisi dengan nama perangkat yang tersedia.
- CoreDispatcher yang diperlukan untuk memperbarui UI dari utas selain utas UI.
DeviceWatcher deviceWatcher;
string deviceSelectorString;
ListBox deviceListBox;
CoreDispatcher coreDispatcher;
Tambahkan properti DeviceInformationCollection yang digunakan untuk mengakses daftar perangkat saat ini dari luar kelas pembantu.
public DeviceInformationCollection DeviceInformationCollection { get; set; }
Di konstruktor kelas, pemanggil meneruskan string pemilih perangkat MIDI, ListBox untuk mencantumkan perangkat, dan Dispatcher yang diperlukan untuk memperbarui UI.
Panggil DeviceInformation.CreateWatcher untuk membuat instans baru kelas DeviceWatcher , meneruskan string pemilih perangkat MIDI.
Daftarkan handler untuk penanganan aktivitas pengamat.
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 memiliki peristiwa berikut:
- Ditambahkan - Dimunculkan saat perangkat baru ditambahkan ke sistem.
- Dihapus - Dimunculkan ketika perangkat dihapus dari sistem.
- Diperbarui - Dimunculkan ketika informasi yang terkait dengan perangkat yang ada diperbarui.
- EnumerationCompleted - Dimunculkan ketika pengamat telah menyelesaikan enumerasi jenis perangkat yang diminta.
Dalam penanganan aktivitas untuk setiap peristiwa ini, metode pembantu, UpdateDevices, dipanggil untuk memperbarui ListBox dengan daftar perangkat saat ini. Karena UpdateDevices memperbarui elemen UI dan penanganan aktivitas ini tidak dipanggil pada utas UI, setiap panggilan harus dibungkus dalam panggilan ke RunAsync, yang menyebabkan kode yang ditentukan dijalankan pada utas UI.
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();
});
}
Metode pembantu UpdateDevices memanggil DeviceInformation.FindAllAsync dan memperbarui ListBox dengan nama perangkat yang dikembalikan seperti yang dijelaskan sebelumnya dalam artikel ini.
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);
}
}
Tambahkan metode untuk memulai pengamat, menggunakan metode Mulai objek DeviceWatcher, dan untuk menghentikan pengamat, menggunakan metode Stop.
public void StartWatcher()
{
deviceWatcher.Start();
}
public void StopWatcher()
{
deviceWatcher.Stop();
}
Berikan destruktor untuk membatalkan pendaftaran penanganan aktivitas pengamat dan mengatur pengamat perangkat ke null.
~MyMidiDeviceWatcher()
{
deviceWatcher.Added -= DeviceWatcher_Added;
deviceWatcher.Removed -= DeviceWatcher_Removed;
deviceWatcher.Updated -= DeviceWatcher_Updated;
deviceWatcher.EnumerationCompleted -= DeviceWatcher_EnumerationCompleted;
deviceWatcher = null;
}
Membuat port MIDI untuk mengirim dan menerima pesan
Dalam kode di belakang untuk halaman Anda, deklarasikan variabel anggota untuk menyimpan dua instans kelas pembantu MyMidiDeviceWatcher , satu untuk perangkat input dan satu untuk perangkat output.
MyMidiDeviceWatcher inputDeviceWatcher;
MyMidiDeviceWatcher outputDeviceWatcher;
Buat instans baru kelas pembantu pengamat, meneruskan string pemilih perangkat, ListBox yang akan diisi, dan objek CoreDispatcher yang dapat diakses melalui properti Dispatcher halaman. Kemudian, panggil metode untuk memulai DeviceWatcher setiap objek.
Tak lama setelah setiap DeviceWatcher dimulai, perangkat akan selesai menghitung perangkat saat ini yang terhubung ke sistem dan menaikkan peristiwa EnumerationCompleted-nya, yang akan menyebabkan setiap ListBox diperbarui dengan perangkat MIDI saat ini.
inputDeviceWatcher =
new MyMidiDeviceWatcher(MidiInPort.GetDeviceSelector(), midiInPortListBox, Dispatcher);
inputDeviceWatcher.StartWatcher();
outputDeviceWatcher =
new MyMidiDeviceWatcher(MidiOutPort.GetDeviceSelector(), midiOutPortListBox, Dispatcher);
outputDeviceWatcher.StartWatcher();
Saat pengguna memilih item di ListBox input MIDI, peristiwa SelectionChanged dinaikkan. Di handler untuk kejadian ini, akses properti DeviceInformationCollection dari kelas pembantu untuk mendapatkan daftar perangkat saat ini. Jika ada entri dalam daftar, pilih objek DeviceInformation dengan indeks yang sesuai dengan SelectedIndex kontrol ListBox.
Buat objek MidiInPort yang mewakili perangkat input yang dipilih dengan memanggil MidiInPort.FromIdAsync, melewati properti Id perangkat yang dipilih.
Daftarkan handler untuk peristiwa MessageReceived , yang dinaikkan setiap kali pesan MIDI diterima melalui perangkat yang ditentukan.
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;
}
Ketika handler MessageReceived dipanggil, pesan terkandung dalam properti Pesan midiMessageReceivedEventArgs. Jenis objek pesan adalah nilai dari enumerasi MidiMessageType yang menunjukkan jenis pesan yang diterima. Data pesan bergantung pada jenis pesan. Contoh ini memeriksa untuk melihat apakah pesan adalah catatan pada pesan dan, jika demikian, menghasilkan saluran midi, catatan, dan kecepatan pesan.
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);
}
}
Handler SelectionChanged untuk perangkat output ListBox berfungsi sama dengan handler untuk perangkat input, kecuali tidak ada penanganan aktivitas yang terdaftar.
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;
}
}
Setelah perangkat output dibuat, Anda dapat mengirim pesan dengan membuat IMidiMessage baru untuk jenis pesan yang ingin Anda kirim. Dalam contoh ini, pesannya adalah NoteOnMessage. Metode SendMessage objek IMidiOutPort dipanggil untuk mengirim pesan.
byte channel = 0;
byte note = 60;
byte velocity = 127;
IMidiMessage midiMessageToSend = new MidiNoteOnMessage(channel, note, velocity);
midiOutPort.SendMessage(midiMessageToSend);
Saat aplikasi Anda dinonaktifkan, pastikan untuk membersihkan sumber daya aplikasi Anda. Batalkan pendaftaran penanganan aktivitas Anda dan atur MIDI di port dan keluar objek port ke null. Hentikan pengamat perangkat dan atur ke null.
inputDeviceWatcher.StopWatcher();
inputDeviceWatcher = null;
outputDeviceWatcher.StopWatcher();
outputDeviceWatcher = null;
midiInPort.MessageReceived -= MidiInPort_MessageReceived;
midiInPort.Dispose();
midiInPort = null;
midiOutPort.Dispose();
midiOutPort = null;
Menggunakan synth WINDOWS General MIDI bawaan
Saat Anda menghitung perangkat MIDI output menggunakan teknik yang dijelaskan di atas, aplikasi Anda akan menemukan perangkat MIDI yang disebut "Microsoft GS Wavetable Synth". Ini adalah synthesizer General MIDI bawaan yang dapat Anda mainkan dari aplikasi Anda. Namun, mencoba membuat outport MIDI ke perangkat ini akan gagal kecuali Anda telah menyertakan ekstensi SDK untuk synth bawaan dalam proyek Anda.
Untuk menyertakan ekstensi SDK Synth MIDI Umum dalam proyek aplikasi Anda
- Di Penjelajah Solusi, di bawah proyek Anda, klik kanan Referensi dan pilih Tambahkan referensi...
- Perluas simpul Universal Windows .
- Pilih Ekstensi.
- Dari daftar ekstensi, pilih Microsoft General MIDI DLS untuk Universal Windows Apps.
Catatan
Jika ada beberapa versi ekstensi, pastikan untuk memilih versi yang cocok dengan target untuk aplikasi Anda. Anda dapat melihat versi SDK mana yang ditargetkan aplikasi Anda pada tab Aplikasi dari Properti proyek.