Mil*_*lKa 5 c# windows midi winapi multimedia
我正在使用 C# 中的古老 Windows 多媒体 API(WinMM.dll 中的 midiXyz 函数)。
在非流模式 ( )下打开 Midi Out 设备/端口后midiOutOpen,使用 ( ) 发送 SysExmidiOutLongMsg可以正常工作。
在流模式 ( )下打开 Midi Out 设备/端口后midiStreamOpen,使用 发送 SysExmidiOutLongMsg不起作用。
相反,midiOutLongMsg失败并出现错误MMSYSERR_NOTSUPPORTED(= 8)。错误文本为:“不支持此功能。使用功能函数来确定驱动程序支持哪些功能和消息。 ”
然而,根据 MSDN,( midiOutLongMsg) 也应该与流句柄一起使用。
Jeff Glatt 的优秀 MIDI 信息页面还声称 SysEx 和流媒体可以一起使用(参见页尾))。
通过使用 ( midiStreamOut) midiStreamOut 排队来发送缓冲的 SysEx 消息效果很好。但是,我也需要/想要直接使用 发送 SysEx midiOutLongMsg。
我已经检查了各种开源 Midi 库(托管和非托管)、几个 Midi 驱动程序源甚至 WINE 的 WinMM.dll 源,但找不到任何提示我做错了什么。
为了用尽可能小的代码重现我的问题,我删除了所有回调、未准备、清理和发布内容,并将多个类压缩为一个函数。以下代码打开第一个 Midi 设备/端口并尝试发送“GM Mode On”SysEx 消息:
2014 年 1 月 12 日更新:请参阅下面的代码版本 3!
public static class MidiTest { // version 1 - x86/32 bit only
public static void Test () {
int moID = 0; // midi out device/port ID
int moHdl; // midi out device/port handle
#if !true
// SysEx via midiOutLongMsg works
Chk (WinMM.midiOutOpen (out moHdl, moID, null, 0, 0)); // open midi out in non-stream mode
#else
// SysEx via midiOutLongMsg fails
Chk (WinMM.midiStreamOpen (out moHdl, ref moID, 1, null, 0, 0)); // open midi out in stream mode
#endif
byte [] sx = { 0xF0, 0x7E, 0x7F, 0x09, 0x01, 0xF7 }; // GM On sysex
int shdr = Marshal.SizeOf (typeof (MidiHdr)); // hdr size
var mhdr = new MidiHdr (); // allocate managed hdr
mhdr.bufferLength = mhdr.bytesRecorded = sx.Length; // length of message bytes
mhdr.data = Marshal.AllocHGlobal (mhdr.bufferLength); // allocate native message bytes
Marshal.Copy (sx, 0, mhdr.data, mhdr.bufferLength); // copy message bytes from managed to native memory
IntPtr nhdr = Marshal.AllocHGlobal (shdr); // allocate native hdr
Marshal.StructureToPtr (mhdr, nhdr, false); // copy managed hdr to native hdr
Chk (WinMM.midiOutPrepareHeader (moHdl, nhdr, shdr)); // prepare native hdr
Chk (WinMM.midiOutLongMsg (moHdl, nhdr, shdr)); // send native message bytes
} // Test
static void Chk (int f) {
if (0 == f) return;
var sb = new StringBuilder (256); // MAXERRORLENGTH
var s = 0 == WMM.midiOutGetErrorText (f, sb, sb.Capacity) ? sb.ToString () : String.Format ("MIDI Error {0}.", f);
System.Diagnostics.Trace.WriteLine (s);
}
[StructLayout (LayoutKind.Sequential)]
internal struct MidiHdr { // sending long MIDI messages requires a header
public IntPtr data; // native pointer to message bytes, allocated on native heap
public int bufferLength; // length of buffer 'data'
public int bytesRecorded; // actual amount of data in buffer 'data'
public int user; // custom user data
public int flags; // information flags about buffer
public IntPtr next; // reserved
public int reserved; // reserved
public int offset; // buffer offset on callback
[MarshalAs (UnmanagedType.ByValArray, SizeConst = 4)]
public int[] reservedArray; // reserved
} // struct MidiHdr
internal sealed class WinMM { // native MIDI calls from WinMM.dll
public delegate void CB (int hdl, int msg, int inst, int p1, int p2); // callback
[DllImport ("winmm.dll")] public static extern int midiStreamOpen (out int hdl, ref int devID, int reserved, CB proc, int inst, int flags);
[DllImport ("winmm.dll")] public static extern int midiOutOpen (out int hdl, int devID, CB proc, int inst, int flags);
[DllImport ("winmm.dll")] public static extern int midiOutPrepareHeader (int hdl, IntPtr pHdr, int sHdr);
[DllImport ("winmm.dll")] public static extern int midiOutLongMsg (int hdl, IntPtr pHdr, int sHdr);
[DllImport ("winmm.dll")] public static extern int midiOutGetErrorText (int err, StringBuilder msg, int sMsg);
} // class WinMM
} // class MidiTest
Run Code Online (Sandbox Code Playgroud)
问题 1:midiOutLongMsg当 Midi 设备/端口以流模式打开时,是否可以通过发送 SysEx ( midiStreamOpen)?
问题 2:如果是,知道我缺少什么吗?
问题 3:我没有找到很多在流模式下使用 MIDI 的源。因此,如果您知道一些在流模式下使用 MIDI 输出的开源库,请给我一个链接,以便我可以比较..
谢谢
更新(2013 年 10 月 19 日):
嗨,CL,
感谢您的回答。是的,也许微软的某个人在维护 WinMM.dll 时搞砸了一些东西 - 但我认为我遗漏某些东西的可能性更高,因为
a) 有一本旧手册“Windows NT DDK - 多媒体驱动程序”(可在此处获取),它比当前的 MSDN 页面更详细地描述了 WinMM 内容。第 56 页显示了一个图表,其中 WinMM.dll 作为应用程序和 MIDI/音频驱动程序之间的中间层。WinMM 的主要工作是将 MIDI 数据从应用程序传递到 MIDI/音频驱动程序之一。当 MIDI 驱动程序是外部键盘/合成器/音调发生器的端口驱动程序时,WinMM 无法对 MIDI 数据进行太多更改。第 94 页描述了驱动程序消息 MODM_LONGDATA 及其参数 - 它与 midiOutLongMsg 的参数几乎相同。这限制了 WinMM.dll 内部出现混乱的机会。
b) WinMM.dll 中从调用 midiOutLongMsg 到使用 MODM_LONGDATA 调用驱动程序的代码路径在 midiOutOpen 之后工作正常,但在 midiStreamOpen 之后则不行。结果代码是 MMSYSERR_NOTSUPPORTED - 这告诉我,我在 WinMM.dll 中的代码路径开头受到了一些健全性检查的打击,例如
if (whatever_weird_condition) return MMSYSERR_NOTSUPPORTED;
Run Code Online (Sandbox Code Playgroud)
而whatever_weird_condition很可能是关于我应该做但没有做的事情..
c) 如果 MIDI 驱动程序本身不支持流输出(其可选),WinMM 会将缓冲/排队输出 (midiStreamOut) 转换为更简单的非流驱动程序调用(这不是可选的)。我的机器上的 8 个 MIDI 驱动程序都不支持流本身,全部依靠 WinMM 来完成。流式传输短消息和长消息效果很好。
d) 我的测试代码在 Windows 7 和 Windows XP 上的行为完全相同。如果微软搞砸了一些事情,那么这个 bug 一定是很久以前(在 XP 之前)就已经被制造出来的了。我要么是第一个发现它的人(多年后),要么其他人都将其保密并且无法通过谷歌搜索。
e) 我的测试代码与我的机器上的所有 8 个 MIDI 驱动程序的行为完全相同。这告诉我,这很可能不是驱动程序问题。
f) 多年的调试告诉我,如果某些东西不能正常工作,问题很可能出在我的屏幕一侧..;-P
| 归档时间: |
|
| 查看次数: |
4300 次 |
| 最近记录: |