Entrée PyAudio débordée
j'essaie de faire du son de traçage en temps réel en python. J'ai besoin d'obtenir des morceaux de mon micro.
utilisez PyAudio, essayez d'utiliser
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()
Après, j'obtiens ce qui suit erreur:
* 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
je ne peux pas comprendre ce tampon. Je veux, pour utiliser le mode IO de blocage, donc si les morceaux ne sont pas disponibles, je veux attendre ces morceaux. Mais quand je crée essayer sauf segment ou sommeil (0.1), j'entends des clics, donc ce n'est pas ce que je veux.
s'il vous Plaît suggérer la meilleure solution pour mon ploblem?
8 réponses
pyaudio.Stream.read()
a un paramètre de mot-clé exception_on_overflow
, Mettez à False.
Pour ton exemple de code qui pourrait ressembler à:
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()
voir le PyAudio documentation pour plus de détails.
j'ai eu la même erreur quand j'ai lancé votre code. J'ai regardé le taux d'échantillon par défaut de mon périphérique audio par défaut, le microphone interne de mon macbook, c'était 48000Hz pas 44100Hz.
p.get_device_info_by_index(0)['defaultSampleRate']
Out[12]: 48000.0
quand j'ai changé le taux à cette valeur, ça a marché.
Il semble que beaucoup de gens rencontrent ce problème. J'ai creusé un peu et je pense que cela signifie qu'entre l'appel précédent stream.read()
et cet appel courant, les données du flux ont été perdues (c'est-à-dire que le buffer s'est rempli plus vite que vous ne l'avez effacé).
Dans la doc de Pa_ReadStream()
(la fonction PortAudio qui stream.read()
finalement finit par appeler):
@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
cause alors un IOError
dans le papier pyaudio).
si vous êtes D'accord pas de capturer chaque image, alors vous pouvez ignorer cette erreur. Si c'est absolument essentiel pour vous d'avoir chaque image, alors vous aurez besoin de trouver un moyen d'augmenter le niveau de priorité de votre demande. Je ne suis pas assez familier avec Python pour connaître une façon pythonique de faire cela, mais cela vaut la peine d'essayer un simple nice
commande, ou changer la Politique de planification en SCHED_DEADLINE.
Edit:
un problème en ce moment est que quand IOError est lancé, vous perdez tous les cadres collectés dans cet appel. Pour ignorer le débordement et simplement retourner ce que nous avons, vous pouvez appliquer le patch ci-dessous, ce qui causera stream.read () pour ignorer les erreurs output underrun et input overflow de PortAudio (mais quand même jeter quelque chose si une erreur différente s'est produite). Une meilleure façon serait de rendre ce comportement (Lancer/pas lancer) personnalisable en fonction de vos besoins.
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;
FORMAT = pyaudio.paInt16
assurez-vous de régler le bon format, mon microphone interne a été réglé à 24 bits (voir application Audio-Midi-Setup).
j'ai travaillé sur OS X 10.10, J'ai eu la même erreur en essayant d'obtenir l'audio à partir du microphone dans une carte USB SYBA (C Media chipset), et le traiter en temps réel avec fft et plus:
IOError: [Errno Input overflowed] -9981
le débordement a été complètement résolu en utilisant un Mode de rappel, au lieu du Mode de blocage, comme écrit par libbkmz.(https://www.python.org/dev/peps/pep-0263/)
basé sur cela, le morceau du code de travail ressemblait à ceci:
"""
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()
ce code va créer une fonction de rappel, puis créer un objet stream, le démarrer et ensuite boucler n'importe quelle fonction. Un thread séparé diffuse l'audio, et ce flux est fermé lorsque la boucle principale est arrêtée. auto.l'audio est utilisé dans n'importe quelle fonction. J'ai aussi eu des problèmes avec le thread tournant à jamais si ce n'est terminé.
puisque Pyaudio exécute ce flux dans un thread séparé, et cela a rendu le flux audio stable, le mode de blocage a pu être saturé en fonction de la vitesse ou du timing du reste des processus dans le script.
notez que la taille des morceaux est 2^12, mais les plus petits morceaux fonctionnent tout aussi bien. Il y a d'autres paramètres que j'ai pris en compte et avec lesquels j'ai joué pour m'assurer qu'ils avaient tous un sens:
- taille des morceaux plus grand ou plus petit (aucun effet)
- nombre et format des bits pour les mots dans le tampon, signé 16 bits dans ce cas.
- signification des variables (essayé avec non signé et a obtenu la saturation des modèles)
- la Nature de l'entrée micro, et la sélection par défaut dans le système, le gain etc.
j'Espère que fonctionne pour quelqu'un!
Mon réponse résolu le problème dans la plupart des cas. Toutefois, l'erreur persiste parfois.
C'est la raison pour laquelle j'ai abandonné pyaudio et je suis passé à pyalsaaudio. Mon râpeur enregistre en douceur n'importe quel son.
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')
j'ai eu le même problème sur le pi à la framboise vraiment lent, mais j'ai pu le résoudre (pour la plupart des cas) en utilisant le plus rapide array
module de stockage des données.
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")
Le contenu data
est plutôt binaire par la suite.
Mais vous pouvez utiliser numpy.array(data, dtype='i')
pour obtenir un tableau numpy d'entiers.
pour moi, cela a aidé: https://stackoverflow.com/a/46787874/5047984
j'ai utilisé le multiprocessing pour écrire le fichier en parallèle à l'enregistrement audio. C'est mon code:
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/"