Подтвердить что ты не робот

Вход PyAudio переполнен

Я пытаюсь сделать звук в реальном времени в python. Мне нужно получить куски из моего микрофона.

Используя PyAudio, попробуйте использовать

import pyaudio
import wave
import sys

chunk = 1024
FORMAT = pyaudio.paInt16
CHANNELS = 1
RATE = 44100
RECORD_SECONDS = 5
WAVE_OUTPUT_FILENAME = "output.wav"

p = pyaudio.PyAudio()

stream = p.open(format = FORMAT,
                channels = CHANNELS,
                rate = RATE,
                input = True,
                frames_per_buffer = chunk)

print "* recording"
all = []
for i in range(0, RATE / chunk * RECORD_SECONDS):
    data = stream.read(chunk)
    all.append(data)
print "* done recording"

stream.close()
p.terminate()

После этого я получаю следующую ошибку:

* recording
Traceback (most recent call last):
  File "gg.py", line 23, in <module>
    data = stream.read(chunk)
  File "/usr/lib64/python2.7/site-packages/pyaudio.py", line 564, in read
    return pa.read_stream(self._stream, num_frames)
IOError: [Errno Input overflowed] -9981

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

Пожалуйста, предложите лучшее решение для моего ploblem?

4b9b3361

Ответ 1

pyaudio.Stream.read() имеет параметр ключевого слова exception_on_overflow, установите для этого значение значение False.

Для вашего образца кода, который будет выглядеть следующим образом:

import pyaudio
import wave
import sys

chunk = 1024
FORMAT = pyaudio.paInt16
CHANNELS = 1
RATE = 44100
RECORD_SECONDS = 5
WAVE_OUTPUT_FILENAME = "output.wav"

p = pyaudio.PyAudio()

stream = p.open(format = FORMAT,
                channels = CHANNELS,
                rate = RATE,
                input = True,
                frames_per_buffer = chunk)

print "* recording"
all = []
for i in range(0, RATE / chunk * RECORD_SECONDS):
    data = stream.read(chunk, exception_on_overflow = False)
    all.append(data)
print "* done recording"

stream.close()
p.terminate()

Подробнее см. документацию PyAudio.

Ответ 2

Я получил ту же ошибку, когда я запустил ваш код. Я посмотрел на частоту дискретизации по умолчанию моего музыкального устройства по умолчанию, мой внутренний микрофон macbook, он составлял 48000 Гц, а не 44100 Гц.

p.get_device_info_by_index(0)['defaultSampleRate']
Out[12]: 48000.0

Когда я изменил значение RATE на это значение, оно сработало.

Ответ 3

Кажется, что многие люди сталкиваются с этой проблемой. Я вникнул в него, и я думаю, что это означает, что между предыдущим вызовом stream.read() и этим текущим вызовом данные из потока были потеряны (т.е. Буфер заполнен быстрее, чем вы его очистили).

Из документа doc для Pa_ReadStream() (функция PortAudio, которая stream.read() в конечном итоге завершает вызов):

@return On success PaNoError will be returned, or PaInputOverflowed if
input data was discarded by PortAudio after the previous call and
before this call.

(PaInputOverflowed затем вызывает IOError в обертке pyaudio).

Если вам не удастся захватить каждый отдельный кадр, вы можете проигнорировать эту ошибку. Если для вас абсолютно необходимо иметь каждый фрейм, вам нужно будет найти способ увеличить приоритет вашего приложения. Я недостаточно знаком с Python, чтобы узнать, как это сделать, но стоит попробовать простую команду nice или изменить политику планирования для SCHED_DEADLINE.

Edit:

Одна проблема прямо сейчас заключается в том, что при броске IOError вы теряете все фреймы, собранные в этом вызове. Чтобы вместо этого игнорировать переполнение и просто вернуть то, что у нас есть, вы можете применить патч ниже, что приведет к тому, что stream.read() проигнорирует ошибки недогрузки и ошибок ввода из PortAudio (но все равно бросает что-то, если возникла другая ошибка). Лучшим способом было бы сделать это поведение (throw/no throw) настраиваемым в зависимости от ваших потребностей.

diff --git a/src/_portaudiomodule.c b/src/_portaudiomodule.c
index a8f053d..0878e74 100644
--- a/src/_portaudiomodule.c
+++ b/src/_portaudiomodule.c
@@ -2484,15 +2484,15 @@ pa_read_stream(PyObject *self, PyObject *args)
     } else {
       /* clean up */
       _cleanup_Stream_object(streamObject);
+
+      /* free the string buffer */
+      Py_XDECREF(rv);
+
+      PyErr_SetObject(PyExc_IOError,
+                       Py_BuildValue("(s,i)",
+                                     Pa_GetErrorText(err), err));
+      return NULL;
     }
-
-    /* free the string buffer */
-    Py_XDECREF(rv);
-
-    PyErr_SetObject(PyExc_IOError,
-                   Py_BuildValue("(s,i)",
-                                 Pa_GetErrorText(err), err));
-    return NULL;
   }

   return rv;

Ответ 4

Я работал над OS X 10.10, получил ту же ошибку, пытаясь получить звук с микрофона на USB-карте SYBA (чипсет C Media) и обрабатывать его в режиме реального времени с помощью fft и более:

IOError: [Errno Input overflowed] -9981

Переполнение было полностью разрешено при использовании режима обратного вызова вместо режима блокировки, как написано libbkmz. (https://www.python.org/dev/peps/pep-0263/)

Исходя из этого, бит рабочего кода выглядел так:

"""
Creating the audio stream from our mic
"""
rate=48000
self.chunk=2**12
width = 2

p = pyaudio.PyAudio()

# callback function to stream audio, another thread.
def callback(in_data,frame_count, time_info, status):
    self.audio = numpy.fromstring(in_data,dtype=numpy.int16)
    return (self.audio, pyaudio.paContinue)

#create a pyaudio object
self.inStream = p.open(format = p.get_format_from_width(width, unsigned=False),
                       channels=1,
                       rate=rate,
                       input=True,
                       frames_per_buffer=self.chunk,
                       stream_callback = callback)

"""
Setting up the array that will handle the timeseries of audio data from our input
"""
self.audio = numpy.empty((self.buffersize),dtype="int16")

    self.inStream.start_stream()

while True:
  try:
    self.ANY_FUNCTION() #any function to run parallel to the audio thread, running forever, until ctrl+C is pressed. 

  except KeyboardInterrupt:

    self.inStream.stop_stream()
    self.inStream.close()
    p.terminate()
    print("* Killed Process")
    quit()

Этот код создаст функцию обратного вызова, затем создаст объект потока, запустит его и затем выполнит любую функцию. Отдельные потоки передают аудио, и этот поток закрывается, когда основной цикл остановлен. self.audio используется в любой функции. У меня также были проблемы с потоком, выполняемым навсегда, если не прекращено.

Так как Pyaudio запускает этот поток в отдельном потоке, и это сделало поток аудио стабильным, режим блокировки мог бы насыщаться в зависимости от скорости или времени остальных процессов в script.

Обратите внимание, что размер куска составляет 2 ^ 12, но небольшие куски работают так же хорошо. Есть другие параметры, которые я рассматривал и играл вокруг, чтобы убедиться, что все они имеют смысл:

  • Размер блока больше или меньше (без эффекта)
  • Число и формат битов для слов в буфере, подписанных в этом случае 16 бит.
  • подпись переменных (с использованием беззнаковых и полученных шаблонов насыщения)
  • Природа входа микрофона и выбор по умолчанию в системе, усиление и т.д.

Надеюсь, что это сработает для кого-то!

Ответ 5

FORMAT = pyaudio.paInt16

Обязательно установите правильный формат, мой внутренний микрофон был установлен на 24 бит (см. приложение Audio-Midi-Setup).

Ответ 6

У меня была такая же проблема на очень медленном малине pi, но я смог ее решить (в большинстве случаев), используя более быстрый array модуль для хранения данных.

import array
import pyaudio 

FORMAT = pyaudio.paInt16
CHANNELS = 1
INPUT_CHANNEL=2
RATE = 48000
CHUNK = 512

p = pyaudio.PyAudio()
stream = p.open(format=FORMAT,
                channels=CHANNELS,
                rate=RATE,
                input=INPUT_CHANNEL,
                frames_per_buffer =CHUNK)

print("* recording")


try:
    data = array.array('h')
    for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
        data.fromstring(stream.read(CHUNK))
finally:
    stream.stop_stream()
    stream.close()
    p.terminate()

print("* done recording")

Содержимое data скорее двоично. Но вы можете использовать numpy.array(data, dtype='i') для получения массива интермедиаторов numpy.

Ответ 7

Мой другой ответ решил проблему в большинстве случаев. Однако иногда ошибка все же происходит.

Вот почему я отказался от pyaudio и переключился на pyalsaaudio. Мой Raspy теперь плавно записывает любой звук.

import alsaaudio   
import numpy as np
import array

# constants
CHANNELS    = 1
INFORMAT    = alsaaudio.PCM_FORMAT_FLOAT_LE
RATE        = 44100
FRAMESIZE   = 1024

# set up audio input
recorder=alsaaudio.PCM(type=alsaaudio.PCM_CAPTURE)
recorder.setchannels(CHANNELS)
recorder.setrate(RATE)
recorder.setformat(INFORMAT)
recorder.setperiodsize(FRAMESIZE)


buffer = array.array('f')
while <some condition>:
    buffer.fromstring(recorder.read()[1])

data = np.array(buffer, dtype='f')

Ответ 8

для меня это помогло: fooobar.com/info/247066/...

Я использовал многопроцессорную обработку для записи файла параллельно записи звука. Это мой код:

recordAudioSamples.py

import pyaudio
import wave
import datetime
import signal
import ftplib
import sys
import os

# configuration for assos_listen
import config


# run the audio capture and send sound sample processes
# in parallel
from multiprocessing import Process

# CONFIG
CHUNK = config.chunkSize
FORMAT = pyaudio.paInt16
CHANNELS = 1
RATE = config.samplingRate
RECORD_SECONDS = config.sampleLength

# HELPER FUNCTIONS

# write to ftp
def uploadFile(filename):

    print("start uploading file: " + filename)
    # connect to container
    ftp = ftplib.FTP(config.ftp_server_ip, config.username, config.password)

    # write file
    ftp.storbinary('STOR '+filename, open(filename, 'rb'))
    # close connection
    ftp.quit()
    print("finished uploading: " +filename)


# write to sd-card
def storeFile(filename,frames):

    print("start writing file: " + filename)
    wf = wave.open(filename, 'wb')
    wf.setnchannels(CHANNELS)
    wf.setsampwidth(p.get_sample_size(FORMAT))
    wf.setframerate(RATE)
    wf.writeframes(b''.join(frames))
    wf.close()
    print(filename + " written")


# abort the sampling process
def signal_handler(signal, frame):
    print('You pressed Ctrl+C!')

    # close stream and pyAudio
    stream.stop_stream()
    stream.close()
    p.terminate()

    sys.exit(0)

# MAIN FUNCTION
def recordAudio(p, stream):

    sampleNumber = 0
    while (True):
        print("*  recording")
        sampleNumber = sampleNumber +1

        frames = []
        startDateTimeStr = datetime.datetime.now().strftime("%Y_%m_%d_%I_%M_%S_%f")
        for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
            data = stream.read(CHUNK)
            frames.append(data)

        fileName =  str(config.sensorID) + "_" + startDateTimeStr + ".wav"

        # create a store process to write the file in parallel
        storeProcess = Process(target=storeFile, args=(fileName,frames))
        storeProcess.start()

        if (config.upload == True):
            # since waiting for the upload to finish will take some time
            # and we do not want to have gaps in our sample
            # we start the upload process in parallel
            print("start uploading...")
            uploadProcess = Process(target=uploadFile, args=(fileName,))
            uploadProcess.start()



# ENTRYPOINT FROM CONSOLE
if __name__ == '__main__':

    p = pyaudio.PyAudio()
    stream = p.open(format=FORMAT,
                    channels=CHANNELS,
                    rate=RATE,
                    input=True,
                    frames_per_buffer=CHUNK)


    # directory to write and read files from
    os.chdir(config.storagePath)

    # abort by pressing C
    signal.signal(signal.SIGINT, signal_handler)
    print('\n\n--------------------------\npress Ctrl+C to stop the recording')

    # start recording
    recordAudio(p, stream)

config.py

### configuration file for assos_listen
# upload
upload = False

# config for this sensor
sensorID = "al_01"

# sampling rate & chunk size
chunkSize = 8192
samplingRate = 44100 # 44100 needed for Aves sampling
# choices=[4000, 8000, 16000, 32000, 44100] :: default 16000

# sample length in seconds
sampleLength = 10

# configuration for assos_store container
ftp_server_ip = "192.168.0.157"
username = "sensor"
password = "sensor"

# storage on assos_listen device
storagePath = "/home/pi/assos_listen_pi/storage/"