Melhor maneira de reproduzir sons MIDI usando C#
Pergunta
Estou tentando reconstruir um aplicativo metrônomo antigo que foi originalmente escrito usando MFC
em C++ para ser escrito em .NET
usando C#
.Um dos problemas que estou enfrentando é a reprodução de arquivos midi usados para representar os "cliques" do metrônomo.
Encontrei alguns artigos online sobre jogar MIDI
no .NET, mas a maioria deles parece depender de bibliotecas personalizadas que alguém montou e disponibilizou.Não tenho aversão a usá-los, mas prefiro entender por mim mesmo como isso está sendo feito, pois parece deve ser um exercício principalmente trivial.
Então, estou faltando alguma coisa?Ou é apenas difícil usar MIDI dentro de um aplicativo .NET?
Solução
Acho que você precisará invocar a API do Windows para poder reproduzir arquivos midi do .net.
Este artigo do codeproject explica bem como fazer isso:artigo vb.net para reproduzir arquivos midi
Para reescrever isso em c#, você precisaria da seguinte instrução de importação para mciSendString:
[DllImport("winmm.dll")]
static extern Int32 mciSendString(String command, StringBuilder buffer,
Int32 bufferSize, IntPtr hwndCallback);
Espero que isso ajude - boa sorte!
Outras dicas
Estou trabalhando em um aplicativo C# MIDI no momento, e os outros estão certos - você precisa usar p/invoke para isso.Estou criando o meu próprio, pois parecia mais apropriado para o aplicativo (só preciso de um pequeno subconjunto de funcionalidades MIDI), mas para seus propósitos o Kit de ferramentas C# MIDI pode ser um ajuste melhor.É pelo menos a melhor biblioteca .NET MIDI que encontrei e pesquisei bastante antes de iniciar o projeto.
midi-ponto-net coloquei-me pronto para funcionar em minutos - leve e do tamanho certo para o meu projeto doméstico.Também está disponível em GitHub.(Não deve ser confundido com o mencionado anteriormente MIDI.NET, que também parece promissor, mas nunca tive tempo para isso.)
Claro NAudio (também mencionado acima) tem muitos recursos, mas como o autor da postagem original, eu só queria tocar algumas notas e ler e entender rapidamente o código-fonte.
Não posso afirmar que sei muito sobre isso, mas não acho que seja tão simples - Carl Franklin, da DotNetRocks a fama fez bastante com isso - você viu seu DNRTV?
Você pode usar o reprodutor de mídia:
using WMPLib;
//...
WindowsMediaPlayer wmp = new WindowsMediaPlayer();
wmp.URL = Path.Combine(Application.StartupPath ,"Resources/mymidi1.mid");
wmp.controls.play();
Uma adição recente é MIDI.NET que suporta portas Midi, arquivos Midi e SysEx.
Sistema.Mídia.Leitor de som é uma maneira boa e simples de reproduzir arquivos WAV.Os arquivos WAV têm algumas vantagens sobre o MIDI, uma delas é que você pode controlar com precisão o som de cada instrumento (em vez de depender do sintetizador integrado do computador).
Desculpe, esta pergunta é um pouco antiga agora, mas o seguinte funcionou para mim (um pouco copiado de Win32 - Loop Midi com MCISendString):
[DllImport("winmm.dll")]
static extern Int32 mciSendString(String command, StringBuilder buffer, Int32 bufferSize, IntPtr hwndCallback);
public static void playMidi(String fileName, String alias)
{
mciSendString("open " + fileName + " type sequencer alias " + alias, new StringBuilder(), 0, new IntPtr());
mciSendString("play " + alias, new StringBuilder(), 0, new IntPtr());
}
public static void stopMidi(String alias)
{
mciSendString("stop " + alias, null, 0, new IntPtr());
mciSendString("close " + alias, null, 0, new IntPtr());
}
Uma lista completa de strings de comando é fornecida aqui.A parte legal disso é que você pode usar coisas diferentes além do sequenciador para tocar coisas diferentes, digamos waveaudio para reproduzir arquivos .wav.Não consigo descobrir como fazê-lo reproduzir .mp3.
Além disso, observe que os comandos stop e close devem ser enviados no mesmo thread em que os comandos open e play foram enviados, caso contrário eles não terão efeito e o arquivo permanecerá aberto.Por exemplo:
[DllImport("winmm.dll")]
static extern Int32 mciSendString(String command, StringBuilder buffer,
Int32 bufferSize, IntPtr hwndCallback);
public static Dictionary<String, bool> playingMidi = new Dictionary<String, bool>();
public static void PlayMidi(String fileName, String alias)
{
if (playingMidi.ContainsKey(alias))
throw new Exception("Midi with alias '" + alias + "' is already playing");
playingMidi.Add(alias, false);
Thread stoppingThread = new Thread(() => { StartAndStopMidiWithDelay(fileName, alias); });
stoppingThread.Start();
}
public static void StopMidiFromOtherThread(String alias)
{
if (!playingMidi.ContainsKey(alias))
return;
playingMidi[alias] = true;
}
public static bool isPlaying(String alias)
{
return playingMidi.ContainsKey(alias);
}
private static void StartAndStopMidiWithDelay(String fileName, String alias)
{
mciSendString("open " + fileName + " type sequencer alias " + alias, null, 0, new IntPtr());
mciSendString("play " + alias, null, 0, new IntPtr());
StringBuilder result = new StringBuilder(100);
mciSendString("set " + alias + " time format milliseconds", null, 0, new IntPtr());
mciSendString("status " + alias + " length", result, 100, new IntPtr());
int midiLengthInMilliseconds;
Int32.TryParse(result.ToString(), out midiLengthInMilliseconds);
Stopwatch timer = new Stopwatch();
timer.Start();
while(timer.ElapsedMilliseconds < midiLengthInMilliseconds && !playingMidi[alias])
{
}
timer.Stop();
StopMidi(alias);
}
private static void StopMidi(String alias)
{
if (!playingMidi.ContainsKey(alias))
throw new Exception("Midi with alias '" + alias + "' is already stopped");
// Execute calls to close and stop the player, on the same thread as the play and open calls
mciSendString("stop " + alias, null, 0, new IntPtr());
mciSendString("close " + alias, null, 0, new IntPtr());
playingMidi.Remove(alias);
}
Surge um novo jogador:
https://github.com/atsushieno/administrado-midi
https://www.nuget.org/packages/administrado-midi/
Não há muita documentação, mas um dos focos desta biblioteca é o suporte multiplataforma.
Acho muito melhor usar alguma biblioteca que tenha recursos avançados para reprodução de dados MIDI em vez de implementá-la por conta própria.Por exemplo, com SecoMolhadoMIDI para reproduzir arquivo MIDI via sintetizador padrão (Sintetizador Wavetable Microsoft GS):
using Melanchall.DryWetMidi.Devices;
using Melanchall.DryWetMidi.Smf;
// ...
var midiFile = MidiFile.Read("Greatest song ever.mid");
using (var outputDevice = OutputDevice.GetByName("Microsoft GS Wavetable Synth"))
{
midiFile.Play(outputDevice);
}
Play
bloqueará o thread de chamada até que todo o arquivo seja reproduzido.Para controlar a reprodução de um arquivo MIDI, obtenha Playback
objeto e usar seu Start
/Stop
métodos (mais detalhes no Reprodução artigo da biblioteca Wikipédia):
var playback = midiFile.GetPlayback(outputDevice);
// You can even loop playback and speed it up
playback.Loop = true;
playback.Speed = 2.0;
playback.Start();
// ...
playback.Stop();
// ...
playback.Dispose();
outputDevice.Dispose();