ALSA e Gambas: Connettere due Client mediante la sottoscrizione delle porte

Da Gambas-it.org - Wikipedia.

Mostriamo di seguito un esempio di applicativo che consente la sottoscrizione della connessione delle porte di due Client di Alsa, nonché la successiva disconnessione.

Private seq As Pointer


Library "libasound:2"

Private Const SND_SEQ_OPEN_DUPLEX As Integer = 3
Private Const SND_SEQ_PORT_CAP_READ As Integer = 1
Private Const SND_SEQ_PORT_CAP_SUBS_READ As Integer = 32


' 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(handle As Pointer, name As String) As Integer

' int snd_seq_client_id(snd_seq_t * seq)
' Get the client id.
Private Extern snd_seq_client_id(handle As Pointer) As Integer

' int snd_seq_parse_address (snd_seq_t *seq, snd_seq_addr_t *addr, const char *str)
' Parse the given string and get the sequencer address.
Private Extern snd_seq_parse_address(handle As Pointer, addr As Pointer, charstr As String) As Integer

' int snd_seq_port_subscribe_malloc (snd_seq_port_subscribe_t **ptr)
' Allocate an empty snd_seq_port_subscribe_t using standard malloc.
Private Extern snd_seq_port_subscribe_malloc(ptr As Pointer) As Integer

' void snd_seq_port_subscribe_set_sender (snd_seq_port_subscribe_t *info, const snd_seq_addr_t *addr)
' Set sender address of a port_subscribe container.
Private Extern snd_seq_port_subscribe_set_sender(pinfo As Pointer, addr As Pointer)

' void snd_seq_port_subscribe_set_dest (snd_seq_port_subscribe_t *info, const snd_seq_addr_t *addr)
' Set destination address of a port_subscribe container.
Private Extern snd_seq_port_subscribe_set_dest(pinfo As Pointer, addr As Pointer)

' void snd_seq_port_subscribe_set_queue (snd_seq_port_subscribe_t *info, int q)
' Set the queue id of a port_subscribe container.
Private Extern snd_seq_port_subscribe_set_queue(pinfo As Pointer, qI As Integer)

' void snd_seq_port_subscribe_set_exclusive (snd_seq_port_subscribe_t *info, int val)
' Set the exclusive mode of a port_subscribe container.
Private Extern snd_seq_port_subscribe_set_exclusive(pinfo As Pointer, vI As Integer)

' void snd_seq_port_subscribe_set_time_update (snd_seq_port_subscribe_t *info, int val)
' Set the time-update mode of a port_subscribe container.
Private Extern snd_seq_port_subscribe_set_time_update(pinfo As Pointer, vI As Integer)

' void snd_seq_port_subscribe_set_time_real (snd_seq_port_subscribe_t *info, int val)
' Set the real-time mode of a port_subscribe container.
Private Extern snd_seq_port_subscribe_set_time_real(pinfo As Pointer, vI As Integer)

' int snd_seq_get_port_subscription (snd_seq_t *handle, snd_seq_port_subscribe_t *sub)
' Obtain subscription information.
Private Extern snd_seq_get_port_subscription(handle As Pointer, subP As Pointer) As Integer

' int snd_seq_subscribe_port (snd_seq_t *handle, snd_seq_port_subscribe_t *sub)
' Subscribe a port connection.
Private Extern snd_seq_subscribe_port(handle As Pointer, subP As Pointer) As Integer

' int snd_seq_unsubscribe_port (snd_seq_t *handle, snd_seq_port_subscribe_t *sub)
' Unsubscribe a connection between ports.
Private Extern snd_seq_unsubscribe_port(handle As Pointer, subP As Pointer) As Integer

' const char * snd_strerror (int errnum)
' Returns the message for an error code.
Private Extern snd_strerror(err As Integer) As Pointer

' int snd_seq_close (snd_seq_t *handle)
' Close the sequencer.
Private Extern snd_seq_close(handle As Pointer)


Public Sub Main()

 inizializza_Alsa()
 
 sottoscrizione_Porte()
 
 chiude_Alsa()

End


Private Procedure inizializza_Alsa()

 Dim err, id As Integer


 err = snd_seq_open(VarPtr(seq), "default", SND_SEQ_OPEN_DUPLEX, 0)
 If err < 0 Then Error.Raise("Errore nell'apertura di Alsa: " & snd_strerror(err))
 
 err = snd_seq_set_client_name(seq, "Connessione Porte Client ALSA")
 If err < 0 Then Error.Raise("Imposibile attribuire un nome al programma: " & snd_strerror(err))
 
 id = snd_seq_client_id(seq)
 If id < 0 Then Error.Raise("Imposibile ottenere un ID del programma: " & snd_strerror(err))

End


Private Procedure sottoscrizione_Porte()

 Dim err, queue, exclusive As Integer
 Dim sender, dest, convert_time, convert_real As Integer
 Dim subs As Pointer


' Imposta la sottoscrizione delle porte:
  err = snd_seq_parse_address(seq, VarPtr(sender), Application.Args[Application.Args.Count - 2])
  If err < 0 Then Error.Raise("Indirizzo non vaido del mittente: " & snd_strerror(err))
 
  err = snd_seq_parse_address(seq, VarPtr(dest), Application.Args[Application.Args.Count - 1])
  If err < 0 Then Error.Raise("Indirizzo non vaido del Client destinatario: " & snd_strerror(err))
 
  snd_seq_port_subscribe_malloc(VarPtr(subs))
  snd_seq_port_subscribe_set_sender(subs, VarPtr(sender))
  snd_seq_port_subscribe_set_dest(subs, VarPtr(dest))
  snd_seq_port_subscribe_set_queue(subs, queue)
  snd_seq_port_subscribe_set_exclusive(subs, exclusive)
  snd_seq_port_subscribe_set_time_update(subs, convert_time)
  snd_seq_port_subscribe_set_time_real(subs, convert_real)
 

' Sottoscrizione e cancellazione della sottoscrizione:

  If Application.Args[1] = "d" Then
    err = snd_seq_get_port_subscription(seq, subs)
    If err < 0 Then Error.Raise("Nessuna sottoscrizione trovata: " & snd_strerror(err))
      
    err = snd_seq_unsubscribe_port(seq, subs)
    If err < 0 Then Error.Raise("Tentativo di disconnessione fallito: " & snd_strerror(err))
    Print "Disconnessione avvenuta !"
  Else
    err = snd_seq_get_port_subscription(seq, subs)
    If err = 0 Then Error.Raise("La connessione è già sottoscritta: " & snd_strerror(err))
 
    err = snd_seq_subscribe_port(seq, subs)
    If err < 0 Then Error.Raise("Tentativo di connessione fallito: " & snd_strerror(err))
    Print "Connessione avvenuta !"
  Endif

End
 

Private Procedure chiude_Alsa()

 snd_seq_close(seq)
 
End

Per eseguire il suddetto codice, bisognerà creare il relativo eseguibile "a riga di comando". L'eseguibile andrà lanciato da Terminale:

  • per connettere due porte: indicare il numero identificativo del Client che invia i dati Midi e quello della sua porta di uscita, e di seguito il numero identificativo del Client ricevente e quello della sua porta di entrata. Esempio:
connettore.gambas 14:0 128:0
  • per disconnettere due Client: come il precedente punto premettendo la lettera "d". Esempio:
connettore.gambas d 14:0 128:0