使用C#播放MIDI声音的最佳方式

时间:2008-08-12 12:34:08

标签: c# .net midi

我正在尝试重建一个旧的节拍器应用程序,该应用程序最初使用C ++中的MFC编写,使用.NETC#中编写。我遇到的一个问题是播放用于代表节拍器“点击”的midi文件。

我在网上发现了一些关于在.NET中播放MIDI的文章,但是大多数文章似乎都依赖于有人拼凑在一起并提供的自定义库。我不反对使用这些,但我宁愿自己理解这是如何完成的,因为它似乎应该是一个非常微不足道的练习。

那么,我错过了什么吗?或者在.NET应用程序中使用MIDI是否很困难?

11 个答案:

答案 0 :(得分:11)

我正在研究一个C#MIDI应用程序,其他人都是对的 - 你需要使用p / invoke来实现这个目的。我正在自己动手,因为它似乎更适合应用程序(我只需要一小部分MIDI功能),但为了您的目的,C# MIDI Toolkit可能更适合。它至少是我找到的最好的.NET MIDI库,在开始项目之前我进行了广泛的搜索。

答案 1 :(得分:10)

我认为你需要p / invoke到windows api才能播放来自.net的midi文件。

此代码项目文章在解释如何执行此操作方面做得很好: vb.net article to play midi files

要重写这是c#,你需要mciSendString的以下import语句:

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

希望这会有所帮助 - 祝你好运!

答案 2 :(得分:4)

midi-dot-net让我在几分钟内完成并运行 - 轻量级,适合我的家庭项目。它也可以在GitHub上找到。 (不要与前面提到的MIDI.NET混淆,后者看起来也很有希望,我从来没有接触到它。)

当然NAudio(上面也提到过)有很多功能,但是就像原版海报一样,我只想播放一些笔记并快速阅读和理解源代码。

答案 3 :(得分:1)

我不能声称对此有太多了解,但我认为这并不是那么简单 - DotNetRocks成名的卡尔·富兰克林已经做了很多 - 你见过his DNRTV吗?

答案 4 :(得分:1)

您可以使用媒体播放器:

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

答案 5 :(得分:1)

对于.NET中广泛的MIDI和Wave操作,我认为放下NAudio是解决方案(也可通过NuGet获得)。

答案 6 :(得分:1)

最近添加了MIDI.NET,支持Midi Ports,Midi Files和SysEx。

答案 7 :(得分:0)

System.Media。 SoundPlayer 是播放WAV文件的一种简单方法。 WAV文件比MIDI有一些优势,其中之一就是你可以精确控制每个乐器的声音(而不是依赖于计算机的内置合成器)。

答案 8 :(得分:0)

对不起,这个问题现在有点老了,但以下对我有用(有点复制自Win32 - Midi looping with 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());
}

命令字符串的完整列表为here。关于这个很酷的部分是你可以使用除音序器之外的其他东西来播放different things,比如waveaudio来播放.wav文件。我无法弄明白如何让它玩.mp3。

另请注意,stop和close命令必须在发送open和play命令的同一线程上发送,否则它们将无效并且文件将保持打开状态。例如:

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

答案 9 :(得分:0)

一个新玩家出现了

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

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

文档编制方式不多,但是该库的重点是跨平台支持。

答案 10 :(得分:0)

我认为最好使用一些具有MIDI数据回放的高级功能的库,而不是自己实现。例如,用DryWetMIDI通过默认合成器( Microsoft GS Wavetable Synth )播放MIDI文件:

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将阻塞调用线程,直到播放完整个文件。要控制MIDI文件的播放,请获取Playback对象并使用其Start / Stop方法(有关详细信息,请参见库PlaybackWiki文章) :

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