题
我正在尝试重建一个旧的节拍器应用程序,该应用程序最初是使用 MFC
在 C++ 中编写 .NET
使用 C#
. 。我遇到的问题之一是播放用于表示节拍器“点击”的 MIDI 文件。
网上找了几篇关于玩的文章 MIDI
在 .NET 中,但其中大多数似乎依赖于某人拼凑起来并提供的自定义库。我并不反对使用这些,但我宁愿自己了解这是如何完成的,因为看起来就像这样 应该 是一个非常简单的练习。
那么,我错过了什么吗?或者只是在 .NET 应用程序中使用 MIDI 很困难?
解决方案
我认为您需要 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);
希望这有帮助,祝你好运!
其他提示
我目前正在开发一个 C# MIDI 应用程序,其他人是对的 - 您需要为此使用 p/invoke。我正在滚动自己的应用程序,因为这似乎更适合该应用程序(我只需要 MIDI 功能的一小部分),但出于您的目的, C# MIDI 工具包 可能更合适。它至少是我发现的最好的 .NET MIDI 库,并且在开始该项目之前我进行了广泛的搜索。
您可以使用媒体播放器:
using WMPLib;
//...
WindowsMediaPlayer wmp = new WindowsMediaPlayer();
wmp.URL = Path.Combine(Application.StartupPath ,"Resources/mymidi1.mid");
wmp.controls.play();
最近添加的是 MIDI网络 支持 Midi 端口、Midi 文件和 SysEx。
系统.媒体.声音播放器 是一种播放 WAV 文件的好、简单的方法。WAV 文件比 MIDI 有一些优势,其中之一是您可以精确控制每种乐器的声音(而不是依赖计算机的内置合成器)。
抱歉,这个问题现在有点老了,但以下内容对我有用(有点复制自 Win32 - 使用 MCISendString 进行 Midi 循环):
[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);
}
一个新的玩家出现了:
https://github.com/atsushieno/management-midi
https://www.nuget.org/packages/management-midi/
文档不多,但该库的重点之一是跨平台支持。
我认为最好使用一些具有 MIDI 数据播放高级功能的库,而不是自己实现。例如,与 干湿MIDI 通过默认合成器播放 MIDI 文件(微软 GS 波表合成器):
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
方法(更多详细信息,请参阅 回放 图书馆的文章 维基百科):
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();