[Python-es] Ploteo en tiempo real con PyAudio y Matplotlib

575 views
Skip to first unread message

Ricardo Cisterna

unread,
Oct 11, 2014, 11:16:52 AM10/11/14
to Lista Python
Saludos a todos:

Estoy trabajando actualmente en un software que debe recuperar datos desde el micrófono de un equipo, y plotearlos en tiempo real en en una interfaz gráfica determinada.

Actualmente estoy recuperando los datos del micrófono con PyAudio, que no me ha presentado problemas. Sin embargo, a la hora de plotear los datos con matplotlib, a medida que los segundos avanzan el programa se va volviendo cada vez mas lento, produciéndose un desfase entre el gráfico mostrado y los sonidos del ambiente.

He pensado que el problema puede ser la cantidad de datos que se le pasan a Matplotlib, ya que PyAudio ingresa arrays de tamaño considerable en cosa de milisegundos. Por ello, estoy buscando alguna forma de "simplificar" estos arrays (reducir su tamaño mediante algún algoritmo). O será tal vez que Matplotlib no es adecuado al problema, y hay herramientas que corren más rápido o son mas livianas.

Saludos, y gracias por sus respuestas.

--
Ricardo Cisterna Santos

Ingenieria Civil Informática
Universidad del Bío-Bío

Kiko

unread,
Oct 11, 2014, 11:19:51 AM10/11/14
to La lista de python en castellano


He pensado que el problema puede ser la cantidad de datos que se le pasan a Matplotlib, ya que PyAudio ingresa arrays de tamaño considerable en cosa de milisegundos. Por ello, estoy buscando alguna forma de "simplificar" estos arrays (reducir su tamaño mediante algún algoritmo). O será tal vez que Matplotlib no es adecuado al problema, y hay herramientas que corren más rápido o son mas livianas.


Matplotlib debería poder sin problemas. Si puedes pasar algo de código lo vemos.

Otra opción es pyQwT, que es más ligera y para determinadas cosas, más recomendable.
 
Saludos, y gracias por sus respuestas.

--
Ricardo Cisterna Santos

Ingenieria Civil Informática
Universidad del Bío-Bío

_______________________________________________
Python-es mailing list
Pyth...@python.org
https://mail.python.org/mailman/listinfo/python-es
FAQ: http://python-es-faq.wikidot.com/


Ricardo Cisterna

unread,
Oct 11, 2014, 11:40:08 AM10/11/14
to La lista de python en castellano
Kiko:

Gracias por tu respuesta. Este es el código que compete a la pregunta:
________________________________________

try:
    import pyaudio
    import numpy as np
    from matplotlib import use
    import  matplotlib.pyplot as plt
    import matplotlib.animation as animation
except ImportError:
    raise ImportError('Faltan modulos externos que instalar')
import wave

SEGUNDOS = 10
MILISEGUNDOS_GRABACION = SEGUNDOS*1000
NOMBRE_ARCHIVO_WAV = "output.wav"

CHUNK = 2**12
FORMAT = pyaudio.paInt16
CHANNELS = 1
RATE = 44100
frames = []

# Definicion de la figura (matplotlib)
fig = plt.figure()
ax = fig.add_subplot(111)
line, = ax.plot(0, 0, lw=2)

# Limites de los ejes
ax.set_xlim(0, MILISEGUNDOS_GRABACION/1000)
ax.set_ylim(-5000, 5000)

# Generar primer plot vacio
xdata = None
ydata = None

# Iniciar stream de audio
p = pyaudio.PyAudio()
stream = p.open(format=FORMAT,
                        channels=CHANNELS,
                        rate=RATE,
                        input=True,
                        frames_per_buffer=CHUNK)

def generador():
    i = generador.i
    while i < range(0, int(RATE / CHUNK * (SEGUNDOS))):
        data = stream.read(CHUNK)
        audio = np.fromstring(data, np.int16)
        tiempo = np.arange((CHUNK * i), audio.shape[0] + (CHUNK * i)) / float(RATE)
        i += 1
        print audio
        yield tiempo, audio
generador.i = 0

def animacion(data):
    x, y = data
    global xdata
    global ydata
    if xdata == None:
        xdata = x
        ydata = y
    else:
        xdata = np.append(xdata, x)
        ydata = np.append(ydata, y)
    global line
    line.set_data(xdata, ydata)

    return line,

ani = animation.FuncAnimation(fig, animacion, generador, blit=True, interval=50, repeat=False)
plt.show()
________________________________________

Gracias de nuevo.

Carlos Zuniga

unread,
Oct 11, 2014, 1:00:03 PM10/11/14
to La lista de python en castellano
Creo que estas redibujado toda la data en cada intervalo. No conozco
mucho de matplotlib, no es posible solo añadir la nueva información al
gráfico en lugar de redibujarlo desde el principio?

Daπid

unread,
Oct 11, 2014, 1:39:17 PM10/11/14
to La lista de python en castellano
2014-10-11 17:16 GMT+02:00 Ricardo Cisterna <r.cister...@gmail.com>:
Sin embargo, a la hora de plotear los datos con matplotlib, a medida que los segundos avanzan el programa se va volviendo cada vez mas lento, produciéndose un desfase entre el gráfico mostrado y los sonidos del ambiente.

Esto suena a un problema que tuve hace bastante tiempo, y es que la figura conservaba datos históricos, así que el uso de memoria se disparaba. En la versión 1.0 se mejoró mucho, pero todavía quedaba una pequeña fuga. La solución era ejecutar plt.clf() de vez en cuando, que elimina por completo la basura acumulada.

Ricardo Cisterna

unread,
Oct 11, 2014, 5:37:43 PM10/11/14
to Lista Python

Hola a todos, gracias por sus respuestas.

Olvidé mencionar que el software debe mantener el "histórico" de datos recopilados, de forma de que el gráfico muestra los datos desde el inicio de la grabación hasta el momento actual.

De todas formas probaré los consejos que me dan y comentaré acá los resultados, gracias de nuevo por su tiempo.

Daπid

unread,
Oct 11, 2014, 5:54:44 PM10/11/14
to La lista de python en castellano

2014-10-11 23:37 GMT+02:00 Ricardo Cisterna <r.cister...@gmail.com>:
Olvidé mencionar que el software debe mantener el "histórico" de datos recopilados, de forma de que el gráfico muestra los datos desde el inicio de la grabación hasta el momento actual.

Mira que lo dudo. :)

Estás recibiendo 44100 muestras por segundo, suficientes para hacer un gráfico suave que te llene la pantalla de lado a lado. Es perfectamente razonable conservar en memoria los datos en crudo, pero no tienes que hacer el gráfico con todos ellos.

Esencialmente, lo que quieres hacer es subsampling de tus datos, y sacar por pantalla sólo eso. Dependiendo del tipo de sonidos que grabes y lo que te importen problemas como "aliasing", puedes simplemente hacer uno regular usando la maquinaria de Numpy:

plt.plot(data[::int(rate)])

O tendrás que buscar algo más complicado (y computacionalmente costoso).

Si quieres pintar la forma de la onda según entra y los datos pueden ser cualquier cosa, puedes copiar lo que Audacity hace.


/David
Reply all
Reply to author
Forward
0 new messages