【问题标题】:PyAudio Input overflowedPyAudio 输入溢出
【发布时间】:2012-05-30 19:48:33
【问题描述】:

我正在尝试在 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

我无法理解这个缓冲区。我想使用阻塞 IO 模式,所以如果块不可用,我想等待这些块。但是当我创建除段或睡眠(0.1)之外的尝试时,我会听到咔嗒声,所以这不是我想要的。

请为我的问题提出最佳解决方案?

【问题讨论】:

  • 也许你的块太小了。也许它在缓冲区中获取的数据多于您提取的数据,因为块大小足够小,Python 代码跟不上。
  • 嗨。只是想知道这个问题是否有任何更新?我间歇性地收到[Errno Input overflowed] -9981 错误。我检查了p.is_format_supported 是否适用于我使用的格式。

标签: python audio pyaudio


【解决方案1】:

我在运行您的代码时遇到了同样的错误。我查看了我的默认音频设备的默认采样率,我的 macbook 的内置麦克风,它是 48000Hz 而不是 44100Hz。

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

当我将 RATE 更改为此值时,它起作用了。

【讨论】:

  • 我遇到了同样的错误,您的解决方案(最高 48000)有效。但我已经运行了代码: if p.is_format_supported(44100.0, # Sample rate input_device=devinfo["index"], input_channels=devinfo['maxInputChannels'], input_format=pyaudio.paInt16): print 'Yay!' ......它奏效了!所以我很困惑问题是什么。有什么见解吗?
  • 尝试升级portaudio,这为我解决了一些速率问题。我使用“brew install portaudio --HEAD”。
  • 这对我有用,我没有意识到声卡的默认采样率为 48khz,谢谢!
  • 谢谢,这也是我的问题。我没想到这会成为预算硬件的问题,但也许 48k 正在成为事实上的标准?
【解决方案2】:
FORMAT = pyaudio.paInt16

确保设置正确的格式,我的内置麦克风设置为 24 位(请参阅 Audio-Midi-Setup 应用程序)。

【讨论】:

    【解决方案3】:

    似乎很多人都遇到了这个问题。我对其进行了一些研究,我认为这意味着在上一次调用 stream.read() 和当前调用之间,流中的数据丢失了(即缓冲区填满的速度比你清除它的速度快)。

    来自Pa_ReadStream() 的文档(stream.read() 最终调用的 PortAudio 函数):

    @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 然后在 pyaudio 包装器中生成 IOError)。

    如果您可以不捕获每一帧,那么您可以忽略此错误。如果拥有每一帧对您来说绝对至关重要,那么您需要找到一种方法来提高应用程序的优先级。我对 Python 不够熟悉,不知道用 Python 的方式来执行此操作,但值得尝试一个简单的 nice 命令,或将调度策略更改为 SCHED_DEADLINE。

    编辑:

    现在的一个问题是,当抛出 IOError 时,您会丢失该调用中收集的所有帧。要忽略溢出并只返回我们拥有的内容,您可以应用下面的补丁,这将导致 stream.read() 忽略来自 PortAudio 的输出欠载和输入溢出错误(但如果发生不同的错误仍然会抛出一些东西)。更好的方法是根据您的需要自定义此行为(投掷/不投掷)。

    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 上工作,在尝试从 SYBA USB 卡(C 媒体芯片组)中的麦克风获取音频时遇到同样的错误,并使用 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 在单独的线程中运行此流,这使得音频流稳定,阻塞模式可能已经饱和,具体取决于脚本中其余进程的速度或时间。

      请注意,块大小为 2^12,但较小的块也可以。我考虑并尝试了其他一些参数,以确保它们都有意义:

      • 块大小变大或变小(无效)
      • 缓冲区中字的位数和格式,在本例中为有符号 16 位。
      • 变量的有符号性(尝试无符号并得到饱和模式)
      • 麦克风输入的性质,系统默认选择,增益等。

      希望对某人有用!

      【讨论】:

        【解决方案5】:

        我在非常慢的树莓派上遇到了同样的问题,但我能够通过使用更快的 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 整数数组。

        【讨论】:

          【解决方案6】:

          我的other answer 在大多数情况下都解决了这个问题。但是,有时错误仍然会发生。

          这就是我放弃 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')
          

          【讨论】:

          • 真的很有帮助,谢谢!我使用常规列表而不是array.array,它更简单但对我来说效果很好,所以主要的变化是pyaudio =>pyalsaaudio。另外,我的麦克风需要PCM_FORMAT_S16_LE
          【解决方案7】:

          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 documentation

          【讨论】:

          • 我得到:TypeError: read() got an unexpected keyword argument 'exception_on_overflow'
          【解决方案8】:

          这对我有帮助:https://stackoverflow.com/a/46787874/5047984

          我使用多处理来并行写入文件以录制音频。这是我的代码:

          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/"
          

          【讨论】:

          • 为什么不使用线程?阻塞 I/O 释放 GIL,有效利用多核而没有多处理的复杂性。
          【解决方案9】:

          这对我很有帮助:

          input_ = stream.read(chunk, exception_on_overflow=False)
          exception_on_overflow = False
          

          【讨论】:

            猜你喜欢
            • 2013-01-17
            • 2013-10-05
            • 1970-01-01
            • 2017-05-28
            • 1970-01-01
            • 2015-03-26
            • 1970-01-01
            • 2022-08-05
            • 2015-07-26
            相关资源
            最近更新 更多