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

发布于 2024-07-04 04:01:59 字数 315 浏览 7 评论 0原文

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

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

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

I'm trying to rebuild an old metronome application that was originally written using MFC in C++ to be written in .NET using C#. One of the issues I'm running into is playing the midi files that are used to represent the metronome "clicks".

I've found a few articles online about playing MIDI in .NET, but most of them seem to rely on custom libraries that someone has cobbled together and made available. I'm not averse to using these, but I'd rather understand for myself how this is being done, since it seems like it should be a mostly trivial exercise.

So, am I missing something? Or is it just difficult to use MIDI inside of a .NET application?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

一个人的旅程 2024-07-11 04:01:59

我不能声称对它了解很多,但我认为事情没有那么简单 - DotNetRocks 名声已经发挥了相当大的作用 - 你看过他的 DNRTV

I can't claim to know much about it, but I don't think it's that straightforward - Carl Franklin of DotNetRocks fame has done a fair bit with it - have you seen his DNRTV?

残月升风 2024-07-11 04:01:59

您可以使用媒体播放器:

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

You can use the media player:

using WMPLib;
//...
WindowsMediaPlayer wmp = new WindowsMediaPlayer();
wmp.URL = Path.Combine(Application.StartupPath ,"Resources/mymidi1.mid");
wmp.controls.play();
清秋悲枫 2024-07-11 04:01:59

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

For extensive MIDI and Wave manipulation in .NET, I think hands down NAudio is the solution (Also available via NuGet).

┈┾☆殇 2024-07-11 04:01:59

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

A recent addition is MIDI.NET that supports Midi Ports, Midi Files and SysEx.

朕就是辣么酷 2024-07-11 04:01:59

抱歉,这个问题现在有点老了,但以下内容对我有用(部分复制自 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);
}

Sorry this question is a little old now, but the following worked for me (somewhat copied from 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());
}

A full listing of command strings is given here. The cool part about this is you can just use different things besides sequencer to play different things, say waveaudio for playing .wav files. I can't figure out how to get it to play .mp3 though.

Also, note that the stop and close command must be sent on the same thread that the open and play commands were sent on, otherwise they will have no effect and the file will remain open. For example:

[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);
}
萌逼全场 2024-07-11 04:01:59

出现了一个新玩家:

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

https://www.nuget.org/packages/management-midi/ 内容

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

A new player emerges:

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

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

Not much in the way of documentation, but one focus of this library is cross platform support.

写给空气的情书 2024-07-11 04:01:59

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

System.Media.SoundPlayer is a good, simple way of playing WAV files. WAV files have some advantages over MIDI, one of them being that you can control precisely what each instrument sounds like (rather than relying on the computer's built-in synthesizer).

暮色兮凉城 2024-07-11 04:01:59

我认为您需要 p/invoke 到 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);

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

I think you'll need to p/invoke out to the windows api to be able to play midi files from .net.

This codeproject article does a good job on explaining how to do this:
vb.net article to play midi files

To rewrite this is c# you'd need the following import statement for mciSendString:

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

Hope this helps - good luck!

一口甜 2024-07-11 04:01:59

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

当然 NAudio (上面也提到过)有很多功能,但就像原来的海报一样,我只是想要玩一些音符并快速阅读和理解源代码。

midi-dot-net got me up and running in minutes - lightweight and right-sized for my home project. It's also available on GitHub. (Not to be confused with the previously mentioned MIDI.NET, which also looks promising, I just never got around to it.)

Of course NAudio (also mentioned above) has tons of capability, but like the original poster I just wanted to play some notes and quickly read and understand the source code.

疾风者 2024-07-11 04:01:59

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

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 方法(更多详细信息请参阅 回放 库文档):

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

I think it's much better to use some library that which has advanced features for MIDI data playback instead of implementing it by your own. For example, with DryWetMIDI (I'm the author of it) to play MIDI file via default synthesizer (Microsoft GS Wavetable Synth):

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 will block the calling thread until entire file played. To control playback of a MIDI file, obtain Playback object and use its Start/Stop methods (more details in the Playback article of the library docs):

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();
薄暮涼年 2024-07-11 04:01:59

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

I'm working on a C# MIDI application at the moment, and the others are right - you need to use p/invoke for this. I'm rolling my own as that seemed more appropriate for the application (I only need a small subset of MIDI functionality), but for your purposes the C# MIDI Toolkit might be a better fit. It is at least the best .NET MIDI library I found, and I searched extensively before starting the project.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文