Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Este artigo mostra-lhe como enumerar dispositivos MIDI (Interface Digital de Instrumentos Musicais) e enviar e receber mensagens MIDI a partir de uma aplicação WinUI. O Windows suporta MIDI por USB (dispositivos compatíveis com a norma e a maioria dos controladores proprietários), MIDI por Bluetooth LE e, através de produtos de terceiros disponibilizados gratuitamente, MIDI por Ethernet e MIDI com encaminhamento.
Criar uma classe auxiliar de observador de dispositivos
O Windows. Devices.Enumeration fornece o espaço DeviceWatcher que pode notificar a sua aplicação se dispositivos forem adicionados ou removidos do sistema, ou se a informação de um dispositivo for atualizada. Como as aplicações com MIDI normalmente se interessam tanto em dispositivos de entrada como de saída, este exemplo cria uma classe auxiliar que implementa o padrão DeviceWatcher , de modo que o mesmo código pode ser usado tanto para dispositivos de entrada MIDI como de saída MIDI, sem necessidade de duplicação.
Adicione uma nova classe ao seu projeto para servir como observador do dispositivo. Neste exemplo, a classe chama-se MidiDeviceWatcher. O resto do código nesta secção é usado para implementar a classe auxiliar.
Adicione algumas variáveis membros à classe:
- Um objeto DeviceWatcher que irá monitorizar alterações no dispositivo.
- Uma cadeia de seleção de dispositivos que conterá, numa instância, a cadeia de seleção da porta de entrada MIDI e, noutra instância, a cadeia de seleção da porta de saída MIDI.
- Um controlo ListBox que será preenchido com os nomes dos dispositivos disponíveis.
- Uma DispatcherQueue que é necessária para atualizar a interface a partir de um thread diferente do thread UI.
DeviceWatcher deviceWatcher;
string deviceSelectorString;
ListBox deviceListBox;
DispatcherQueue dispatcherQueue;
Adicione uma propriedade DeviceInformationCollection que é usada para aceder à lista atual de dispositivos fora da classe auxiliar.
public DeviceInformationCollection? DeviceInformationCollection { get; set; }
No construtor de classes, o chamador passa a cadeia seletora de dispositivos MIDI, a ListBox para listar os dispositivos e a DispatcherQueue necessária para atualizar a interface.
Chame DeviceInformation.CreateWatcher para criar uma nova instância da classe DeviceWatcher , passando a cadeia seletora de dispositivos MIDI.
Registe manipuladores para os manipuladores de eventos do observador.
public MidiDeviceWatcher(string midiDeviceSelectorString, ListBox midiDeviceListBox, DispatcherQueue dispatcher)
{
deviceListBox = midiDeviceListBox;
dispatcherQueue = dispatcher;
deviceSelectorString = midiDeviceSelectorString;
deviceWatcher = DeviceInformation.CreateWatcher(deviceSelectorString);
deviceWatcher.Added += DeviceWatcher_Added;
deviceWatcher.Removed += DeviceWatcher_Removed;
deviceWatcher.Updated += DeviceWatcher_Updated;
deviceWatcher.EnumerationCompleted += DeviceWatcher_EnumerationCompleted;
}
O DeviceWatcher tem os seguintes eventos:
- Adicionado - Elevado quando um novo dispositivo é adicionado ao sistema.
- Removido - Elevado quando um dispositivo é removido do sistema.
- Atualizado - Levantado quando a informação associada a um dispositivo existente é atualizada.
- EnumerationCompleted - Aumentada quando o observador concluiu a enumeração do tipo de dispositivo solicitado.
No gestor de eventos para cada um destes eventos, é chamado um método auxiliar, UpdateDevices, para atualizar a ListBox com a lista atual de dispositivos. Como o UpdateDevices atualiza elementos da interface e estes gestores de eventos não são chamados no thread da interface, cada chamada deve ser encapsulada numa chamada para DispatcherQueue.TryEnqueue.
private void DeviceWatcher_Removed(DeviceWatcher sender, DeviceInformationUpdate args)
{
dispatcherQueue.TryEnqueue(() =>
{
UpdateDevices();
});
}
private void DeviceWatcher_Added(DeviceWatcher sender, DeviceInformation args)
{
dispatcherQueue.TryEnqueue(() =>
{
UpdateDevices();
});
}
private void DeviceWatcher_EnumerationCompleted(DeviceWatcher sender, object args)
{
dispatcherQueue.TryEnqueue(() =>
{
UpdateDevices();
});
}
private void DeviceWatcher_Updated(DeviceWatcher sender, DeviceInformationUpdate args)
{
dispatcherQueue.TryEnqueue(() =>
{
UpdateDevices();
});
}
O método auxiliar UpdateDevices chama DeviceInformation.FindAllAsync e atualiza a ListBox com os nomes dos dispositivos retornados, conforme descrito anteriormente neste artigo.
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);
}
}
Adicione métodos para iniciar o watcher, usando o método Start do objeto DeviceWatcher, e para parar o watcher, usando o método Stop.
public void StartWatcher()
{
deviceWatcher.Start();
}
public void StopWatcher()
{
deviceWatcher.Stop();
}
Forneça um destrutor para desregistar os gestores de eventos do observador e defina o observador do dispositivo como null.
~MidiDeviceWatcher()
{
deviceWatcher.Added -= DeviceWatcher_Added;
deviceWatcher.Removed -= DeviceWatcher_Removed;
deviceWatcher.Updated -= DeviceWatcher_Updated;
deviceWatcher.EnumerationCompleted -= DeviceWatcher_EnumerationCompleted;
}
Criar portas MIDI para enviar e receber mensagens
No código por trás da sua janela, declare as variáveis membro para conter duas instâncias da classe auxiliar MidiDeviceWatcher , uma para dispositivos de entrada e outra para dispositivos de saída.
MidiDeviceWatcher? inputDeviceWatcher;
MidiDeviceWatcher? outputDeviceWatcher;
Declare também variáveis de membro para os objetos de entrada e saída da porta MIDI.
MidiInPort? midiInPort;
IMidiOutPort? midiOutPort;
Crie uma nova instância das classes auxiliares de monitorização, passando a cadeia de seleção de dispositivos, a ListBox a ser preenchida e o objeto DispatcherQueue. Depois, chama o método para iniciar o DeviceWatcher de cada objeto.
Pouco depois de cada DeviceWatcher ser iniciado, termina a enumeração dos dispositivos atualmente ligados ao sistema e desencadeia o evento EnumerationCompleted, o que fará com que cada ListBox seja atualizada com os dispositivos MIDI atuais.
inputDeviceWatcher =
new MidiDeviceWatcher(MidiInPort.GetDeviceSelector(), midiInPortListBox, DispatcherQueue);
inputDeviceWatcher.StartWatcher();
outputDeviceWatcher =
new MidiDeviceWatcher(MidiOutPort.GetDeviceSelector(), midiOutPortListBox, DispatcherQueue);
outputDeviceWatcher.StartWatcher();
Quando o utilizador seleciona um item na ListBox de entrada MIDI, o evento SelectionChanged é levantado. No handler deste evento, acede à propriedade DeviceInformationCollection da classe helper para obter a lista atual de dispositivos. Se houver entradas na lista, selecione o objeto DeviceInformation com o índice correspondente ao SelectedIndex do controlo ListBox.
Crie o objeto MidiInPort que representa o dispositivo de entrada selecionado chamando MidiInPort.FromIdAsync, passando a propriedade Id do dispositivo selecionado.
Registar um manipulador para o evento MessageReceived, que é acionado sempre que uma mensagem MIDI é recebida no dispositivo especificado.
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;
}
Quando o handler MessageReceived é chamado, a mensagem fica contida na propriedade Message da MidiMessageReceivedEventArgs. O Type do objeto mensagem é um valor da enumeração MidiMessageType que indica o tipo de mensagem que foi recebida. Os dados da mensagem dependem do tipo da mensagem. Este exemplo verifica se a mensagem é uma mensagem Note On e, em caso afirmativo, devolve o canal MIDI, a nota e a velocidade.
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);
}
}
O handler SelectionChanged para o dispositivo de saída ListBox funciona da mesma forma que o handler para dispositivos de entrada, exceto que não há nenhum handler de eventos registado.
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;
}
}
Depois de criado o dispositivo de saída, pode enviar uma mensagem criando uma nova IMidiMessage para o tipo de mensagem que pretende enviar. Neste exemplo, a mensagem é um NoteOnMessage. O método SendMessage do objeto IMidiOutPort é chamado para enviar a mensagem.
byte channel = 0;
byte note = 60;
byte velocity = 127;
IMidiMessage midiMessageToSend = new MidiNoteOnMessage(channel, note, velocity);
midiOutPort.SendMessage(midiMessageToSend);
Quando a sua aplicação estiver a fechar, certifique-se de limpar os recursos da sua aplicação. Desregista os teus gestores de eventos e define os objetos da porta de entrada e saída MIDI para null. Pare os monitorizadores de dispositivos e defina-os como nulos.
inputDeviceWatcher?.StopWatcher();
inputDeviceWatcher = null;
outputDeviceWatcher?.StopWatcher();
outputDeviceWatcher = null;
if (midiInPort != null)
{
midiInPort.MessageReceived -= MidiInPort_MessageReceived;
midiInPort.Dispose();
midiInPort = null;
}
if (midiOutPort != null)
{
midiOutPort.Dispose();
midiOutPort = null;
}
Usando o sintetizador MIDI Windows General incorporado
Ao enumerar dispositivos MIDI de saída usando a técnica descrita acima, a sua aplicação irá descobrir um dispositivo MIDI chamado "Microsoft GS Wavetable Synth". Este é um sintetizador MIDI General incorporado que podes tocar a partir da tua aplicação.
O SDK da Extensão UWP para General MIDI ("Microsoft General MIDI DLS for Universal Windows Apps") não está disponível nos projetos WinUI 3. O antigo diálogo Adicionar Extensões de referência > era específico para o UWP. No entanto, para a maioria dos cenários de ambiente de trabalho, o GS Wavetable Synth funciona sem o SDK de extensão porque as aplicações de ambiente de trabalho podem aceder diretamente ao banco de sons do gm.dls sistema. Se verificar que a saída MIDI não produz som, verifique se um dispositivo de saída MIDI está selecionado e se a sua saída de áudio está configurada corretamente.
Tópicos relacionados
Windows developer