我正在使用C ++和Windows API编写Windows程序,并且正在尝试在MIDI流中对MIDI消息进行排队,但在尝试这样做时却收到了一个奇怪的错误。如果我使用midiOutShortMsg
将非排队的MIDI消息发送到流,它可以正常工作。但是,midiStreamOut
始终会返回错误代码68,即#define
d到MCIERR_WAVE_OUTPUTUNSPECIFIED
。 midiOutGetErrorText
给出了错误的以下描述:
当前的MIDI Mapper设置是指未安装在系统上的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 ) );
如何解决此问题?
答案 0 :(得分:2)
问题是我使用整个事件结构作为MIDI流的缓冲区。事实证明,结构的第四个成员dwParms
实际上应该从短消息中省略。要更正已发布问题中的代码,可以将两行代码更改为以下内容:
header.dwBufferLength = sizeof( *event ) - sizeof( event->dwParms );
header.dwBytesRecorded = sizeof( *event ) - sizeof( event->dwParms );
在向流中添加多个事件时,使用DWORD
数组实际上要轻松得多,而不是使用MIDIEVENT
结构。
对于使用Windows API进行MIDI编程的任何其他人,请注意某些MSDN文档具有误导性,不充分或完全错误。
MIDIEVENT
结构的文档说明如下:
dwParms
如果dwEvent指定MEVT_F_SHORT,请不要在流缓冲区中使用此成员。
这是不明确的,因为不清楚“使用”是指“包括”而不是“指定”。
以下是程序员需要注意的文档中的另外两个缺陷:
dwEvent
事件代码和事件参数或长度。 [...]该成员的高字节包含标志和事件代码。必须指定MEVT_F_LONG或MEVT_F_SHORT标志。 MEVT_F_CALLBACK标志是可选的。
当检查头文件时,MEVT_F_
预处理器定义实际上指定了完整的DWORD
而不仅仅是单个标志,所以在我的问题代码中,指定该成员的行应该是如下:
event->dwEvent = MEVT_F_SHORT | MEVT_SHORTMSG << 24 | ( msg & 0x00FFFFFF );
除此之外,还发现包含MIDIHDR结构的内存应该保留到缓冲区完成播放,因此对于大多数实现,它应该在堆而不是堆栈上分配。