在Windows中使用MIDI流时出现问题

时间:2010-01-21 08:33:12

标签: c++ windows winapi midi

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

  

当前的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 ) );

如何解决此问题?

1 个答案:

答案 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结构的内存应该保留到缓冲区完成播放,因此对于大多数实现,它应该在堆而不是堆栈上分配。