Notas breves sobre cómo enviar "Mensajes MIDI" a un softsynth vía A.L.S.A., de la manera más sencilla posible.
Como es sabido, un flujo de datos MIDI no contiene - como es el caso del audio - la información que describe las características de la onda sonora, sino información que será utilizada por otros programas para ejecutar un sonido localizado entre una colección. Esta colección estandarizada de sonidos de varios instrumentos musicales, en su mayoría de formato WAV, está contenida en un archivo especial, llamado banco de fuentes de sonido.
El programa que debe utilizar los sonidos, contenidos en los archivos del banco de fuentes sonoras (soundfont bank), para que una nota MIDI pueda ser escuchada, se llama softsynth.
El protocolo MIDI a través de un "Mensaje MIDI" informa al softsynth de que debe usar una fuente de sonido WAV (que en la práctica corresponde a un instrumento musical) para cambiar a una cierta frecuencia de sonido durante un período de tiempo determinado.
Por lo tanto, un programa, que produce "Mensajes MIDI", debe entregar los datos necesarios al softsynth. Solo así el "Mensaje MIDI" podrá proporcionar la producción del sonido requerido.
Actualmente, la comunicación entre un programa que envía datos MIDI y el softsynth suministrado se realiza a través del sistema de sonido A.L.S.A. que se encarga de gestionar el protocolo de transmisión, recepción y eventual temporización de los datos MIDI, así como la relación con el sistema operativo y el hardware de audio suministrado.
ALSA actúa como un "Servidor" central que proporciona funciones y servicios a los programas que deben tratar con él para la reproducción de audio.
Dichos programas, por lo tanto, se convierten en "Clientes" de ALSA, y se relacionan con dicho sistema central de sonido para poder comunicarse con otros "Clientes" y con el sistema operativo.
Los "Clientes" del "Servidor" de ALSA son visibles en el archivo "/proc/asound/seq/clients".
Por lo tanto, si queremos en Gambas realizar cualquier programa que envíe "Mensajes MIDI" al softsynth en punto para su gestión sonora, deberá relacionarse con ALSA.
El sistema ALSA se compone de algunos subsistemas. El sub-sistema que gestiona los datos MIDI se llama "secuenciador" de ALSA e identificado con la abreviatura "seq". Por lo tanto, será necesario abrir ese sub-sistema para poder, por un lado, transformar nuestro programa en un "cliente" de ALSA y, por otro, aprovechar los recursos que ese sub-sistema proporciona.
Para operar directamente con ALSA y sus recursos, nuestro programa Gambas deberá utilizar las funciones externas de ALSA mediante la instrucción "Extern", después de haber declarado la biblioteca compartida de ALSA, que contiene las funciones externas que servirán para el envío de "Mensajes de Midi".
La biblioteca compartida externa de ALSA se declarará como sigue:
Cita: Library "libasound:2.0.0"
La función externa de ALSA que permite hacer de nuestro programa Gambas un "Cliente" de ALSA es:
snd_seq_open()
De los cuatro parámetros formales aquí se encuentra en particular el primero, que es un puntero de puntero, y que en Gambas se debe reproducir: VarPtr(Pointer); así como el tercero: el argumento pasado será una Constante para la configuración del "secuenciador" de ALSA para los datos en "Salida".
Esta función externa figurará en nuestro programa como sigue:
Cita: Private Extern snd_seq_open(handle As Pointer, name As String, streams As Integer, mode As Integer) As Integer
Una vez terminado, el sub-sistema "seq" debe cerrarse para liberar la memoria utilizada para la gestión de los recursos proporcionados por ALSA.
El cierre se efectuará con la función externa de ALSA declarada en Gambas:
Cita:Private Extern snd_seq_close(handle As Pointer) As Integer
La gestión de los posibles errores con las funciones externas de ALSA se realizará con la función externa específica declarada en Gambas:
Cita:Private Extern snd_strerror(err As Integer) As String
Por lo tanto, al disponer la apertura del sub-sistema "seq" de ALSA mediante un 'ToggleButton', tendremos hasta ahora el siguiente código:
Código:
Private midi As Pointer
Library "libasound:2.0.0"
Private Const SND_SEQ_OPEN_OUTPUT As Integer = 1
Private Extern snd_seq_open(handle As Pointer, name As String, streams As Integer, mode As Integer) As Integer
Private Extern snd_strerror(err As Integer) As String
Private Extern snd_seq_close(handle As Pointer) As Integer
Public Sub ToggleButton1_Click()
If ToggleButton1.Value Then
Dim rit As Integer
rit = snd_seq_open(VarPtr(midi), "default", SND_SEQ_OPEN_OUTPUT, 0)
If rit < 0 Then Error.Raise("Errore: " & snd_strerror(rit))
Else
snd_seq_close(midi)
Endif
End
Al hacer clic en 'ToggleButton', nuestro programa se convertirá en un "Cliente" de ALSA. Esto puede comprobarse en el archivo de sistema "/proc/asound/seq/clients". Si ya ha lanzado el sofsynth, nuestro programa recibirá el número de identificación 129 de "Cliente" de ALSA.
Nuestro programa MIDI ya está listo para enviar datos MIDI al softsynth a través de ALSA.
ALSA es muy estricto y requiere que el "Mensaje MIDI" esté compuesto por varios datos específicos que se deben tener en un área de memoria reservada, formada por 28 bytes.
Cada "Mensaje MIDI" está representado en el protocolo de ALSA con un "Evento MIDI" que consiste en la zona de memoria asignada de 28 bytes, para la cual el protocolo de ALSA prevé el uso de una Estructura.
Gambas nos ofrece más de una opción para crear ese área de memoria reservada. Dado que nuestro ejemplo se limitará solo a "Mensajes MIDI" de encendido y apagado de una nota MIDI, podremos utilizar con tranquilidad un vector de tipo Byte[].
Al hacer clic en un 'Botón', de los 28 bytes, solo valoraremos algunos:
Al elemento de índice cero asignaremos un entero que represente de vez en cuando el "Mensaje MIDI" de Note-ON (encendido de la nota MID) o de Note-OFF (apagado de la nota MIDI que se está ejecutando).
Al elemento de índice 3 asignaremos el valor 253 para indicar que el envío del "Evento MIDI" de ALSA es directo.
Al elemento del índice 14 le asignaremos el número de identificación del softsynth, al que enviar el "Evento MIDI" de ALSA, y que suele ser 128, como se puede comprobar en el archivo "/proc/asound/seq/clients".
Al elemento de índice 17 le asignaremos un número de nota MIDI, que se ejecutará o silenciará.
Al elemento del índice 18 asignaremos el valor 100 de "velocidad del tacto".
El elemento de índice 16 representa el canal MIDI, que permanecerá - en nuestro caso - preestablecido en cero (canal MIDI 1). Si lo ponemos a 9 (canal MIDI 10), escucharemos un instrumento de percusión.
Constituido el "Evento MIDI" de ALSA en el vector de tipo Byte[], es necesario enviarlo al softsynth a través de ALSA.
Esto se hace en nuestro caso esencial a través de una función externa específica, como se indica en nuestro programa:
Cita:Private Extern snd_seq_event_output_direct(handle As Pointer, ev As Byte[])
Todo está en orden.
A continuación, el código completo de nuestro sencillo y esencial programa Gambas:
Código:
Private midi As Pointer
Private evento As New Byte[28]
Library "libasound:2.0.0"
Private Const SND_SEQ_OPEN_OUTPUT As Integer = 1
Private Const SND_SEQ_QUEUE_DIRECT As Byte = 253
Private Enum SND_SEQ_EVENT_NOTEON = 6, SND_SEQ_EVENT_NOTEOFF
Private Extern snd_seq_open(handle As Pointer, name As String, streams As Integer, mode As Integer) As Integer
Private Extern snd_strerror(err As Integer) As String
Private Extern snd_seq_event_output_direct(handle As Pointer, ev As Byte[])
Private Extern snd_seq_close(handle As Pointer) As Integer
Public Sub Form_Open()
Button1.Enabled = False
End
Public Sub ToggleButton1_Click()
If ToggleButton1.Value Then
Dim rit As Integer
rit = snd_seq_open(VarPtr(midi), "default", SND_SEQ_OPEN_OUTPUT, 0)
If rit < 0 Then Error.Raise("Errore: " & snd_strerror(rit))
Button1.Enabled = True
Else
snd_seq_close(midi)
Button1.Enabled = False
Endif
End
Public Sub Button1_MouseDown()
evento[0] = SND_SEQ_EVENT_NOTEON
evento[3] = SND_SEQ_QUEUE_DIRECT
evento[14] = 128
evento[17] = 64
evento[18] = 100
snd_seq_event_output_direct(midi, evento)
End
Public Sub Button1_MouseUp()
evento[0] = SND_SEQ_EVENT_NOTEOFF
evento[3] = SND_SEQ_QUEUE_DIRECT
evento[14] = 128
evento[17] = 64
evento[18] = 0
snd_seq_event_output_direct(midi, evento)
End
Si ha instalado softsynth Fluidsynth en el sistema, entonces el programa, descrito anteriormente, debería conectarse automáticamente a ese softsynth.
En caso contrario, proceder como sigue:
1) desde Terminal lanzar esta línea: ~$
fluidsynth reload 0 ;
2) sin cerrar el terminal, comprobar en la utilidad "Monitor de sistema" que Fluidsynth está presente entre los procesos activos;
3) si Fluidsynth está presente entre los procesos, verificar también que esté presente en el num. 128 en el fichero /proc/asound/seq/clients;
4) en caso afirmativo - sin cerrar el Terminal - lanzar el código, mostrado arriba.
Si utiliza el softsynth de Fluidsynth, debe tener instalado en su sistema el paquete Fluid (R3) General MIDI SoundFont (GM):
fluid-soundfont-gm