Versión: 2.x.
Descargas: pyvst, ATKUniversalDelay (plugin VST de ejemplo), utility.py (funciones de conversión), NumPy, ejemplos.zip.
Plataforma: Microsoft Windows.
Introducción
VST es una interfaz de procesamiento de audio y MIDI desarrollada por Steinberg pero adoptada por la mayoría del software del tipo DAW (Digital Audio Workstation), por ejemplo, Cubase, SONAR, Pro Tools. Otras interfaces similares incluyen RTAS (desarrollada por AVID) y Audio Units (Apple).
PyVST es un módulo escrito íntegramente en Python utilizando ctypes que permite interactuar con plugins VST (escritos en C/C++ y distribuidos como archivos DLL).
Un plugin VST está constituido, principalmente, por tres partes.
- Un algoritmo de procesamiento de audio y/o MIDI.
- Un conjunto de parámetros que permiten modificar diferentes aspectos del algoritmo. Estos pueden ser ajustados utilizando PyVST o bien…
- Una interfaz gráfica.
El procesamiento de audio se realiza a través de las funciones process_replacing
y process_double_replacing
, de acuerdo a la capacidad de manejo de datos de 32 o 64 bits del plugin (que puede ser determinado utilizando can_process_double()
). La función process
llama a alguna de las dos funciones anteriores dependiendo del tipo de datos de entrada que se le han pasado como argumeto. Las tres funciones requieren como parámetro dos vectores de NumPy: uno de entrada y otro de salida.
Descarga e instalación
Una vez descargado el archivo ZIP y antes de iniciar la instalación es necesario realizar un pequeño cambio en el archivo pyvst/aeffect.py
para prevenir errores durante la posterior ejecución. Simplemente reemplazar el siguiente bloque de código.
class VstStringConstants(object): kVstMaxProgNameLen = 24 kVstMaxParamStrLen = 8 kVstMaxVendorStrLen = 64 kVstMaxProductStrLen = 64 kVstMaxEffectNameLen = 32
Por este otro:
class VstStringConstants(object): kVstMaxProgNameLen = 256 kVstMaxParamStrLen = 256 kVstMaxVendorStrLen = 256 kVstMaxProductStrLen = 256 kVstMaxEffectNameLen = 256
Luego de guardar el archivo procedemos con la instalación.
python setup.py install
Para todos los ejemplos en este artículo puedes utilizar el plugin ATKUniversalDelay.
Cargar un plugin
Se importa la clase VSTPlugin
y se pasa como argumento el nombre del archivo DLL.
from pyvst.vstplugin import VSTPlugin plugin = VSTPlugin("ATKUniversalDelay.dll")
Puede obtenerse información del plugin utilizando la función dump_effect_properties
.
from pyvst.vstplugin import dump_effect_properties, VSTPlugin plugin = VSTPlugin("ATKUniversalDelay.dll") dump_effect_properties(plugin)
Imprime en pantalla:
Plugin name: ATKUniversalDelay
Vendor name: MatthieuBrucher
Product name:
numPrograms = 1
numParams = 4
numInputs = 1
numOutputs = 1
Param 000: Delay [1.0 ms] (normalized = 0.301511)
Param 001: Blend [100.00 %] (normalized = 1.000000)
Param 002: Feedforward [50.00 %] (normalized = 0.750000)
Param 003: Feedback [0.00 %] (normalized = 0.500000)
Interfaz Gráfica
La mayoría de los plugins VST integran una interfaz gráfica. La función VSTPlugin.has_editor
permite determinar si se encuentra disponible. La función VSTPlugin.open_edit
espera como argumento el identificador (handle) de una ventana para incluir el editor.
A continuación tres ejemplos utilizando las librerías gráficas Tkinter, wxPython y PyQt.
#!/usr/bin/env python # -*- coding: utf-8 -*- # gui_tk.py from pyvst.vstplugin import VSTPlugin from Tkinter import Tk, Frame plugin = VSTPlugin("ATKUniversalDelay.dll") if plugin.has_editor(): rect = plugin.get_erect() root = Tk() frame = Frame(root, height=rect.bottom, width=rect.right) frame.pack() root.title("Ejemplo PyVST con Tkinter") plugin.open_edit(frame.winfo_id()) root.mainloop() plugin.close_edit() else: print "El plugin no tiene editor."
#!/usr/bin/env python # -*- coding: utf-8 -*- # gui_wx.py from pyvst.vstplugin import VSTPlugin import wx plugin = VSTPlugin("ATKUniversalDelay.dll") if plugin.has_editor(): app = wx.App() frame = wx.Frame(None, -1, "Ejemplo PyVST con wxPython") plugin.open_edit(frame.GetHandle()) rect = plugin.get_erect() frame.SetClientSize((rect.right, rect.bottom)) frame.Show() app.MainLoop() plugin.close_edit() else: print "El plugin no tiene editor."
#!/usr/bin/env python # -*- coding: utf-8 -*- # gui_qt.py from pyvst.vstplugin import VSTPlugin from PyQt4.QtGui import QApplication, QMainWindow plugin = VSTPlugin("ATKUniversalDelay.dll") if plugin.has_editor(): app = QApplication([]) window = QMainWindow() window.setWindowTitle("Ejemplo PyVST con Qt") rect = plugin.get_erect() window.resize(rect.right, rect.bottom) window.show() plugin.open_edit(int(window.winId())) app.exec_() plugin.close_edit() else: print "El plugin no tiene editor."
Parámetros
Cada parámetro de un plugin (en caso de tener) se encuentra identificado por un número. De esta forma, podemos obtener su valor utilizando VSTPlugin.get_parameter(numero_de_parametro)
y análogamente ajustarlo con la función VSTPlugin.set_parameter(numero_de_parametro, nuevo_valor)
.
El siguiente código de ejemplo abre el editor del plugin y luego guarda los valores de los parámetros en un archivo de configuración.
#!/usr/bin/env python # -*- coding: utf-8 -*- from ConfigParser import SafeConfigParser from pyvst.vstplugin import VSTPlugin import wx def main(): plugin = VSTPlugin("ATKUniversalDelay.dll") app = wx.App() frame = wx.Frame(None, -1, "VST Interface") plugin.open_edit(frame.GetHandle()) rect = plugin.get_erect() frame.SetClientSize((rect.right, rect.bottom)) frame.Show() app.MainLoop() config = SafeConfigParser() config.add_section("Parameters") for i in range(plugin.get_number_of_parameters()): config.set("Parameters", "P" + str(i), str(plugin.get_parameter(i))) with open("ATKUniversalDelay.ini", "w") as f: config.write(f) plugin.close() if __name__ == "__main__": main()
Para cargar los valores almacenados debe utilizarse:
config = SafeConfigParser() config.read("ATKUniversalDelay.ini") for i in range(plugin.get_number_of_parameters()): plugin.set_parameter( i, config.getfloat("Parameters", "P" + str(i)) )
Ejemplos
Ejemplo 1
Toma como entrada un archivo mono WAV (input.wav
), lo procesa utilizando el plugin especificado y crea un nuevo archivo de nombre output.wav
. Requiere SciPy.
#!/usr/bin/env python # -*- coding: utf-8 -*- from pyvst.vstplugin import VSTPlugin from scipy.io import wavfile import numpy as np from Tkinter import Tk, Frame from utility import * def main(): # Cargar plugin VST. plugin = VSTPlugin("ATKUniversalDelay.dll") if plugin.has_editor(): # Obtener dimensiones de la interfaz gráfica. rect = plugin.get_erect() # Crear ventana de Tkinter. root = Tk() frame = Frame(root, height=rect.bottom, width=rect.right) frame.pack() root.title("Ejemplo PyVST") plugin.open_edit(frame.winfo_id()) root.mainloop() # Cargar el archivo WAV en un vector de NumPy y # crear otro vector vacío de las mismas dimensiones # para almacenar el audio procesado por el plugin. rate, input_audio = wavfile.read("input.wav") # Convertir los valores a coma flotante de 32 / 64 bits # para poder ser pasados a la función process(). float_type = "float" + str(32 * plugin.can_process_double() + 32) input_audio = pcm2float(input_audio, float_type) output_audio = np.zeros_like(input_audio) plugin.set_sample_rate(rate) plugin.set_block_size(2048) print "Processing ({})...".format(float_type) # Procesar el audio en bloques de 2048 bytes. for i in range(0, input_audio.size, 2048): plugin.process([input_audio[:i]], [output_audio[:i]]) print "Done." # Cerrar el plugin y escribir el resultado en un archivo WAV. plugin.close() # Convertir nuevamente a entero de 16 bits. wavfile.write("output.wav", rate, float2pcm(output_audio, "int16")) if __name__ == "__main__": main()
Ejemplo 2
Procesamiento de audio en tiempo real. Aplica el efecto del plugin VST a la entrada capturada por el micrófono y lo reproduce simultáneamente. Requiere PyAudio.
#!/usr/bin/env python # -*- coding: utf-8 -*- from ConfigParser import SafeConfigParser from pyvst.vstplugin import VSTPlugin import numpy as np import pyaudio from utility import * def main(): CHUNK = 1024 FORMAT = pyaudio.paInt16 CHANNELS = 1 RATE = 44100 print "Loading VST..." # Cargar plugin VST. plugin = VSTPlugin("ATKUniversalDelay.dll") # Determinar el tipo de datos soportado por el plugin. float_type = "float" + str(32 * plugin.can_process_double() + 32) # Cargar parámetros. config = SafeConfigParser() config.read("ATKUniversalDelay.ini") for i in range(plugin.get_number_of_parameters()): plugin.set_parameter( i, config.getfloat("Parameters", "P" + str(i)) ) plugin.set_sample_rate(RATE) plugin.set_block_size(CHUNK) print "Done." print "Opening stream..." # Abrir un canal de entrada y salida. p = pyaudio.PyAudio() stream = p.open(format=FORMAT, channels=CHANNELS, rate=RATE, input=True, output=True, frames_per_buffer=CHUNK) print("Done.") while True: try: # Leer audio del micrófono. data = stream.read(CHUNK) # Convertir los valores a coma flotante. input_audio = pcm2float(np.fromstring(data, dtype="int16"), float_type) # Vector de almacenamiento para el audio procesado. output_audio = np.zeros_like(input_audio) # Procesar audio. plugin.process([input_audio], [output_audio]) # Convertir nuevamente a entero de 16 bits. data = float2pcm(output_audio, "int16").tostring() # Reproducir. stream.write(data) except KeyboardInterrupt: break except Exception as e: from traceback import format_exc print format_exc() break print("Done recording.") stream.stop_stream() stream.close() plugin.close() p.terminate() if __name__ == "__main__": main()
Curso online 👨💻
¡Ya lanzamos el curso oficial de Recursos Python en Udemy!
Un curso moderno para aprender Python desde cero con programación orientada a objetos, SQL y tkinter
en 2024.
Consultoría 💡
Ofrecemos servicios profesionales de desarrollo y capacitación en Python a personas y empresas. Consultanos por tu proyecto.
Antonio says:
hola que tal!! los links estan caidos! pudieras volver a subirlos!!
Recursos Python says:
Hola Antonio. Ya arreglamos los enlaces, gracias por avisar.
D. Jordan says:
No me deja importar la librería.
Si introduzco:
from pyvst.vstplugin import VSTPlugin
Me aparece:
Traceback (most recent call last):
File «», line 1, in
from pyvst.vstplugin import VSTPlugin
File «C:\Users\thejo\AppData\Local\Programs\Python\Python37-32\lib\site-packages\pyvst-0.1.0-py3.7.egg\pyvst\__init__.py», line 2, in
from vstplugin import VSTPlugin, dump_effect_properties
ModuleNotFoundError: No module named ‘vstplugin’
Recursos Python says:
Hola. Dudo que el módulo corra en Python 3 ya que no tiene actualizaciones desde el 2010.
Ruben says:
Hola, muchas gracias por contestar.
Me acabo de registrar y acabo de publicar el tema en el foro…
Muy buena la página, por cierto.
Un saludo
Ruben says:
Hola,
es genial poder encontrar este tutorial en castellano. Lo he probado y funciona perfectamente con los ejemplos que indicas.
El problema que encuentro es que el plugin de muestra funciona con ‘float64’, pero al tratar de cargar otro plugin que trabaje en ‘float32’ me da errores, y no encuentro la solución.
¿sabrías como hacer que funcione?
Muchísimas gracias,
un cordial saludo.
Recursos Python says:
Hola Ruben, me alegro que te haya servido. ¿Qué plugin VST estás utilizando y qué error obtuviste? Si te resulta más cómodo, te invito a registrarte y crear un tema en el foro con todos los detalles.
Un saludo.