Небольшая задержка в начале записи, а конец обрезается низкоуровневыми потоками в звуковом устройстве python - PullRequest
0 голосов
/ 20 июня 2020

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

def callback(indata, outdata, frames, time, status):
  assert frames == args.blocksize
  qr.put(indata.copy())
  rec_file.write(qr.get())
  if status.output_underflow:
    print('Output underflow: increase blocksize?', file=sys.stderr)
    raise sd.CallbackAbort
  assert not status
  try:
    data = q.get_nowait()
  except queue.Empty:
    print('Buffer is empty: increase buffersize?', file=sys.stderr)
    raise sd.CallbackAbort
  if data.size < outdata.size:
    outdata[:len(data),0] = data
    outdata[len(data):] = 0
    raise sd.CallbackStop
  else:
    outdata[:,0] = data

try:
    with sf.SoundFile(args.filename) as f:
        #queue for recording input
        qr = queue.Queue(maxsize=args.buffersize)
        #queue for output WAVE file
        q = queue.Queue(maxsize=args.buffersize)
        event = threading.Event()
        for _ in range(args.buffersize):
            data = f.read(frames=args.blocksize, dtype='float32')
            if data.size == 0:
                break
            q.put_nowait(data)  # Pre-fill queue
        stream = sd.Stream(   
            samplerate=f.samplerate, blocksize=args.blocksize,
            dtype='float32', callback=callback, finished_callback=event.set,
            latency='low')
        with sf.SoundFile('output'+str(itr)+'.wav', mode='x', samplerate=f.samplerate,
                          channels=1) as rec_file:
            with stream:
                timeout = args.blocksize * args.buffersize / f.samplerate
                while data.size != 0:
                    data = f.read(args.blocksize, dtype='float32')
                    q.put(data, timeout=timeout)
                event.wait()  # Wait until playback is finished

1 Ответ

0 голосов
/ 21 июня 2020

Если вы не возражаете, чтобы все входные и выходные сигналы одновременно находились в памяти, вы можете использовать sd.playrec(). Вы не сможете уменьшить задержку с помощью собственного кода, используя sd.Stream. sd.playrec() внутренне использует sd.Stream и не добавляет задержки.

Если вы хотите уменьшить задержку, вы должны попытаться использовать более низкие значения для параметров blocksize и / или latency. Учтите, однако, что низкие значения будут более нестабильными и могут привести к сбоям при воспроизведении / записи.

Если вы не хотите, чтобы все данные одновременно находились в памяти, вы не можете использовать sd.playrec() и можете попробовать его с sd.Stream, как в приведенном выше примере.

Однако обратите внимание , что очередь в этих двух соседних строках в лучшем случае бесполезна:

qr.put(indata.copy())
rec_file.write(qr.get())

Можно также написать:

rec_file.write(indata)

Но, пожалуйста, не надо!

Запись в файл может слишком долго блокировать обратный вызов звука, что приводит к пропаданию звука.

Поэтому рекомендуется использовать очередь (и indata.copy() в качестве ну).

Но вы должны только записать в свой qr в функции обратного вызова. чтение должно произойти в другой точке.

Вы должны сделать неблокирующий qr.get_nowait() в while l oop до или после q.put(...) и запишите данные в файл.

В функции обратного вызова вы не должны выполнять блокировку qr.put(indata.copy()), потому что это опять же может заблокировать обратный вызов аудио, что приведет к выпадения. Вместо этого вы должны использовать qr.put_nowait(). Чтобы избежать полной очереди, вы должны удалить аргумент maxsize из qr (но оставить его в другой очереди!).

Наконец, после выхода из диспетчера контекста with stream может все еще быть данные, оставшиеся в qr, которые еще не были записаны в файл.

Итак, после того, как поток закрыт, вы должны убедиться, что очистили «очередь записи» и записать оставшиеся блоки в файл.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...