Alsa e Gambas: Ricezione dei dati Midi con un ciclo

Da Gambas-it.org - Wikipedia.

Mostreremo di seguito il codice di un semplice Client Midi di Alsa capace di ricevere dati Midi (a titolo esemplificativo sono stati individuati i quattro tipi più importanti e frequenti) da un altro Client di Alsa (ad esempio da una tastiera Midi esterna) mediante un ciclo infinito.
Ovviamente bisognerà connettere via Alsa il secondo Client (la tastiera Midi) al presente applicativo Midi, ad esempio lanciando nel Terminale la seguente riga di comando:

~ $ aconnect 24:0 ID_programma_Midi:0

oppure indirettamente connettendolo prima al sistema interno di ALSA:

~ $ aconnect 24:0 14:0

e successivamente

~ $ aconnect 14:0 ID_programma_Midi:0

Di seguito il codice dell'esempio pratico:

Library "libasound:2.0.0"

Public Struct snd_seq_event
  type As Byte                ' byte n° 0
  flag As Byte
  tag As Byte 
  queue As Byte 
  tick_o_Tv_sec As Integer
  Tv_nsec As Integer
  source_id As Byte
  source_porta As Byte
  dest_id As Byte
  dest_porta As Byte
    channel As Byte           ' byte n° 16
    note As Byte
    velocity As Byte
    off_velocity As Byte
      param As Integer        ' byte n° 20
      value As Integer        ' byte n° 24
End Struct
 
Private Const SND_SEQ_OPEN_INPUT As Integer = 2
Private Const SND_SEQ_PORT_CAP_WRITE As Integer = 2
Private Const SND_SEQ_PORT_CAP_SUBS_WRITE As Integer = 64
Private Const SND_SEQ_PORT_TYPE_MIDI_GENERIC As Integer = 2
Private Const SND_SEQ_PORT_TYPE_APPLICATION As Integer = 1048576
Private Enum SND_SEQ_EVENT_NOTEON = 6, SND_SEQ_EVENT_NOTEOFF, SND_SEQ_EVENT_CONTROLLER = 10, SND_SEQ_EVENT_PGMCHANGE

' 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_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(handle As Pointer, name As String, caps As Integer, type As Integer) As Integer
 
' int snd_seq_event_input(snd_seq_t *handle, snd_seq_event **ev)
' Retrieve an event from sequencer.
Private Extern snd_seq_event_input(handle As Pointer, ev As Snd_seq_event) As Integer

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


Public Sub Main()

 Dim idporta, err As Integer
 Dim seq As Pointer
 
 err = snd_seq_open(VarPtr(seq), "default", SND_SEQ_OPEN_INPUT, 0)
 If err < 0 Then Error.Raise("Impossibile inizializzare il subsistema 'seq' di ALSA: " & snd_strerror(err))
   
 err = snd_seq_set_client_name(seq, "Sequencer dimostrativo 1 di ALSA")
 If err < 0 Then Error.Raise("Impossibile assegnare un nome al Client applicativo di ALSA: " & snd_strerror(err))
   
 idporta = snd_seq_create_simple_port(seq, "Porta del sequencer dimostrativo di ALSA", SND_SEQ_PORT_CAP_WRITE Or SND_SEQ_PORT_CAP_SUBS_WRITE, SND_SEQ_PORT_TYPE_MIDI_GENERIC Or SND_SEQ_PORT_TYPE_APPLICATION)
 If idporta < 0 Then Error.Raise("Impossibile creare una porta del Client applicativo di ALSA: " & snd_strerror(err))
 
 intercettaMessaggiMidi(seq)

End

Private Procedure intercettaMessaggiMidi(sq As Pointer)
 
 Dim i As Integer
 Dim p As Pointer
 Dim midi@ As Snd_seq_event
     
 Do ' Avvia il ciclo infinito

   i = snd_seq_event_input(sq, VarPtr(p))
   If i < 0 Then Break

   midi@ = p
   
   Select Case midi@.type
     Case SND_SEQ_EVENT_NOTEON
       Print "Evento 'NoteOn': "; midi@.channel, midi@.note, midi@.velocity
     Case SND_SEQ_EVENT_NOTEOFF
       Print "Evento 'NoteOff': "; midi@.channel, midi@.note, midi@.velocity
     Case SND_SEQ_EVENT_CONTROLLER
       Print "Evento 'Control Change': "; midi@.channel, midi@.param, midi@.value
     Case SND_SEQ_EVENT_PGMCHANGE
       Print "Evento 'Program Change': "; midi@.channel, midi@.value
   End Select

 Loop
  
End

o anche il seguente molto simile al precedente, nel quale inviando il carattere "q" dalla console/Terminale si chiuderà il programma.
Riguardo alla connessione in questo esempio sarà sufficiente connettere la tastiera Midi al sistema ALSA:

~ $ aconnect 24:0 14:0

il programma provvederà automaticamente a connettersi al sistema Alsa mediante la funzione prevista "snd_seq_connect_from( ) ".

Private bo As Boolean


Library "libasound:2.0.0"

Public Struct snd_seq_event
  type As Byte                ' byte n° 0
  flag As Byte
  tag As Byte 
  queue As Byte 
  tick_o_Tv_sec As Integer
  Tv_nsec As Integer         
  source_id As Byte
  source_porta As Byte
  dest_id As Byte
  dest_porta As Byte
    channel As Byte           ' byte n° 16
    note As Byte
    velocity As Byte
    off_velocity As Byte
      param As Integer        ' byte n° 20
      value As Integer        ' byte n° 24
End Struct
 
Private Const SND_SEQ_OPEN_INPUT As Integer = 2
Private Const SND_SEQ_PORT_CAP_WRITE As Integer = 2
Private Const SND_SEQ_PORT_CAP_SUBS_WRITE As Integer = 64
Private Const SND_SEQ_PORT_TYPE_MIDI_GENERIC As Integer = 2
Private Const SND_SEQ_PORT_TYPE_APPLICATION As Integer = 1048576
Private Enum SND_SEQ_EVENT_NOTEON = 6, SND_SEQ_EVENT_NOTEOFF, SND_SEQ_EVENT_KEYPRESS, SND_SEQ_EVENT_CONTROLLER = 10, SND_SEQ_EVENT_PGMCHANGE

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

' int snd_seq_connect_from(seq as pointer, myport as integer, src_client as integer, src_port as integer)
' Simple subscription (w/o exclusive & time conversion).
PRIVATE EXTERN snd_seq_connect_from(seq AS Pointer, myport AS Integer, src_client AS Integer, src_port AS Integer) As Integer

' int snd_seq_event_input(snd_seq_t *handle, snd_seq_event **ev)
' Retrieve an event from sequencer.
Private Extern snd_seq_event_input(handle As Pointer, ev As Snd_seq_event) As Integer

' 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 seq, po As Pointer
 Dim i, porta_in As Integer
 Dim evento AS Snd_seq_event
 
 i = snd_seq_open(VarPtr(seq), "default", SND_SEQ_OPEN_INPUT, 0)
 If i < 0 THEN Error.Raise("Errore !")

 i = snd_seq_set_client_name(seq, "Sequencer dimostrativo 2 di ALSA")
 If i < 0 Then Error.Raise("Errore !")
 
 porta_in = snd_seq_create_simple_port(seq, "gambas", SND_SEQ_PORT_CAP_WRITE OR SND_SEQ_PORT_CAP_SUBS_WRITE, SND_SEQ_PORT_TYPE_MIDI_GENERIC OR SND_SEQ_PORT_TYPE_APPLICATION)
 If porta_in < 0 THEN Error.Raise("Errore !")
   
 i = snd_seq_connect_from(seq, porta_in, 14, 0)
 If i < 0 THEN Error.Raise("Errore !")
 
 While Not bo   ' Per uscire arbitrariamente dal ciclo, premere il tasto "Invio" della tastiera
   i = snd_seq_event_input(seq, VarPtr(po))
   If i = 0 Then Break
   evento = po
   Eventum(evento)
   Wait 0.01       ' Attesa necessaria per consentire l'effetto della pressione sul tasto "Invio" della tastiera
 Wend

 snd_seq_close(seq)
 Print "\nProgramma terminato !"
 Quit

End

Private Procedure Eventum(ev AS Snd_seq_event)

 Select Case ev.type
   Case SND_SEQ_EVENT_NOTEOFF
     With ev
       Print "Note Off: ", .channel, .note, .velocity
     End With
   Case SND_SEQ_EVENT_NOTEON
     With ev
       Print "Note On: ", .channel, .note, .velocity
     End With
   Case SND_SEQ_EVENT_CONTROLLER
     With ev
       Print "Control Change: ", .channel, .param, .value
     End With
   Case SND_SEQ_EVENT_PGMCHANGE
     With ev
       Print "Program Change: ", .channel, .value
     End With
 End Select

End

Public Sub Application_Read()   ' Per terminare il programma, premere il tasto "Invio" della tastiera

 bo = True

End