Отправка большого количества данных с pyportmidi перестает работать, если я не добавлю задержку
Я пытаюсь поговорить с панелью запуска Novation с pyportmidi. Я заметил, что если я просто продолжаю посылать ему инструкции, используя midiOut.WriteShort()
он обработает первые 100 или около того, а затем потеряет остальные.
Я предполагаю, что где-то есть буфер, который заполняется, и как только он заполнен, инструкции теряются. Я могу обойти эту проблему, добавив time.sleep(.1) после каждого сообщения, но это, очевидно, делает вещи очень медленными. Есть ли способ проверить, заполнен ли буфер, и спать только, если мне нужно? Или способ подождать, пока буфер опустеет, прежде чем я отправлю больше данных?
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 сообщений в секунду.
Я заметил, что в следующем коде, если я опущу время ожидания ниже, чем.002, MIDI не будет отправляться вообще, пока я не выйду из программы, тогда все события будут перенаправлены на MIDI BUS. Так что может быть проблема с ограничением скорости portmidi или в OSX.
Еще одна вещь, которую нужно иметь в виду, если вы действительно используете MIDI - скорее всего, значения смены элемента управления, если вы изменяете что-то вроде фильтра верхних частот, значение "1" звучит во многом как "2", поэтому если вы сделаете ваши сообщения менее детализированными (с приращением или уменьшением на 2 или 4), вы можете сократить количество сообщений без заметного различия в аудио. Это неоптимальное решение, и, по всей вероятности, ваш стек MIDI, вероятно, поддерживает намного быстрее, чем 31250 бод.
Еще одна вещь, которую следует учитывать, это то, что если вы подчиняете свое приложение portmidi к часам MIDI, вы можете получить надежный поток тиков от хоста MIDI, который вы могли бы использовать в качестве триггера для записи данных MIDI обратно (без спящего режима).
Удачи!
-n