Alsa e Gambas: Invio dei dati senza connessione della porta del programma con il Softsynth

Da Gambas-it.org - Wikipedia.

Per inviare utilmente dati Midi ad altro Client di ALSA, non è necessario connettere i due Client (l'applicativo Gambas ed il Softsynth) mediante la funzione snd_seq_connect_to(), purché si presti attenzione ad alcuni elementi che integrano due casi.


1° caso

Nel primo caso il codice dovrà prevedere:

  • la presenza della funzione di ALSA snd_seq_alloc_queue();
  • il membro .queue della Struttura relativa agli eventi Midi dovrà essere posto a zero;
  • al membro .dest_id e a quello .dest_port della Struttura relativa agli eventi Midi dovranno essere rispettivamente assegnati i valori dell'Id e della porta del Client (Softsynth), al quale l'applicativo Gambas dovrà inviare i dati Midi.

Mostriamo un esempio pratico:

 Public Struct snd_seq_event_t
  type As Byte
  flags As Byte
  tag As Byte
  queue As Byte
  tick_time As Integer
  real_time As Integer
  source_client As Byte
  source_port As Byte
  dest_client As Byte
  dest_port As Byte
  channel As Byte
  note As Byte
  velocity As Byte
  off_velocity As Byte
  duration As Integer
  value As Integer
End Struct

Private sndseq As Pointer
Private Const cl_dest As Byte = 128
Private Const po_dest As Byte = 0

Library "libasound:2"

Private SND_SEQ_OPEN_OUTPUT As Integer = 1
Private SND_SEQ_PORT_CAP_READ As Integer = 1
Private SND_SEQ_PORT_TYPE_MIDI_GENERIC As Integer = 2
Private SND_SEQ_PORT_TYPE_APPLICATION As Integer = 1 * CInt(2 ^ 20)
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

' 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_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
 
' int snd_seq_alloc_queue (snd_seq_t *handle)
' Allocate a queue.
Private Extern snd_seq_alloc_queue(handle As Pointer) As Integer
 
' int snd_seq_event_output (snd_seq_t *handle, snd_seq_event_t *ev)
' Output an event.
Private Extern snd_seq_event_output(handle As Pointer, ev As Snd_seq_event_t)

' int snd_seq_drain_output (snd_seq_t * seq)
' Drain output buffer to sequencer.
Private Extern snd_seq_drain_output(seq As Pointer) 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 note As Byte[] = [60, 62, 64, 65, 67, 69, 71, 72]
 Dim porta_source, b As Byte
 Dim ev As New Snd_seq_event_t
 
  porta_source = Connessione()
  
  With ev
    .queue = 0
    .source_client = 0 'snd_seq_client_id(sndseq)
    .source_port = porta_source
    .dest_client = cl_dest
    .dest_port = po_dest
    .channel = 0
  End With
       
  program_change(ev, 48)
   
  For b = 0 To note.Max
    Note_On(ev, note[b])
    Sleep 1
    Note_Off(ev, note[b])
  Next
   
  snd_seq_close(sndseq)
  
End


Private Function Connessione() As Byte
 
 Dim rit, porta As Integer
 
  rit = snd_seq_open(VarPtr(sndseq), "default", SND_SEQ_OPEN_OUTPUT, 0)
  If rit < 0 Then Error.Raise("Impossibile aprire il subsistema 'seq' di ALSA: " & String@(snd_strerror(rit)))
   
  porta = snd_seq_create_simple_port(sndseq, "porta_", SND_SEQ_PORT_CAP_READ, SND_SEQ_PORT_TYPE_MIDI_GENERIC Or SND_SEQ_PORT_TYPE_APPLICATION)
  If porta < 0 Then Error.Raise("Impossibile creare la porta di uscita dati ALSA !")
      
  snd_seq_alloc_queue(sndseq)
 
  Return porta
 
End


Private Procedure program_change(ev_prch As Snd_seq_event_t, strum As Integer)
 
  With ev_prch
    .type = SND_SEQ_EVENT_PGMCHANGE
    .value = strum
  End With
   
  InviaEvento(ev_prch)
  
End


Private Procedure Note_On(ev_nton As Snd_seq_event_t, nota As Byte)
 
  With ev_nton
    .type = SND_SEQ_EVENT_NOTEON
    .note = nota
    .velocity = 64
  End With
  
  InviaEvento(ev_nton)
  
End


Private Procedure Note_Off(ev_ntoff As Snd_seq_event_t, nota As Byte)
 
  With ev_ntoff
    .type = SND_SEQ_EVENT_NOTEOFF
    .note = nota
    .velocity = 0
  End With
  
  InviaEvento(ev_ntoff)
  
End


Private Procedure InviaEvento(evento As Snd_seq_event_t)
 
  snd_seq_event_output(sndseq, evento)
  
  snd_seq_drain_output(sndseq)
 
End


2° caso

Nel secondo caso si dovrà tenere conto di quanto segue:

  • * il membro .queue della Struttura relativa agli eventi Midi dovrà essere posto a zero;
  • al membro .dest_id e a quello .dest_port della Struttura relativa agli eventi Midi dovranno essere rispettivamente assegnati i valori dell'Id e della porta del Client (Softsynth), al quale l'applicativo Gambas dovrà inviare i dati Midi.

Mostriamo un esempio pratico:

 Public Struct snd_seq_event_t
  type As Byte
  flags As Byte
  tag As Byte
  queue As Byte
  tick_time As Integer
  real_time As Integer
  source_client As Byte
  source_port As Byte
  dest_client As Byte
  dest_port As Byte
  channel As Byte
  note As Byte
  velocity As Byte
  off_velocity As Byte
  duration As Integer
  value As Integer
End Struct

Private sndseq As Pointer
Private Const cl_dest As Byte = 128
Private Const po_dest As Byte = 0

Library "libasound:2"

Private SND_SEQ_OPEN_OUTPUT As Integer = 1
Private SND_SEQ_PORT_CAP_READ As Integer = 1
Private SND_SEQ_PORT_TYPE_MIDI_GENERIC As Integer = 2
Private SND_SEQ_PORT_TYPE_APPLICATION As Integer = 1 * CInt(2 ^ 20)
Private SND_SEQ_QUEUE_DIRECT As Byte = 253
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

' 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_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
  
' int snd_seq_event_output (snd_seq_t *handle, snd_seq_event_t *ev)
' Output an event.
Private Extern snd_seq_event_output(handle As Pointer, ev As Snd_seq_event_t)

' int snd_seq_drain_output (snd_seq_t * seq)
' Drain output buffer to sequencer.
Private Extern snd_seq_drain_output(seq As Pointer) 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 note As Byte[] = [60, 62, 64, 65, 67, 69, 71, 72]
 Dim porta_source, b As Byte
 Dim ev As New Snd_seq_event_t
 
  porta_source = Connessione()
  
  With ev
    .queue = SND_SEQ_QUEUE_DIRECT
    .source_client = 0 'snd_seq_client_id(sndseq)
    .source_port = porta_source
    .dest_client = cl_dest
    .dest_port = po_dest
    .channel = 0
  End With
       
  program_change(ev, 48)
   
  For b = 0 To note.Max
    Note_On(ev, note[b])
    Sleep 1
    Note_Off(ev, note[b])
  Next
   
  snd_seq_close(sndseq)
  
End


Private Function Connessione() As Byte
 
 Dim rit, porta As Integer
 
  rit = snd_seq_open(VarPtr(sndseq), "default", SND_SEQ_OPEN_OUTPUT, 0)
  If rit < 0 Then Error.Raise("Impossibile aprire il subsistema 'seq' di ALSA: " & String@(snd_strerror(rit)))
   
  porta = snd_seq_create_simple_port(sndseq, "porta_", SND_SEQ_PORT_CAP_READ, SND_SEQ_PORT_TYPE_MIDI_GENERIC Or SND_SEQ_PORT_TYPE_APPLICATION)
  If porta < 0 Then Error.Raise("Impossibile creare la porta di uscita dati ALSA !")
      
  Return porta
 
End


Private Procedure program_change(ev_prch As Snd_seq_event_t, strum As Integer)
 
  With ev_prch
    .type = SND_SEQ_EVENT_PGMCHANGE
    .value = strum
  End With
   
  InviaEvento(ev_prch)
  
End


Private Procedure Note_On(ev_nton As Snd_seq_event_t, nota As Byte)
 
  With ev_nton
    .type = SND_SEQ_EVENT_NOTEON
    .note = nota
    .velocity = 64
  End With
  
  InviaEvento(ev_nton)
  
End


Private Procedure Note_Off(ev_ntoff As Snd_seq_event_t, nota As Byte)
 
  With ev_ntoff
    .type = SND_SEQ_EVENT_NOTEOFF
    .note = nota
    .velocity = 0
  End With
  
  InviaEvento(ev_ntoff)
  
End


Private Procedure InviaEvento(evento As Snd_seq_event_t)
 
  snd_seq_event_output(sndseq, evento)
  
  snd_seq_drain_output(sndseq)
 
End