GStreamer: Visualización de audio

GStreamer permite representar gráficamente los datos de sonido de los archivos de audio usando diferentes complementos (plugins). En este documento dejo algunos ejemplos escritos en Python, basados en la información de los tutoriales de GStreamer, que para la fecha solo estaban disponibles en el lenguaje de programación C. También facilito la creación del entorno para ejecutar los ejemplos usando manifiestos y entornos de GNU Guix.
El archivo comprimido audio-visualization.tar.xz contiene todos los archivos que se mencionan en este documento.
Prueba de reproducción
Para empezar, simplemente reproduzcamos un sonido sin ningún tipo de visualización, solo para confirmar que el entorno tiene lo esencial para empezar a trabajar.
cd ruta/a/audio-visualization/ guix shell --pure -m manifest.scm python3 play-audio.py
La primera línea de la Secuencia 1 simplemente cambia de directorio a la carpeta de archivos de ejemplo usados en este documento.
La segunda línea crea un perfil de GNU Guix con todos los paquetes de software definidos en el manifiesto (ver Manifiesto 1) y nos deja en un entorno de shell con las variables de entorno apropiadas para hacer uso de estos paquetes.
(use-modules (gnu packages)) (define DEV_PACKAGES (list "coreutils")) (define PRODUCTION_PACKAGES (list "gobject-introspection" "gstreamer" "gst-plugins-bad" ; Provides audiovisualizers. "gst-plugins-good" ; Provides goom, goom2k1, monoscope. "python" "python-pygobject")) (specifications->manifest (append DEV_PACKAGES PRODUCTION_PACKAGES))
Ya con el entorno listo, la última línea simplemente ejecuta el
Programa play-audio
,
que reproduce el archivo song18.mp3. El
programa finaliza al terminarse el flujo de audio.
import os import gi gi.require_version("Gst", "1.0") from gi.repository import Gst # Initialize GStreamer. Gst.init(None) # Define audio file location. audio_file_path = os.path.abspath("song18.mp3") # Build a playbin pipeline. textual_pipeline = "playbin uri=file://{}".format(audio_file_path) pipeline = Gst.parse_launch(textual_pipeline) # Start playing. pipeline.set_state(Gst.State.PLAYING) # Wait until error or EOS. bus = pipeline.get_bus() message = bus.timed_pop_filtered( Gst.CLOCK_TIME_NONE, Gst.MessageType.ERROR | Gst.MessageType.EOS ) # Free resources. pipeline.set_state(Gst.State.NULL)
play-audio
. Reproduce el archivo
de audio song18.mp3.
Este programa, y los demás programas en este documento, usan un
conducto playbin
.
Este tipo de conducto viene con todos los elementos necesarios
para reproducir datos multimedia sin mayor esfuerzo. Así se puede
concentrar uno en la visualización y dejar a un lado los detalles
de la reproducción del sonido mismo.
Visualización predeterminada
Activar la visualización de audio en un conducto
playbin
es sencillo; basta con agregarle
la bandera de visualización antes de reproducir el archivo.
El Programa play-vis
activa una visualización predeterminada de esta manera.
import os import gi gi.require_version("Gst", "1.0") from gi.repository import Gst # Initialize GStreamer. Gst.init(None) # Define audio file location. audio_file_path = os.path.abspath("song18.mp3") # Build a playbin pipeline. textual_pipeline = "playbin uri=file://{}".format(audio_file_path) pipeline = Gst.parse_launch(textual_pipeline) # Set the visualization flag. flags = pipeline.get_property("flags") vis_flag = (1 << 3) flags |= vis_flag pipeline.set_property("flags", flags) # Start playing. pipeline.set_state(Gst.State.PLAYING) # Wait until error or EOS. bus = pipeline.get_bus() message = bus.timed_pop_filtered( Gst.CLOCK_TIME_NONE, Gst.MessageType.ERROR | Gst.MessageType.EOS ) # Free resources. pipeline.set_state(Gst.State.NULL)
play-vis
. Reproduce el archivo
de audio song18.mp3 con una visualización
predeterminada por GStreamer.
Ahora ejecutemos el programa en el mismo entorno de la Prueba de reproducción:
python3 play-vis.py
Debería verse un resultado similar al de la Figura 1:

play-vis
.
Cuando no se especifica qué complemento de visualización usar,
se selecciona uno predeterminadamente, que parece ser el
complemento goom
, que viene en el
paquete gst-plugins-good
(ver
Manifiesto 1).
Algo que se nota en este tipo de visualización es que sus gráficos no son idénticos en cada ejecución del programa. La sección Otras visualizaciones incluye algunas que sí parecen ser reproducibles.
Sobre las banderas de playbin
La activación de la bandera de visualización en el
Programa play-vis
ocurre en estas líneas:
# Set the visualization flag. flags = pipeline.get_property("flags") vis_flag = (1 << 3) flags |= vis_flag pipeline.set_property("flags", flags)
Pero, ¿por qué (1 << 3)
? Las banderas que
reconoce el conducto playbin
están
definidas en la documentación de uno de sus elementos:
playsink,
como parte de la constante
GstPlayFlags, que dice:
video (0x00000001) – Render the video stream audio (0x00000002) – Render the audio stream text (0x00000004) – Render subtitles vis (0x00000008) – Render visualisation when no video is present soft-volume (0x00000010) – Use software volume native-audio (0x00000020) – Only use native audio formats native-video (0x00000040) – Only use native video formats download (0x00000080) – Attempt progressive download buffering buffering (0x00000100) – Buffer demuxed/parsed data deinterlace (0x00000200) – Deinterlace video if necessary soft-colorbalance (0x00000400) – Use software color balance force-filters (0x00000800) – Force audio/video filter(s) to be applied force-sw-decoders (0x00001000) – Force only software-based decoders (no effect for playbin3)
El conducto activa varias de estas predeterminadamente, como se
ve al inspeccionar el valor devuelto por
pipeline.get_property("flags")
:
<flags Render the video stream | Render the audio stream | Render subtitles | Use software volume | Deinterlace video if necessary | Use software color balance of type __main__.GstPlayFlags>
Otras visualizaciones
Para usar un tipo de visualización diferente se modifica
la propiedad vis-plugin
del conducto. Así:
pipeline.set_property("vis-plugin", plugin)
Donde plugin
debe ser un objeto
Gst.Element
que represente alguno de los complementos de visualización del
registro de complementos
de GStreamer.
Los siguientes programas muestran cómo inspeccionar el registro de complementos y cómo seleccionar uno de ellos para generar otro tipo de visualización.
Primero miremos cómo ver el registro de todos los complementos, sin importar si son de visualización de audio o no:
import gi gi.require_version("Gst", "1.0") from gi.repository import Gst # Initialize GStreamer. Gst.init(None) # Show available plugins. registry = Gst.Registry.get() plugins = registry.get_feature_list(Gst.ElementFactory) print("{} Plugins found.".format(len(plugins))) for plugin in plugins: name = plugin.get_plugin_name() long_name = plugin.get_metadata("long-name") print("NAME: {}: {}".format(name, long_name)) print("KLASS: {}".format(plugin.get_metadata("klass"))) print("-" * 10)
show-plugins
. Lista todos los
complementos del registro de GStreamer.
Ejecutemos el
Programa show-plugins
:
python3 show-plugins.py
Se listan 601 complementos de la siguiente manera:
601 Plugins found. NAME: staticelements: Generic bin KLASS: Generic/Bin ---------- NAME: staticelements: Pipeline object KLASS: Generic/Bin ---------- NAME: sndfile: Sndfile decoder KLASS: Decoder/Audio ---------- NAME: vorbis: Vorbis audio encoder KLASS: Codec/Encoder/Audio ... NAME: xvimagesink: Video sink KLASS: Sink/Video ---------- NAME: audiovisualizers: Stereo visualizer KLASS: Visualization ...
De todos estos complementos solo nos interesan los de la klase Visualization, que se pueden filtrar como se muestra en este otro programa:
import gi gi.require_version("Gst", "1.0") from gi.repository import Gst # FUNCTIONS # ========= def is_visualizer(feature): """Return True if FEATURE's klass is Visualization.""" return ( isinstance(feature, Gst.ElementFactory) and "Visualization" in feature.get_metadata("klass") ) # MAIN # ==== # Initialize GStreamer. Gst.init(None) # Show available visualization plugins. registry = Gst.Registry.get() vis_plugins = registry.feature_filter(is_visualizer, False) print("{} Visualization plugins found.".format(len(vis_plugins))) for plugin in vis_plugins: name = plugin.get_plugin_name() long_name = plugin.get_metadata("long-name") print("NAME: {}: {}".format(name, long_name)) print("KLASS: {}".format(plugin.get_metadata("klass"))) print("-" * 10)
show-vis-plugins
. Lista solo
los complementos de visualización disponibles en el registro de
GStreamer.
Ejecutemos el
Programa show-vis-plugins
:
python3 show-vis-plugins.py
Ahora se listan solo 7 complementos:
7 Visualization plugins found. NAME: goom: GOOM: what a GOOM! KLASS: Visualization ---------- NAME: goom2k1: GOOM: what a GOOM! 2k1 edition KLASS: Visualization ---------- NAME: monoscope: Monoscope KLASS: Visualization ---------- NAME: audiovisualizers: Stereo visualizer KLASS: Visualization ---------- NAME: audiovisualizers: Frequency spectrum scope KLASS: Visualization ---------- NAME: audiovisualizers: Synaescope KLASS: Visualization ---------- NAME: audiovisualizers: Waveform oscilloscope KLASS: Visualization ----------
La Figura 2 muestra el tipo de visualización que genera cada uno de estos complementos.

Ya con la lista de visualizadores, podemos seleccionar el
que queramos. La forma de hacerlo no es muy conveniente porque
parece que los complementos no tienen identificadores únicos.
El siguiente programa selecciona el complemento de visualización
por nombre corto, por ejemplo
audiovisualizers
, o por nombre largo, por
ejemplo, Waveform oscilloscope
:
import os import sys import gi gi.require_version("Gst", "1.0") from gi.repository import Gst # CONSTANTS # ========= DEFAULT_VIS_PLUGIN = "Waveform oscilloscope" # FUNCTIONS # ========= def get_vis_plugin(name): """Return the visualization plugin called NAME or None.""" registry = Gst.Registry.get() vis_plugins = registry.feature_filter(is_visualizer, False) vis_plugin = None for plugin in vis_plugins: short_name = plugin.get_plugin_name() long_name = plugin.get_metadata("long-name") if short_name == name or long_name == name: vis_plugin = plugin.create() return vis_plugin def is_visualizer(feature): """Return True if FEATURE's klass is Visualization.""" return ( isinstance(feature, Gst.ElementFactory) and "Visualization" in feature.get_metadata("klass") ) # MAIN # ==== # Initialize GStreamer. Gst.init(None) # Define audio file location. audio_file_path = os.path.abspath("song18.mp3") # Build a playbin pipeline. textual_pipeline = "playbin uri=file://{}".format(audio_file_path) pipeline = Gst.parse_launch(textual_pipeline) # Set the visualization flag. flags = pipeline.get_property("flags") vis_flag = (1 << 3) flags |= vis_flag pipeline.set_property("flags", flags) # Set visualization plugin. vis_plugin = get_vis_plugin(DEFAULT_VIS_PLUGIN) if len(sys.argv) == 2: # A plugin name was passed as argument on CLI, use it. vis_plugin = get_vis_plugin(sys.argv[1]) pipeline.set_property("vis-plugin", vis_plugin) # Start playing. pipeline.set_state(Gst.State.PLAYING) # Wait until error or EOS. bus = pipeline.get_bus() message = bus.timed_pop_filtered( Gst.CLOCK_TIME_NONE, Gst.MessageType.ERROR | Gst.MessageType.EOS ) # Free resources. pipeline.set_state(Gst.State.NULL)
play-another-vis
.
Reproduce el archivo de audio
song18.mp3 con la visualización del
complemento Waveform oscilloscope de
audiovisualizers.
Ejecutemos el
Programa play-another-vis
:
python3 play-another-vis.py
Debería verse un resultado como el de la Figura 2, fotograma G. Para ver otra visualización, se puede pasar como argumento el nombre corto o largo del complemento deseado. Por ejemplo:
python3 play-another-vis.py "Frequency spectrum scope"
Pendiente
Integrar la visualización en una aplicación con interfaz gráfica escrita en GTK.
Temas relacionados: