python - grabar - Entrada de PyAudio desbordada
pyaudio record (8)
Mi otra respuesta resolvió el problema en la mayoría de los casos. Sin embargo, a veces el error todavía ocurre.
Esa fue la razón por la que deseché pyaudio y cambié a pyalsaaudio. My Raspy ahora graba suavemente cualquier sonido.
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'')
Estoy tratando de hacer un sonido de trazado en tiempo real en Python. Necesito obtener trozos de mi micrófono.
Usando PyAudio, intente usar
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()
Después, obtengo el siguiente error:
* 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
No puedo entender este buffer. Quiero usar el bloqueo del modo IO, por lo tanto, si los fragmentos no están disponibles, quiero esperar a esos fragmentos. Pero cuando intento try except segment o sleep (0.1), oigo clics, por lo que esto no es lo que quiero.
Por favor, sugiero la mejor solución para mi ploblem?
Obtuve el mismo error cuando ejecuté su código. Miré la frecuencia de muestreo predeterminada de mi dispositivo de audio predeterminado, el micrófono interno de mi macbook, era 48000Hz no 44100Hz.
p.get_device_info_by_index(0)[''defaultSampleRate'']
Out[12]: 48000.0
Cuando cambié la VELOCIDAD a este valor, funcionó.
Parece que mucha gente está encontrando este problema. Cavé un poco en él y creo que significa que entre la llamada anterior a stream.read()
y esta llamada actual, los datos de la transmisión se perdieron (es decir, el buffer se llenó más rápido de lo que lo borraste).
Del doc para Pa_ReadStream()
(la función stream.read()
que stream.read()
eventualmente termina llamando):
@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
causa un IOError
en la envoltura de pyaudio).
Si está bien que no capture cada fotograma, puede ignorar este error. Si es absolutamente crítico para ti tener cada cuadro, entonces necesitarás encontrar una manera de aumentar la prioridad de tu aplicación. No estoy lo suficientemente familiarizado con Python como para saber una manera pitónica de hacerlo, pero vale la pena intentar con un simple comando nice
o cambiar la política de programación a SCHED_DEADLINE.
Editar:
Un problema ahora es que cuando se lanza IOError, pierdes todos los fotogramas recopilados en esa llamada. Para ignorar el desbordamiento y simplemente devolver lo que tenemos, puedes aplicar el siguiente parche, lo que hará que stream.read () ignore el subdesbordamiento de la salida y los errores de desbordamiento de entrada desde PortAudio (pero aún arrojará algo si se produce un error diferente). Una mejor manera sería personalizar este comportamiento (throw / no throw) dependiendo de sus necesidades.
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;
Trabajé esto en OS X 10.10, obtuve el mismo error al intentar obtener audio del micrófono en una tarjeta USB SYBA (conjunto de chips C Media), y procesarlo en tiempo real con FFT''s y más:
IOError: [Errno Input overflowed] -9981
El desbordamiento se resolvió completamente al usar un Modo de devolución de llamada, en lugar del Modo de bloqueo, tal como lo escribió libbkmz. ( https://www.python.org/dev/peps/pep-0263/ )
Basado en eso, el bit del código de trabajo se veía así:
"""
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()
Este código creará una función de devolución de llamada, luego creará un objeto de transmisión, lo iniciará y luego repetirá cualquier función. Una secuencia separada transmite audio, y esa secuencia se cierra cuando se detiene el ciclo principal. self.audio se usa en cualquier función. También tuve problemas con el hilo que se ejecuta para siempre si no se termina.
Como Pyaudio ejecuta esta secuencia en una secuencia separada, y esto hizo que la secuencia de audio sea estable, el modo de Bloqueo pudo haber estado saturando según la velocidad o el tiempo del resto de los procesos en la secuencia de comandos.
Tenga en cuenta que el tamaño del fragmento es 2 ^ 12, pero los trozos más pequeños funcionan igual de bien. Hay otros parámetros que consideré y con los que jugué para asegurarme de que todos tuvieran sentido:
- Tamaño del trozo más grande o más pequeño (sin efecto)
- Número y formato de bits para las palabras en el búfer, firmado 16 bits en este caso.
- firmado de variables (probado con patrones de saturación sin signo y obtenido)
- Naturaleza de la entrada de micrófono, y selección como predeterminada en el sistema, ganancia, etc.
Espero que funcione para alguien!
Tuve el mismo problema en la pi de frambuesa realmente lenta, pero pude resolverla ( en la mayoría de los casos ) utilizando el módulo de array
más rápido para almacenar los datos.
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")
El contenido de los data
es más bien binario después. Pero puedes usar numpy.array(data, dtype=''i'')
para obtener una matriz nuper de intergers.
para mí esto ayudó: https://.com/a/46787874/5047984
Usé multiprocesamiento para escribir el archivo en paralelo a la grabación de audio. Este es mi código:
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/"
pyaudio.Stream.read()
tiene un parámetro de palabra clave exception_on_overflow
, establezca esto en False.
Para su código de muestra que se vería así:
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()
Consulte la documentación de PyAudio para más detalles.
FORMAT = pyaudio.paInt16
Asegúrese de establecer el formato correcto, mi micrófono interno se configuró en 24 bits (consulte la aplicación Audio-Midi-Setup).