【问题标题】:Best way to play MIDI sounds using C#使用 C# 播放 MIDI 声音的最佳方式
【发布时间】:2010-09-05 17:51:39
【问题描述】:

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

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

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

【问题讨论】:

    标签: c# .net midi


    【解决方案1】:

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

    【讨论】:

    • Leslie 的 MIDI 工具包绝对是最全面的 C# 解决方案来播放、录制 MIDI。我已经将它用于一个非常复杂的项目并且效果很好。
    【解决方案2】:

    我认为你需要 p/invoke 到 windows api 才能从 .net 播放 MIDI 文件。

    这篇 codeproject 文章很好地解释了如何做到这一点: vb.net article to play midi files

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

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

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

    【讨论】:

    • 这个答案是公认的,但不完整且(非常)过时。当你想创建节拍器时,正确的方法是让声卡决定时间,而不是Windows。只是为每个刻度发出哔哔声 mciSendString 是不行的,字符串应该包含所有刻度并以某种方式循环。
    【解决方案3】:

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

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

    【讨论】:

      【解决方案4】:

      我认为最好使用一些具有高级功能的库来播放 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 对象并使用其Start/Stop 方法(更多详细信息请参见library docsPlayback 文章):

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

      【讨论】:

      • 感谢Maxim的辛勤工作。在VS2017中使用它,现在使用VS2019通过NuGet下载它。我真的很喜欢在假期里玩你的图书馆!在这里添加一些信息:这个库不仅允许播放,它可以创建 midi 文件,发出正确定时的 midi 命令 - 就像节拍器需要 - 它有一个非常方便的 managed 内部数据结构来分析 Midi文件!对游戏开发者来说也非常方便。playback.GetDuration() 以及 Stop() 和 Start() 再次播放的能力。它可以整齐地淡出,正确完成当前的Note,停止时没有裂缝。
      【解决方案5】:

      我不能声称对此了解很多,但我认为它并不那么简单 - DotNetRocks 成名的卡尔富兰克林在这方面做了相当多的事情 - 你见过 his DNRTV 吗?

      【讨论】:

        【解决方案6】:

        您可以使用媒体播放器:

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

        【讨论】:

          【解决方案7】:

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

          【讨论】:

            【解决方案8】:

            最近添加了支持 Midi 端口、Midi 文件和 SysEx 的 MIDI.NET

            【讨论】:

              【解决方案9】:

              对不起,这个问题现在有点老了,但以下对我有用(从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。

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

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

              【讨论】:

              • 使用 mciSendString("open " + fileName + " alias " + alias, new StringBuilder(), 0, new IntPtr());而不是 mciSendString("open " + fileName + " type sequencer alias " + alias, new StringBuilder(), 0, new IntPtr());可以播放 .mp3 文件(以及更多类似 .wav、.wma 等的文件)
              • 2021 年 1 月 .NET 5,这仍然有效。干得好。
              【解决方案10】:

              一个新玩家出现了:

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

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

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

              【讨论】:

                【解决方案11】:

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

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2011-03-09
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2021-03-14
                  相关资源
                  最近更新 更多