задержка ввода с обратным вызовом PortAudio и ASIO SDK

Я пытаюсь получить вход от моей гитары, чтобы играть через мой компьютер, используя библиотеку portaudio и ASIO SDK.

Я следовал некоторым учебникам на официальном сайте, чтобы настроить основы. В настоящее время у меня все работает так, что portaudio слушает правильное устройство ввода и вывода, и у меня есть настройка обратного вызова, чтобы просто выводить ввод и ничего с ним не делать, например:

static int paTestCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void *userData)
{
float *out = (float*)outputBuffer;
float* in = (float*)inputBuffer;

for (int i = 0; i<framesPerBuffer; i++)
{
*out++ = *in++;  /* left */
*out++ = *in++;  /* right */
}
return 0;
}

Этот обратный вызов настраивается путем вызова этого:

PaError error = Pa_OpenDefaultStream(&stream, 2, 2, paFloat32, 44100, paFramesPerBufferUnspecified, paTestCallback, &data);
Pa_StartStream(stream);

Теперь, это работает, но у меня много задержек (около 0,5 с), когда я бью струну по моей гитаре и когда я слышу ее через мониторы.

Есть ли способ решить эту задержку? Нужно ли переписывать метод обратного вызова?

РЕДАКТИРОВАТЬ:

Итак, я получил задержку, чтобы быть намного лучше, используя этот код вместо основного Pa_OpenDefaultStream()

int defaultIn = Pa_GetDefaultInputDevice();
int defaultOut = Pa_GetDefaultOutputDevice();

PaStreamParameters *inParam = new PaStreamParameters();
inParam->channelCount = 2;
inParam->device = defaultIn;
inParam->sampleFormat = paFloat32;
inParam->suggestedLatency = 0.05;

PaStreamParameters *outParam = new PaStreamParameters();
outParam->channelCount = 2;
outParam->device = defaultOut;
outParam->sampleFormat = paFloat32;
outParam->suggestedLatency = 0;

error = Pa_OpenStream(&stream, inParam, outParam, 44100, paFramesPerBufferUnspecified, paNoFlag, paTestCallback, &data);
if (error != paNoError) {
Logger::log("[PortAudioManager] Could not open default stream. Exiting function...");
return;
}

Pa_StartStream(stream);

Тем не менее, все еще есть небольшая задержка, в основном заметная при игре более чем одной ноты.

РЕДАКТИРОВАТЬ:

С помощью Ross-Bencina я выяснил, что устройство ввода и вывода Windows по умолчанию ничего не меняет на индекс API хоста в PortAudio. Кажется, я все это время использовал MME. Я сделал следующее, чтобы получить правильный индекс для устройства ASIO:

int hostNr =  Pa_GetHostApiCount();

std::vector<const PaHostApiInfo*> infoVertex;
for (int t = 0; t < hostNr; ++t) {
infoVertex.push_back(Pa_GetHostApiInfo(t));
}

Затем я только что проверил, какой из них с ASIO, и установил предлагаемую латентность в обоих PaStreamParameters до 0, и задержка теперь прошла, и звук хороший (хотя сейчас он моно).

3

Решение

Вы на правильном пути, используя paFramesPerBufferUnspecified,

Время ожидания ASIO зависит от драйвера. Есть две возможности:

  1. Драйвер ASIO позволяет коду (т.е. PortAudio) запрашивать задержку (возможно, с некоторыми ограничениями). PortAudio находит наилучшее соответствие между поддерживаемым размером буфера драйвера и задержкой, которую вы запрашиваете.

  2. Другая возможность заключается в том, что ваш аудиоинтерфейс не обеспечивает программного управления настройками задержки. Вместо этого задержку можно выбрать только из пользовательского интерфейса панели управления ASIO драйвера (и драйвер принудительно установит фиксированный размер буфера на PortAudio). В этом случае вам следует изучить пользовательский интерфейс панели управления драйвером, чтобы установить минимальную возможную задержку.

В любом случае, ваш подход с Pa_OpenStream близка к оптимальной, но вы должны запросить нулевую задержку как для ввода, так и для вывода (при редактировании вы запрашиваете задержку на входе 50 мс, нулевую задержку на выходе) Конечным результатом будет то, что PortAudio выбирает наименьший доступный размер буфера ASIO. Если это оказывается нестабильным (аудио глюки), то вам нужно увеличить запрошенную задержку.

include/pa_asio.h предоставляет специфичный для хоста API интерфейс для запроса разрешенных драйвером размеров буфера ASIO (имейте в виду, что это может измениться, если вы измените настройки на панели управления). Он также предоставляет функцию отображения интерфейса панели управления водителя.

РЕДАКТИРОВАТЬ: Обратите внимание, что Pa_GetDefaultInputDevice() а также Pa_GetDefaultOutputDevice() вернет только устройства ASIO, если вы создали PortAudio только для ASIO. Если вы включили в сборку другие более распространенные API-интерфейсы (например, WMME или DirectSound), они получат приоритет как устройство по умолчанию (самый низкий общий знаменатель). Вы можете добавить проверку, что вы на самом деле получаете доступ к устройству ASIO:

assert(Pa_GetHostApiInfo(Pa_GetDeviceInfo(Pa_GetDefaultOutputDevice())->hostApi)->type == paASIO);

Если PortAudio скомпилирован с поддержкой нескольких API хостов: Чтобы получить устройство ASIO по умолчанию: перечислите API хостов, используя Pa_GetHostApiCount а также Pa_GetHostApiInfo найти API хоста ASIO. Затем извлеките индексы устройства ASIO по умолчанию из возвращенного PaHostApiInfo структура.

4

Другие решения

Других решений пока нет …