2012-05-24 79 views
21

我试图在python中实时绘制声音。我需要从麦克风中取出大块。PyAudio输入溢出

使用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)之外的尝试时,我听到点击,所以这不是我想要的。

请为我的建议提出最佳解决方案?

+2

也许你的块大小太小。也许它在缓冲区中获取的数据比您要取出的数据要多,因为块大小足够小,Python代码不能跟上。 – Demolishun

+0

嗨。只是想知道这个问题上有没有更新?我正在间歇地得到'[Errno Input overflowed] -9981'错误。我已经检查过'p.is_format_supported'对于我使用的格式是否正确。 –

回答

8

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

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

当我将RATE更改为此值时,它工作。

+1

我得到了同样的错误,你的解决方案(提高到48000)工作。但我有运行代码:如果p.is_format_supported(44100.0,#采样率 input_device =发展信息[ “指数”], input_channels =发展信息[ 'maxInputChannels'], input_format = pyaudio.paInt16): 打印“耶! “ ...它的工作!所以我对这个问题是什么感到困惑。任何见解? – user426364

+0

尝试升级portaudio,这为我解决了一些费率问题。我用“brew install portaudio --HEAD”。 – velotron

+0

这为我工作,我没有意识到声卡的默认采样率是48khz,谢谢! – Jeff

2
FORMAT = pyaudio.paInt16 

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

5

看起来很多人都遇到这个问题。我挖了一点,我认为这意味着在之前调用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然后引起pyaudio包装的IOError)。

如果你没有捕捉到每一帧都可以,那么你可以忽略这个错误。如果您对每一帧都有绝对的关键性,那么您需要找到一种方法来提高应用程序的优先级。我对Python不太了解,无法知道Python的这种做法,但值得尝试一下简单的nice命令,或将调度策略更改为SCHED_DEADLINE。

编辑:

之一,现在的问题是,当IO错误被抛出时,你失去了在通话中收集的所有帧。要改为忽略溢出并返回我们所拥有的内容,可以应用下面的补丁,这将导致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; 
3

我在OS X 10上工作过。当使用

IOError: [Errno Input overflowed] -9981 

溢出完全解决:10,得到了尝试获取来自于西霸USB卡(C媒体芯片组)的麦克风音频相同的错误,并对其进行处理,实时与FFT的多。一个回调模式,而不是阻塞模式,如写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在这种情况下。
  • 符号性的变量(试图与未签名,得到了饱和度模式)系统
  • 自然麦克风输入,并选择为默认值,增益等

。希望有人的作品!

1

我对慢速覆盆子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')来得到一个nger数组的整数。

1

我的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') 
10

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

+4

我得到:TypeError:read()得到了一个意外的关键字参数'exception_on_overflow' –

0

这对我来说帮助: 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/"