2

我正在使用 C++ 和 Windows API 编写一个 Windows 程序,并且我试图在 MIDI 流中对 MIDI 消息进行排队,但是当我尝试这样做时收到一个奇怪的错误。如果我使用midiOutShortMsg向流发送非排队 MIDI 消息,它可以正常工作。但是,midiStreamOut总是返回错误代码 68,即#defined to MCIERR_WAVE_OUTPUTUNSPECIFIEDmidiOutGetErrorText给出了错误的以下描述:

当前的 MIDI 映射器设置是指系统上未安装的 MIDI 设备。使用 MIDI Mapper 编辑设置。

我正在使用 Windows 7(64 位)并尝试使用 MIDI_MAPPER 和我系统上所有四个 MIDI 输出设备的设备 ID 打开 MIDI 流,但仍然收到完全相同的错误消息。

这是打开 MIDI 流的代码:

UINT device_id = MIDI_MAPPER; //Also tried 0, 1, 2 and 3
midiStreamOpen( &midi, &device_id, 1, ( DWORD_PTR )hwnd, 0, CALLBACK_WINDOW );

这是发送 MIDI 消息的代码:

MIDIHDR header;
MIDIEVENT *event;

event = ( MIDIEVENT * )malloc( sizeof( *event ) );
event->dwDeltaTime = delta_time;
event->dwStreamID = 0;
event->dwEvent = ( MEVT_F_SHORT | MEVT_SHORTMSG ) << 24 | ( msg & 0x00FFFFFF );

header.lpData = ( LPSTR )event;
header.dwBufferLength = sizeof( *event );
header.dwBytesRecorded = sizeof( *event );
header.dwUser = 0;
header.dwFlags = 0;
header.dwOffset = 0;

midiOutPrepareHeader( ( HMIDIOUT )midi, &header, sizeof( header ) );
midiStreamOut( midi, &header, sizeof( header ) );

我该如何解决这个问题?

4

1 回答 1

2

问题是我使用整个事件结构作为 MIDI 流的缓冲区。事实证明,结构的第四个成员,dwParms实际上应该从短消息中省略。要更正已发布问题中的代码,可以将其中两行代码更改为以下内容:

header.dwBufferLength = sizeof( *event ) - sizeof( event->dwParms );
header.dwBytesRecorded = sizeof( *event ) - sizeof( event->dwParms );

当向流中添加多个事件时,实际上只使用 s 数组DWORD而不是MIDIEVENT结构要容易得多。

对于使用 Windows API 进行 MIDI 编程的任何其他人,请注意某些 MSDN 文档具有误导性、不充分或完全错误。

MIDIEVENT结构的文档说明如下:

dwParms

如果 dwEvent 指定 MEVT_F_SHORT,请不要在流缓冲区中使用该成员。

这是模棱两可的,因为不清楚“使用”的意思是“包含”而不是“指定”。

以下是程序员需要注意的文档中的另外两个缺陷:

dwEvent

事件代码和事件参数或长度。[...] 该成员的高字节包含标志和事件代码。必须指定 MEVT_F_LONG 或 MEVT_F_SHORT 标志。MEVT_F_CALLBACK 标志是可选的。

检查头文件时,MEVT_F_预处理器定义实际上指定了 complete DWORDs 而不仅仅是单个标志,因此在我的问题代码中,指定此成员的行应该如下所示:

event->dwEvent = MEVT_F_SHORT | MEVT_SHORTMSG << 24 | ( msg & 0x00FFFFFF );

除此之外,事实证明,包含 MIDI HDR 结构的内存应该保留到缓冲区播放完毕,因此对于大多数实现来说,它应该分配在堆上而不是堆栈上。

于 2010-01-22T00:03:12.767 回答