INTERCONECTAR UN PROGRAMA GAMBAS A OTRA APLICACION A TRAVÉS ALSA PARA ENVIAR DATOS MIDI
Quid est A.L.S.A. ?
ALSA (Advanced Linux Sound Architecture) es un componente del kernel que sustituye al original Open Sound System (OSS) utilizado para proporcionar driver de dispositivo para tarjetas de audio.
Además de los driver de audio, ALSA también ofrece una libreria en espacio-usuario para desarrolladores de aplicaciones que deseen utilizar las funciones de los driver a través de una API en lugar de una interacción directa con los driver del kernel.
Es posible enviar datos Midi desde una aplicación a otra aplicación a través ALSA. De misma manera una aplicación puede rebir datos Midi de otros aplicaciones.
Para ello, las aplicaciones que deseen intercambiar datos Midi deben conectarse al ALSA, y en particolar al sub-sistema de ALSA que gestiona el Midi: la "Interfaz Sequencer".
Las aplicaciones, por lo tanto, se convierten en "Client" de ALSA, que representa su "Server".
APLICACIÓN-Client_A --->
A L S A <---
APLICACIÓN-Client_B
Así, si se pretende realizar una aplicación Midi que debe relacionarse con ALSA, será necesario que se conciba en primer lugar como "Client" Midi de ALSA.
ALSA sólo se ocupa de programar eventos Midi y enviarlos al destino en el momento adecuado. Todo el procesamiento de eventos MIDI tiene que hacerse dentro de los Clientes.
En efecto la interfaz de ALSA está diseñada para ofrecer eventos MIDI entre los Clientes/Puertos.
USAR EN GAMBAS LOS RECURSOS DE ALSA
Gambas, por ahora, no tiene un su Componente para gestionar directamente los recursos del sistema de sonido ALSA.
Es posible, pero, usar las funciones externas del API de ALSA, invocando su libreria:
Library "libasound:2"
A continuación, cada una de las funciones externas de ALSA se declarará con EXTERN, para que puedan utilizarse en las rutinas.
CREAR UN CLIENT DE ALSA
Para crear una aplicación, que se relaciona con el ALSA como su "Client", se necesitan varias fases de desarrollo.
El API de ALSA proporciona una función específica para crear un ''Client'' Midi de ALSA:
int snd_seq_open (snd_seq_t ** seqp, const char * name, int streams, int mode)
Esta función será declarada en Gambas:
Private Extern snd_seq_open(seqp As Pointer, name As String, streams As Integer, mode As Integer) As Integer
Donde:
- "seqp" es un handle al sub-sistema "Sequencer" de ALSA;
- "name" es en nombre del dispositivo de sonido;
- "streams" establece la gestión de datos Midis del sub-sistema "Sequencer" de ALSA;
- "mode" que hará que las operaciones de lectura/escritura bloqueen o no bloqueen.
DAR UN NOMBRE AL "CLIENT"
Si lo desea, es posible asignar un nombre al "Client" que estamos creando.
El API de ALSA proporciona una función específica para eso:
int snd_seq_set_client_name(snd_seq_t *seq, const char *name)
Esta función será declarada en Gambas:
Private Extern snd_seq_set_client_name(seq As Pointer, name As String) As Integer
OBTENER EL NUMERO DE IDENTIFICACION DEL "CLIENT"
Para obtener el número de identificación del nuestro "Client", ALSA proporciona esta función:
int snd_seq_client_id(snd_seq_t *seq)
que será declarada en Gambas:
Private Extern snd_seq_client_id(seq As Pointer) As Integer
CREACION DEL PUERTO DEL "CLIENT" Y SU CAPACIDAD
Hemos dicho que una aplicación-Client de ALSA envia o recibe datos Midi desde otras aplicaciones-Client a través ALSA (más precisamente a través un sub-sistema de ALSA adecuado para el tipo de datos enviados).
Un "Client" envia o recibe datos mediante una o más sus Puertos.
Cada Puerto del "Client" tiene su particular "Capacidad"; es decir que puede solo
ser "Leído" (READ) o solo
ser "Escrito" (WRITE), o ambas (DUPLEX).
ATENCIÓN: la definición de la "Capacidad " ("READ", "WRITE" o "DUPLEX") del Puerto de un Client debe considerarse siempre "
desde el punto de vista del otro Client " de ALSA, al que el nuestro Client desea conectarse.
Por lo tanto, si configuramos - por ejemplo - la "Capacidad" del puerto del nuestro Client a READ (en lectura), significa que el
otro Client, al que el nuestro Cliente se conecta, podrá "leer" los datos desde el Puerto de nuestro Client. En este caso el nuestro Client sólo puede escribir (enviar) datos a su propio puerto.
Es decir, en este caso, si la Capacidad del Puerto del nuestro Client es "READ", el nuestro Client puede escibir (es decir "enviar") datos Midi a su Puerto y un otro Client (al que el nuestro està conectado) puede
LEER (es decir "recibir") aquellos datos Midi desde el Puerto de nuestro Client.
Para establecer la capacidad de un puerto del Client, que estamos creando, utilizamos las siguientes Constantes de ALSA:
SND_SEQ_PORT_CAP_READ
SND_SEQ_PORT_CAP_WRITE
SND_SEQ_PORT_CAP_DUPLEX
Para crear un Puerto del Client, se usa esta función:
int snd_seq_create_simple_port(snd_seq_t *seq, const char *name, unsigned int caps, unsigned int type)
que será declarada en Gambas:
Private Extern snd_seq_create_simple_port(seq As Pointer, name As String, caps As Integer, type As Integer) As Integer
EJEMPLO PRACTICO
Vamos a crear ahora una muy simple aplicación-Client con su Puerto con
capacidad "READ":
Código:
Library "libasound:2"
Private Const SND_SEQ_OPEN_OUTPUT As Integer = 1
Private Const SND_SEQ_PORT_CAP_READ As Integer = 1
Private Const SND_SEQ_PORT_CAP_SUBS_READ As Integer = 32
Private Const SND_SEQ_PORT_TYPE_MIDI_GENERIC As Integer = 2
Private Const SND_SEQ_PORT_TYPE_APPLICATION As Integer = 1048576
' int snd_seq_open (snd_seq_t **handle, const char * name, int streams, int mode)
' Open the ALSA sequencer.
Private Extern snd_seq_open(handle As Pointer, name As String, streams As Integer, mode As Integer) As Integer
' int snd_seq_set_client_name (snd_seq_t *seq, const char *name)
' Set client name.
Private Extern snd_seq_set_client_name(seq As Pointer, name As String) As Integer
' int snd_seq_client_id (snd_seq_t *handle)
' Get the client id.
Private Extern snd_seq_client_id(handle As Pointer) As Integer
' int snd_seq_create_simple_port (snd_seq_t *seq, const char *name, unsigned int caps, unsigned int type)
' Create a port - simple version.
Private Extern snd_seq_create_simple_port(seq As Pointer, name As String, caps As Integer, type As Integer) As Integer
' const char* snd_strerror (int errnum)
' Returns the message for an error code.
Private Extern snd_strerror(errnum As Integer) As String
' int snd_seq_close (snd_seq_t *handle)
' Close the sequencer.
Private Extern snd_seq_close(handle As Pointer) As Integer
Public Sub Main()
Dim handle As Pointer
Dim rit, id, porta As Integer
' Crea un Client de ALSA:
rit = snd_seq_open(VarPtr(handle), "default", SND_SEQ_OPEN_OUTPUT, 0)
If rit < 0 Then Error.Raise("Imposible abrir el subsistema 'seq' de ALSA: " & snd_strerror(rit))
' Da un nombre al Client:
snd_seq_set_client_name(handle, "Mi primer Client de ALSA")
' Obtiene el número de identificación del Client:
id = snd_seq_client_id(handle)
If id < 0 Then
snd_seq_close(handle)
Error.Raise("Imposible obtener el número de identificación del Client: " & snd_strerror(id))
Endif
Print "Número de identificación del Client: "; id
' Crea un Puerto del Client; le asigna la capacidad de "ser leído" (READ) por otros Clientes y realiza la suscripción.
' Obtiene tambien el número de identificación del Puerto del Client.
porta = snd_seq_create_simple_port(handle, "Puerto", SND_SEQ_PORT_CAP_READ Or SND_SEQ_PORT_CAP_SUBS_READ, SND_SEQ_PORT_TYPE_MIDI_GENERIC Or SND_SEQ_PORT_TYPE_APPLICATION)
If porta < 0 Then
snd_seq_close(handle)
Error.Raise("Imposible crear el Puerto del Client: " & snd_strerror(porta))
Endif
Print "Número del Puerto del Client: "; porta
' Mantiene vivo a este Client solo por 20 segundos:
Wait 20
' Cerra el subsistema 'seq' de ALSA y destruye el Client:
snd_seq_close(handle)
End
Podemos ver el efecto practico de este codigo, abriendo el archivo de sistema:
/proc/asound/seq/clients .
En este archivo de texto encontraremos la referencia a nuestro
Client creado.
En particular, si no hay otro Client de ALSA activo, nosotros veremos esta referencia:
...........................................................................
Client 128 : "Mi primer Client de ALSA" [User]
Port 0 : "Puerto" (R-e-)
...........................................................................
Donde las palabras:
"
Client " = ...el nuestro programa es un
Client de ALSA;
"
128 " = el número de identificación del nuestro
Client;
"
Mi primer Client de ALSA " = el nombre que le dimos al nuestro
Client;
"
[User] " = especifica que este
Client ha sido creado por un usuario;
"
Port " = se refiere a el Puerto del nuestro
Client;
"
0 " = el número de identificación del Puerto del nuestro
Client;
"
Puerto " = el nombre que le dimos al Puerto (2° argumento de la función "
snd_seq_create_simple_port( ) " )
"
R " = el Puerto del
Client tiene su
Capacidad a READ.