PyAudio - синхронизация воспроизведения и записи

В настоящее время я использую PyAudio для работы над облегченной утилитой записи, которая соответствует конкретным потребностям приложения, которое я планирую. Я работаю с аудиоинтерфейсом ASIO. Я пишу программу, чтобы воспроизводить wav-файл через интерфейс, одновременно записывая вывод из интерфейса. Интерфейс обрабатывает сигнал в реальном времени и изменяет звук. Поскольку я собираюсь импортировать этот визуализированный вывод в DAW, мне нужно, чтобы вывод был идеально синхронизирован с входным звуком. Используя DAW, я могу одновременно воспроизводить звук в своем интерфейсе и записывать результат. Когда я это делаю, он отлично синхронизируется в DAW. Цель моей утилиты - иметь возможность запускать это из скрипта Python.

Используя метод грубой силы, я нашел решение, которое работает, но теперь я застрял с магическим числом и не уверен, является ли это какой-то константой или чем-то, что я могу вычислить. Если это число, которое я могу вычислить, это было бы идеально, но я все же хотел бы понять, откуда оно взялось.

Мой обратный вызов выглядит следующим образом:

def testCallback(in_data, frame_count, time_info, status):
    #read data from wave file
    data = wave_file.readframes(frame_count)
    #calculate number of latency frames for playback and recording
    #1060 is my magic number
    latencyCalc = math.ceil((stream.get_output_latency() + stream.get_input_latency()) * wave_file.getframerate()) + 1060

    #no more data in playback file
    if data == "":
        #this is the number of times we must keep the loop alive to capture all playback
        recordEndBuffer = latencyCalc / frame_count
        if lastCt < recordEndBuffer:
            #return 0-byte data to keep callback alive
            data = b"0"*wave_file.getsampwidth()*frame_count
            lastCt += 1
    #we start recording before playback, so this accounts for the initial "pre-playback" data in the output file
    if firstCt > (latencyCalc/frame_count):
        wave_out.writeframes(in_data)
    else:
       firstCt += 1
    return (data, pyaudio.paContinue)

Меня беспокоит функция:

latencyCalc = math.ceil((stream.get_output_latency() + stream.get_input_latency()) * wave_file.getframerate()) + 1060

Я объединил этот расчет, наблюдая смещение моего выходного файла по сравнению с исходным файлом воспроизведения. Произошли две вещи: мой выходной файл начинался позже, чем исходный файл при одновременном воспроизведении, и он также заканчивался раньше. Методом проб и ошибок я определил, что это было определенное количество кадров, лишних в начале и отсутствующих в конце. Это вычисляет это количество кадров. Я понимаю первую часть, это задержки ввода / вывода (предоставленные с точностью до секунды / субсекунды), преобразованные в кадры с использованием частоты дискретизации. Но я не совсем уверен, как ввести значение 1060, поскольку я не уверен, откуда оно взялось.

Я обнаружил, что, играя с настройками задержки в моем драйвере ASIO, мое приложение продолжает правильно синхронизировать записанный файл, даже если указанные выше задержки вывода / ввода изменяются из-за настройки (задержки ввода / вывода всегда одинаковы), так что 1060 кажется стабильным на моей машине. Однако я просто не знаю, можно ли рассчитать это значение. Или, если это конкретная константа, я не уверен, что именно она представляет.

Приветствуется любая помощь в лучшем понимании этих ценностей. Я рад, что моя утилита теперь работает правильно, но я хотел бы полностью понять, что здесь происходит, поскольку я подозреваю, что потенциально использование другого интерфейса, вероятно, больше не будет работать правильно (я хотел бы поддержать это в будущем по нескольким причинам ).

ИЗМЕНИТЬ 8 апреля 2014 г. в ответ Роберто: значение, которое я получаю для latencyCalc = math.ceil ((stream.get_output_latency () + stream.get_input_latency ()) * wave_file.getframerate ()) + 1060 равно 8576, а дополнительные 1060 увеличивают общую задержку до 9636 кадров. Вы правы в своем предположении, почему я добавил 1060 кадров. Я воспроизводю файл через внешний интерфейс ASIO, и обработка, которую я надеюсь зафиксировать в моем записанном файле, является результатом обработки, происходящей в интерфейсе (а не тем, что я закодировал). Чтобы сравнить выходные данные, я просто воспроизвел тестовый файл и записал выходные данные интерфейса без каких-либо эффектов обработки, задействованных в интерфейсе. Затем я исследовал две дорожки в Audacity и методом проб и ошибок определил, что 1060 - это самое близкое, что я мог бы выровнять. С тех пор я понял, что он все еще не совсем идеален, но он невероятно близок и не слышен при одновременном воспроизведении (что неверно, когда удалено смещение 1060, есть заметная задержка). Добавление / удаление дополнительного кадра тоже слишком большая компенсация по сравнению с 1060.

Я считаю, что вы правы, что дополнительная задержка связана с внешним интерфейсом. Сначала мне было интересно, можно ли это вычислить с помощью имеющейся у меня числовой информации, но я пришел к выводу, что это просто константа в интерфейсе. Я считаю, что это правда, поскольку я определил, что если я удалю 1060, смещение файла будет точно таким же, как при выполнении того же теста, но вручную в Reaper (это именно тот процесс, который я автоматизирую). У меня задержка намного лучше, чем у жнеца с моим новым смещением грубой силы, поэтому я собираюсь назвать это победой. В моем приложении цель состоит в том, чтобы полностью заменить исходный файл новым обработанным файлом, поэтому желательна абсолютная минимальная задержка между ними.

Отвечая на ваш вопрос об ASIO в PyAudio, к счастью, ответ положительный. Вы должны скомпилировать PortAudio с помощью ASIO SDK для PortAudio для работы с ASIO, а затем обновить настройку PyAudio для компиляции таким образом. К счастью, я работаю над Windows, http://www.lfd.uci.edu/~gohlke/pythonlibs/#pyaudio со встроенной поддержкой ASIO, и тогда устройства будут доступны через ASIO.


person user3500739    schedule 05.04.2014    source источник


Ответы (1)


Поскольку мне не разрешено комментировать, я спрошу вас здесь: каково значение stream.get_output_latency() + stream.get_input_latency()) * wave_file.getframerate()? И как вы вообще получили это число 1060?
Выделив строку кода:
latencyCalc = math.ceil((stream.get_output_latency() + stream.get_input_latency()) * wave_file.getframerate()) + 1060, вы просто добавляете дополнительные 1060 кадров к общей задержке. Из вашего описания мне не ясно, почему вы это делаете, но я предполагаю, что вы измерили общую задержку в полученном файле, и всегда есть постоянное количество дополнительных кадров, помимо суммы задержки ввода + задержки вывода. Итак, считали ли вы, что эта дополнительная задержка может быть связана с обработкой? Вы сказали, что делаете некоторую обработку входного аудиосигнала; и обработка, безусловно, требует времени. Попробуйте сделать то же самое с неизмененным входным сигналом и посмотрите, не будет ли уменьшена / удалена дополнительная задержка. Даже другие части вашего приложения, например если у приложения есть графический интерфейс, все это может замедлить запись. Вы не описали свое приложение полностью, но я предполагаю, что дополнительная задержка вызвана вашим кодом и операциями, которые он выполняет. И почему «магическое число» всегда одно и то же? Потому что ваш код всегда один и тот же.

резюме:
Что представляет собой «магическое число»?
Очевидно, что это означает некоторую дополнительную задержку в дополнение к вашей общей задержке в оба конца.
Что вызывает эту дополнительную задержку?
Причина, скорее всего, находится где-то в вашем коде. Ваше приложение делает что-то, что требует дополнительного времени и, следовательно, вызывает дополнительную задержку. Единственное, что мне приходит в голову, это то, что вы добавили дополнительный «период тишины» где-то в своих настройках, чтобы вы тоже могли это проверить.

person roberto    schedule 08.04.2014
comment
Комментарии слишком ограничены для правильного ответа, поэтому я отредактировал свой исходный вопрос, чтобы ответить на ваши вопросы. - person user3500739; 09.04.2014
comment
Скомпилировали ли вы portaudio с помощью инструментария MinGW / MSYS? - person roberto; 10.04.2014
comment
Нет, я использовал предварительно скомпилированный установщик по предоставленной мной ссылке. - person user3500739; 11.04.2014