繁体   English   中英

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

[英]Best way to play MIDI sounds using C#

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

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

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

我目前正在开发一个 C# MIDI 应用程序,其他人是对的 - 您需要为此使用 p/invoke。 我正在滚动我自己的,因为这似乎更适合应用程序(我只需要一小部分 MIDI 功能),但出于您的目的, C# MIDI 工具包可能更适合。 它至少是我找到的最好的 .NET MIDI 库,并且在开始该项目之前我进行了广泛的搜索。

我认为您需要调用/调用 windows api 才能从 .net 播放 midi 文件。

这篇 codeproject 文章很好地解释了如何执行此操作: vb.net 文章播放 midi 文件

要重写这是 c#,您需要以下 mciSendString 的导入语句:

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

希望这有帮助,祝你好运!

midi-dot-net让我在几分钟内启动并运行 - 轻巧且适合我的家庭项目。 它也可以在GitHub找到 (不要与前面提到的MIDI.NET混淆,它看起来也很有前途,我只是从来没有接触过它。)

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

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

using Melanchall.DryWetMidi.Devices;
using Melanchall.DryWetMidi.Core;

// ...

var midiFile = MidiFile.Read("Greatest song ever.mid");

using (var outputDevice = OutputDevice.GetByName("Microsoft GS Wavetable Synth"))
{
    midiFile.Play(outputDevice);
}

Play将阻塞调用线程,直到播放整个文件。 要控制 MIDI 文件的Playback ,请获取Playback对象并使用其Start / Stop方法(更多详细信息请参见库文档Playback文章):

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

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

最近添加的是MIDI.NET ,它支持 Midi 端口、Midi 文件和 SysEx。

一个新玩家出现:

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

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

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

您可以使用媒体播放器:

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

我不能声称对此了解很多,但我认为它没有那么简单——DotNetRocks名声中的卡尔富兰克林对此做了很多——你看过他的 DNRTV吗?

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

此处给出了命令字符串的完整列表。 关于这个很酷的部分是你可以使用除了音序器之外的不同东西来播放不同的东西,比如用于播放 .wav 文件的 waveaudio。 我不知道如何让它播放 .mp3。

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

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

系统.媒体。 SoundPlayer是一种播放 WAV 文件的好方法。 WAV 文件比 MIDI 有一些优势,其中之一是您可以精确控制每种乐器的声音(而不是依赖计算机的内置合成器)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM