مشكلة باستخدام تدفقات MIDI في Windows
سؤال
أنا أكتب برنامج Windows باستخدام C ++ و Windows API ، وأحاول طوابير رسائل MIDI في دفق MIDI ، لكنني أتلقى خطأً غريبًا عندما أحاول القيام بذلك. إذا استخدمت midiOutShortMsg
لإرسال رسالة MIDI غير المطلقة إلى الدفق ، فهي تعمل بشكل صحيح. لكن، midiStreamOut
يعيد دائمًا رمز الخطأ 68 ، وهو #define
د ل 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 باستخدام Windows API ، احذر من أن بعض وثائق 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 يجب الاحتفاظ بها حتى ينتهي المخزن المؤقت من اللعب ، لذلك يجب تخصيصه على الكومة بدلاً من المكدس لمعظم التطبيقات.