Differenze tra le versioni di "Alsa e Gambas: Invio dei dati senza connessione della porta del programma con il Softsynth"

Da Gambas-it.org - Wikipedia.
 
(25 versioni intermedie di uno stesso utente non sono mostrate)
Riga 1: Riga 1:
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.
+
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 esterna di ALSA "snd_seq_connect_to()", purché si presti attenzione ad alcuni elementi che integrano due casi.
 +
 
 +
Di seguito vedremo tre casi possibili, che saranno dimostrati con esempi pratici.
 +
<BR>Se nel proprio sistema è stato installato il softsynth ''Fluidsynth'', allora i programmi, appresso descritti, dovrebbero connettersi automaticamente a quel softsynth, consentendo così la riproduzione dei suoni. Qualora non avvenga, procedere come segue:
 +
<BR>1) da Terminale lanciare questa riga: ":~$ ''fluidsynth reload 0''", o meglio: ":~$ ''fluidsynth <FONT Color=darkgreen>/percorso/del/file/soundfont/.sf2</font>''"
 +
<BR>2) senza chiudere il Terminale, verificare nell'utilità "Monitor di sistema" che ''Fluidsynth'' sia presente fra i processi attivi;
 +
<BR>3) se ''Fluidsynth'' è presente fra i processi, verificare anche che esso sia presente al num. 128 nel file ''/proc/asound/seq/clients'';
 +
<BR>4) in caso affermativo - senza chiudere il Terminale - lanciare il codice sopra esposto.
 +
 
 +
 
 +
==Casi==
  
  
 
===1° caso===
 
===1° caso===
Nel primo caso il codice dovrà prevedere:
+
In tale caso si deve sempre utilizzare la funzione esterna di ALSA: "[https://www.alsa-project.org/alsa-doc/alsa-lib/group___seq_event.html#ga63986686b918abeff9902108638c5b2f snd_seq_event_output_direct()]" per l'invio diretto di ciascun Evento Midi ALSA.
* la presenza della funzione di ALSA ''snd_seq_alloc_queue()'';
+
<BR>Inoltre il codice dovrà prevedere che al membro ''dest_client'' 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.
* il membro ''.queue'' della Struttura relativa agli eventi Midi dovrà essere posto a zero;
+
<BR>Il dispositivo destinatario degli ''Eventi Midi ALSA'' sarà individuato, dunque, esclusivamente attraverso il suo numero identificativo e il numero della sua porta quale Client di ALSA. 
* 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:
+
Mostriamo un esempio <U>astratto</u> facendo uso di una Struttura dichiarata ad immagine della citata Struttura di ALSA ''[https://www.alsa-project.org/alsa-doc/alsa-lib/structsnd__seq__event__t.html snd_seq_event_t]'':
   Public Struct snd_seq_event_t
+
Private Const <FONT Color=#B22222><B>CLIENT_DESTINATARIO</b></font> As Byte = 128   <FONT Color=gray>' ''Solitamente l'identificativo è 128''</font>
 +
Private Const <FONT Color=#B22222><B>PORTA_DESTINATARIO</b></font> As Byte = 0
 +
 +
 +
Library "libasound:2.0.0"
 +
 +
Public Struct snd_seq_event_t
 
   type As Byte
 
   type As Byte
 
   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
Riga 24: Riga 39:
 
   velocity As Byte
 
   velocity As Byte
 
   off_velocity As Byte
 
   off_velocity As Byte
   duration As Integer
+
   param As Integer
 
   value As Integer
 
   value As Integer
 
  End Struct
 
  End Struct
 
   
 
   
Private sndseq As Pointer
+
  Private Const SND_SEQ_OPEN_OUTPUT As Integer = 1
Private Const cl_dest As Byte = 128
+
  Private Const SND_SEQ_PORT_CAP_READ As Integer = 1
  Private Const po_dest As Byte = 0
+
  Private Const SND_SEQ_PORT_TYPE_MIDI_GENERIC As Integer = 2
+
  Private Const SND_SEQ_PORT_TYPE_APPLICATION As Integer = 1048576
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
 
  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)''
 
  <FONT Color=gray>' ''int snd_seq_open (snd_seq_t **handle, const char * name, int streams, int mode)''
 
  ' ''Open the ALSA sequencer.''</font>
 
  ' ''Open the ALSA sequencer.''</font>
Riga 48: Riga 56:
 
  ' ''Returns the message for an error code.''</font>
 
  ' ''Returns the message for an error code.''</font>
 
  Private Extern snd_strerror(err As Integer) As Pointer
 
  Private Extern snd_strerror(err As Integer) As Pointer
 +
 +
<FONT Color=gray>' ''int snd_seq_client_id (snd_seq_t * seq)''
 +
' ''Get the client id.''</font>
 +
Private Extern <FONT Color=darkgreen><B>snd_seq_client_id</b></font>(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_create_simple_port (snd_seq_t *seq, const char *name, unsigned int caps, unsigned int type)''
 
  ' ''Create a port - simple version.''</font>
 
  ' ''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
+
  Private Extern <FONT Color=darkgreen><B>snd_seq_create_simple_port</b></font>(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)''
 
  <FONT Color=gray>' ''int snd_seq_alloc_queue (snd_seq_t *handle)''
 
  ' ''Allocate a queue.''</font>
 
  ' ''Allocate a queue.''</font>
  Private Extern snd_seq_alloc_queue(handle As Pointer) As Integer
+
  Private Extern <FONT Color=darkorange><B>snd_seq_alloc_queue</b></font>(handle As Pointer) As Integer
 
 
<FONT Color=gray>' ''int snd_seq_event_output (snd_seq_t *handle, snd_seq_event_t *ev)
 
' ''Output an event.''</font>
 
Private Extern snd_seq_event_output(handle As Pointer, ev As Snd_seq_event_t)
 
 
   
 
   
  <FONT Color=gray>' ''int snd_seq_drain_output (snd_seq_t * seq)
+
  <FONT Color=gray>' ''int snd_seq_event_output_direct (snd_seq_t *handle, snd_seq_event_t *ev)''
  ' ''Drain output buffer to sequencer.''</font>
+
  ' ''Output an event directly to the sequencer NOT through output buffer.''</font>
  Private Extern snd_seq_drain_output(seq As Pointer) As Integer
+
  Private Extern <FONT Color=blue><B>snd_seq_event_output_direct</b></font>(handle As Pointer, ev As Snd_seq_event_t) As Integer
 
   
 
   
 
  <FONT Color=gray>' ''int snd_seq_close (snd_seq_t *handle)
 
  <FONT Color=gray>' ''int snd_seq_close (snd_seq_t *handle)
Riga 69: Riga 77:
 
  Private Extern snd_seq_close(handle As Pointer) As Integer
 
  Private Extern snd_seq_close(handle As Pointer) As Integer
 
   
 
   
 +
Public Sub Main()
 
   
 
   
'''Public''' Sub Main()
+
   Dim handle As Pointer
    
+
   Dim rit As Integer
   Dim note As Byte[] = [60, 62, 64, 65, 67, 69, 71, 72]
+
   Dim id, porta, que, b As Byte
   Dim porta_source, b As Byte
 
 
   Dim ev As New Snd_seq_event_t
 
   Dim ev As New Snd_seq_event_t
 
 
  porta_source = Connessione()
 
 
 
  With ev
 
    <FONT Color=#B22222>.queue = 0</font>
 
    .source_client = 0
 
    .source_port = porta_source
 
    <FONT Color=#B22222>.dest_client = cl_dest</font>
 
    <FONT Color=#B22222>.dest_port = po_dest</font>
 
    .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'''
 
 
   
 
   
 +
  rit = snd_seq_open(VarPtr(handle), "default", SND_SEQ_OPEN_OUTPUT, 0)
 +
  If rit < 0 Then Error.Raise("Impossibile aprire il subsistema 'seq' di ALSA: " & String@(snd_strerror(rit)))
 
   
 
   
'''Private''' Function Connessione() As Byte
+
   id = snd_seq_client_id(handle)
    
+
  If id < 0 Then Error.Raise("Errore: " & String@(snd_strerror(rit)))
  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 !")
 
     
 
  <FONT Color=#B22222>snd_seq_alloc_queue(sndseq)</font>
 
 
 
  Return porta
 
 
 
'''End'''
 
 
   
 
   
 +
  porta = snd_seq_create_simple_port(handle, "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 !")
 
   
 
   
'''Private''' Procedure program_change(ev_prch As Snd_seq_event_t, strum As Integer)
+
   <FONT Color=darkorange><B>que</b></font> = snd_seq_alloc_queue(handle)
    
 
  With ev_prch
 
    .type = SND_SEQ_EVENT_PGMCHANGE
 
    .value = strum
 
  End With
 
   
 
  InviaEvento(ev_prch)
 
 
 
'''End'''
 
 
   
 
   
 +
  With ev
 +
    .type = ...etc...
 +
    .queue = <FONT Color=darkorange><B>que</b></font>
 +
    .source_client = id
 +
    .source_port = porta
 +
    .dest_client = <FONT Color=#B22222><B>CLIENT_DESTINATARIO</b></font>
 +
    .dest_port = <FONT Color=#B22222><B>PORTA_DESTINATARIO</b></font>
 +
    ...etc...
 +
  End With
 
   
 
   
'''Private''' Procedure Note_On(ev_nton As Snd_seq_event_t, nota As Byte)
+
  snd_seq_event_output_direct(handle, ev)
 
 
  With ev_nton
 
    .type = SND_SEQ_EVENT_NOTEON
 
    .note = nota
 
    .velocity = 64
 
  End With
 
 
 
  InviaEvento(ev_nton)
 
 
 
'''End'''
 
 
   
 
   
 +
  snd_seq_close(handle)
 
   
 
   
  '''Private''' Procedure Note_Off(ev_ntoff As Snd_seq_event_t, nota As Byte)
+
  End
 
 
  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===
 
===2° caso===
 
Nel secondo caso si dovrà tenere conto di quanto segue:
 
Nel secondo caso si dovrà tenere conto di quanto segue:
* viene eliminata la funzione esterna specifica di Alsa per la creazione della porta del Client applicativo;
+
* non sono utilizzate le funzioni esterne "snd_seq_client_id()" e "snd_seq_create_simple_port()", usate invece nel caso precedente, specifiche di ALSA per la creazione della porta del nostro Client applicativo in Gambas;
* al membro ''.queue'' della Struttura relativa agli eventi Midi dovrà essere assegnato il valore della Costante Alsa "''SND_SEQ_QUEUE_DIRECT''" (253);
+
* pertanto non si useranno, lasciandoli al valore predefinito di zero, i membri ''.source_client'' e ''..source_port'' della Struttura relativa agli eventi Midi;
 +
* neppure sarà usata la funzione esterna "snd_seq_alloc_queue()" di ALSA;
 +
* al membro ''queue'' della Struttura relativa agli eventi Midi dovrà essere assegnato il valore della Costante ALSA "SND_SEQ_QUEUE_DIRECT" (253);
 
* 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.
 
* 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:
+
Mostriamo un esempio astratto:
   Public Struct snd_seq_event_t
+
Private Const <FONT Color=#B22222><B>CLIENT_DESTINATARIO</b></font> As Byte = 128   <FONT Color=gray>' ''Solitamente l'identificativo è 128''</font>
 +
Private Const <FONT Color=#B22222><B>PORTA_DESTINATARIO</b></font> As Byte = 0
 +
 +
 +
Library "libasound:2.0.0"
 +
 +
Public Struct snd_seq_event_t
 
   type As Byte
 
   type As Byte
 
   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
Riga 187: Riga 142:
 
   velocity As Byte
 
   velocity As Byte
 
   off_velocity As Byte
 
   off_velocity As Byte
   duration As Integer
+
   param As Integer
 
   value As Integer
 
   value As Integer
 
  End Struct
 
  End Struct
 
   
 
   
 +
Private Const SND_SEQ_OPEN_OUTPUT As Integer = 1
 +
Private Const <FONT Color=#006400><B>SND_SEQ_QUEUE_DIRECT</b></font> 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
 +
 
 +
<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)''
 +
' ''Returns the message for an error code.''</font>
 +
Private Extern snd_strerror(err As Integer) As Pointer
 +
 +
<FONT Color=gray>' ''int snd_seq_event_output_direct (snd_seq_t *handle, snd_seq_event_t *ev)''
 +
' ''Output an event directly to the sequencer NOT through output buffer.''</font>
 +
Private Extern <FONT Color=blue><B>snd_seq_event_output_direct</b></font>(handle As Pointer, ev As Snd_seq_event_t) 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()
 
   
 
   
  Library "libasound:2"
+
  Dim handle As Pointer
 +
  Dim rit As Integer
 +
  Dim ev As New Snd_seq_event_t
 +
   
 +
  rit = snd_seq_open(VarPtr(handle), "default", SND_SEQ_OPEN_OUTPUT, 0)
 +
  If rit < 0 Then Error.Raise("Impossibile aprire il subsistema 'seq' di ALSA: " & String@(snd_strerror(rit)))
 +
 +
  With ev
 +
    .type = ...etc...
 +
    .queue = <FONT Color=#006400><B>SND_SEQ_QUEUE_DIRECT</b></font>
 +
    .dest_client = <FONT Color=#B22222><B>CLIENT_DESTINATARIO</b></font>
 +
    .dest_port = <FONT Color=#B22222><B>PORTA_DESTINATARIO</b></font>
 +
    ...etc...
 +
  End With
 +
     
 +
  snd_seq_event_output_direct(handle, ev)
 
   
 
   
Private SND_SEQ_OPEN_OUTPUT As Integer = 1
+
  snd_seq_close(handle)
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
 
 
    
 
    
 +
End
 +
 +
 +
 +
===Esempio più essenziale per invio diretto degli Eventi Midi ALSA===
 +
Mostriamo ora la modalità più breve ed essenziale per inviare ad altro Client di Alsa alcuni eventi Midi, e nella quale <U>non</u> è prevista la creazione di porte del nostro Client applicativo.
 +
<BR>Si utilizzerà ovviamente la funzione esterna di ALSA "snd_seq_event_output_direct()" per l'invio diretto di ciascun Evento Midi ALSA.
 +
Private Const <FONT Color=#B22222><B>CLIENT_DESTINATARIO</b></font> As Byte = 128  <FONT Color=gray>' ''Solitamente l'identificativo è 128''</font>
 +
Private Const <FONT Color=#B22222><B>PORTA_DESTINATARIO</b></font> As Byte = 0
 +
 +
Library "libasound:2.0.0"
 +
 +
Public Struct snd_seq_event_t
 +
  type As Byte
 +
  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
 +
  channel As Byte
 +
  note As Byte
 +
  velocity As Byte
 +
  off_velocity As Byte
 +
  param As Integer
 +
  value As Integer
 +
End Struct
 +
 +
Private Const SND_SEQ_OPEN_OUTPUT As Integer = 1
 +
Private Const <FONT Color=#006400><B>SND_SEQ_QUEUE_DIRECT</b></font> As Byte = 253
 +
Private Enum SND_SEQ_EVENT_NOTEON = 6, SND_SEQ_EVENT_NOTEOFF
 +
 
  <FONT Color=gray>' ''int snd_seq_open (snd_seq_t **handle, const char * name, int streams, int mode)''
 
  <FONT Color=gray>' ''int snd_seq_open (snd_seq_t **handle, const char * name, int streams, int mode)''
 
  ' ''Open the ALSA sequencer.''</font>
 
  ' ''Open the ALSA sequencer.''</font>
Riga 205: Riga 230:
 
  ' ''Returns the message for an error code.''</font>
 
  ' ''Returns the message for an error code.''</font>
 
  Private Extern snd_strerror(err As Integer) As Pointer
 
  Private Extern snd_strerror(err As Integer) As Pointer
   
 
<FONT Color=gray>' ''int snd_seq_event_output (snd_seq_t *handle, snd_seq_event_t *ev)
 
' ''Output an event.''</font>
 
Private Extern snd_seq_event_output(handle As Pointer, ev As Snd_seq_event_t)
 
 
   
 
   
  <FONT Color=gray>' ''int snd_seq_drain_output (snd_seq_t * seq)
+
  <FONT Color=gray>' ''int snd_seq_event_output_direct (snd_seq_t *handle, snd_seq_event_t *ev)''
  ' ''Drain output buffer to sequencer.''</font>
+
  ' ''Output an event directly to the sequencer NOT through output buffer.''</font>
  Private Extern snd_seq_drain_output(seq As Pointer) As Integer
+
  Private Extern <FONT Color=blue><B>snd_seq_event_output_direct</b></font>(handle As Pointer, ev As Snd_seq_event_t) As Integer
 
   
 
   
 
  <FONT Color=gray>' ''int snd_seq_close (snd_seq_t *handle)
 
  <FONT Color=gray>' ''int snd_seq_close (snd_seq_t *handle)
Riga 219: Riga 240:
 
   
 
   
 
   
 
   
  '''Public''' Sub Main()
+
  Public Sub Main()
 
+
   Dim sndseq As Pointer
+
   Dim seq As Pointer
  Dim note As Byte[] = [60, 62, 64, 65, 67, 69, 71, 72]
 
  Dim b As Byte
 
 
   Dim evento As New Snd_seq_event_t
 
   Dim evento As New Snd_seq_event_t
    
+
   Dim rit As Integer
  sndseq = Connessione()
 
 
 
  With evento
 
    <FONT Color=#B22222>.queue = SND_SEQ_QUEUE_DIRECT</font>
 
    <FONT Color=#B22222>.dest_client = 128</font>
 
    <FONT Color=#B22222>.dest_port = 0</font>
 
    .channel = 0
 
  End With
 
       
 
  program_change(sndseq, evento, 48)
 
   
 
  For b = 0 To note.Max
 
    Note_On(sndseq, evento, note[b])
 
    InviaEvento(sndseq)
 
    Sleep 1
 
    Note_Off(sndseq, evento, note[b])
 
    InviaEvento(sndseq)
 
  Next
 
   
 
  snd_seq_close(sndseq)
 
 
 
'''End'''
 
 
   
 
   
 +
  snd_seq_open(VarPtr(seq), "default", SND_SEQ_OPEN_OUTPUT, 0)
 +
  If rit < 0 Then Error.Raise("Impossibile aprire il subsistema 'seq' di ALSA: " & String@(snd_strerror(rit)))
 
   
 
   
'''Private''' Function Connessione() As Pointer
+
  With evento
 
+
    .type = SND_SEQ_EVENT_NOTEON
  Dim snd As Pointer
+
    .queue = <FONT Color=#006400><B>SND_SEQ_QUEUE_DIRECT</b></font>
  Dim rit, porta As Integer
+
    .dest_client = <FONT Color=#B22222><B>CLIENT_DESTINATARIO</b></font>
 
+
    .dest_port = <FONT Color=#B22222><B>PORTA_DESTINATARIO</b></font>
  rit = snd_seq_open(VarPtr(snd), "default", SND_SEQ_OPEN_OUTPUT, 0)
+
    .channel = 0
  If rit < 0 Then Error.Raise("Impossibile aprire il subsistema 'seq' di ALSA: " & String@(snd_strerror(rit)))
+
    .note = 64
         
+
    .velocity = 100
  Return snd
+
   End With
    
 
'''End'''
 
 
   
 
   
 +
  snd_seq_event_output_direct(seq, evento)
 
   
 
   
  '''Private''' Procedure program_change(pc_seq As Pointer, 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".''
  With ev_prch
+
' ''Qui si fa uso della funzione "Wait" soltanto per meri motivi didattici ed esemplificativi del funzionamento degli altri aspetti trattati in questa pagina.</font>
    .type = SND_SEQ_EVENT_PGMCHANGE
+
  Wait 1
    .value = strum
 
  End With
 
 
 
  <FONT Color=gray>' ''Inserisce l'evento "Program_Change" nel buffer:''</font>
 
  snd_seq_event_output(pc_seq, ev_prch)
 
 
 
'''End'''
 
 
   
 
   
 +
  evento.type = SND_SEQ_EVENT_NOTEOFF
 +
  evento.velocity = 0
 
   
 
   
'''Private''' Procedure Note_On(nto As Pointer, ev_nton As Snd_seq_event_t, nota As Byte)
+
  snd_seq_event_output_direct(seq, evento)
 
 
  With ev_nton
 
    .type = SND_SEQ_EVENT_NOTEON
 
    .note = nota
 
    .velocity = 64
 
  End With
 
 
 
<FONT Color=gray>' ''Inserisce l'evento "Note_On" nel buffer:''</font>
 
  snd_seq_event_output(nto, ev_nton)
 
 
 
'''End'''
 
 
   
 
   
 +
  snd_seq_close(seq)
 
   
 
   
  '''Private''' Procedure Note_Off(ntf As Pointer, ev_ntoff As Snd_seq_event_t, nota As Byte)
+
  End
 
+
Quest'altro esempio, simile al precedente, è realizzato in ambiente grafico: sul ''Form'' sono posti uno ''SpinBox'' e un ''Button''.
  With ev_ntoff
+
<BR>Cliccando sul ''Button'' si ascolterà il suono della nota Midi espressa dal valore mostrato dallo ''SpinBox'' che potrà essere variata manualmente.
    .type = SND_SEQ_EVENT_NOTEOFF
+
  Private Const CLIENT_DESTINATARIO As Byte = 128  <FONT Color=gray>' ''Solitamente l'identificativo è 128''</font>
    .note = nota
+
Private Const PORTA_DESTINATARIO As Byte = 0
    .velocity = 0
+
  Private seq As Pointer
  End With
 
 
 
  <FONT Color=gray>' ''Inserisce l'evento "Note_Off" nel buffer:''</font>
 
  snd_seq_event_output(ntf, ev_ntoff)
 
 
 
  '''End'''
 
 
   
 
   
 
   
 
   
  '''Private''' Procedure InviaEvento(seqP As Pointer)
+
  Library "libasound:2.0.0"
 
   
 
   
<FONT Color=gray>' ''Invia l'intero contenuto del buffer ad Alsa:''</font>
 
  snd_seq_drain_output(seqP)
 
 
 
'''End'''
 
 
 
 
===Esempio più essenziale per invio dati Midi===
 
Mostriamo ora la modalità più breve ed essenziale per inviare ad altro Client di Alsa alcuni eventi Midi, e nella quale non è prevista la creazione di porte del nostro Client applicativo:
 
 
  Public Struct snd_seq_event_t
 
  Public Struct snd_seq_event_t
 
   type As Byte
 
   type As Byte
Riga 320: Riga 288:
 
   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
Riga 330: Riga 298:
 
   velocity As Byte
 
   velocity As Byte
 
   off_velocity As Byte
 
   off_velocity As Byte
   duration As Integer
+
   param As Integer
 
   value As Integer
 
   value As Integer
 
  End Struct
 
  End Struct
 +
 +
Private Const SND_SEQ_OPEN_OUTPUT As Integer = 1
 +
Private Const SND_SEQ_QUEUE_DIRECT As Byte = 253
 +
Private Enum SND_SEQ_EVENT_NOTEON = 6, SND_SEQ_EVENT_NOTEOFF
 +
 +
<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)''
 +
' ''Returns the message for an error code.''</font>
 +
Private Extern snd_strerror(err As Integer) As Pointer
 +
 +
<FONT Color=gray>' ''int snd_seq_event_output_direct (snd_seq_t *handle, snd_seq_event_t *ev)''
 +
' ''Output an event directly to the sequencer NOT through output buffer.''</font>
 +
Private Extern <FONT Color=blue><B>snd_seq_event_output_direct</b></font>(handle As Pointer, ev As Snd_seq_event_t) 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 Form_Open()
 +
 +
  SpinBox1.MaxValue = 127
 +
  SpinBox1.Value = 64
 +
 +
End
 +
 +
 +
Public Sub Button1_MouseDown()
 +
 +
  Dim rit As Integer
 +
 +
  snd_seq_open(VarPtr(seq), "default", SND_SEQ_OPEN_OUTPUT, 0)
 +
  If rit < 0 Then Error.Raise("Impossibile aprire il subsistema 'seq' di ALSA: " & String@(snd_strerror(rit)))
 +
 +
  InvioMessaggioMidi(SND_SEQ_EVENT_NOTEON, SpinBox1.Value, 100)
 +
 +
End
 +
 +
 +
Public Sub Button1_MouseUp()
 +
 +
  InvioMessaggioMidi(SND_SEQ_EVENT_NOTEOFF, SpinBox1.Value, 0)
 +
 +
  snd_seq_close(seq)
 +
 +
End
 +
 +
 +
Private Procedure InvioMessaggioMidi(tipo As Byte, nota As Byte, veloc As Byte)
 +
 +
  Dim evento As Snd_seq_event_t
 +
 +
  With evento = New Snd_seq_event_t
 +
    .type = tipo
 +
    .queue = SND_SEQ_QUEUE_DIRECT
 +
    .dest_client = CLIENT_DESTINATARIO
 +
    .dest_port = PORTA_DESTINATARIO
 +
    .channel = 0
 +
    .note = nota
 +
    .velocity = veloc
 +
  End With
 +
 +
  snd_seq_event_output_direct(seq, evento)
 +
 +
End
 +
In questo esempio, per inviare l'evento Midi di ALSA, invece della Struttura ''snd_seq_event_t'' sarà utilizzato un vettore di tipo Byte[].
 +
Private Const CLIENT_DESTINATARIO As Byte = 128  <FONT Color=gray>' ''Solitamente l'identificativo è 128''</font>
 +
Private Const PORTA_DESTINATARIO As Byte = 0
 +
Private seq As Pointer
 
   
 
   
 
   
 
   
  Library "libasound:2"
+
  Library "libasound:2.0.0"
 
   
 
   
  Private SND_SEQ_OPEN_OUTPUT As Integer = 1
+
  Private Const SND_SEQ_OPEN_OUTPUT As Integer = 1
 +
Private Const SND_SEQ_QUEUE_DIRECT As Byte = 253
 
  Private Enum SND_SEQ_EVENT_NOTEON = 6, SND_SEQ_EVENT_NOTEOFF
 
  Private Enum SND_SEQ_EVENT_NOTEON = 6, SND_SEQ_EVENT_NOTEOFF
Private SND_SEQ_QUEUE_DIRECT As Byte = 253
 
 
   
 
   
 
  <FONT Color=gray>' ''int snd_seq_open (snd_seq_t **handle, const char * name, int streams, int mode)''
 
  <FONT Color=gray>' ''int snd_seq_open (snd_seq_t **handle, const char * name, int streams, int mode)''
Riga 351: Riga 391:
 
  <FONT Color=gray>' ''int snd_seq_event_output_direct (snd_seq_t *handle, snd_seq_event_t *ev)''
 
  <FONT Color=gray>' ''int snd_seq_event_output_direct (snd_seq_t *handle, snd_seq_event_t *ev)''
 
  ' ''Output an event directly to the sequencer NOT through output buffer.''</font>
 
  ' ''Output an event directly to the sequencer NOT through output buffer.''</font>
  Private Extern snd_seq_event_output_direct(handle As Pointer, ev As Snd_seq_event_t) As Integer
+
  Private Extern <FONT Color=blue><B>snd_seq_event_output_direct</b></font>(handle As Pointer, ev As Pointer) As Integer
 
   
 
   
 
  <FONT Color=gray>' ''int snd_seq_close (snd_seq_t *handle)
 
  <FONT Color=gray>' ''int snd_seq_close (snd_seq_t *handle)
Riga 358: Riga 398:
 
   
 
   
 
   
 
   
  '''Public''' Sub Main()
+
  Public Sub Form_Open()
 +
 +
  SpinBox1.MaxValue = 127
 +
  SpinBox1.Value = 64
 +
 +
End
 +
 +
 +
Public Sub Button1_MouseDown()
 
   
 
   
  Dim seq As Pointer
 
  Dim evento As New Snd_seq_event_t
 
 
   Dim rit As Integer
 
   Dim rit As Integer
 +
 +
  snd_seq_open(VarPtr(seq), "default", SND_SEQ_OPEN_OUTPUT, 0)
 +
  If rit < 0 Then Error.Raise("Impossibile aprire il subsistema 'seq' di ALSA: " & String@(snd_strerror(rit)))
 +
 +
  InvioMessaggioMidi(SND_SEQ_EVENT_NOTEON, SpinBox1.Value, 100)
 +
 +
End
 +
 +
 +
Public Sub Button1_MouseUp()
 +
 +
  InvioMessaggioMidi(SND_SEQ_EVENT_NOTEOFF, SpinBox1.Value, 0)
 +
 +
  snd_seq_close(seq)
 +
 +
End
 +
 +
 +
Private Procedure InvioMessaggioMidi(tipo As Byte, nota As Byte, veloc As Byte)
 +
 +
  Dim ev As New Byte[28]
 +
 +
<FONT Color=gray>' ''Vengono assegnati al vettore i valori fondamentali per generare l'evento Midi di ALSA:''</font>
 +
  ev[0] = tipo
 +
  ev[3] = SND_SEQ_QUEUE_DIRECT
 +
  ev[14] = CLIENT_DESTINATARIO
 +
  ev[15] = PORTA_DESTINATARIO
 +
  ev[17] = SpinBox1.Value
 +
  ev[18] = 100
 +
 +
  snd_seq_event_output_direct(seq, ev.Data)
 +
 +
End
 +
 +
 +
===3° caso===
 +
In questo caso si farà uso delle funzioni esterne di ALSA "snd_seq_event_output()" e "snd_seq_drain_output()" per l'invio di ciascun Evento Midi ALSA.
 +
 +
Nell'esempio pratico che segue viene rappresentata sul ''Form'' una tastierina Midi che può inviare dati Midi grezzi attraverso ALSA al Client-softsynth (ad esempio QSynth). Premendo un tasto di questa tastiera Midi interna, udiremo il suono della sua nota con il timbro dello strumento musicale prescelto in un ''ComboBox'' sul ''Form''.
 +
Private cb As ComboBox
 +
Private Const ALTEZZA_TASTI_BIANCHI As Single = 0.35
 +
Private bb As New Byte[3]
 +
Private instrumenta As String[] = ["Acustic Grand Piano", "Bright Acustic Piano", "Electric Grand Piano", "Honky-tonk",
 +
  "Electric Piano 1", "Electric Piano 2", "Harpsichord", "Clavinet", "Celesta", "Glockenspiel", "Music Box", "Vibraphone",
 +
  "Marimba", "Xylophone", "Tubular Bells", "Dulcimer", "Hammond Organ", "Percussive Organ", "Rock Organ", "Church Organ",
 +
  "Reed Organ", "Accordion", "Harmonica", "Tango Accordion", "Acoustic Guitar (nylon)", "Acoustic Guitar (steel)",
 +
  "Electric Guitar (jazz)", "Electric Guitar (clean)", "Electric Guitar(muted)", "Overdriven Guitar", "Distortion Guitar",
 +
  "Guitar Harmonics", "Acoustic Bass", "Electric Bass (finger)", "Electric Bass (pick)", "Fretless Bass", "Slap Bass 1",
 +
  "Slap Bass 2", "Synth Bass 1", "Synth Bass 2", "Violin", "Viola", "Cello", "Contrabass", "Tremolo Strings",
 +
  "Pizzicato Strings", "Orchestral Harp", "Timpani", "String Ensemble 1", "String Ensemble 2", "SynthStrings 1",
 +
  "SynthStrings 2", "Choir Aahs", "Voice Oohs", "Synth Voice", "Orchestra Hit", "Trumpet", "Trombone", "Tuba", "Muted Trumpet",
 +
  "French Horn", "Brass Section", "Synth Brass 1", "Synth Brass 2", "Soprano Sax", "Alto Sax", "Tenor Sax", "Baritone Sax",
 +
  "Oboe", "English Horn", "Basson", "Clarinet", "Piccolo", "Flute", "Recorder", "Pan Flute", "Bottle Blow", "Shakuhachi",
 +
  "Whistle", "Ocarina", "Lead 1 (square)", "Lead 2 (sawtooth)", "Lead 3 (caliope lead)", "Lead 4 (chiff lead)",
 +
  "Lead 5 (charang)", "Lead 6 (voice)", "Lead 7 (fifths)", "Lead 8(brass+lead)", "Pad 1 (new age)", "Pad 2 (warm)",
 +
  "Pad 3 (polysynth)", "Pad 4 (choir)", "Pad 5 (bowed)", "Pad 6 (metallic)", "Pad 7 (halo)", "Pad 8 (sweep)", "FX 1 (rain)",
 +
  "FX 2 (soundtrack)", "FX 3 (crystal)", "FX 4 (atmosphere)", "FX 5 (brightness)", "FX 6 (goblins)", "FX 7 (echoes)",
 +
  "FX 8 (sci-fi)", "Sitar", "Banjo", "Shamisen", "Koto", "Kalimba", "Bagpipe", "Fiddle", "Shanai", "Tinkle Bell", "Agogo",
 +
  "Steel Drums", "Woodblock", "Taiko Drum", "Melodic Tom", "Synth Drum", "Reverse Cymbal", "Guitar Fret Noise", "Breath Noise",
 +
  "Seashore", "Bird Tweet", "Telephone Ring", "Helicopter", "Applause", "Gunshot"]
 +
 +
 +
Public Sub Form_Open()
 +
 +
  With Me
 +
    .W = Screen.AvailableWidth * 0.5
 +
    .H = Screen.AvailableHeight * 0.2
 +
    .Center
 +
  End With
 +
  CreaClient()
 +
  With cb = New ComboBox(Me) As "Combo"
 +
    .W = 160
 +
    .H = 25
 +
    .X = (Me.W * 0.94) - .W
 +
    .Y = 0
 +
    .List = instrumenta
 +
    .Placeholder = "Strumenti musicali GM"
 +
  End With
 +
 +
  CreaTastiera()
 +
 +
End
 +
 +
Private Procedure CreaTastiera()
 +
 +
  Dim pn As Panel
 +
  Dim neri As New Byte[40]
 +
  Dim tasti As Button[]
 +
  Dim b, c, n As Byte
 
    
 
    
  snd_seq_open(VarPtr(seq), "default", SND_SEQ_OPEN_OUTPUT, 0)
+
  With pn = New Panel(Me)
  If rit < 0 Then Error.Raise("Impossibile aprire il subsistema 'seq' di ALSA: " & String@(snd_strerror(rit)))
+
    .W = Me.W * 0.88
 +
    .H = Me.H * 0.2
 +
    .X = (Me.W / 2) - (pn.W / 2)
 +
    .Y = Me.H * 0.2
 +
    .Border = Border.Sunken
 +
    .Background = &8b4513
 +
  End With
 +
 +
  Repeat
 +
    neri[b] = 25 + (12 * b / 5)
 +
    neri[b + 1] = 27 + (12 * b / 5)
 +
    neri[b + 2] = 30 + (12 * b / 5)
 +
    neri[b + 3] = 32 + (12 * b / 5)
 +
    neri[b + 4] = 34 + (12 * b / 5)
 +
    b += 5
 +
  Until b == neri.Count
 +
 +
  tasti = New Button[109]
 +
 +
  For t As Short = 0 To tasti.Max
 +
    With tasti[t] = New Button(Me) As "Tasti"
 +
      .W = 0
 +
      If t > 23 Then
 +
        If neri.Exist(t) Then  <FONT Color=gray>' ''Imposta i tasti neri''</font>
 +
          .W = Me.W * 0.013
 +
          .H = ((Me.H * ALTEZZA_TASTI_BIANCHI) * 66.66) / 100
 +
          Select Case t
 +
            Case 25 + (12 * n)
 +
              .X = tasti[t - 1].X + (((Me.W * 0.025) / 2))
 +
            Case 27 + (12 * n)
 +
              .X = tasti[t - 1].X + (((Me.W * 0.025) / 2))
 +
            Case 30 + (12 * n)
 +
              .X = tasti[t - 1].X + (((Me.W * 0.025) / 2))
 +
            Case 32 + (12 * n)
 +
              .X = tasti[t - 1].X + (((Me.W * 0.025) / 2))
 +
            Case 34 + (12 * n)
 +
              .X = tasti[t - 1].X + (((Me.W * 0.025) / 2))
 +
              Inc n
 +
          End Select
 +
          .Y = Me.H * 0.375
 +
          .Background = Color.Black
 +
          .Tag = t
 +
        Else
 +
<FONT Color=gray>' ''Imposta i tasti bianchi:''</font>
 +
          .W = Me.W * 0.018
 +
          .H = Me.H * ALTEZZA_TASTI_BIANCHI
 +
          .X = (.W * c) + (Me.W / 16)
 +
          .Y = Me.H * 0.37
 +
          .Background = Color.White
 +
          .Tag = t
 +
          .Lower
 +
          Inc c
 +
        Endif
 +
      Endif
 +
    End With
 +
  Next
 +
 +
End
 +
 +
 +
Public Sub Tasti_MouseDown()
 +
 +
  bb[0] = 0
 +
  bb[1] = Last.Tag
 +
  bb[2] = &64
 +
 +
  InvioMIDI(SND_SEQ_EVENT_NOTEON, bb)
 +
 +
  If Last.Background = Color.Black Then Last.Background = Color.DarkGray
 +
  Me.Title = "Nota Midi:  " & Last.Tag
 +
 +
End
 +
 +
Public Sub Tasti_MouseUp()
 +
 +
  bb[0] = 0
 +
  bb[1] = Last.Tag
 +
  bb[2] = 0
 +
 +
  InvioMIDI(SND_SEQ_EVENT_NOTEOFF, bb)
 +
 +
  If Last.Background = Color.DarkGray Then Last.Background = Color.Black
 +
  Me.Title = " "
 +
 +
End
 +
 +
<FONT Color=gray>''''''''''''''''''''''''''''''''''''''''''''''''''''</font>
 +
 +
Private seq As Pointer
 +
 +
 +
Library "libasound:2"
 +
 +
Public Struct snd_seq_event_t  <FONT Color=gray>' ''Struttura dell'Evento Midi di ALSA''</font>
 +
  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
 +
  param As Integer
 +
  value As Integer
 +
End Struct
 +
 +
Private Const SND_SEQ_OPEN_OUTPUT As Integer = 1
 +
Private Const SND_SEQ_QUEUE_DIRECT As Byte = 253
 +
Private Enum SND_SEQ_EVENT_NOTEON = 6, SND_SEQ_EVENT_NOTEOFF, SND_SEQ_EVENT_PGMCHANGE = 11
 +
 +
<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)''
 +
' ''Returns the message for an error code.''</font>
 +
Private Extern snd_strerror(err As Integer) As String
 
    
 
    
  With evento
+
<FONT Color=gray>' ''int snd_seq_event_output (snd_seq_t *handle, snd_seq_event_t *ev)''
    .type = SND_SEQ_EVENT_NOTEON
+
' ''Output an event.''</font>
    .queue = SND_SEQ_QUEUE_DIRECT
+
Private Extern snd_seq_event_output(handle As Pointer, ev As Snd_seq_event_t)
    .dest_client = 128
+
    .dest_port = 0
+
<FONT Color=gray>' ''int snd_seq_drain_output (snd_seq_t * seq)''
    .channel = 0
+
' ''Drain output buffer to sequencer.''</font>
    .note = 64
+
Private Extern snd_seq_drain_output(seq As Pointer) As Integer
    .velocity = 100
+
  End With
+
<FONT Color=gray>' ''int snd_seq_close (snd_seq_t *handle)''
   
+
' ''Close the sequencer.''</font>
  snd_seq_event_output_direct(seq, evento)
+
Private Extern snd_seq_close(handle As Pointer) As Integer
      
+
  Sleep 1
+
      
+
Private Procedure CreaClient()
  evento.type = SND_SEQ_EVENT_NOTEOFF
+
  snd_seq_event_output_direct(seq, evento)
+
  Dim rit As Integer
   
+
  snd_seq_close(seq)
+
  rit = snd_seq_open(VarPtr(seq), "default", SND_SEQ_OPEN_OUTPUT, 0)
 
+
  If rit < 0 Then Error.Raise("Impossibile aprire il subsistema 'seq' di ALSA: " & snd_strerror(rit))
  '''End'''
+
 +
End
 +
 +
 +
Public Sub Combo_Change()  <FONT Color=gray>' ''Imposta lo strumento musicale''</font>
 +
 +
  Dim evento As New Snd_seq_event_t
 +
 +
  With evento
 +
    .queue = SND_SEQ_QUEUE_DIRECT
 +
    .dest_client = 128
 +
    .dest_port = 0
 +
    .channel = 0
 +
  End With
 +
 +
<FONT Color=gray>' ''Imposta il tipo di strumento musicale mediante il Messaggio Midi "Program-Change":''</font>
 +
  Messaggio(evento, SND_SEQ_EVENT_PGMCHANGE, [0, 0, 0], cb.Index)
 +
 +
End
 +
 +
 +
Private Procedure InvioMIDI(tipo As Byte, mid As Byte[])
 +
 +
  Dim evento As New Snd_seq_event_t
 +
 +
  With evento
 +
    .queue = SND_SEQ_QUEUE_DIRECT
 +
    .dest_client = 128
 +
    .dest_port = 0
 +
    .channel = mid[0]
 +
  End With
 +
 +
<FONT Color=gray>' ''Imposta il Messaggio Midi:''</font>
 +
  Messaggio(evento, tipo, mid, 0)
 +
 +
End
 +
 +
 +
Private Procedure Messaggio(ev As Snd_seq_event_t, tipo As Byte, nota As Byte[], strum As Integer)
 +
 +
  With ev
 +
    .type = tipo
 +
     .channel = nota[0]
 +
    .note = nota[1]
 +
     .velocity = nota[2]
 +
    .value = strum
 +
  End With
 +
 +
<FONT Color=gray>' ''Inserisce l'Evento di ALSA nel buffer:''</font>
 +
  snd_seq_event_output(seq, ev)
 +
 +
<FONT Color=gray>' ''Invia l'Evento:''</font>
 +
  snd_seq_drain_output(seq)
 +
 +
End
 +
 +
 +
Public Sub Form_Close()
 +
 +
  snd_seq_close(seq)
 +
 +
  End

Versione attuale delle 12:03, 24 dic 2023

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 esterna di ALSA "snd_seq_connect_to()", purché si presti attenzione ad alcuni elementi che integrano due casi.

Di seguito vedremo tre casi possibili, che saranno dimostrati con esempi pratici.
Se nel proprio sistema è stato installato il softsynth Fluidsynth, allora i programmi, appresso descritti, dovrebbero connettersi automaticamente a quel softsynth, consentendo così la riproduzione dei suoni. Qualora non avvenga, procedere come segue:
1) da Terminale lanciare questa riga: ":~$ fluidsynth reload 0", o meglio: ":~$ fluidsynth /percorso/del/file/soundfont/.sf2"
2) senza chiudere il Terminale, verificare nell'utilità "Monitor di sistema" che Fluidsynth sia presente fra i processi attivi;
3) se Fluidsynth è presente fra i processi, verificare anche che esso sia presente al num. 128 nel file /proc/asound/seq/clients;
4) in caso affermativo - senza chiudere il Terminale - lanciare il codice sopra esposto.


Casi

1° caso

In tale caso si deve sempre utilizzare la funzione esterna di ALSA: "snd_seq_event_output_direct()" per l'invio diretto di ciascun Evento Midi ALSA.
Inoltre il codice dovrà prevedere che al membro dest_client 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.
Il dispositivo destinatario degli Eventi Midi ALSA sarà individuato, dunque, esclusivamente attraverso il suo numero identificativo e il numero della sua porta quale Client di ALSA.

Mostriamo un esempio astratto facendo uso di una Struttura dichiarata ad immagine della citata Struttura di ALSA snd_seq_event_t:

Private Const CLIENT_DESTINATARIO As Byte = 128   ' Solitamente l'identificativo è 128
Private Const PORTA_DESTINATARIO As Byte = 0


Library "libasound:2.0.0"

Public Struct snd_seq_event_t
  type As Byte
  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
  channel As Byte
  note As Byte
  velocity As Byte
  off_velocity As Byte
  param 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
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

' 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_client_id (snd_seq_t * seq)
' Get the client id.
Private Extern snd_seq_client_id(seq 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
 
' 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_direct (snd_seq_t *handle, snd_seq_event_t *ev)
' Output an event directly to the sequencer NOT through output buffer.
Private Extern snd_seq_event_output_direct(handle As Pointer, ev As Snd_seq_event_t) 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 handle As Pointer
 Dim rit As Integer
 Dim id, porta, que, b As Byte
 Dim ev As New Snd_seq_event_t

 rit = snd_seq_open(VarPtr(handle), "default", SND_SEQ_OPEN_OUTPUT, 0)
 If rit < 0 Then Error.Raise("Impossibile aprire il subsistema 'seq' di ALSA: " & String@(snd_strerror(rit)))

 id = snd_seq_client_id(handle)
 If id < 0 Then Error.Raise("Errore: " & String@(snd_strerror(rit)))

 porta = snd_seq_create_simple_port(handle, "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 !")

 que = snd_seq_alloc_queue(handle)

 With ev
   .type = ...etc...
   .queue = que
   .source_client = id
   .source_port = porta
   .dest_client = CLIENT_DESTINATARIO
   .dest_port = PORTA_DESTINATARIO
   ...etc...
 End With

 snd_seq_event_output_direct(handle, ev)

 snd_seq_close(handle)

End


2° caso

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

  • non sono utilizzate le funzioni esterne "snd_seq_client_id()" e "snd_seq_create_simple_port()", usate invece nel caso precedente, specifiche di ALSA per la creazione della porta del nostro Client applicativo in Gambas;
  • pertanto non si useranno, lasciandoli al valore predefinito di zero, i membri .source_client e ..source_port della Struttura relativa agli eventi Midi;
  • neppure sarà usata la funzione esterna "snd_seq_alloc_queue()" di ALSA;
  • al membro queue della Struttura relativa agli eventi Midi dovrà essere assegnato il valore della Costante ALSA "SND_SEQ_QUEUE_DIRECT" (253);
  • 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 astratto:

Private Const CLIENT_DESTINATARIO As Byte = 128   ' Solitamente l'identificativo è 128
Private Const PORTA_DESTINATARIO As Byte = 0


Library "libasound:2.0.0"

Public Struct snd_seq_event_t
  type As Byte
  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
  channel As Byte
  note As Byte
  velocity As Byte
  off_velocity As Byte
  param As Integer
  value As Integer
End Struct

Private Const SND_SEQ_OPEN_OUTPUT As Integer = 1
Private Const 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_event_output_direct (snd_seq_t *handle, snd_seq_event_t *ev)
' Output an event directly to the sequencer NOT through output buffer.
Private Extern snd_seq_event_output_direct(handle As Pointer, ev As Snd_seq_event_t) 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 handle As Pointer
 Dim rit As Integer
 Dim ev As New Snd_seq_event_t

 rit = snd_seq_open(VarPtr(handle), "default", SND_SEQ_OPEN_OUTPUT, 0)
 If rit < 0 Then Error.Raise("Impossibile aprire il subsistema 'seq' di ALSA: " & String@(snd_strerror(rit)))

 With ev
   .type = ...etc...
   .queue = SND_SEQ_QUEUE_DIRECT
   .dest_client = CLIENT_DESTINATARIO
   .dest_port = PORTA_DESTINATARIO
   ...etc...
 End With
     
 snd_seq_event_output_direct(handle, ev)

 snd_seq_close(handle)
 
End


Esempio più essenziale per invio diretto degli Eventi Midi ALSA

Mostriamo ora la modalità più breve ed essenziale per inviare ad altro Client di Alsa alcuni eventi Midi, e nella quale non è prevista la creazione di porte del nostro Client applicativo.
Si utilizzerà ovviamente la funzione esterna di ALSA "snd_seq_event_output_direct()" per l'invio diretto di ciascun Evento Midi ALSA.

Private Const CLIENT_DESTINATARIO As Byte = 128   ' Solitamente l'identificativo è 128
Private Const PORTA_DESTINATARIO As Byte = 0

Library "libasound:2.0.0"

Public Struct snd_seq_event_t
  type As Byte
  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
  channel As Byte
  note As Byte
  velocity As Byte
  off_velocity As Byte
  param As Integer
  value As Integer
End Struct

Private Const SND_SEQ_OPEN_OUTPUT As Integer = 1
Private Const SND_SEQ_QUEUE_DIRECT As Byte = 253
Private Enum SND_SEQ_EVENT_NOTEON = 6, SND_SEQ_EVENT_NOTEOFF

' 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_event_output_direct (snd_seq_t *handle, snd_seq_event_t *ev)
' Output an event directly to the sequencer NOT through output buffer.
Private Extern snd_seq_event_output_direct(handle As Pointer, ev As Snd_seq_event_t) 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 As Pointer
 Dim evento As New Snd_seq_event_t
 Dim rit As Integer

 snd_seq_open(VarPtr(seq), "default", SND_SEQ_OPEN_OUTPUT, 0)
 If rit < 0 Then Error.Raise("Impossibile aprire il subsistema 'seq' di ALSA: " & String@(snd_strerror(rit)))

 With evento
   .type = SND_SEQ_EVENT_NOTEON
   .queue = SND_SEQ_QUEUE_DIRECT
   .dest_client = CLIENT_DESTINATARIO
   .dest_port = PORTA_DESTINATARIO
   .channel = 0
   .note = 64
   .velocity = 100
 End With

 snd_seq_event_output_direct(seq, evento)

' 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 1

 evento.type = SND_SEQ_EVENT_NOTEOFF
 evento.velocity = 0

 snd_seq_event_output_direct(seq, evento)

 snd_seq_close(seq)

End

Quest'altro esempio, simile al precedente, è realizzato in ambiente grafico: sul Form sono posti uno SpinBox e un Button.
Cliccando sul Button si ascolterà il suono della nota Midi espressa dal valore mostrato dallo SpinBox che potrà essere variata manualmente.

Private Const CLIENT_DESTINATARIO As Byte = 128   ' Solitamente l'identificativo è 128
Private Const PORTA_DESTINATARIO As Byte = 0
Private seq As Pointer


Library "libasound:2.0.0"

Public Struct snd_seq_event_t
  type As Byte
  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
  channel As Byte
  note As Byte
  velocity As Byte
  off_velocity As Byte
  param As Integer
  value As Integer
End Struct

Private Const SND_SEQ_OPEN_OUTPUT As Integer = 1
Private Const SND_SEQ_QUEUE_DIRECT As Byte = 253
Private Enum SND_SEQ_EVENT_NOTEON = 6, SND_SEQ_EVENT_NOTEOFF

' 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_event_output_direct (snd_seq_t *handle, snd_seq_event_t *ev)
' Output an event directly to the sequencer NOT through output buffer.
Private Extern snd_seq_event_output_direct(handle As Pointer, ev As Snd_seq_event_t) 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 Form_Open()

 SpinBox1.MaxValue = 127
 SpinBox1.Value = 64

End


Public Sub Button1_MouseDown()

 Dim rit As Integer

 snd_seq_open(VarPtr(seq), "default", SND_SEQ_OPEN_OUTPUT, 0)
 If rit < 0 Then Error.Raise("Impossibile aprire il subsistema 'seq' di ALSA: " & String@(snd_strerror(rit)))

 InvioMessaggioMidi(SND_SEQ_EVENT_NOTEON, SpinBox1.Value, 100)

End


Public Sub Button1_MouseUp()

 InvioMessaggioMidi(SND_SEQ_EVENT_NOTEOFF, SpinBox1.Value, 0)

 snd_seq_close(seq)

End


Private Procedure InvioMessaggioMidi(tipo As Byte, nota As Byte, veloc As Byte)

 Dim evento As Snd_seq_event_t

 With evento = New Snd_seq_event_t
   .type = tipo
   .queue = SND_SEQ_QUEUE_DIRECT
   .dest_client = CLIENT_DESTINATARIO
   .dest_port = PORTA_DESTINATARIO
   .channel = 0
   .note = nota
   .velocity = veloc
 End With

 snd_seq_event_output_direct(seq, evento)

End

In questo esempio, per inviare l'evento Midi di ALSA, invece della Struttura snd_seq_event_t sarà utilizzato un vettore di tipo Byte[].

Private Const CLIENT_DESTINATARIO As Byte = 128   ' Solitamente l'identificativo è 128
Private Const PORTA_DESTINATARIO As Byte = 0
Private seq As Pointer


Library "libasound:2.0.0"

Private Const SND_SEQ_OPEN_OUTPUT As Integer = 1
Private Const SND_SEQ_QUEUE_DIRECT As Byte = 253
Private Enum SND_SEQ_EVENT_NOTEON = 6, SND_SEQ_EVENT_NOTEOFF

' 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_event_output_direct (snd_seq_t *handle, snd_seq_event_t *ev)
' Output an event directly to the sequencer NOT through output buffer.
Private Extern snd_seq_event_output_direct(handle As Pointer, ev 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 Form_Open()

 SpinBox1.MaxValue = 127
 SpinBox1.Value = 64

End


Public Sub Button1_MouseDown()

 Dim rit As Integer

 snd_seq_open(VarPtr(seq), "default", SND_SEQ_OPEN_OUTPUT, 0)
 If rit < 0 Then Error.Raise("Impossibile aprire il subsistema 'seq' di ALSA: " & String@(snd_strerror(rit)))

 InvioMessaggioMidi(SND_SEQ_EVENT_NOTEON, SpinBox1.Value, 100)

End


Public Sub Button1_MouseUp()

 InvioMessaggioMidi(SND_SEQ_EVENT_NOTEOFF, SpinBox1.Value, 0)

 snd_seq_close(seq)

End


Private Procedure InvioMessaggioMidi(tipo As Byte, nota As Byte, veloc As Byte)

 Dim ev As New Byte[28]

' Vengono assegnati al vettore i valori fondamentali per generare l'evento Midi di ALSA:
 ev[0] = tipo
 ev[3] = SND_SEQ_QUEUE_DIRECT
 ev[14] = CLIENT_DESTINATARIO
 ev[15] = PORTA_DESTINATARIO
 ev[17] = SpinBox1.Value
 ev[18] = 100

 snd_seq_event_output_direct(seq, ev.Data)

End


3° caso

In questo caso si farà uso delle funzioni esterne di ALSA "snd_seq_event_output()" e "snd_seq_drain_output()" per l'invio di ciascun Evento Midi ALSA.

Nell'esempio pratico che segue viene rappresentata sul Form una tastierina Midi che può inviare dati Midi grezzi attraverso ALSA al Client-softsynth (ad esempio QSynth). Premendo un tasto di questa tastiera Midi interna, udiremo il suono della sua nota con il timbro dello strumento musicale prescelto in un ComboBox sul Form.

Private cb As ComboBox
Private Const ALTEZZA_TASTI_BIANCHI As Single = 0.35
Private bb As New Byte[3]
Private instrumenta As String[] = ["Acustic Grand Piano", "Bright Acustic Piano", "Electric Grand Piano", "Honky-tonk",
  "Electric Piano 1", "Electric Piano 2", "Harpsichord", "Clavinet", "Celesta", "Glockenspiel", "Music Box", "Vibraphone",
  "Marimba", "Xylophone", "Tubular Bells", "Dulcimer", "Hammond Organ", "Percussive Organ", "Rock Organ", "Church Organ",
  "Reed Organ", "Accordion", "Harmonica", "Tango Accordion", "Acoustic Guitar (nylon)", "Acoustic Guitar (steel)",
  "Electric Guitar (jazz)", "Electric Guitar (clean)", "Electric Guitar(muted)", "Overdriven Guitar", "Distortion Guitar",
  "Guitar Harmonics", "Acoustic Bass", "Electric Bass (finger)", "Electric Bass (pick)", "Fretless Bass", "Slap Bass 1",
  "Slap Bass 2", "Synth Bass 1", "Synth Bass 2", "Violin", "Viola", "Cello", "Contrabass", "Tremolo Strings",
  "Pizzicato Strings", "Orchestral Harp", "Timpani", "String Ensemble 1", "String Ensemble 2", "SynthStrings 1",
  "SynthStrings 2", "Choir Aahs", "Voice Oohs", "Synth Voice", "Orchestra Hit", "Trumpet", "Trombone", "Tuba", "Muted Trumpet",
  "French Horn", "Brass Section", "Synth Brass 1", "Synth Brass 2", "Soprano Sax", "Alto Sax", "Tenor Sax", "Baritone Sax",
  "Oboe", "English Horn", "Basson", "Clarinet", "Piccolo", "Flute", "Recorder", "Pan Flute", "Bottle Blow", "Shakuhachi",
  "Whistle", "Ocarina", "Lead 1 (square)", "Lead 2 (sawtooth)", "Lead 3 (caliope lead)", "Lead 4 (chiff lead)",
  "Lead 5 (charang)", "Lead 6 (voice)", "Lead 7 (fifths)", "Lead 8(brass+lead)", "Pad 1 (new age)", "Pad 2 (warm)",
  "Pad 3 (polysynth)", "Pad 4 (choir)", "Pad 5 (bowed)", "Pad 6 (metallic)", "Pad 7 (halo)", "Pad 8 (sweep)", "FX 1 (rain)",
  "FX 2 (soundtrack)", "FX 3 (crystal)", "FX 4 (atmosphere)", "FX 5 (brightness)", "FX 6 (goblins)", "FX 7 (echoes)",
  "FX 8 (sci-fi)", "Sitar", "Banjo", "Shamisen", "Koto", "Kalimba", "Bagpipe", "Fiddle", "Shanai", "Tinkle Bell", "Agogo",
  "Steel Drums", "Woodblock", "Taiko Drum", "Melodic Tom", "Synth Drum", "Reverse Cymbal", "Guitar Fret Noise", "Breath Noise",
  "Seashore", "Bird Tweet", "Telephone Ring", "Helicopter", "Applause", "Gunshot"]


Public Sub Form_Open()

 With Me
   .W = Screen.AvailableWidth * 0.5
   .H = Screen.AvailableHeight * 0.2
   .Center
 End With
 CreaClient()
 With cb = New ComboBox(Me) As "Combo"
   .W = 160
   .H = 25
   .X = (Me.W * 0.94) - .W
   .Y = 0
   .List = instrumenta
   .Placeholder = "Strumenti musicali GM"
 End With

 CreaTastiera()

End

Private Procedure CreaTastiera()

 Dim pn As Panel
 Dim neri As New Byte[40]
 Dim tasti As Button[]
 Dim b, c, n As Byte
 
 With pn = New Panel(Me)
   .W = Me.W * 0.88
   .H = Me.H * 0.2
   .X = (Me.W / 2) - (pn.W / 2)
   .Y = Me.H * 0.2
   .Border = Border.Sunken
   .Background = &8b4513
 End With

 Repeat 
   neri[b] = 25 + (12 * b / 5)
   neri[b + 1] = 27 + (12 * b / 5)
   neri[b + 2] = 30 + (12 * b / 5)
   neri[b + 3] = 32 + (12 * b / 5)
   neri[b + 4] = 34 + (12 * b / 5)
   b += 5
 Until b == neri.Count

 tasti = New Button[109]

 For t As Short = 0 To tasti.Max
   With tasti[t] = New Button(Me) As "Tasti"
     .W = 0
     If t > 23 Then
       If neri.Exist(t) Then  ' Imposta i tasti neri
         .W = Me.W * 0.013
         .H = ((Me.H * ALTEZZA_TASTI_BIANCHI) * 66.66) / 100
         Select Case t 
           Case 25 + (12 * n)
             .X = tasti[t - 1].X + (((Me.W * 0.025) / 2))
           Case 27 + (12 * n)
             .X = tasti[t - 1].X + (((Me.W * 0.025) / 2))
           Case 30 + (12 * n)
             .X = tasti[t - 1].X + (((Me.W * 0.025) / 2))
           Case 32 + (12 * n)
             .X = tasti[t - 1].X + (((Me.W * 0.025) / 2))
           Case 34 + (12 * n)
             .X = tasti[t - 1].X + (((Me.W * 0.025) / 2))
             Inc n
         End Select
         .Y = Me.H * 0.375
         .Background = Color.Black
         .Tag = t
       Else
' Imposta i tasti bianchi:
         .W = Me.W * 0.018
         .H = Me.H * ALTEZZA_TASTI_BIANCHI
         .X = (.W * c) + (Me.W / 16)
         .Y = Me.H * 0.37
         .Background = Color.White
         .Tag = t
         .Lower
         Inc c
       Endif
     Endif 
   End With
 Next

End


Public Sub Tasti_MouseDown()

 bb[0] = 0
 bb[1] = Last.Tag
 bb[2] = &64

 InvioMIDI(SND_SEQ_EVENT_NOTEON, bb)

 If Last.Background = Color.Black Then Last.Background = Color.DarkGray
 Me.Title = "Nota Midi:  " & Last.Tag

End

Public Sub Tasti_MouseUp()

 bb[0] = 0
 bb[1] = Last.Tag
 bb[2] = 0

 InvioMIDI(SND_SEQ_EVENT_NOTEOFF, bb)

 If Last.Background = Color.DarkGray Then Last.Background = Color.Black
 Me.Title = " "

End

'''''''''''''''''''''''''''''''''''''''''''''''

Private seq As Pointer


Library "libasound:2"

Public Struct snd_seq_event_t   ' Struttura dell'Evento Midi di ALSA
  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
  param As Integer
  value As Integer
End Struct

Private Const SND_SEQ_OPEN_OUTPUT As Integer = 1
Private Const SND_SEQ_QUEUE_DIRECT As Byte = 253
Private Enum SND_SEQ_EVENT_NOTEON = 6, SND_SEQ_EVENT_NOTEOFF, SND_SEQ_EVENT_PGMCHANGE = 11

' 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 String
  
' 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


Private Procedure CreaClient()

 Dim rit As Integer

 rit = snd_seq_open(VarPtr(seq), "default", SND_SEQ_OPEN_OUTPUT, 0)
 If rit < 0 Then Error.Raise("Impossibile aprire il subsistema 'seq' di ALSA: " & snd_strerror(rit))

End


Public Sub Combo_Change()  ' Imposta lo strumento musicale

 Dim evento As New Snd_seq_event_t

 With evento
   .queue = SND_SEQ_QUEUE_DIRECT
   .dest_client = 128
   .dest_port = 0
   .channel = 0
 End With

' Imposta il tipo di strumento musicale mediante il Messaggio Midi "Program-Change":
 Messaggio(evento, SND_SEQ_EVENT_PGMCHANGE, [0, 0, 0], cb.Index)

End


Private Procedure InvioMIDI(tipo As Byte, mid As Byte[])

 Dim evento As New Snd_seq_event_t

 With evento
   .queue = SND_SEQ_QUEUE_DIRECT
   .dest_client = 128
   .dest_port = 0
   .channel = mid[0]
 End With

' Imposta il Messaggio Midi:
 Messaggio(evento, tipo, mid, 0)

End


Private Procedure Messaggio(ev As Snd_seq_event_t, tipo As Byte, nota As Byte[], strum As Integer)

 With ev
   .type = tipo
   .channel = nota[0]
   .note = nota[1]
   .velocity = nota[2]
   .value = strum
 End With

' Inserisce l'Evento di ALSA nel buffer:
 snd_seq_event_output(seq, ev)

' Invia l'Evento:
 snd_seq_drain_output(seq)

End


Public Sub Form_Close()

 snd_seq_close(seq)

End