我正在尝试使用 pyportmidi 与 Novation Launchpad 交谈。我注意到,如果我只是继续使用midiOut.WriteShort()
它发送指令,它将处理前 100 个左右,然后释放其余的。
我猜某处有一个缓冲区快满了,一旦它满了,指令就会丢失。我可以通过在每条消息后添加 time.sleep(.1) 来解决该问题,但这显然会使事情变得非常缓慢。有没有办法测试缓冲区是否已满,只有在需要时才睡觉?或者在我发送更多数据之前等待缓冲区清空的方法?
我正在尝试使用 pyportmidi 与 Novation Launchpad 交谈。我注意到,如果我只是继续使用midiOut.WriteShort()
它发送指令,它将处理前 100 个左右,然后释放其余的。
我猜某处有一个缓冲区快满了,一旦它满了,指令就会丢失。我可以通过在每条消息后添加 time.sleep(.1) 来解决该问题,但这显然会使事情变得非常缓慢。有没有办法测试缓冲区是否已满,只有在需要时才睡觉?或者在我发送更多数据之前等待缓冲区清空的方法?
当我查看 SVN 存储库时,我在包装器代码中遇到了这个问题,请注意“为什么 bufferSize 在这里为 0?” 评论..
def __init__(self, OutputDevice, latency=0):
...stuff...
# Why is bufferSize 0 here?
err = Pm_OpenOutput(&(self.midi), self.i, NULL, 0, PmPtr, NULL, latency)
API 文档显示 Pm_OpenOutput 具有以下签名
PmError Pm_OpenOutput (
PortMidiStream **stream,
PmDeviceID outputDevice,
void *outputDriverInfo,
long bufferSize,
PmTimeProcPtr time_proc,
void *time_info,
long latency
)
似乎没有任何明显的方法可以找出当前缓冲区堆栈长度,更重要的是,Python 包装器似乎完全忽略了缓冲区设置。
portmidi.c 讲述了一个稍微不同的故事:
if (bufferSize <= 0) bufferSize = 256; /* default buffer size */
midi->queue = Pm_QueueCreate(bufferSize, sizeof(PmEvent));
if (!midi->queue) {
/* free portMidi data */
*stream = NULL;
pm_free(midi);
err = pmInsufficientMemory;
goto error_return;
}
因此,256 是默认值。这可以解释为什么你会遇到大约 100 个左右的问题。
但是,需要记住的一点 - MIDI 非常慢,31250 波特(每秒 31250 位),因为 MIDI 消息(通常)是 2 字节(16 位),这意味着每秒最多 1953 条消息。(我可能在这里错了,但如果我错了,我已经很接近了)
但是,有希望:一个简单的解决方法是,您可以在大多数操作系统上休眠至 2 毫秒,而不会搞砸。
time.sleep(.002) # 2 millisecond sleep
但是,由于您使用的是 write_short(),因此每秒只会给您 500 条消息。所以你可能想做一些事情,比如有一个队列,每 0.002 秒轮询一次传出消息,从堆栈中弹出 16 个,写入它们然后休眠。这样,如果您的整个 MIDI 堆栈支持如此快的速率,您每秒可以获得 8000 条消息。
我注意到在下面的代码中,如果我将睡眠时间设置为低于 0.002,则在我退出程序之前根本不会发送任何 MIDI ,然后所有事件都会涌入 MIDI BUS。因此,portmidi 速率限制或 OSX 可能存在问题。
要记住的另一件事是,如果您真的要爆破 MIDI - 很可能是控制更改值,如果您正在修改诸如高通滤波器之类的东西,“1”的值听起来很像“2”,所以如果您减少消息的粒度(增加或减少 2 或 4),您可以减少消息的数量,而不会在音频中产生明显的差异。这是一个次优的解决方案,您的 MIDI 堆栈很可能支持比 31250 波特快得多的速度。
要考虑的另一件事是,如果您将 portmidi 应用程序从属到 MIDI 时钟,您可以从 MIDI 主机获得可靠的滴答流,您可以将其用作将 MIDI 数据写回的触发器(无需休眠)。
祝你好运!
-n