Domanda

Sto cercando di ricostruire una vecchia applicazione del metronomo originariamente scritta utilizzando MFC in C++ per essere scritto .NET utilizzando C#.Uno dei problemi che sto riscontrando è la riproduzione dei file midi utilizzati per rappresentare i "clic" del metronomo.

Ho trovato alcuni articoli online sul gioco MIDI in .NET, ma la maggior parte di essi sembra fare affidamento su librerie personalizzate che qualcuno ha messo insieme e reso disponibili.Non sono contrario all'utilizzo di questi, ma preferisco capire da solo come viene fatto, poiché sembra che Dovrebbe essere un esercizio per lo più banale.

Allora, mi sto perdendo qualcosa?O è semplicemente difficile utilizzare il MIDI all'interno di un'applicazione .NET?

È stato utile?

Soluzione

Penso che dovrai eseguire p/invoke sull'API di Windows per poter riprodurre file midi da .net.

Questo articolo del codeproject spiega bene come eseguire questa operazione:Articolo vb.net per riprodurre file midi

Per riscrivere questo è C# avrai bisogno della seguente istruzione di importazione per mciSendString:

[DllImport("winmm.dll")] 
static extern Int32 mciSendString(String command, StringBuilder buffer, 
                                  Int32 bufferSize, IntPtr hwndCallback);

Spero che questo aiuti: buona fortuna!

Altri suggerimenti

Al momento sto lavorando su un'applicazione MIDI C# e gli altri hanno ragione: per questo è necessario utilizzare p/invoke.Sto realizzando il mio perché mi è sembrato più appropriato per l'applicazione (ho bisogno solo di un piccolo sottoinsieme di funzionalità MIDI), ma per i tuoi scopi il Kit di strumenti MIDI C# potrebbe essere una soluzione migliore.È almeno la migliore libreria MIDI .NET che ho trovato e ho effettuato ricerche approfondite prima di iniziare il progetto.

midi-dot-net mi ha reso operativo in pochi minuti: leggero e delle dimensioni giuste per il mio progetto di casa.È disponibile anche su GitHub.(Da non confondere con quanto menzionato in precedenza MIDI.NET, che sembra promettente, ma non ci sono mai riuscito.)

Ovviamente NAudio (menzionato anche sopra) ha tantissime capacità, ma come il poster originale volevo solo suonare alcune note e leggere e comprendere rapidamente il codice sorgente.

Non posso affermare di saperne molto, ma non penso che sia così semplice - Carl Franklin di DotNetRocks la fama ha fatto un bel po' di questo, hai visto il suo DNRTV?

È possibile utilizzare il lettore multimediale:

using WMPLib;
//...
WindowsMediaPlayer wmp = new WindowsMediaPlayer();
wmp.URL = Path.Combine(Application.StartupPath ,"Resources/mymidi1.mid");
wmp.controls.play();

Per un'ampia manipolazione MIDI e Wave in .NET, penso a mani basse NAudio è la soluzione (disponibile anche tramite NuGet).

Una recente aggiunta è MIDI.NET che supporta porte Midi, file Midi e SysEx.

Sistema.Media.SoundPlayer è un modo semplice e valido per riprodurre file WAV.I file WAV presentano alcuni vantaggi rispetto al MIDI, uno dei quali è che puoi controllare esattamente il suono di ogni strumento (invece di fare affidamento sul sintetizzatore integrato del computer).

Mi dispiace, questa domanda è un po' vecchia ormai, ma la seguente ha funzionato per me (in qualche modo copiata da Win32: loop Midi con 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());
}

Viene fornito un elenco completo delle stringhe di comando Qui.La parte interessante di questo è che puoi semplicemente usare cose diverse oltre al sequenziatore per suonare cose differenti, ad esempio waveaudio per riprodurre file .wav.Tuttavia non riesco a capire come farlo riprodurre .mp3.

Inoltre, tieni presente che il comando stop e chiudi deve essere inviato sullo stesso thread su cui sono stati inviati i comandi open e play, altrimenti non avranno alcun effetto e il file rimarrà aperto.Per esempio:

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

Emerge un nuovo giocatore:

https://github.com/atsushieno/activated-midi

https://www.nuget.org/packages/owned-midi/

Non c'è molta documentazione, ma uno degli obiettivi di questa libreria è il supporto multipiattaforma.

Penso che sia molto meglio utilizzare qualche libreria che abbia funzionalità avanzate per la riproduzione dei dati MIDI invece di implementarla da soli.Ad esempio, con DryWetMIDI per riprodurre file MIDI tramite il sintetizzatore predefinito (Sintetizzatore 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 bloccherà il thread chiamante fino alla riproduzione dell'intero file.Per controllare la riproduzione di un file MIDI, ottenere Playback oggetto e usarlo Start/Stop metodi (maggiori dettagli nel Riproduzione articolo della biblioteca Wiki):

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();
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top