代码之家  ›  专栏  ›  技术社区  ›  jerhinesmith

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

  •  18
  • jerhinesmith  · 技术社区  · 17 年前

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

    我在网上找到一些关于玩的文章 MIDI 在.NET中,但大多数库似乎都依赖于某个人拼凑起来并提供的自定义库。我不反对使用这些,但我宁愿自己理解这是怎么做的,因为它看起来像 应该 只是一个微不足道的练习。

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

    11 回复  |  直到 7 年前
        1
  •  10
  •   John Sibly    11 年前

    我认为您需要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
  •  11
  •   Kjetil Limkjær    17 年前

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

        3
  •  4
  •   Paul Williams    10 年前

    midi-dot-net 在几分钟内让我起床并开始跑步-重量轻,适合我的家庭项目。它也可以在 GitHub . (不要与前面提到的混淆 MIDI.NET 这看起来也很有希望,但我从来没有接触过。)

    当然 NAudio (上面也提到)有很多功能,但是像最初的海报一样,我只想播放一些注释,快速阅读和理解源代码。

        4
  •  1
  •   Whisk    17 年前

    我不能声称对这件事了解得太多,但我不认为那是直接的——卡尔·富兰克林 DotNetRocks 名声对它起了很大作用-你见过吗 his DNRTV ?

        5
  •  1
  •   Carra    14 年前

    您可以使用媒体播放器:

    using WMPLib;
    //...
    WindowsMediaPlayer wmp = new WindowsMediaPlayer();
    wmp.URL = Path.Combine(Application.StartupPath ,"Resources/mymidi1.mid");
    wmp.controls.play();
    
        6
  •  1
  •   Shimmy Weitzhandler 500 - Internal Server Error    12 年前

    对于.NET中广泛的MIDI和Wave操作,我认为应该放在手边。 NAudio 解决方案(也可通过 NuGet )

        7
  •  1
  •   obiwanjacobi    12 年前

    最近添加的是 MIDI.NET 支持MIDI端口、MIDI文件和Sysex。

        8
  •  0
  •   MusiGenesis    17 年前

    系统。媒体。 声音播放器 是播放wav文件的一种好的、简单的方法。wav文件比midi有一些优势,其中之一就是你可以精确地控制每种乐器的声音(而不是依赖于电脑内置的合成器)。

        9
  •  0
  •   Community Mohan Dere    8 年前

    对不起,这个问题现在有点老了,但下面的内容对我很有用(有些抄袭自 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 ,播放.wav文件时说waveaudio。我不知道怎么让它播放。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);
    }
    
        10
  •  0
  •   Ronnie Overby    7 年前

    新玩家出现:

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

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

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

        11
  •  0
  •   Maxim    7 年前

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

    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 方法(更多详细信息请参见 Playback 图书馆的文章 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();
    
    推荐文章