MidiInProc 回调在哪个上下文中执行?
Posted
技术标签:
【中文标题】MidiInProc 回调在哪个上下文中执行?【英文标题】:In which context is the MidiInProc callback executed? 【发布时间】:2020-02-10 20:18:13 【问题描述】:帮助!需要心平气和。 我正在编写一个使用 Windows MidiInProc 作为回调和虚拟 MIDI 端口的照明控制台应用程序。
'打开midi设备并设置回调 ret=midiInOpen(@hDevice,devNo,cast(uinteger,@MidiInProc),0,CALLBACK_FUNCTION)
当收到一条 midi 消息时,midiInProc 会访问一个循环队列来存储 midi 消息,因此不会丢弃任何消息(对于剧院照明非常重要),并且主程序会将它们出列以进行后续处理。
回调是如何工作的。它是中断主程序还是在自己的线程中运行还是什么?
回调是否有可能与主程序在尝试同时访问队列时发生冲突。如果是这样,我该如何防止这种情况发生?
已经用了3年了,至今没有出现任何问题,但不知道。
【问题讨论】:
移除标签 processing>。 Processing 是一个灵活的软件速写本,也是一种用于学习如何在视觉艺术环境中编码的语言。 【参考方案1】:回调是从另一个线程调用的,大部分时间。为了证明这一点,考虑这个改编自midi sample program gist的示例程序:
#include <Windows.h>
#include <stdio.h>
#include <conio.h>
#include <mmsystem.h>
#pragma comment(lib, "winmm.lib")
void CALLBACK MidiInProc(HMIDIIN hMidiIn, UINT wMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
printf("Callback thread id=%ld\n", GetCurrentThreadId());
switch (wMsg)
case MIM_OPEN:
printf("wMsg=MIM_OPEN\n");
break;
case MIM_CLOSE:
printf("wMsg=MIM_CLOSE\n");
break;
case MIM_DATA:
printf("wMsg=MIM_DATA, dwInstance=%Ix, dwParam1=%Ix, dwParam2=%Ix\n", dwInstance, dwParam1, dwParam2);
break;
case MIM_LONGDATA:
printf("wMsg=MIM_LONGDATA\n");
break;
case MIM_ERROR:
printf("wMsg=MIM_ERROR\n");
break;
case MIM_LONGERROR:
printf("wMsg=MIM_LONGERROR\n");
break;
case MIM_MOREDATA:
printf("wMsg=MIM_MOREDATA\n");
break;
default:
printf("wMsg = unknown\n");
break;
return;
int main(int argc, char* argv[])
HMIDIIN hMidiDevice = nullptr;;
DWORD nMidiPort = 2;
UINT nMidiDeviceNum;
MMRESULT rv;
printf("Main thread id=%ld\n", GetCurrentThreadId());
nMidiDeviceNum = midiInGetNumDevs();
if (nMidiDeviceNum == 0)
fprintf(stderr, "midiInGetNumDevs() return 0...");
return -1;
rv = midiInOpen(&hMidiDevice, nMidiPort, (DWORD_PTR)(void*)MidiInProc, 0, CALLBACK_FUNCTION);
if (rv != MMSYSERR_NOERROR)
fprintf(stderr, "midiInOpen() failed...rv=%d", rv);
return -1;
midiInStart(hMidiDevice);
while (true)
if (!_kbhit())
Sleep(100);
continue;
int c = _getch();
if (c == VK_ESCAPE) break;
if (c == 'q') break;
midiInStop(hMidiDevice);
midiInClose(hMidiDevice);
return 0;
在我的系统中执行它,连接了 3 个 MIDI 设备(#2 是一个控制器),按下并释放一个键后我得到这个输出:
Main thread id=9656
Callback thread id=9656
wMsg=MIM_OPEN
Callback thread id=5684
wMsg=MIM_DATA, dwInstance=0, dwParam1=513190, dwParam2=cfb
Callback thread id=5684
wMsg=MIM_DATA, dwInstance=0, dwParam1=403180, dwParam2=eaa
您可以在运行时在ProcessHacker2 或 SysInternals 的ProcessExplorer 中检查您的程序线程:
您可能会观察到您的进程中至少有 2 个线程 ID:9656 和 5684。您的 main()
函数线程 ID 是 9656,midiInOpen()
和 midiInClose()
函数调用的回调打印相同ID。但是对于note事件来说,线程id是5684,这个线程的起始地址对应的是wdmaud.drv
模块,这是一个Windows驱动程序。
这是处理 MIDI 输入的任何进程的典型场景:producer and consumer 问题。您的方法是合理的:您将接收到的 MIDI 事件排入回调函数(生产者)中,然后另一个线程使用排队的事件。您会发现许多适合该任务的lock free ring buffer 实现。
midiInOpen()
函数还有另一个变体,它使用最后一个参数标志 CALLBACK_WINDOW 或 CALLBACK_THREAD。在这种情况下,您可以向 Windows 提供窗口句柄或线程 ID,而不是回调函数,您的窗口或线程过程将接收排队的 MIDI 消息,并与其他不相关的窗口事件交错。我的偏好是使用 CALLBACK_FUNCTION。
【讨论】:
以上是关于MidiInProc 回调在哪个上下文中执行?的主要内容,如果未能解决你的问题,请参考以下文章
使用类方法作为回调时的 Promise.then 执行上下文