Проблема с использованием потоков MIDI в Windows
Вопрос
Я пишу программу Windows, используя C ++ и API Windows, и пытаюсь стоять в очереди MIDI -сообщения в потоке MIDI, но получаю странную ошибку, когда пытаюсь это сделать. Если я использую midiOutShortMsg
Чтобы отправить не обездоленное сообщение MIDI в поток, оно работает правильно. Однако, midiStreamOut
всегда возвращает код ошибки 68, который #define
D к MCIERR_WAVE_OUTPUTUNSPECIFIED
. midiOutGetErrorText
дает следующее описание ошибки:
Текущая настройка MIDI Mapper относится к устройству MIDI, которое не установлено в системе. Используйте Midi Mapper, чтобы редактировать настройку.
Я использую Windows 7 (64-бит) и попытался открыть поток MIDI с идентификаторами устройства как MIDI_MAPPER, так и всех четырех устройств 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 ) );
Как я могу решить эту проблему?
Решение
Проблема заключалась в том, что я использовал всю структуру событий в качестве буфера для потока MIDI. Оказывается, четвертый член структуры, dwParms
, на самом деле должно быть опущено из коротких сообщений. Чтобы исправить код в опубликованном вопросе, две строки кода могут быть изменены на следующее:
header.dwBufferLength = sizeof( *event ) - sizeof( event->dwParms );
header.dwBytesRecorded = sizeof( *event ) - sizeof( event->dwParms );
При добавлении нескольких событий в поток на самом деле намного проще использовать массив DWORD
S вместо того, чтобы даже беспокоиться о MIDIEVENT
структуры.
Для тех, кто занимается программированием MIDI, используя API Windows, остерегайтесь, что часть документации MSDN вводит в заблуждение, неадекватно или совершенно неправильно.
Документация для MIDIEVENT
Структура говорит следующее:
DWPARMS
Если DWEVENT указывает MEVT_F_SHORT, не используйте этот член в буфере потока.
Это неоднозначно, потому что неясно, что «использование» предназначено для обозначения «включать», а не «указать».
Вот два других недостатка в документации, о которых должны знать программисты:
Dwevent
Код события и параметры или длину события. [...] Высокий байт этого участника содержит флаги и код события. Либо должен быть указан флаг MEVT_F_LONG или MEVT_F_SHORT. Флаг MEVT_F_CALLBACK не является обязательным.
Когда файлы заголовков проверяются, MEVT_F_
Определения препроцессора фактически указывают комплекс DWORD
S, а не только отдельные флаги, поэтому в моем коде в вопросе строка, указанная на этом участнике, должна была быть следующей:
event->dwEvent = MEVT_F_SHORT | MEVT_SHORTMSG << 24 | ( msg & 0x00FFFFFF );
В дополнение к этому, также выяснилось, что память, содержащая структуру Midihdr, должна быть сохранена до тех пор, пока буфер не закончит игру, поэтому она должна быть выделена на кучу, а не в стеке для большинства реализаций.