3

我正在尝试使用 pyportmidi 与 Novation Launchpad 交谈。我注意到,如果我只是继续使用midiOut.WriteShort()它发送指令,它将处理前 100 个左右,然后释放其余的。

我猜某处有一个缓冲区快满了,一旦它满了,指令就会丢失。我可以通过在每条消息后添加 time.sleep(.1) 来解决该问题,但这显然会使事情变得非常缓慢。有没有办法测试缓冲区是否已满,只有在需要时才睡觉?或者在我发送更多数据之前等待缓冲区清空的方法?

4

1 回答 1

4

当我查看 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

PPQN 时钟 MIDI 1.0

于 2011-11-21T09:14:48.570 回答