web-dev-qa-db-fra.com

PyAudio Input débordé

J'essaie de créer un son en temps réel en python. Je dois obtenir des morceaux de mon microphone.

En utilisant 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 l'erreur suivante:

* 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 utiliser le mode de blocage IO, donc si les morceaux ne sont pas disponibles, je veux attendre ces morceaux. Mais lorsque je crée, sauf segment ou sommeil (0,1), j'entends des clics; ce n'est donc pas ce que je veux.

S'il vous plaît suggérer la meilleure solution pour mon problème?

25
libbkmz

pyaudio.Stream.read() a un mot clé paramètre exception_on_overflow, définissez-le sur False. 

Pour votre exemple de code qui ressemblerait à ceci:

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 la documentation PyAudio pour plus de détails. 

15
Jesquik

J'ai eu la même erreur quand j'ai couru votre code. J'ai examiné la fréquence d'échantillonnage par défaut de mon périphérique audio par défaut, le microphone interne de mon macbook, à 48000Hz et non à 44100Hz.

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

Lorsque j'ai changé RATE à cette valeur, cela a fonctionné.

9
Joseph Sheedy

Il semble que beaucoup de gens rencontrent ce problème. J'ai creusé un peu dedans et je pense que cela signifie que, entre l'appel précédent à stream.read() et cet appel en cours, les données du flux ont été perdues (c'est-à-dire que la mémoire tampon s'est remplie plus rapidement que vous ne l'avez effacé).

Dans le document relatif à Pa_ReadStream() (la fonction PortAudio que stream.read() 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 provoque alors une IOError dans le wrapper pyaudio).

Si vous ne pouvez pas capturer chaque image, vous pouvez ignorer cette erreur. Si vous avez absolument besoin de chaque image, vous devez trouver un moyen d’augmenter la priorité de votre application. Je ne connais pas suffisamment Python pour connaître le moyen de le faire, mais cela vaut la peine d’essayer une simple commande Nice ou de modifier la règle de planification en SCHED_DEADLINE.

Modifier:

Un problème actuel est que, quand IOError est lancé, vous perdez toutes les images collectées lors de cet appel. Pour ignorer le débordement et renvoyer ce que nous avons, vous pouvez appliquer le correctif ci-dessous, ce qui obligera stream.read () à ignorer les erreurs de saturation de sortie et de débordement d'entrée de PortAudio (tout en lançant un problème en cas d'erreur différente). Une meilleure solution serait de rendre ce comportement (lancer/pas de 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;
6
Meta
FORMAT = pyaudio.Paint16

Assurez-vous de définir le format correct, mon microphone interne a été réglé sur 24 bits (voir Application Audio-Midi-Setup).

3
w-m

Je travaillais sur OS X 10.10. Vous rencontriez la même erreur en essayant d’obtenir de l’audio du microphone d’une carte USB SYBA (chipset C Media), et de 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, écrit par libbkmz. ( https://www.python.org/dev/peps/pep-0263/ )

Sur cette base, le 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 de flux, le démarrer puis faire une boucle dans n'importe quelle fonction. Un thread séparé diffuse de l'audio et ce flux est fermé lorsque la boucle principale est arrêtée. self.audio est utilisé dans n'importe quelle fonction. J'ai également eu des problèmes avec le fil en cours d'exécution pour toujours si elle n'est pas terminée. 

Étant donné que Pyaudio exécute ce flux dans un thread distinct, ce qui a stabilisé le flux audio, le mode Blocage peut s'être saturé en fonction de la vitesse ou du minutage du reste des processus du script. 

Notez que la taille de bloc est 2 ^ 12, mais les plus petits morceaux fonctionnent tout aussi bien. J'ai pris en compte et joué avec d'autres paramètres pour m'assurer qu'ils avaient tous un sens:

  • Taille de morceau plus grande ou plus petite (aucun effet)
  • Nombre et format des bits pour les mots du tampon, signé 16 bits dans ce cas.
  • signature des variables (essayée avec des motifs non signés et saturés)
  • Nature de l'entrée du micro et sélection par défaut dans le système, gain, etc. 

J'espère que ça marche pour quelqu'un!

3
Roberto Becerra

Mon autre réponse a résolu le problème dans la plupart des cas. Cependant, parfois, l'erreur persiste. 

C'est la raison pour laquelle j'ai abandonné pyaudio et suis passé à pyalsaaudio. Mon Raspy enregistre maintenant tout son en douceur.

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')
2
Harald Thomson

J'avais le même problème avec le très lent Raspberry Pi, mais j'ai pu le résoudre ( dans la plupart des cas ) en utilisant le module plus rapide array pour stocker les 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 de data est plutôt binaire par la suite. Mais vous pouvez utiliser numpy.array(data, dtype='i') pour obtenir un tableau d’interpers numpy.

1
Harald Thomson

pour moi cela a aidé: https://stackoverflow.com/a/46787874/5047984

J'ai utilisé le multitraitement pour écrire le fichier en parallèle de l'enregistrement audio. Ceci 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 __== '__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/"
0
gannebamm