PyAudio 输入溢出
Posted
技术标签:
【中文标题】PyAudio 输入溢出【英文标题】:PyAudio Input overflowed 【发布时间】: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
是否适用于我使用的格式。
【参考方案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://***.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
【讨论】:
以上是关于PyAudio 输入溢出的主要内容,如果未能解决你的问题,请参考以下文章