Differenze tra le versioni di "Alsa e Gambas: Client e Porte in Invio dati - Connessione della porta del programma con il Client Softsynth"

Da Gambas-it.org - Wikipedia.
(Creata pagina con "Per essere in grado di inviare dati Midi via ALSA dal nostro applicativo ad un altro programma, è necessario che <SPAN Style="text-decoration:underline">entrambi i programmi...")
 
Riga 25: Riga 25:
 
  ''Connecting To: 128:0''
 
  ''Connecting To: 128:0''
 
che ovviamente vanno così interpretate: il ''Client'' avente numero identificativo 129 e chiamato "Nome del nostro applicativo Client", è connesso tramite la sua Porta, avente numero identificativo 0 e chiamata "Nome della porta", alla porta avente numero identificativo 0 di un altro ''Client'' di ALSA avente numero identificativo 128.
 
che ovviamente vanno così interpretate: il ''Client'' avente numero identificativo 129 e chiamato "Nome del nostro applicativo Client", è connesso tramite la sua Porta, avente numero identificativo 0 e chiamata "Nome della porta", alla porta avente numero identificativo 0 di un altro ''Client'' di ALSA avente numero identificativo 128.
 +
 +
----
 +
V'è da sottolineare che ai fini pratici dell'invio degli ''Eventi Midi ALSA'' la connessione così realizzata mediante la funzione esterna "snd_seq_connect_to()" si rende necessaria <SPAN Style="text-decoration:underline">se ai membri ''client'' e ''port'' del membro ''[https://www.alsa-project.org/alsa-doc/alsa-lib/structsnd__seq__addr__t.html snd_seq_addr_t]'' dellaa Struttura degli Eventi Midi ALSA ''[https://www.alsa-project.org/alsa-doc/alsa-lib/structsnd__seq__event__t.html snd_seq_event_t]'' sono rispettivamente valorizzati con le Costanti di ALSA:</span>
 +
SND_SEQ_ADDRESS_SUBSCRIBERS = 254
 +
SND_SEQ_ADDRESS_UNKNOWN = 253
 +
----
  
 
===Esempio pratico===
 
===Esempio pratico===
Vediamo un esempio di codice essenziale pratico, con il quale, dopo che sia stato preliminarmente lanciato il ''Softsynth'' (che così assumerà l'identificativo ''Client'' ALSA n. 128 e numero porta 0), il nostro applicativo si connetterà al ''Softsynth'' e gli invierà alcuni dati di eventi Midi ALSA gestiti con apposita ''Struttura'':
+
  Library "libasound:2.0.0"
Private sndseq As Pointer
 
Private Const cl_dest As Byte = 128 <FONT Color=gray>' ''Identificativo del Client al quale connettersi''</font>
 
Private Const po_dest As Byte = 0 <FONT Color=gray>' ''Numero della Porta del Client al quale connettersi''</font>
 
 
 
  Library "libasound:2"
 
 
   
 
   
  Public Struct snd_seq_event_t
+
  '''Public''' Struct <Font Color= #B22222>snd_seq_event_t</font>
  type As Byte
+
  type As Byte             <Font Color= #006400>' '' byte indice n° 0''</font>
  flags As Byte
+
  flags As Byte
  tag As Byte
+
  tag As Byte
  queue As Byte
+
  queue As Byte
  tick_time As Integer
+
  tick_o_tv_sec As Integer
  real_time As Integer
+
  tv_nsec As Integer
  source_client As Byte
+
  source_client As Byte
  source_port As Byte
+
  source_port As Byte
  dest_client As Byte
+
  dest_client As Byte
  dest_port As Byte
+
  dest_port As Byte       <Font Color= #006400>' ''byte n° 15''</font>
  channel As Byte
+
    channel As Byte           <Font Color= #006400>' ''byte n° 16''</font>
  note As Byte
+
    note As Byte             <Font Color= #006400>' ''byte n° 17''</font>
  velocity As Byte
+
    velocity As Byte         <Font Color= #006400>' ''byte n° 18''</font>
  off_velocity As Byte
+
    off_velocity As Byte     <Font Color= #006400>' ''byte n° 19''</font>
  duration As Integer
+
      param As Integer         <Font Color= #006400>' ''byte n° 20''</font>
  value As Integer
+
      value As Integer         <Font Color= #006400>' ''byte n° 24''</font>
  End Struct
+
  '''End''' Struct
 
   
 
   
  Private Const SND_SEQ_OPEN_OUTPUT As Integer = 1
+
  Private Const SND_SEQ_OPEN_DUPLEX As Integer = 3
Private Const SND_SEQ_PORT_CAP_READ As Integer = 1
 
 
  Private Const SND_SEQ_PORT_TYPE_MIDI_GENERIC As Integer = 2
 
  Private Const SND_SEQ_PORT_TYPE_MIDI_GENERIC As Integer = 2
 
  Private Const SND_SEQ_PORT_TYPE_APPLICATION As Integer = 1048576
 
  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
+
Private Const <FONT Color=#B22222><B>SND_SEQ_ADDRESS_UNKNOWN</b></font> As Byte = <FONT Color=#B22222><B>253</b></font>
 
+
Private Const <FONT Color=blue><B>SND_SEQ_ADDRESS_SUBSCRIBERS</b></font> As Byte = <FONT Color=blue><B>254</b></font>
  <FONT Color=gray>' ''int snd_seq_open (snd_seq_t **handle, const char * name, int streams, int mode)''
+
  Private Enum SND_SEQ_EVENT_NOTEON = 6, SND_SEQ_EVENT_NOTEOFF, SND_SEQ_EVENT_KEYPRESS,
 +
              SND_SEQ_EVENT_CONTROLLER = 10, SND_SEQ_EVENT_PGMCHANGE, SND_SEQ_EVENT_CHANPRESS, SND_SEQ_EVENT_PITCHBEND
 +
 +
  <FONT Color=gray>' ''int snd_seq_open (snd_seq_t **seqp, const char * name, int streams, int mode)''
 
  ' ''Open the ALSA sequencer.''</font>
 
  ' ''Open the ALSA sequencer.''</font>
  Private Extern snd_seq_open(handle As Pointer, name As String, streams As Integer, mode As Integer) As Integer
+
  Private Extern snd_seq_open(seqp As Pointer, name As String, streams As Integer, mode As Integer) As Integer
 +
 +
<FONT Color=gray>' ''int snd_seq_set_client_name (snd_seq_t* seq, const char* name)''
 +
' ''Set client name.''</font>
 +
Private Extern snd_seq_set_client_name(seq As Pointer, name As String) As Integer
 
   
 
   
  <FONT Color=gray>' ''const char * snd_strerror (int errnum)
+
  <FONT Color=gray>' ''int snd_seq_create_simple_port (snd_seq_t* seq, const char* name, unsigned int caps, unsigned int type)''
  ' ''Returns the message for an error code.''</font>
+
  ' ''Creates a port with the given capability and type bits.''</font>
  Private Extern snd_strerror(err As Integer) As String
+
  Private Extern snd_seq_create_simple_port(seq As Pointer, name As String, caps As Integer, type As Integer) As Integer
 
   
 
   
  <FONT Color=gray>' ''int snd_seq_client_id (snd_seq_t *seq)''
+
  <FONT Color=gray>' ''int snd_seq_client_id (snd_seq_t * seq)''
 
  ' ''Get the client id.''</font>
 
  ' ''Get the client id.''</font>
 
  Private Extern snd_seq_client_id(seq As Pointer) As Integer
 
  Private Extern snd_seq_client_id(seq As Pointer) As Integer
 
   
 
   
  <FONT Color=gray>' ''int snd_seq_create_simple_port (snd_seq_t *seq, const char *name, unsigned int caps, unsigned int type)''
+
  <FONT Color=gray>' ''int snd_seq_connect_to ( snd_seq_t * seq, int myport, int dest_client, int dest_port )''
  ' ''Create a port - simple version.''</font>
+
  ' ''Connect from the given receiver port in the current client to the given destination client:port.''</font>
  Private Extern snd_seq_create_simple_port(seq As Pointer, name As String, caps As Integer, type As Integer) As Integer
+
  Private Extern <FONT Color=#006400><B>snd_seq_connect_to</b></font>(seq As Pointer, myport As Integer, dest_client As Integer, dest_port As Integer) As Integer
 
   
 
   
  <FONT Color=gray>' ''int snd_seq_alloc_queue (snd_seq_t *handle)''
+
  <FONT Color=gray>' ''int snd_seq_alloc_named_queue (snd_seq_t * seq, CONST char * name)''
 
  ' ''Allocate a queue.''</font>
 
  ' ''Allocate a queue.''</font>
  Private Extern snd_seq_alloc_queue(handle As Pointer) As Integer
+
  Private Extern snd_seq_alloc_queue(seq As Pointer, name As String) As Integer
+
 
<FONT Color=gray>' ''int snd_seq_connect_to (snd_seq_t *seq, int my_port, int dest_client, int dest_port)''
+
  <FONT Color=gray>' ''int snd_seq_event_output (snd_seq_t * seq, snd_seq_event_t *ev)''
' ''Simple subscription (w/o exclusive & time conversion).''</font>
 
Private Extern snd_seq_connect_to(seq As Pointer, my_port As Integer, dest_client As Integer, dest_port As Integer) As Integer
 
 
  <FONT Color=gray>' ''int snd_seq_event_output (snd_seq_t *handle, snd_seq_event_t *ev)
 
 
  ' ''Output an event.''</font>
 
  ' ''Output an event.''</font>
  Private Extern snd_seq_event_output(handle As Pointer, ev As Snd_seq_event_t)
+
  Private Extern snd_seq_event_output(seq As Pointer, ev As Snd_seq_event_t) As Integer
 
   
 
   
  <FONT Color=gray>' ''int snd_seq_drain_output (snd_seq_t * seq)
+
  <FONT Color=gray>' ''int snd_seq_drain_output (snd_seq_t * seq)''
 
  ' ''Drain output buffer to sequencer.''</font>
 
  ' ''Drain output buffer to sequencer.''</font>
 
  Private Extern snd_seq_drain_output(seq As Pointer) As Integer
 
  Private Extern snd_seq_drain_output(seq As Pointer) As Integer
 
<FONT Color=gray>' ''int snd_seq_close (snd_seq_t *handle)
 
' ''Close the sequencer.''</font>
 
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]  <FONT Color=gray>' <SUP>&#091;[[#Note|nota 2]]&#093;</sup></font>
 
  Dim client As new Byte[2]
 
  Dim b As Byte
 
  Dim ev As New Snd_seq_event_t
 
 
 
<FONT Color=gray>' ''Crea il Client di ALSA:''</font>
 
  CreaClient(client)
 
 
 
<FONT Color=gray>' ''Imposta l'Evento Midi di ALSA con alcuni valori:''</font>
 
  With ev
 
    .queue = 0
 
    .source_client = client[0]
 
    .source_port =client[1]
 
    .dest_client = cl_dest  <FONT Color=gray>' ''Si può assegnare anche il valore della Costante "SND_SEQ_ADDRESS_SUBSCRIBERS", che è 254''</font>
 
    .dest_port = po_dest    <FONT Color=gray>' ''Si può assegnare anche il valore della Costante "SND_SEQ_ADDRESS_UNKNOWN", che è 253''</font>
 
    .channel = 0
 
  End With
 
 
 
<FONT Color=gray>' ''Invoca la sotto-procedura per inviare ad ALSA il Messaggio Midi "Program Change", specificando nel secondo argomento il numero dello strumento musicale da usare:''</font>
 
  program_change(ev, 48)
 
 
 
  For b = 0 To note.Max
 
<FONT Color=gray>' ''Invoca la sotto-procedura per inviare ad ALSA il Messaggio Midi "Note ON":''</font>
 
    Note_On(ev, note[b])
 
    Wait 1
 
<FONT Color=gray>' ''Invoca la sotto-procedura per inviare ad ALSA il Messaggio Midi "Note OFF":''</font>
 
    Note_Off(ev, note[b])
 
  Next
 
   
 
  snd_seq_close(sndseq)
 
 
 
'''End'''
 
 
 
'''Private''' Function CreaClient(source As Byte[])
 
 
 
  Dim rit 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: " & snd_strerror(rit))
 
 
 
  source[0] = snd_seq_client_id(sndseq)
 
 
 
  source[1] = 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 source[1] < 0 Then Error.Raise("Impossibile creare la porta di uscita dati ALSA !")
 
 
 
<FONT Color=gray>' ''Connette il Client Gambas all'altro Client (ad esempio QSynth):''</font>
 
  rit = snd_seq_connect_to(sndseq, source[1], cl_dest, po_dest)
 
  If rit < 0 Then Error.Raise("Impossibile connettersi al Client di ALSA destinatario dei dati: " & snd_strerror(rit))
 
   
 
  snd_seq_alloc_queue(sndseq)
 
 
 
'''End'''
 
 
 
'''Private''' Procedure program_change(ev_prch As Snd_seq_event_t, strum As Integer)
 
 
 
  With ev_prch
 
<FONT Color=gray>' ''Specifica che si tratta di un Messaggio Midi "Program Change":''</font>
 
    .type = SND_SEQ_EVENT_PGMCHANGE
 
<FONT Color=gray>' ''Assegna il valore relativo allo strumento musicale secondo l'elenco GM":''</font>
 
    .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
 
<FONT Color=gray>' ''Specifica che si tratta di un Messaggio Midi "Note ON":''</font>
 
    .type = SND_SEQ_EVENT_NOTEON
 
<FONT Color=gray>' ''Specifica il numero della nota Midi da eseguire:''</font>
 
    .note = nota
 
<FONT Color=gray>' ''Specifica il valore di "velocity":''</font>
 
    .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
 
<FONT Color=gray>' ''Specifica che si tratta di un Messaggio Midi "Note OFF":''</font>
 
    .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'''
 
 
 
In quest'altro esempio, molto simile al precedente, il programma cercherà la presenza del softsynth tra i Client di ALSA, individuandone anche il numero della porta:
 
Private sndseq As Pointer
 
 
 
Library "libasound:2"
 
 
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 Const SND_SEQ_OPEN_OUTPUT As Integer = 1
 
Private Const SND_SEQ_PORT_CAP_READ As Integer = 1    <FONT Color=gray>' ''La porta del Client può essere "letta" da un altro Client esterno''</font>
 
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
 
 
<FONT Color=gray>' ''int snd_seq_open (snd_seq_t **handle, const char * name, int streams, int mode)''
 
' ''Open the ALSA sequencer.''</font>
 
Private Extern snd_seq_open(handle As Pointer, name As String, streams As Integer, mode As Integer) As Integer
 
 
   
 
   
 
  <FONT Color=gray>' ''const char * snd_strerror (int errnum)''
 
  <FONT Color=gray>' ''const char * snd_strerror (int errnum)''
 
  ' ''Returns the message for an error code.''</font>
 
  ' ''Returns the message for an error code.''</font>
  Private Extern snd_strerror(err As Integer) As String
+
  Private Extern snd_strerror(errnum As Integer) As String
 
   
 
   
  <FONT Color=gray>' ''int snd_seq_client_id (snd_seq_t *seq)''
+
  <FONT Color=gray>' ''int snd_seq_close (snd_seq_t * seq)''
  ' ''Get the client id.''</font>
+
  ' ''Close the sequencer.''</font>
  Private Extern snd_seq_client_id(seq As Pointer) As Integer
+
  Private Extern snd_seq_close(seq As Pointer) As Integer
 
<FONT Color=gray>' ''int snd_seq_create_simple_port (snd_seq_t *seq, const char *name, unsigned int caps, unsigned int type)''
 
' ''Create a port - simple version.''</font>
 
Private Extern snd_seq_create_simple_port(seq As Pointer, name As String, caps As Integer, type As Integer) As Integer
 
 
   
 
   
<FONT Color=gray>' ''int snd_seq_alloc_queue (snd_seq_t *handle)''
 
' ''Allocate a queue.''</font>
 
Private Extern snd_seq_alloc_queue(handle As Pointer) As Integer
 
 
   
 
   
  <FONT Color=gray>' ''int snd_seq_connect_to (snd_seq_t *seq, int my_port, int dest_client, int dest_port)''
+
  '''Public''' Sub Main()
' ''Simple subscription (w/o exclusive & time conversion).''</font>
 
Private Extern snd_seq_connect_to(seq As Pointer, my_port As Integer, dest_client As Integer, dest_port As Integer) As Integer
 
 
   
 
   
<FONT Color=gray>' ''int snd_seq_event_output (snd_seq_t *handle, snd_seq_event_t *ev)''
+
  Dim handle As Pointer
' ''Output an event.''</font>
+
  Dim err, id, porta, que As Integer
Private Extern snd_seq_event_output(handle As Pointer, ev As Snd_seq_event_t)
+
  Dim ev_midi As New Snd_seq_event_t   <FONT Color=gray>' ''Dichiara la variabile del tipo della Struttura "snd_seq_event_t"''</font>
 
<FONT Color=gray>' ''int snd_seq_drain_output (snd_seq_t * seq)''
 
' ''Drain output buffer to sequencer.''</font>
 
Private Extern snd_seq_drain_output(seq As Pointer) As Integer
 
 
   
 
   
<FONT Color=gray>' ''int snd_seq_close (snd_seq_t *handle)''
+
  err = snd_seq_open(VarPtr(handle), "default", SND_SEQ_OPEN_DUPLEX, 0)
' ''Close the sequencer.''</font>
+
  Print "Apertura del subsistema 'seq' di ALSA = "; IIf(err == 0, "corretta !", "errata !")
Private Extern snd_seq_close(handle As Pointer) As Integer
+
  If err < 0 Then error.RAISE("Errore: " & snd_strerror(err))
 
   
 
   
 +
  snd_seq_set_client_name(handle, "Test")
 +
  id = snd_seq_client_id(handle)
 +
  Print "Alsa ClientID = "; id
 
   
 
   
'''Public''' Sub Main()
+
   porta = snd_seq_create_simple_port(handle, "Uscita", 0, SND_SEQ_PORT_TYPE_MIDI_GENERIC + SND_SEQ_PORT_TYPE_APPLICATION)
 
+
   Print "Porta del programma = "; porta
   Dim note As Byte[] = [60, 62, 64, 65, 67, 69, 71, 72]
+
   If porta < 0 Then error.Raise("Errore: " & snd_strerror(porta))
   Dim source_dest As New Byte[4]
 
  Dim b As Byte
 
  Dim ev As New Snd_seq_event_t
 
 
 
<FONT Color=gray>' ''Crea il Client di ALSA:''</font>
 
   CreaClient(source_dest)
 
 
 
<FONT Color=gray>' ''Imposta l'Evento Midi di ALSA con alcuni valori:''</font>
 
  With ev
 
    .flags = 0
 
    .tag = 0
 
    .queue = 0
 
    .tick_time = 0
 
    .real_time = 0
 
    .source_client = source_dest[0]
 
    .source_port = source_dest[1]
 
    .dest_client = source_dest[2]  <FONT Color=gray>' ''si può assegnare anche il valore della Costante "SND_SEQ_ADDRESS_SUBSCRIBERS", che è 254''</font>
 
    .dest_port = source_dest[3]  <FONT Color=gray>' ''si può assegnare anche il valore della Costante "SND_SEQ_ADDRESS_UNKNOWN", che è 253''</font>
 
    .channel = 0
 
  End With
 
 
   
 
   
<FONT Color=gray>' ''Invoca la sotto-procedura per inviare ad ALSA il Messaggio Midi "Program Change", specificando nel secondo argomento il numero identificativo GM dello strumento musicale da usare:''</font>
+
  que = snd_seq_alloc_queue(handle, "queue")
   program_change(ev, 48)
+
   If que < 0 Then error.Raise("Errore: " & snd_strerror(que))
 
   
 
   
  For b = 0 To note.Max
+
  <FONT Color=gray>' ''Assume che l'ID/porta dell'altro Client (Softsynth) sia 128:0''</font>
  <FONT Color=gray>' ''Invoca la sotto-procedura per inviare ad ALSA il Messaggio Midi "Note ON":''</font>
+
  err = <FONT Color=#006400><B>snd_seq_connect_to</b></font>(handle, porta, <FONT Color=darkorange><B>128</b></font>, <FONT Color=darkorange><B>0</b></font>)
    Note_On(ev, note[b])
+
   If err < 0 Then error.Raise("Errore: " & snd_strerror(err))
    Wait 1
 
<FONT Color=gray>' ''Invoca la sotto-procedura per inviare ad ALSA il Messaggio Midi "Note OFF":''</font>
 
    Note_Off(ev, note[b])
 
   Next
 
 
 
  snd_seq_close(sndseq)
 
 
 
'''End'''
 
 
   
 
   
  '''Private''' Function CreaClient(srcdst As Byte[])
+
  <FONT Color=gray>' '''Program Change'''</font>
    
+
   ev_midi.type = SND_SEQ_EVENT_PGMCHANGE
   Dim rit As Integer
+
   ev_midi.flags = 0
    
+
   ev_midi.tag = 0
   rit = snd_seq_open(VarPtr(sndseq), "default", SND_SEQ_OPEN_OUTPUT, 0)
+
   ev_midi.queue = que
   If rit < 0 Then Error.Raise("Impossibile aprire il subsistema 'seq' di ALSA: " & snd_strerror(rit))
+
  ev_midi.tick_o_tv_sec = 0
 +
   ev_midi.source_client = id
 +
  ev_midi.source_port = porta
 +
  ev_midi.dest_client = <FONT Color=blue><B>SND_SEQ_ADDRESS_SUBSCRIBERS</b></font>
 +
  ev_midi.dest_port = <FONT Color=#B22222><B>SND_SEQ_ADDRESS_UNKNOWN</b></font>
 +
  ev_midi.channel = 0
 +
  ev_midi.value = 44    <FONT Color=gray>' ''Imposta il suono dello strumento musicale da usare''</font>
 
   
 
   
   srcdst[0] = snd_seq_client_id(sndseq)
+
   InviaEventoMidi(handle, ev_midi)
 
   
 
   
   srcdst[1] = snd_seq_create_simple_port(sndseq, "porta_", SND_SEQ_PORT_CAP_READ, SND_SEQ_PORT_TYPE_MIDI_GENERIC Or SND_SEQ_PORT_TYPE_APPLICATION)
+
<FONT Color=gray>' '''Note ON'''</font>
   If srcdst[1] < 0 Then Error.Raise("Impossibile creare la porta di uscita dati ALSA !")
+
   ev_midi.type = SND_SEQ_EVENT_NOTEON
    
+
   ev_midi.flags = 0
   Fluidsynth(srcdst)
+
  ev_midi.tag = 0
    
+
  ev_midi.queue = que
<FONT Color=gray>' ''Connette il Client Gambas all'altro Client (ad esempio QSynth):''</font>
+
  ev_midi.tick_o_tv_sec = 0
   rit = snd_seq_connect_to(sndseq, srcdst[1], srcdst[2], srcdst[3])
+
  ev_midi.source_client = id
  If rit < 0 Then Error.Raise("Impossibile connettersi al Client di ALSA destinatario dei dati: " & snd_strerror(rit))
+
  ev_midi.source_port = porta
 
+
   ev_midi.dest_client = SND_SEQ_ADDRESS_SUBSCRIBERS
  snd_seq_alloc_queue(sndseq)
+
   ev_midi.dest_port = SND_SEQ_ADDRESS_UNKNOWN
 +
   ev_midi.channel = 0
 +
  ev_midi.note = 64      <FONT Color=gray>' ''Imposta una nota Midi da eseguire''</font>
 +
   ev_midi.velocity = 100  <FONT Color=gray>' ''Imposta la velocità di tocco''</font>
 
    
 
    
'''End'''
+
  InviaEventoMidi(handle, ev_midi)
 
   
 
   
  '''Private''' Procedure program_change(ev_prch As Snd_seq_event_t, strum As Integer)
+
  <FONT Color=gray>' ''Imposta la durata dell'esecuzione della nota.''
 +
' ''Va precisato che l'uso della funzione "Wait" NON è un modo coerente con il protocollo di ALSA per la temporizzazione degli Eventi Midi: dato che ALSA usa a tale fine la cosiddetta "marcatura degli Eventi Midi" assegnando degli opportuni valori nel membro "flags" e in quelli del "Timestamp" della Struttura "snd_seq_event_t".''
 +
' ''Qui si fa uso della funzione "Wait" soltanto per meri motivi didattici ed esemplificativi del funzionamento degli altri aspetti trattati in questa pagina.</font>
 +
  Wait 3
 
   
 
   
  With ev_prch
+
  <FONT Color=gray>' '''Note OFF'''</font>
  <FONT Color=gray>' ''Specifica che si tratta di un Messaggio Midi "Program Change":''</font>
+
  ev_midi.type = SND_SEQ_EVENT_NOTEOFF
    .type = SND_SEQ_EVENT_PGMCHANGE
+
  ev_midi.flags = 0
<FONT Color=gray>' ''Assegna il valore relativo allo strumento musicale secondo l'elenco GM":''</font>
+
  ev_midi.tag = 0
    .value = strum
+
  ev_midi.queue = que
  End With
+
  ev_midi.tick_o_tv_sec = 0
    
+
  ev_midi.source_client = id
  InviaEvento(ev_prch)
+
  ev_midi.source_port = porta
 +
  ev_midi.dest_client = SND_SEQ_ADDRESS_SUBSCRIBERS
 +
  ev_midi.dest_port = SND_SEQ_ADDRESS_UNKNOWN
 +
  ev_midi.channel = 0
 +
  ev_midi.note = 64      <FONT Color=gray>' ''Imposta una nota Midi da eseguire''</font>
 +
  ev_midi.velocity = 0   <FONT Color=gray>' ''Imposta la velocità di tocco''</font>
 
    
 
    
'''End'''
+
  InviaEventoMidi(handle, ev_midi)
 
   
 
   
  '''Private''' Procedure Note_On(ev_nton As Snd_seq_event_t, nota As Byte)
+
  <FONT Color=gray>' ''Va in Chiusura:''</font>
 +
  snd_seq_close(handle)
 
   
 
   
  With ev_nton
 
<FONT Color=gray>' ''Specifica che si tratta di un Messaggio Midi "Note ON":''</font>
 
    .type = SND_SEQ_EVENT_NOTEON
 
<FONT Color=gray>' ''Specifica il numero della nota Midi da eseguire:''</font>
 
    .note = nota
 
<FONT Color=gray>' ''Specifica il valore di "velocity":''</font>
 
    .velocity = 64
 
  End With
 
 
 
  InviaEvento(ev_nton)
 
 
 
 
  '''End'''
 
  '''End'''
 
   
 
   
  '''Private''' Procedure Note_Off(ev_ntoff As Snd_seq_event_t, nota As Byte)
+
  '''Private''' Procedure InviaEventoMidi(p As Pointer, ev As Snd_seq_event_t)
 
   
 
   
   With ev_ntoff
+
   Dim err As Integer
<FONT Color=gray>' ''Specifica che si tratta di un Messaggio Midi "Note OFF":''</font>
 
    .type = SND_SEQ_EVENT_NOTEOFF
 
    .note = nota
 
    .velocity = 0
 
  End With
 
 
 
  InviaEvento(ev_ntoff)
 
 
 
'''End'''
 
 
   
 
   
'''Private''' Procedure InviaEvento(evento As Snd_seq_event_t)
+
  err = snd_seq_event_output(p, ev)
 +
  If err < 0 Then error.Raise("Errore: " & snd_strerror(err))
 
   
 
   
   snd_seq_event_output(sndseq, evento)
+
   err = snd_seq_drain_output(p)
    
+
   If err < 0 Then error.Raise("Errore: " & snd_strerror(err))
  snd_seq_drain_output(sndseq)
 
 
   
 
   
'''End'''
 
 
'''Private''' Function Fluidsynth(sd As Byte[])
 
 
 
  Dim s As String
 
  Dim ss As String[]
 
  Dim c As Short
 
 
 
  s = File.Load("/proc/asound/seq/clients")
 
  If InStr(s, "FLUID Synth") == 0 Then Error.Raise("FluidSinth non trovato !")
 
  ss = Split(s, "\n")
 
  For c = 0 To ss.Max
 
    If InStr(ss[c], "FLUID Synth") > 0 Then
 
      sd[2] = Scan(ss[c], "Client * : \"FLUID Synth*")[0]
 
      sd[3] = Scan(ss[c + 1], "  Port * : \"Synth*")[0]
 
    Endif
 
  Next
 
 
 
 
  '''End'''
 
  '''End'''
  

Versione delle 17:47, 23 gen 2022

Per essere in grado di inviare dati Midi via ALSA dal nostro applicativo ad un altro programma, è necessario che entrambi i programmi si rapportino ad ALSA come suoi Client. Il sistema ALSA, pertanto, diventa Server dei due suoi Client.

Riferendoci al capitolo che stiamo trattando, quello relativo dell'invio dati Midi ad ALSA, la fattispecie più evidente e probabile è quella dell'invio di dati Midi a un Softsynth, affinché siano emessi i suoni e gli effetti connessi agli eventi Midi prodotti. In tal caso, dunque, anche il programma Softsynth prescelto, per poter ricevere i dati Midi, dovrà diventare un Client di ALSA, e il nostro applicativo, capace di inviare eventi Midi, dovrà connettersi a detto Softsynth.
In vero, la connessione avviene tra le porte dei due Client di ALSA, ed avviene sempre e comunque attraverso il sistema e le risorse di ALSA.

La connessione delle porte del nostro applicativo con quella del Client Softsynth [nota 1] può essere effettuata mediante la funzione:

int snd_seq_connect_to(snd_seq_t *seq, int myport, int src_client, int src_port)

Nel nostro codice Gambas la dichiareremo con la consueta funzione Extern:

Private EXTERN  snd_seq_connect_to(seq As Pointer, myport As Integer, src_client As Integer, src_port As Integer) As Integer

Questa funzione sarà utilizzata in routine come segue:

 err = snd_seq_connect_to(handle, outport, dclient, dport)

A questo punto, lanciando da terminale il comando: cat /proc/asound/seq/clients, possiamo verificare:

  • il numero identificativo e il nome del nostro Client;
  • il numero ed il nome della porta del nostro Client;
  • il numero identificativo del device (ad esempio QSynth) e della sua porta (con relativa indicazione delle capacità), al quale il Client si è connesso.

Cercando il nostro Client avremo infatti informazioni simili alle seguenti:

Client 129 : "Nome del nostro applicativo Client" [User]

Port   0 : "Nome della Porta" (--e-)

Connecting To: 128:0

che ovviamente vanno così interpretate: il Client avente numero identificativo 129 e chiamato "Nome del nostro applicativo Client", è connesso tramite la sua Porta, avente numero identificativo 0 e chiamata "Nome della porta", alla porta avente numero identificativo 0 di un altro Client di ALSA avente numero identificativo 128.


V'è da sottolineare che ai fini pratici dell'invio degli Eventi Midi ALSA la connessione così realizzata mediante la funzione esterna "snd_seq_connect_to()" si rende necessaria se ai membri client e port del membro snd_seq_addr_t dellaa Struttura degli Eventi Midi ALSA snd_seq_event_t sono rispettivamente valorizzati con le Costanti di ALSA:

SND_SEQ_ADDRESS_SUBSCRIBERS = 254
SND_SEQ_ADDRESS_UNKNOWN = 253

Esempio pratico

Library "libasound:2.0.0"

Public Struct snd_seq_event_t
 type As Byte              '  byte indice n° 0
 flags As Byte
 tag As Byte
 queue As Byte
 tick_o_tv_sec As Integer
 tv_nsec As Integer
 source_client As Byte
 source_port As Byte
 dest_client As Byte
 dest_port As Byte        ' byte n° 15
   channel As Byte           ' byte n° 16
   note As Byte              ' byte n° 17
   velocity As Byte          ' byte n° 18
   off_velocity As Byte      ' byte n° 19
     param As Integer          ' byte n° 20
     value As Integer          ' byte n° 24
End Struct

Private Const SND_SEQ_OPEN_DUPLEX As Integer = 3
Private Const SND_SEQ_PORT_TYPE_MIDI_GENERIC As Integer = 2
Private Const SND_SEQ_PORT_TYPE_APPLICATION As Integer = 1048576
Private Const SND_SEQ_ADDRESS_UNKNOWN As Byte = 253
Private Const SND_SEQ_ADDRESS_SUBSCRIBERS As Byte = 254
Private Enum SND_SEQ_EVENT_NOTEON = 6, SND_SEQ_EVENT_NOTEOFF, SND_SEQ_EVENT_KEYPRESS,
             SND_SEQ_EVENT_CONTROLLER = 10, SND_SEQ_EVENT_PGMCHANGE, SND_SEQ_EVENT_CHANPRESS, SND_SEQ_EVENT_PITCHBEND

' int snd_seq_open (snd_seq_t **seqp, const char * name, int streams, int mode)
' Open the ALSA sequencer.
Private Extern snd_seq_open(seqp 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_create_simple_port (snd_seq_t* seq, const char* name, unsigned int caps, unsigned int type)
' Creates a port with the given capability and type bits.
Private Extern snd_seq_create_simple_port(seq As Pointer, name As String, caps As Integer, type As Integer) As Integer

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

' int snd_seq_connect_to ( snd_seq_t * seq, int myport, int dest_client, int dest_port )
' Connect from the given receiver port in the current client to the given destination client:port.
Private Extern snd_seq_connect_to(seq As Pointer, myport As Integer, dest_client As Integer, dest_port As Integer) As Integer

' int snd_seq_alloc_named_queue (snd_seq_t * seq, CONST char * name)
' Allocate a queue.
Private Extern snd_seq_alloc_queue(seq As Pointer, name As String) As Integer
 
' int snd_seq_event_output (snd_seq_t * seq, snd_seq_event_t *ev)
' Output an event.
Private Extern snd_seq_event_output(seq As Pointer, ev As Snd_seq_event_t) As Integer

' 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

' 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 * seq)
' Close the sequencer.
Private Extern snd_seq_close(seq As Pointer) As Integer


Public Sub Main()

 Dim handle As Pointer
 Dim err, id, porta, que As Integer
 Dim ev_midi As New Snd_seq_event_t   ' Dichiara la variabile del tipo della Struttura "snd_seq_event_t"

 err = snd_seq_open(VarPtr(handle), "default", SND_SEQ_OPEN_DUPLEX, 0)
 Print "Apertura del subsistema 'seq' di ALSA = "; IIf(err == 0, "corretta !", "errata !")
 If err < 0 Then error.RAISE("Errore: " & snd_strerror(err))

 snd_seq_set_client_name(handle, "Test")
 id = snd_seq_client_id(handle)
 Print "Alsa ClientID = "; id

 porta = snd_seq_create_simple_port(handle, "Uscita", 0, SND_SEQ_PORT_TYPE_MIDI_GENERIC + SND_SEQ_PORT_TYPE_APPLICATION) 
 Print "Porta del programma = "; porta
 If porta < 0 Then error.Raise("Errore: " & snd_strerror(porta))

 que = snd_seq_alloc_queue(handle, "queue")
 If que < 0 Then error.Raise("Errore: " & snd_strerror(que))

' Assume che l'ID/porta dell'altro Client (Softsynth) sia 128:0
 err = snd_seq_connect_to(handle, porta, 128, 0)
 If err < 0 Then error.Raise("Errore: " & snd_strerror(err))

' Program Change
 ev_midi.type = SND_SEQ_EVENT_PGMCHANGE
 ev_midi.flags = 0
 ev_midi.tag = 0
 ev_midi.queue = que
 ev_midi.tick_o_tv_sec = 0
 ev_midi.source_client = id
 ev_midi.source_port = porta
 ev_midi.dest_client = SND_SEQ_ADDRESS_SUBSCRIBERS
 ev_midi.dest_port = SND_SEQ_ADDRESS_UNKNOWN
 ev_midi.channel = 0
 ev_midi.value = 44    ' Imposta il suono dello strumento musicale da usare

 InviaEventoMidi(handle, ev_midi)

' Note ON
 ev_midi.type = SND_SEQ_EVENT_NOTEON
 ev_midi.flags = 0
 ev_midi.tag = 0
 ev_midi.queue = que
 ev_midi.tick_o_tv_sec = 0
 ev_midi.source_client = id
 ev_midi.source_port = porta
 ev_midi.dest_client = SND_SEQ_ADDRESS_SUBSCRIBERS
 ev_midi.dest_port = SND_SEQ_ADDRESS_UNKNOWN
 ev_midi.channel = 0
 ev_midi.note = 64       ' Imposta una nota Midi da eseguire
 ev_midi.velocity = 100  ' Imposta la velocità di tocco
 
 InviaEventoMidi(handle, ev_midi)

' Imposta la durata dell'esecuzione della nota.
' Va precisato che l'uso della funzione "Wait" NON è un modo coerente con il protocollo di ALSA per la temporizzazione degli Eventi Midi: dato che ALSA usa a tale fine la cosiddetta "marcatura degli Eventi Midi" assegnando degli opportuni valori nel membro "flags" e in quelli del "Timestamp" della Struttura "snd_seq_event_t".
' Qui si fa uso della funzione "Wait" soltanto per meri motivi didattici ed esemplificativi del funzionamento degli altri aspetti trattati in questa pagina.
 Wait 3

' Note OFF
 ev_midi.type = SND_SEQ_EVENT_NOTEOFF
 ev_midi.flags = 0
 ev_midi.tag = 0
 ev_midi.queue = que
 ev_midi.tick_o_tv_sec = 0
 ev_midi.source_client = id
 ev_midi.source_port = porta
 ev_midi.dest_client = SND_SEQ_ADDRESS_SUBSCRIBERS
 ev_midi.dest_port = SND_SEQ_ADDRESS_UNKNOWN
 ev_midi.channel = 0
 ev_midi.note = 64       ' Imposta una nota Midi da eseguire
 ev_midi.velocity = 0    ' Imposta la velocità di tocco
 
 InviaEventoMidi(handle, ev_midi)

' Va in Chiusura:
 snd_seq_close(handle)

End

Private Procedure InviaEventoMidi(p As Pointer, ev As Snd_seq_event_t)

 Dim err As Integer

 err = snd_seq_event_output(p, ev)
 If err < 0 Then error.Raise("Errore: " & snd_strerror(err))

 err = snd_seq_drain_output(p)
 If err < 0 Then error.Raise("Errore: " & snd_strerror(err))

End


Note

[1] Qualora la connessione sia invece impostata esplicitamente con il subsistema ALSA:

client 14: 'Midi Through' [type=kernel]
0 'Midi Through Port-0'

(solitamente 14:0), bisognerà ricordare, dopo aver lanciato il nostro applicativo, di connettere - ad esempio con il comando aconnect da terminale - il sistema ALSA con il softsynth. In sostanza la catena di connessione risulterà in questo caso essere la seguente: Applicativo-->ALSA-->Softsynth.

[2] Corrispondenza tra "Note MIDI", frequenza sonora e note sul pentragramma