Differenze tra le versioni di "Alsa e Gambas: Invio dati con l'uso delle Strutture"

Da Gambas-it.org - Wikipedia.
 
(22 versioni intermedie di uno stesso utente non sono mostrate)
Riga 2: Riga 2:
  
 
In questa pagina verrà preso in considerazione l'uso delle ''[[Strutture:_dichiarazione_ed_uso|Strutture]]'' per l'invio degli Eventi Midi ad ALSA.
 
In questa pagina verrà preso in considerazione l'uso delle ''[[Strutture:_dichiarazione_ed_uso|Strutture]]'' per l'invio degli Eventi Midi ad ALSA.
<BR>Le '''Strutture''', introdotte in [[GAMBAS|Gambas]] con la versione 3.0, possono essere utilizzate per scrivere e gestire i messaggi Midi. Infatti dichiarando una Struttura (''Struct'') ci si riserva una quantità di memoria, definita dal tipo (byte, short, integer, etc.) dei valori dichiarati in ciascun membro della [[Strutture:_dichiarazione_ed_uso|Struttura]]. Dichiarando poi una variabile del tipo di quella Struttura si ha automaticamente una zona di memoria utilizzabile e "lavorabile". Pertanto, non sarà più necessario l'uso di funzioni e stratagemmi per riservare una zona di memoria specifica, né di puntatori e di comandi ''Write'' per scriverci dentro. Per tale ragione le funzioni presenti nella classe secondaria CAlsa.class, comprese le dichiarazioni esterne, che facevano riferimento a quella zona allocata e ai suoi puntatori, devono essere modificate inserendo la dichiarazione di una variabile-Struttura che fa riferimento a quelle istanziate per ciascun messaggio Midi, le quali assumono - come vedremo più sotto - la composizione e l'organizzazione della Struttura-modello:
+
<BR>Le '''Strutture''', introdotte in [[GAMBAS|Gambas]] con la versione 3.0, possono essere utilizzate per scrivere e gestire i messaggi Midi. Infatti dichiarando una Struttura (''Struct'') ci si riserva una quantità di memoria, definita dal tipo (byte, short, integer, etc.) dei valori dichiarati in ciascun membro della [[Strutture:_dichiarazione_ed_uso|Struttura]].
 
+
<BR>Dichiarando poi una variabile del tipo di quella Struttura, si ha automaticamente una zona di memoria utilizzabile.
Private Extern snd_seq_event_output(handle As Pointer, variabileStruttura As eventimidiStruttura) As Integer
 
  
 
=Scrittura dei dati dei messaggi Midi nelle Strutture=
 
=Scrittura dei dati dei messaggi Midi nelle Strutture=
  
 
Inseriremo una [[Strutture:_dichiarazione_ed_uso|Struttura]] ''modello'' all'inizio della classe secondaria CAlsa.class. Porremo quindi, quali ''campi'' della struttura-tipo, i dati necessari per la definizione degli eventi Midi, come richiesti da ALSA, attribuendo a ciascun campo il tipo di dato desiderato da ALSA nel rispetto dell'ordine previsto dei dati, nonché del numero del byte al quale ciascun dato dovrà essere assegnato. Inseriremo non solo i dati comuni per tutti i messaggi Midi, ma anche quelli previsti per gli specifici messaggi:
 
Inseriremo una [[Strutture:_dichiarazione_ed_uso|Struttura]] ''modello'' all'inizio della classe secondaria CAlsa.class. Porremo quindi, quali ''campi'' della struttura-tipo, i dati necessari per la definizione degli eventi Midi, come richiesti da ALSA, attribuendo a ciascun campo il tipo di dato desiderato da ALSA nel rispetto dell'ordine previsto dei dati, nonché del numero del byte al quale ciascun dato dovrà essere assegnato. Inseriremo non solo i dati comuni per tutti i messaggi Midi, ma anche quelli previsti per gli specifici messaggi:
 +
In <FONT Color=red>rosso</font> sono rappresentati i membri che contengono dati identici per qualunque ''Evento Midi ALSA'';
 +
In <FONT Color=green>verde</font> sono rappresentati i membri che contengono dati relativi allo specifico ''Evento Midi ALSA''.
 +
 +
'''struct snd_seq_event_t {                          Public Struct Snd_seq_event_t'''
 +
 +
snd_seq_event_type_t type                            <FONT Color=green>type</font> As Byte          byte di indice 0
 +
unsigned char flags                                  <FONT Color=red>flags</font> As Byte
 +
unsigned char tag                                    <FONT Color=red>tag</font> As Byte
 +
unsigned char queue                                  <FONT Color=red>queue</font> As Byte
 +
snd_seq_timestamp_t time
 +
    ↳ snd_seq_tick_time_t / snd_seq_real_time_t   
 +
        ↳ tick                ↳ tv_sec              <FONT Color=green>tick_o_tv_sec</font> As Integer
 +
                              ↳ tv_nsec              <FONT Color=green>tv_nsec</font> As Integer
 +
snd_seq_addr_t source
 +
    ↳ unsigned char client                            <FONT Color=red>source_client</font> As Byte
 +
    ↳ unsigned char port                              <FONT Color=red>source_port</font> As Byte
 +
snd_seq_addr_t dest
 +
    ↳ unsigned char client                            <FONT Color=red>dest_client</font> As Byte
 +
    ↳ unsigned char port                              <FONT Color=red>dest_port</font> As Byte
 +
snd_seq_ev_note_t note
 +
    ↳ unsigned char channel                          <FONT Color=green>channel</font> As Byte      byte di indice 16
 +
    ↳ unsigned char note                              <FONT Color=green>note</font> As Byte          byte di indice 17
 +
    ↳ unsigned char velocity                          <FONT Color=green>velocity</font> As Byte      byte di indice 18
 +
    ↳ unsigned char off_velocity                      <FONT Color=green>off_velocity</font> As Byte  byte di indice 19
 +
unsigned int param                                  <FONT Color=green>param</font> As Integer      byte di indice 20
 +
signed int value                                    <FONT Color=green>value</font> As Integer      byte di indice 24
 +
 +
'''}                                                  End Struct'''
 +
I dati appartenenti agli specifici ''Messaggi Midi'' non vengono utilizzati tutti insieme da ciascun ''Evento Midi ALSA'', ma solo alcuni di essi a seconda dello specifico Messaggio. Ovviamente nella Struttura-tipo essi saranno comunque posti e dichiarati, al fine di consentirne l'uso.
  
Public Struct EventoMidiAlsa
+
In Gambas si istanzierà una variabile del tipo della Struttura ''snd_seq_event_t'', sopra descritta, con la quale gestire gli specifici ''Eventi Midi'' di ALSA, assegnando a ciascun membro (sia quelli comuni, sia quelli specifici di ogni tipo di ''Evento Midi ALSA'') il relativo valore. Ovviamente si potrà evitare di richiamare i membri che eventualmente non devono essere lasciati con valore zero.
+
 
  <FONT Color=red>type</font> As Byte                byte n° 0 \
+
 
  <FONT Color=red>flag</font> As Byte                          \
+
=Riassunto delle fasi di creazione, allocamento e invio degli Eventi Midi ad ALSA=
  <FONT Color=red>tag</font> As Byte                            \
+
Prima di mostrare dei codici esemplificativi sulle varie modalità di invio degli'<I>Eventi Midi</i> al sistema ALSA, è opportuno ricordare quanto descritto nela capitolo precedente "[[La_gestione_dei_dati_Midi_con_il_subsistema_Seq#ALSA_e_Gambas_-_Gestione_dei_Messaggi_Midi_standard|ALSA e Gambas - Gestione dei Messaggi Midi standard]]".
  <FONT Color=red>queue</font> As Byte                            \  dati valorizzati
 
  <FONT Color=red>tick_o_tv_sec</font> As Integer                  \ per tutti
 
  <FONT Color=red>tv_nsec</font> As Integer                        / i Messaggi Midi
 
  <FONT Color=red>source_id</font> As Byte                        /
 
  <FONT Color=red>source_porta</font> As Byte                    /
 
  <FONT Color=red>dest_id</font> As Byte                        /
 
  <FONT Color=red>dest_porta</font> As Byte                    /
 
  <FONT Color=red>channel</font> As Byte          byte n° 16 /
 
    <FONT Color=green>note</font> As Byte                  byte n° 17  \  dati valorizzati
 
    <FONT Color=green>velocity</font> As Byte              byte n° 18    \ solo in base agli specifici
 
    <FONT Color=green>off_velocity</font> As Byte          byte n° 19    / messaggi Midi
 
      <FONT Color=orange>param</font> As Integer              byte n° 20  /
 
      <FONT Color=orange>value</font> As Integer              byte n° 24 /
 
 
 
End Struct
 
<BR>I dati appartenenti agli specifici messaggi Midi non vengono utilizzati tutti insieme da ciascun messaggio, ma solo alcuni di essi a seconda dello specifico messaggio. Ovviamente nella Struttura-tipo essi saranno comunque posti e dichiarati, al fine di consentirne l'uso.
 
  
 +
Dunque possiamo elencare per sommicapi le seguenti fasi da seguire:
 +
<BR><B>1</b>) comporre l'<I>Evento Midi ALSA</i> nei membri essenziali della sua Struttura [[Alsa_e_Gambas_-_Dai_Messaggi_Midi_standard_all'Evento_Midi_di_ALSA#Confronto_fra_la_rappresentazione_in_C_e_quella_in_Gambas_della_Struttura_di_ALSA_.22seq_event_t.22|snd_seq_event_t]], ed in particolare attribuire la temporizzazione mediante il "[[ALSA_e_Gambas_-_La_marcatura_temporale_degli_eventi:_il_Timestamp|Timestamp]]" in "tick Midi" o in "real time";
 +
<BR><B>2</b>) allocare la ''Coda'' degli ''Eventi Midi ALSA'' con la funzione esterna  [https://www.alsa-project.org/alsa-doc/alsa-lib/group___seq_queue.html#ga036e62b321b56bee2e8d2e8280a6416a snd_seq_alloc_queue()];
 +
<BR><B>3</b>) avviare il controllo della ''Coda'' degli ''Eventi Midi ALSA'' con la funzione esterna [https://www.alsa-project.org/alsa-doc/alsa-lib/group___seq_middle.html#ga36dee7982baa47ad22ab8437541ea5ef snd_seq_control_queue(seq, q, type, value, 0, ev)], passandole come 3° argomento la Costante di ALSA "SND_SEQ_EVENT_START";
 +
<BR><B>4</b>) accodare gli ''Eventi Midi'' nel buffer mediante la funzione esterna [https://www.alsa-project.org/alsa-doc/alsa-lib/group___seq_event.html#ga41be1e09173957944352c50067a686ea snd_seq_event_output_buffer()];
 +
<BR><B>5</b>) inviare al sistema ALSA mediante la funzione esterna [https://www.alsa-project.org/alsa-doc/alsa-lib/group___seq_event.html#ga64a0ed5488504ef91b5b6b92172bc0aa snd_seq_drain_output()] l'intera ''Coda'' degli ''Eventi Midi'', memorizzati nel buffer, affinche siano processati;
 +
<BR><B>6</b>) arrestare la ''Coda'' degli ''Eventi Midi ALSA'' richiamando la funzione esterna [https://www.alsa-project.org/alsa-doc/alsa-lib/group___seq_middle.html#ga36dee7982baa47ad22ab8437541ea5ef snd_seq_control_queue(seq, q, type, value, 0, ev)], passandole però questa volta come suo 3° argomento la Costante di ALSA "SND_SEQ_EVENT_STOP".
  
=I messaggi Midi specifici=
 
===Preambolo===
 
  
Organizzeremo nella classe secondaria CAlsa.class le routine per la gestione dei messaggi Midi. Poiché per i valori specifici di ciascun messaggio è prevista una disposizione all'interno della zona di memoria (come abbiamo già appreso con l'[[Alsa_e_Gambas:_Uso_di_un'area_di_memoria_allocata|uso di un'area di memoria allocata]]) comune con altri messaggi, prevederemo - per ciascun gruppo di messaggi dalla [[Strutture:_dichiarazione_ed_uso|Struttura]] simile - delle variabili-Struttura del tipo della ''Struttura'' dell'Evento Midi di ALSA, ad esempio:
+
=Esempi di codice=
ev As New EventoMidiAlsa
+
====1° esempio pratico====
Con la variabile-Struttura "ev" si definirà ''concretamente'' il messaggio Midi richiesto, fissando in ciascun membro (quelli comuni e quelli specifici di ogni tipo di Evento Midi ALSA) il relativo valore. Si potrà evitare di richiamare i campi che eventualmente non servono.  
+
Nel seguente esempio in ambiente grafico avremo la Classe principale "FMain.Class" e una Classe secondaria, che incapsula le risorse di ALSA da utilizzare.
 +
<BR>Dichiareremo una [[Strutture:_dichiarazione_ed_uso|Struttura]], chiamata ''[[Alsa_e_Gambas_-_Dai_Messaggi_Midi_standard_all'Evento_Midi_di_ALSA#Confronto_fra_la_rappresentazione_in_C_e_quella_in_Gambas_della_Struttura_di_ALSA_.22seq_event_t.22|snd_seq_event_t]]'', che farà da ''modello'' alla particolare variabile di quel tipo di ''Struttura'' da usare per la organizzazione di ciascun ''Evento Midi ALSA''.
 +
<BR>In questo esempio sarà effettuato un invio diretto e immediato di un singolo ''Evento Midi'' per volta: cliccando su un qualunque tasto della tastiera del computer, si invierà un ''Evento Midi'' "NoteON"; rilasciando il tasto si invierà un ''Evento Midi'' "NoteOFF", per far cesssare l'esecuzione della nota Midi prima avviata.
 +
<BR>Saranno gestiti comunque in apertura del programma anche gli <I>Eventi Midi</i> "Control Change" e "Program Change".
 +
<BR>Riguardo al ''Timestamp'' di ogni ''Evento Midi ALSA'' inviato, trattandosi di invio singolo e diretto con esecuzione immediata è indifferente la modalità scelta: il membro ''time'' della predetta Struttura ''[[Alsa_e_Gambas_-_Dai_Messaggi_Midi_standard_all'Evento_Midi_di_ALSA#Confronto_fra_la_rappresentazione_in_C_e_quella_in_Gambas_della_Struttura_di_ALSA_.22seq_event_t.22|snd_seq_event_t]]'' relativo al ''Timestamp'' del singolo ''Evento Midi'' sarà posto a zero.
  
==Gruppo messaggi: <FONT Color= "red">NoteON - NoteOFF</font> - <FONT Color= "red">Polyphonic Aftertouch</font>==
+
Dunque avremo la Classe principale "FMain.Class":
Questi tre messaggi Midi hanno tra loro in comune tre medesimi specifici dati posti ai byte di indice 16, 17 e 18 della "Struttura-modello", e che abbiamo chiamato: ".channel", ".note" e ".velocity".
+
Private Const id_dev As Integer = 128  <FONT Color=gray>' ''14 = midi out oppure  128 (solitamente) = softsynth''</font>
  ev.type = tipo_Evento          <Font Color=Gray>' ''= 6, 7 oppure 8 a seconda del tipo di Evento Midi ALSA''</font>
+
Private Const p_dev As Integer = 0      <FONT Color=gray>' ''porta del Client destinatario dei dati Midi: solitamente 0''</font>
  ev.queue = outq
+
Public alsa As CAlsa                    <FONT Color=gray>' ''Classe che incapsula le funzioni ALSA''</font>
  ev.source_id = id
+
Private bo As Boolean
  ev.source_porta = outport
+
  ev.dest_id = SND_SEQ_ADDRESS_SUBSCRIBERS
+
'''Public''' Sub Form_Open()
  ev.dest_porta = SND_SEQ_ADDRESS_UNKNOWN
+
  ev.<FONT Color=green>channel</font> = valore
+
<FONT Color=gray>' ''Crea ("istanzia") un Oggetto della Classe "CAlsa" per poterla usare:''</font>
  ev.<FONT Color=green>note</font> = valore
+
  With alsa = New CAlsa As "Alsa"
  ev.<FONT Color=green>velocity</font> = valore
+
<FONT Color=gray>' ''Apre ALSA e assegna un nome al Client:''</font>
 +
    Me.Title = "|| Client ALSA " & .AlsaIni("Programma di prova")
 +
<FONT Color=gray>' ''Sceglie la periferica (Softsynth) su cui suonare:''</font>
 +
    .ImpostaDispositivo(id_dev, p_dev)
 +
  End With
 
   
 
   
   err = snd_seq_event_output(handle, ev)  <Font Color=Gray>' ''Si passa ad ALSA la Struttura appena valorizzata''</font>
+
<FONT Color=gray>' ''Imposta il Volume gestendo l'Evento Midi "Control Change":''</font>
 +
  alsa.ControlChange(0, 7, 64)
 +
<FONT Color=gray>' ''Imposta il suono dello strumento musicale gestendo l'Evento Midi "Program Change":''</font>
 +
  alsa.ProgramChange(0, 0, 44)
 +
 +
'''End'''
 +
 +
'''Public''' Sub Form_KeyPress()
 +
 +
   If bo Then Return
 +
<FONT Color=gray>' ''Imposta la nota midi da eseguire gestendo l'Evento Midi "Note-ON":''</font>
 +
  alsa.NoteON(0, 64, 100)
 +
   alsa.Flush()
 +
  bo = True
 +
 +
'''End'''
 +
 +
'''Public''' Sub Form_KeyRelease()
 +
 +
<FONT Color=gray>' ''Imposta la nota midi da eseguire gestendo l'Evento Midi "Note-OFF":''</font>
 +
  alsa.NoteOFF(0, 64, 0)
 +
  alsa.Flush()
 +
  bo = False
 +
 +
'''End'''
 +
 +
'''Public''' Sub Form_Close()
 +
 +
  alsa.Chiude()
 +
 +
'''End'''
  
==Gruppo messaggi: <FONT Color= "red">Program Change</font> - <FONT Color= "red">Channel Aftertouch (Key Pressure)</font>==
+
Nella Classe secondaria, chiamata "CAlsa", avremo il seguente codice:
Questi due messaggi Midi hanno tra loro in comune due medesimi specifici dati posti ai byte di indice 16 e 24 della Struttura-modello, e che abbiamo chiamato: ".channel" e ".value".
+
Private handle As Pointer
  ev.type = tipo_Evento          <Font Color=Gray>' ''= 11 oppure 12 a seconda del tipo di Evento Midi ALSA''</font>
+
Private id As Integer
  ev.queue = outq
+
Private s_port As Integer
  ev.source_id = id
+
Private que As Integer
  ev.source_porta = outport
+
Private dclient As Byte
  ev.dest_id = SND_SEQ_ADDRESS_SUBSCRIBERS
+
Private dport As Byte
  ev.dest_porta = SND_SEQ_ADDRESS_UNKNOWN
 
  ev.<FONT Color=green>channel</font> = valore
 
  ev.<FONT Color=orange>value</font> = valore
 
 
   
 
   
  err = snd_seq_event_output(handle, ev)      <Font Color=Gray>' ''Si passa ad ALSA la Struttura appena valorizzata''</font>
 
 
==Gruppo messaggi: <FONT Color= "red">Control Change</font> - <FONT Color= "red">Pitch Bend (Pitch Wheel)</font>==
 
Questi due messaggi Midi hanno tra loro in comune tre medesimi specifici dati posti ai byte num. 16, 20 e 24 della Struttura-modello, e che abbiamo chiamato: ".channel", ".param" e ".value".
 
  ev.type = tipo_Evento          <Font Color=Gray>' ''= 10 oppure 13 a seconda del tipo di Evento Midi ALSA''</font>
 
  ev.queue = outq
 
  ev.source_id = id
 
  ev.source_porta = outport
 
  ev.dest_id = SND_SEQ_ADDRESS_SUBSCRIBERS
 
  ev.dest_porta = SND_SEQ_ADDRESS_UNKNOWN
 
  ev.<FONT Color=green>channel</font> = 0
 
  ev.<FONT Color=orange>param</font> = valore
 
  ev.<FONT Color=orange>value</font> = valore
 
 
   
 
   
  err = snd_seq_event_output(handle, ev)      <Font Color=Gray>' ''Si passa ad ALSA la Struttura appena valorizzata''</font>
+
  Library "libasound:2.0.0"
Va detto che per l'Evento ''Pitch-Bend'' il membro più importante da valorizzare è ".value".
 
 
 
=Esempi di codice=
 
Nel seguente esempio dichiareremo una [[Strutture:_dichiarazione_ed_uso|Struttura]], chiamata ''EventoMidiAlsa'', che farà da ''modello'' alla particolare variabile di quel tipo di ''Struttura''.
 
<BR>In questo esempio andremo a gestire soltanto tre tipi di Messaggi Midi: il ''Program Change'', il ''Note On'' e il ''Note OFF''.
 
  Library "libasound:2"
 
 
   
 
   
  '''Public''' Struct <Font Color= #B22222>EventoMidiAlsa</font>
+
  '''Public''' Struct <Font Color= #B22222>snd_seq_event_t</font>
   type As Byte             <Font Color= #006400>' '' byte indice n° 0''</font>
+
   type As Byte
   flag As Byte
+
   flags As Byte
 
   tag As Byte
 
   tag As Byte
 
   queue As Byte
 
   queue As Byte
 
   tick_o_tv_sec As Integer
 
   tick_o_tv_sec As Integer
 
   tv_nsec As Integer
 
   tv_nsec As Integer
   source_id As Byte
+
   source_client As Byte
   source_porta As Byte
+
   source_port As Byte
   dest_id As Byte
+
   dest_client As Byte
   dest_porta As Byte       <Font Color= #006400>' ''byte n° 15''</font>
+
   dest_port As Byte
    channel As Byte           <Font Color= #006400>' ''byte n° 16''</font>
+
  channel As Byte
    note As Byte             <Font Color= #006400>' ''byte n° 17''</font>
+
  note As Byte
    velocity As Byte         <Font Color= #006400>' ''byte n° 18''</font>
+
  velocity As Byte
    off_velocity As Byte     <Font Color= #006400>' ''byte n° 19''</font>
+
  off_velocity As Byte
      param As Integer         <Font Color= #006400>' ''byte n° 20''</font>
+
  param As Integer
      value As Integer         <Font Color= #006400>' ''byte n° 24''</font>
+
  value As Integer
 
  '''End''' Struct
 
  '''End''' Struct
 
   
 
   
  Private Const SND_SEQ_OPEN_DUPLEX As Integer = 3
+
  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_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 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,
 
  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
 
               SND_SEQ_EVENT_CONTROLLER = 10, SND_SEQ_EVENT_PGMCHANGE, SND_SEQ_EVENT_CHANPRESS, SND_SEQ_EVENT_PITCHBEND
Riga 121: Riga 153:
 
  ' ''Set client name.''</font>
 
  ' ''Set client name.''</font>
 
  Private Extern snd_seq_set_client_name(seq As Pointer, name As String) As Integer
 
  Private Extern snd_seq_set_client_name(seq As Pointer, name As String) As Integer
 +
 +
<FONT Color=gray>' ''int snd_seq_client_id (snd_seq_t * seq)''
 +
' ''Get the client id.''</font>
 +
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_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.''</font>
 
  ' ''Creates a port with the given capability and type bits.''</font>
 
  Private Extern snd_seq_create_simple_port(seq As Pointer, name As String, caps As Integer, type As Integer) As Integer
 
  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 * seq)''
 +
' ''Allocate a queue.''</font>
 +
Private Extern snd_seq_alloc_queue(seq As Pointer) As Integer
 +
 
 +
<FONT Color=gray>' ''int snd_seq_event_output_buffer (snd_seq_t * seq, snd_seq_event_t *ev)''
 +
' ''Output an event.''</font>
 +
Private Extern snd_seq_event_output_buffer(seq As Pointer, ev As Snd_seq_event_t) As Integer
 +
 +
<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>' ''const char * snd_strerror (int errnum)''
 +
' ''Returns the message for an error code.''</font>
 +
Private Extern snd_strerror(errnum As Integer) As String
 +
 +
<FONT Color=gray>' ''int snd_seq_close (snd_seq_t * seq)''
 +
' ''Close the sequencer.''</font>
 +
Private Extern snd_seq_close(seq As Pointer) As Integer
 +
 +
 +
'''Public''' Function AlsaIni(nome As String) As String
 +
 +
  Dim err As Integer
 +
 +
  err = snd_seq_open(VarPtr(handle), "default", SND_SEQ_OPEN_OUTPUT, 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, nome)
 +
  id = snd_seq_client_id(handle)
 +
  Print "Alsa ClientID = "; id
 +
 +
  s_port = snd_seq_create_simple_port(handle, "Uscita", SND_SEQ_PORT_CAP_READ, SND_SEQ_PORT_TYPE_MIDI_GENERIC + SND_SEQ_PORT_TYPE_APPLICATION)
 +
  Print "Porta del programma = "; s_port
 +
  If s_port < 0 Then error.Raise("Errore: " & snd_strerror(s_port))
 +
 +
  que = snd_seq_alloc_queue(handle)
 +
  If que < 0 Then error.Raise("Errore: " & snd_strerror(que))
 +
 +
  Return CStr(id) & ":" & CStr(s_port)
 +
 +
'''End'''
 +
 +
'''Public''' Procedure ImpostaDispositivo(client As Integer, port As Integer)
 +
 +
  dclient = client
 +
  dport = port
 +
 
 +
'''End'''
 +
 +
'''Public''' Procedure ControlChange(canale As Byte, prm As Integer, value As Integer)
 +
 +
  Dim ev_midi As New Snd_seq_event_t
 +
  Dim err As Integer
 +
 +
  With ev_midi
 +
    .type = SND_SEQ_EVENT_CONTROLLER
 +
    .queue = que
 +
    .source_client = id
 +
    .source_port = s_port
 +
    .dest_client = dclient
 +
    .dest_port = dport
 +
    .channel = canale
 +
    .param = prm
 +
    .value = value
 +
  End With
 +
 +
  err = snd_seq_event_output_buffer(handle, ev_midi)
 +
  If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))
 +
 +
'''End'''
 +
 +
'''Public''' Procedure ProgramChange(canale As Byte, non_usato As Byte, value As Integer)
 +
 +
  Dim err As Integer
 +
  Dim ev_midi As New Snd_seq_event_t
 +
 +
  With ev_midi
 +
    .type = SND_SEQ_EVENT_PGMCHANGE
 +
    .queue = que
 +
    .source_client = id
 +
    .source_port = s_port
 +
    .dest_client = dclient
 +
    .dest_port = dport
 +
    .channel = canale
 +
    .value = value
 +
  End With
 +
 +
  err = snd_seq_event_output_buffer(handle, ev_midi)
 +
  If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))
 +
 +
'''End'''
 +
 +
'''Public''' Procedure NoteON(canale As Byte, nota As Byte, vel As Byte)
 +
 +
  Dim ev_midi As New Snd_seq_event_t
 +
  Dim err As Integer
 +
 +
  With ev_midi
 +
    .type = SND_SEQ_EVENT_NOTEON
 +
    .queue = que
 +
    .source_client = id
 +
    .source_port = s_port
 +
    .dest_client = dclient
 +
    .dest_port = dport
 +
    .channel = canale
 +
    .note = nota
 +
    .velocity = vel
 +
  End With
 +
 +
  err = snd_seq_event_output_buffer(handle, ev_midi)
 +
  If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))
 +
 +
'''End'''
 +
 +
'''Public''' Procedure NoteOFF(canale As Byte, nota As Byte, vel As Byte)
 +
 +
  Dim ev_midi As New Snd_seq_event_t
 +
  Dim err As Integer
 +
 +
  With ev_midi
 +
    .type = SND_SEQ_EVENT_NOTEOFF
 +
    .queue = que
 +
    .source_client = id
 +
    .source_port = s_port
 +
    .dest_client = dclient
 +
    .dest_port = dport
 +
    .channel = canale
 +
    .note = nota
 +
    .velocity = vel
 +
  End With
 +
 +
  err = snd_seq_event_output_buffer(handle, ev_midi)
 +
  If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))
 +
 +
'''End'''
 +
 +
'''Public''' Procedure Flush()
 +
 +
  Dim err As Integer
 +
 +
  err = snd_seq_drain_output(handle)
 +
  If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))
 +
 +
'''End'''
 +
 +
'''Public''' Procedure Chiude()
 +
 +
  snd_seq_close(handle)
 +
 +
'''End'''
 +
 +
 +
====2° esempio pratico====
 +
Di seguito un programma simile al precedente, nel quale si farà uso di uno ''Slider'' per agire sul Messaggio Midi ''Pitch Bend''.
 +
 +
Nella Classe principale avremo il seguente codice:
 +
Private Const id_dev As Integer = 128  <FONT Color=gray>' ''14 = midi out oppure  128 (solitamente) = softsynth''</font>
 +
Private Const p_dev As Integer = 0      <FONT Color=gray>' ''porta del Client destinatario dei dati Midi: solitamente 0''</font>
 +
Public alsa As CAlsa                    <FONT Color=gray>' ''Classe che incapsula le funzioni ALSA''</font>
 +
Private bo As Boolean
 +
 +
'''Public''' Sub Form_Open()
 +
 +
  With Slider1
 +
    .MinValue = -8192
 +
    .MaxValue = 8192
 +
  End With
 +
 +
<FONT Color=gray>' ''Crea ("istanzia") un Oggetto della Classe "CAlsa" per poterla usare:''</font>
 +
  With alsa = New CAlsa As "Alsa"
 +
<FONT Color=gray>' ''Apre ALSA e assegna un nome al Client:''</font>
 +
    Me.Title = "|| Client ALSA " & .AlsaIni("Programma di prova")
 +
<FONT Color=gray>' ''Sceglie la periferica (Softsynth) su cui suonare:''</font>
 +
    .ImpostaDispositivo(id_dev, p_dev)
 +
  End With
 +
 +
<FONT Color=gray>' ''Imposta il Volume gestendo l'Evento Midi "Control Change":''</font>
 +
  alsa.ControlChange(0, 7, 64)
 +
<FONT Color=gray>' ''Imposta il suono dello strumento musicale gestendo l'Evento Midi "Program Change":''</font>
 +
  alsa.ProgramChange(0, 0, 44)
 +
 +
'''End'''
 +
 +
'''Public''' Sub Form_KeyPress()
 +
 +
  If bo Then Return
 +
<FONT Color=gray>' ''Imposta la nota midi da eseguire gestendo l'Evento Midi "Note-ON":''</font>
 +
  alsa.NoteON(0, 64, 100)
 +
  alsa.Flush()
 +
  bo = True
 +
 +
'''End'''
 +
 +
'''Public''' Sub Form_KeyRelease()
 +
 +
<FONT Color=gray>' ''Imposta la nota midi da eseguire gestendo l'Evento Midi "Note-OFF":''</font>
 +
  alsa.NoteOFF(0, 64, 0)
 +
  alsa.Flush()
 +
  bo = False
 +
 +
'''End'''
 +
 +
'''Public''' Sub Slider1_Change()
 +
 +
  alsa.PitchBend(0, 0, Slider1.Value)
 +
  alsa.Flush()
 +
 +
'''End'''
 +
 +
'''Public''' Sub Form_Close()
 +
 +
  alsa.Chiude()
 +
 +
'''End'''
 +
 +
Nella Classe secondaria, chiamata "CAlsa", avremo il seguente codice:
 +
Private handle As Pointer
 +
Private id As Integer
 +
Private s_port As Integer
 +
Private que As Integer
 +
Private dclient As Byte
 +
Private dport As Byte
 +
 +
 +
Library "libasound:2.0.0"
 +
 +
'''Public''' Struct <Font Color= #B22222>snd_seq_event_t</font>
 +
  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, 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>
 +
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>' ''int snd_seq_client_id (snd_seq_t * seq)''
 
  <FONT Color=gray>' ''int snd_seq_client_id (snd_seq_t * seq)''
Riga 130: Riga 428:
 
  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_connect_to ( snd_seq_t * seq, int myport, int dest_client, int dest_port )''
+
  <FONT Color=gray>' ''int snd_seq_create_simple_port (snd_seq_t* seq, const char* name, unsigned int caps, unsigned int type)''
  ' ''Connect from the given receiver port in the current client to the given destination client:port.''</font>
+
  ' ''Creates a port with the given capability and type bits.''</font>
  Private Extern snd_seq_connect_to(seq As Pointer, myport As Integer, dest_client As Integer, dest_port As Integer) As Integer
+
  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_named_queue (snd_seq_t * seq, CONST char * name)''
+
  <FONT Color=gray>' ''int snd_seq_alloc_queue (snd_seq_t * seq)''
 
  ' ''Allocate a queue.''</font>
 
  ' ''Allocate a queue.''</font>
  Private Extern snd_seq_alloc_queue(seq As Pointer, name As String) As Integer
+
  Private Extern snd_seq_alloc_queue(seq As Pointer) As Integer
 
    
 
    
  <FONT Color=gray>' ''int snd_seq_event_output (snd_seq_t * seq, snd_seq_event_t *ev)''
+
  <FONT Color=gray>' ''int snd_seq_event_output_buffer (snd_seq_t * seq, snd_seq_event_t *ev)''
 
  ' ''Output an event.''</font>
 
  ' ''Output an event.''</font>
  Private Extern snd_seq_event_output(seq As Pointer, ev As EventoMidiAlsa) As Integer
+
  Private Extern snd_seq_event_output_buffer(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)''
Riga 155: Riga 453:
 
   
 
   
 
   
 
   
  '''Public''' Sub Main()
+
  '''Public''' Function AlsaIni(nome As String) As String
 
   
 
   
  Dim handle As Pointer
+
   Dim err As Integer
   Dim err, id, porta, que As Integer
 
  Dim ev_midi As New EventoMidiAlsa    <FONT Color=gray>' ''Dichiara la variabile del tipo della Struttura "EventoMidiAlsa"''</font>
 
 
   
 
   
   err = snd_seq_open(VarPtr(handle), "default", SND_SEQ_OPEN_DUPLEX, 0)
+
   err = snd_seq_open(VarPtr(handle), "default", SND_SEQ_OPEN_OUTPUT, 0)
   Print "Apertura del subsistema 'seq' di ALSA = "; err
+
   Print "Apertura del subsistema 'seq' di ALSA = "; IIf(err == 0, "corretta !", "errata !")
 
   If err < 0 Then error.RAISE("Errore: " & snd_strerror(err))
 
   If err < 0 Then error.RAISE("Errore: " & snd_strerror(err))
 
   
 
   
   snd_seq_set_client_name(handle, "Test")
+
   snd_seq_set_client_name(handle, nome)
 
   id = snd_seq_client_id(handle)
 
   id = snd_seq_client_id(handle)
 
   Print "Alsa ClientID = "; id
 
   Print "Alsa ClientID = "; id
 
   
 
   
   porta = snd_seq_create_simple_port(handle, "Uscita", 0, SND_SEQ_PORT_TYPE_MIDI_GENERIC + SND_SEQ_PORT_TYPE_APPLICATION)  
+
   s_port = snd_seq_create_simple_port(handle, "Uscita", SND_SEQ_PORT_CAP_READ, SND_SEQ_PORT_TYPE_MIDI_GENERIC + SND_SEQ_PORT_TYPE_APPLICATION)  
   Print "Porta del programma = "; porta
+
   Print "Porta del programma = "; s_port
   If porta < 0 Then error.Raise("Errore: " & snd_strerror(porta))
+
   If s_port < 0 Then error.Raise("Errore: " & snd_strerror(s_port))
 
   
 
   
   que = snd_seq_alloc_queue(handle, "queue")
+
   que = snd_seq_alloc_queue(handle)
 
   If que < 0 Then error.Raise("Errore: " & snd_strerror(que))
 
   If que < 0 Then error.Raise("Errore: " & snd_strerror(que))
 
   
 
   
<FONT Color=gray>' ''Assume che l'ID/porta dell'altro Client (Softsynth) sia 128:0''</font>
+
   Return CStr(id) & ":" & CStr(s_port)
   err = snd_seq_connect_to(handle, porta, 128, 0)
 
  If err < 0 Then error.Raise("Errore: " & snd_strerror(err))
 
 
   
 
   
  <FONT Color=gray>' '''Program Change'''</font>
+
  '''End'''
  ev_midi.type = SND_SEQ_EVENT_PGMCHANGE
 
  ev_midi.flag = 0
 
  ev_midi.tag = 0
 
  ev_midi.queue = que
 
  ev_midi.tick_o_tv_sec = 0
 
  ev_midi.source_id = id
 
  ev_midi.source_porta = porta
 
  ev_midi.dest_id = SND_SEQ_ADDRESS_SUBSCRIBERS
 
  ev_midi.dest_porta = SND_SEQ_ADDRESS_UNKNOWN
 
  ev_midi.channel = 0
 
  ev_midi.value = 44    <FONT Color=gray>' ''Imposta il suono dello strumento musicale da usare''</font>
 
 
   
 
   
  InviaEventoMidi(handle, ev_midi)
+
'''Public''' Procedure ImpostaDispositivo(client As Integer, port As Integer)
 
   
 
   
<FONT Color=gray>' '''Note ON'''</font>
+
   dclient = client
  ev_midi.type = SND_SEQ_EVENT_NOTEON
+
   dport = port
  ev_midi.flag = 0
 
  ev_midi.tag = 0
 
  ev_midi.queue = que
 
  ev_midi.tick_o_tv_sec = 0
 
   ev_midi.source_id = id
 
   ev_midi.source_porta = porta
 
  ev_midi.dest_id = SND_SEQ_ADDRESS_SUBSCRIBERS
 
  ev_midi.dest_porta = 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>
 
 
    
 
    
   InviaEventoMidi(handle, ev_midi)
+
'''End'''
 +
 +
'''Public''' Procedure ControlChange(canale As Byte, prm As Integer, value As Integer)
 +
 +
   Dim ev_midi As New Snd_seq_event_t
 +
  Dim err As Integer
 +
 +
  With ev_midi
 +
    .type = SND_SEQ_EVENT_CONTROLLER
 +
    .queue = que
 +
    .source_client = id
 +
    .source_port = s_port
 +
    .dest_client = dclient
 +
    .dest_port = dport
 +
    .channel = canale
 +
    .param = prm
 +
    .value = value
 +
  End With
 +
 +
  err = snd_seq_event_output_buffer(handle, ev_midi)
 +
  If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))
 +
 +
'''End'''
 +
 +
'''Public''' Procedure ProgramChange(canale As Byte, non_usato As Byte, value As Integer)
 +
 +
  Dim err As Integer
 +
  Dim ev_midi As New Snd_seq_event_t
 
   
 
   
<FONT Color=gray>' ''Imposta la durata dell'esecuzione della nota:''</font>
+
  With ev_midi
   Wait 3
+
    .type = SND_SEQ_EVENT_PGMCHANGE
 +
    .queue = que
 +
    .source_client = id
 +
    .source_port = s_port
 +
    .dest_client = dclient
 +
    .dest_port = dport
 +
    .channel = canale
 +
    .value = value
 +
   End With
 
   
 
   
  <FONT Color=gray>' '''Note OFF'''</font>
+
  err = snd_seq_event_output_buffer(handle, ev_midi)
   ev_midi.type = SND_SEQ_EVENT_NOTEOFF
+
  If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))
   ev_midi.flag = 0
+
   
   ev_midi.tag = 0
+
'''End'''
  ev_midi.queue = que
+
  ev_midi.tick_o_tv_sec = 0
+
'''Public''' Procedure NoteON(canale As Byte, nota As Byte, vel As Byte)
  ev_midi.source_id = id
+
  ev_midi.source_porta = porta
+
   Dim ev_midi As New Snd_seq_event_t
  ev_midi.dest_id = SND_SEQ_ADDRESS_SUBSCRIBERS
+
   Dim err As Integer
  ev_midi.dest_porta = SND_SEQ_ADDRESS_UNKNOWN
+
  ev_midi.channel = 0
+
   With ev_midi
   ev_midi.note = 64      <FONT Color=gray>' ''Imposta una nota Midi da eseguire''</font>
+
    .type = SND_SEQ_EVENT_NOTEON
   ev_midi.velocity = 0   <FONT Color=gray>' ''Imposta la velocità di tocco''</font>
+
    .queue = que
 
+
    .source_client = id
  InviaEventoMidi(handle, ev_midi)
+
    .source_port = s_port
 +
    .dest_client = dclient
 +
    .dest_port = dport
 +
    .channel = canale
 +
    .note = nota
 +
    .velocity = vel
 +
  End With
 +
 +
   err = snd_seq_event_output_buffer(handle, ev_midi)
 +
  If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))
 +
 +
'''End'''
 +
 +
'''Public''' Procedure NoteOFF(canale As Byte, nota As Byte, vel As Byte)
 +
 +
  Dim ev_midi As New Snd_seq_event_t
 +
  Dim err As Integer
 +
 +
   With ev_midi
 +
    .type = SND_SEQ_EVENT_NOTEOFF
 +
    .queue = que
 +
    .source_client = id
 +
    .source_port = s_port
 +
    .dest_client = dclient
 +
    .dest_port = dport
 +
    .channel = canale
 +
    .note = nota
 +
    .velocity = vel
 +
  End With
 +
 +
  err = snd_seq_event_output_buffer(handle, ev_midi)
 +
  If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))
 +
 +
'''End'''
 +
 +
'''Public''' Procedure Flush()
 +
 +
  Dim err As Integer
 
   
 
   
<FONT Color=gray>' ''Va in Chiusura:''</font>
+
  err = snd_seq_drain_output(handle)
   snd_seq_close(handle)
+
   If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))
 
   
 
   
 
  '''End'''
 
  '''End'''
 
   
 
   
  '''Private''' Procedure InviaEventoMidi(p As Pointer, ev As EventoMidiAlsa)
+
  '''Public''' Procedure PitchBend(canale As Byte, prm As Integer, val As Integer)
 
   
 
   
 +
  Dim ev_midi As New Snd_seq_event_t
 
   Dim err As Integer
 
   Dim err As Integer
 
   
 
   
   err = snd_seq_event_output(p, ev)
+
  With ev_midi
   If err < 0 Then error.Raise("Errore: " & snd_strerror(err))
+
    .type = SND_SEQ_EVENT_PITCHBEND
 +
    .queue = que
 +
    .source_client = id
 +
    .source_port = s_port
 +
    .dest_client = dclient
 +
    .dest_port = dport
 +
    .channel = canale
 +
    .param = prm
 +
    .value = val
 +
  End With
 +
 +
   err = snd_seq_event_output_buffer(handle, ev_midi)
 +
   If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))
 +
 
 +
'''End'''
 +
 +
'''Public''' Procedure Stop()
 
   
 
   
   err = snd_seq_drain_output(p)
+
   snd_seq_close(handle)  
  If err < 0 Then error.Raise("Errore: " & snd_strerror(err))
 
 
   
 
   
 
  '''End'''
 
  '''End'''
  
===Altro esempio generico===
+
 
In questo altro esempio saranno suonate 8 note della scala musicale di Do maggiore.
+
===Esempi con invio di più Eventi Midi ALSA temporizzati in sequenza===
<BR>Dal punto di vista del Midi, quindi, dovremo inviare Messaggi Midi "Note On", per attivare l'esecuzione di una nota Midi musicale, e Messaggi Midi "Note Off" per fermare l'esecuzione della nota. Pre-invieremo anche un Messaggio Midi "Program Change" per impostare il suono dello strumento selezionato sullo standard General Midi.
+
Vedremo di seguito due semplici esempi sull'uso del membro ''flags'' della Struttura [[Alsa_e_Gambas_-_Dai_Messaggi_Midi_standard_all'Evento_Midi_di_ALSA#Confronto_fra_la_rappresentazione_in_C_e_quella_in_Gambas_della_Struttura_di_ALSA_.22seq_event_t.22|snd_seq_event_t]] e della [[ALSA_e_Gambas_-_La_marcatura_temporale_degli_eventi:_il_Timestamp|temporizzazione in "tick time" e in "real time"]].
<BR>Sappiamo che ALSA accetta eventi Midi solo se inviati nella modalità rigida rappresentata dalla Struttura "snd_seq_event_t". Pertanto, gli eventi Midi di ALSA, che corrispondono ai Messaggi Midi, devono essere rigidamente e correttamente configurati e inviati ad ALSA attraverso questa Struttura. Nel codice stabiliremo alcuni membri di quella Struttura in modo generico e valido per i tre Midi Message che dovremo inviare.
+
 
<BR>Prima di avviare il programma, è necessario avviare il Softsyhth "Qsynth" per consentire l'ascolto del suono. Il codice rileverà automaticamente il programma "Qsynth" (che è un altro "Client" ALSA) e si connetterà ad esso. Speciali routine si occuperanno dei dati relativi allo specifico Midi Message, che verranno inviati al sottosistema "Seq" di ALSA.
+
====Invio Eventi Midi con temporizzazione in "tick time"====
  Private sndseq As Pointer
+
Ricordiamo che la temporizzazione in ALSA nella modalità in "<B>tick</b>" Midi è attivata assegnando al membro ''flags'' della [[Alsa_e_Gambas_-_Dai_Messaggi_Midi_standard_all'Evento_Midi_di_ALSA#Confronto_fra_la_rappresentazione_in_C_e_quella_in_Gambas_della_Struttura_di_ALSA_.22seq_event_t.22|snd_seq_event_t]] la Costante "SND_SEQ_TIME_STAMP_TICK" di ALSA, mentre i valori temporali sono espressi appunto in "tick" Midi al membro ''tick'' del membro ''snd_seq_timestamp_t time'' della predetta Struttura.
      
+
 
Library "libasound:2"
+
Mostriamo un esempio pratico in ambiente grafico, nel quale cliccando su un "ToggleButton" si avvierà l'invio ad ALSA degli ''Eventi Midi'' e il loro processamento. Essi saranno eseguiti secondo la propria ''temporizzazione'' impostata nella Struttura [[Alsa_e_Gambas_-_Dai_Messaggi_Midi_standard_all'Evento_Midi_di_ALSA#Confronto_fra_la_rappresentazione_in_C_e_quella_in_Gambas_della_Struttura_di_ALSA_.22seq_event_t.22|snd_seq_event_t]] costitutiva.
 
+
<BR>Dunque avremo la Classe principale "FMain.Class":
  Public Struct snd_seq_event_t
+
Private Const id_dev As Integer = 128  <FONT Color=gray>' ''14 = midi out oppure  128 (solitamente) = softsynth''</font>
  type As Byte
+
  Private Const p_dev As Integer = 0     <FONT Color=gray>' ''porta del Client destinatario dei dati Midi: solitamente 0''</font>
  flags As Byte
+
  Public alsa As CAlsa                    <FONT Color=gray>' ''Classe che incapsula le funzioni ALSA''</font>
  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
 
 
    
 
    
 +
'''Public''' Sub Form_Open()
 +
 +
<FONT Color=gray>' ''Crea ("istanzia") un Oggetto della Classe "CAlsa" per poterla usare:''</font>
 +
  With alsa = New CAlsa As "Alsa"
 +
<FONT Color=gray>' ''Apre ALSA e assegna un nome al Client:''</font>
 +
    Me.Title = "|| Client ALSA " & .AlsaIni("Programma di prova")
 +
<FONT Color=gray>' ''Sceglie la periferica (Softsynth) su cui suonare:''</font>
 +
    .ImpostaDispositivo(id_dev, p_dev)
 +
  End With
 +
 +
'''End'''
 +
 +
'''Public''' Sub ToggleButton1_Click()
 +
 +
If ToggleButton1.Value Then
 +
    With alsa
 +
<FONT Color=gray>' ''Avvia la "Coda" degli Eventi Midi ALSA:''</font>
 +
      .AvvioCoda()
 +
<FONT Color=gray>' ''Imposta il Volume gestendo l'Evento Midi "Control Change":''</font>
 +
      .ControlChange(0, 0, 0, 7, 50)
 +
<FONT Color=gray>' ''Imposta il suono dello strumento musicale gestendo l'Evento Midi "Program Change":''</font>
 +
      .ProgramChange(0, 0, 0, 0, 44)
 +
<FONT Color=gray>' ''Imposta la prima nota Midi da eseguire:''</font>
 +
      .NoteON(0, 0, 0, 64, 100)
 +
<FONT Color=gray>' ''Imposta la nota Midi da silenziare dopo 96 tick dall'inizio della "Coda":''</font>
 +
      .NoteOFF(0, <FONT Color=#B222222>96</font>, 0, 64, 0)
 +
 +
<FONT Color=gray>' ''Imposta il suono di un altro strumento musicale dopo 150 tick dall'inizio della "Coda" (quindi crea una pausa):''</font>
 +
      .ProgramChange(0, <FONT Color=#B222222>182</font>, 0, 0, 18)
 +
<FONT Color=gray>' ''Imposta la nota Midi da eseguire dopo 182 tick dall'inizio della "Coda":''</font>
 +
      .NoteON(0, <FONT Color=#B222222>182</font>, 0, 66, 100)
 +
<FONT Color=gray>' ''Imposta la nota Midi da silenziare dopo 300 tick dall'inizio della "Coda":''</font>
 +
      .NoteOFF(0, <FONT Color=#B222222>300</font>, 0, 66, 0)
 +
 +
<FONT Color=gray>' ''Imposta il suono di un altro strumento musicale dopo 300 tick dall'inizio della "Coda":''</font>
 +
      .ProgramChange(0, <FONT Color=#B222222>300</font>, 0, 0, 18)
 +
<FONT Color=gray>' ''Imposta la nota Midi da eseguire dopo 300 tick dall'inizio della "Coda":''</font>
 +
      .NoteON(0, <FONT Color=#B222222>300</font>, 0, 68, 100)
 +
<FONT Color=gray>' ''Imposta la nota Midi da silenziare dopo 500 tick dall'inizio della "Coda":''</font>
 +
      .NoteOFF(0, <FONT Color=#B222222>500</font>, 0, 68, 0)
 +
<FONT Color=gray>' ''Dispone infine l'invio di tutti gli Eventi Midi bufferizzati nella "Coda":''</font>
 +
      .Flush()
 +
    End With
 +
  Else
 +
    alsa.StopCoda()
 +
    Me.Close
 +
  Endif
 +
 +
'''End'''
 +
Nella Classe secondaria, chiamata "CAlsa", vi sarà invece il seguente codice:
 +
Private handle As Pointer
 +
Private id As Integer
 +
Private s_port As Integer
 +
Private que As Integer
 +
Private dclient As Byte
 +
Private dport As Byte
 +
 +
 +
Library "libasound:2.0.0"
 +
 +
'''Public''' Struct <Font Color= #B22222>snd_seq_event_t</font>
 +
  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_OPEN_OUTPUT As Integer = 1
  Private Const SND_SEQ_PORT_CAP_READ As Integer = 1 <FONT Color=gray>' ''La porta di questo programma Client di ALSA può essere "letta" dal un altro Client esterno di ALSA''</font>
+
  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,
<FONT Color=gray>' ''Numerazione per specificare i Messaggi Midi:''</font>
+
              SND_SEQ_EVENT_CONTROLLER = 10, SND_SEQ_EVENT_PGMCHANGE, SND_SEQ_EVENT_CHANPRESS,
  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_PITCHBEND, SND_SEQ_EVENT_START = 30, SND_SEQ_EVENT_STOP = 32
 
+
  <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 **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>' ''const char * snd_strerror (int errnum)''
+
  <FONT Color=gray>' ''int snd_seq_set_client_name (snd_seq_t* seq, const char* name)''
  ' ''Returns the message for an error code.''</font>
+
  ' ''Set client name.''</font>
  Private Extern snd_strerror(err As Integer) As String
+
  Private Extern snd_seq_set_client_name(seq As Pointer, name As String) 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_create_simple_port (snd_seq_t* seq, const char* name, unsigned int caps, unsigned int type)''
  ' ''Create a port - simple version.''</font>
+
  ' ''Creates a port with the given capability and type bits.''</font>
 
  Private Extern snd_seq_create_simple_port(seq As Pointer, name As String, caps As Integer, type As Integer) As Integer
 
  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)''
+
  <FONT Color=gray>' ''int snd_seq_alloc_queue (snd_seq_t * seq)''
 
  ' ''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) 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_control_queue(snd_seq_t * seq, int q, int type, int value, snd_seq_event_t * ev)''
  ' ''Simple subscription (w/o exclusive & time conversion).''</font>
+
  ' ''Queue controls - start/stop/continue.''</font>
  Private Extern snd_seq_connect_to(seq As Pointer, my_port As Integer, dest_client As Integer, dest_port As Integer) As Integer
+
  Private Extern snd_seq_control_queue(seq As Pointer, q As Integer, type As Integer, value As Integer, ev As Pointer) As Integer
 +
 
 +
<FONT Color=gray>' ''int snd_seq_event_output_buffer (snd_seq_t * seq, snd_seq_event_t *ev)''
 +
' ''Output an event.''</font>
 +
Private Extern snd_seq_event_output_buffer(seq As Pointer, ev As Snd_seq_event_t) 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_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)''
+
<FONT Color=gray>' ''const char * snd_strerror (int errnum)''
 +
' ''Returns the message for an error code.''</font>
 +
Private Extern snd_strerror(errnum As Integer) As String
 +
 +
  <FONT Color=gray>' ''int snd_seq_close (snd_seq_t * seq)''
 
  ' ''Close the sequencer.''</font>
 
  ' ''Close the sequencer.''</font>
  Private Extern snd_seq_close(handle As Pointer) As Integer
+
  Private Extern snd_seq_close(seq As Pointer) As Integer
 +
 +
 +
'''Public''' Function AlsaIni(nome As String) As String
 +
 +
  Dim err As Integer
 +
 +
  err = snd_seq_open(VarPtr(handle), "default", SND_SEQ_OPEN_OUTPUT, 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, nome)
 +
  id = snd_seq_client_id(handle)
 +
  Print "Alsa ClientID = "; id
 +
 +
  s_port = snd_seq_create_simple_port(handle, "Uscita", SND_SEQ_PORT_CAP_READ, SND_SEQ_PORT_TYPE_MIDI_GENERIC + SND_SEQ_PORT_TYPE_APPLICATION)
 +
  Print "Porta del programma = "; s_port
 +
  If s_port < 0 Then error.Raise("Errore: " & snd_strerror(s_port))
 +
 +
  que = snd_seq_alloc_queue(handle)
 +
  If que < 0 Then error.Raise("Errore: " & snd_strerror(que))
 +
 +
  Return CStr(id) & ":" & CStr(s_port)
 +
 +
'''End'''
 +
 +
'''Public''' Procedure ImpostaDispositivo(client As Integer, port As Integer)
 +
 +
  dclient = client
 +
  dport = port
 
    
 
    
 
+
'''End'''
  '''Public''' Sub Main()
+
   
+
  '''Public''' Procedure AvvioCoda()
   Dim note As Byte[] = [60, 62, 64, 65, 67, 69, 71, 72]    <FONT Color=gray>' ''Le 8 note Midi da eseguire''</font>
+
  Dim source_dest As New Byte[4]
+
   Dim err As Integer
  Dim n As Byte
+
   Dim ev As New Snd_seq_event_t
+
  err = snd_seq_control_queue(handle, que, SND_SEQ_EVENT_START, 0, 0)
   
+
  If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))
<FONT Color=gray>' ''Crea il Client di ALSA:''</font>
+
   CreaClient(source_dest)
+
'''End'''
   
+
  <FONT Color=gray>' ''Imposta l'Evento Midi di ALSA con alcuni valori:''</font>
+
'''Public''' Procedure ControlChange(flg As Byte, tick As Integer, canale As Byte, prm As Integer, value As Integer)
   With ev
+
     .flags = 0
+
   Dim ev_midi As New Snd_seq_event_t
     .tag = 0
+
   Dim err As Integer
     .queue = 0
+
   
     .tick_o_tv_sec = 0
+
   With ev_midi
     .source_client = source_dest[0]
+
     .type = SND_SEQ_EVENT_CONTROLLER
     .source_port = source_dest[1]
+
     .flags = flg
     .dest_client = source_dest[2]
+
     .tick_o_tv_sec = tick
     .dest_port = source_dest[3]
+
     .queue = que
     .channel = 0
+
     .source_client = id
 +
     .source_port = s_port
 +
     .dest_client = dclient
 +
     .dest_port = dport
 +
     .channel = canale
 +
    .param = prm
 +
    .value = value
 
   End With
 
   End With
   
+
   
  <FONT Color=gray>' ''Invoca la sotto-procedura per inviare ad ALSA il Messaggio-Midi "Program Change", pecificando nel secondo argomento il numero identificativo GM dello strumento musicale da utilizzare:''</font>
+
   err = snd_seq_event_output_buffer(handle, ev_midi)
   program_change(ev, 48)
+
   If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))
   
+
   For n = 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[n])
 
<FONT Color=gray>' ''Imposta la durata del suono della nota musicale:''</font>
 
    Wait 1
 
<FONT Color=gray>' ''Invoca la sotto-procedura per inviare ad ALSA il Messaggio-MIDI "Note OFF":''</font>
 
    Note_Off(ev, note[n])
 
  Next
 
   
 
  snd_seq_close(sndseq)
 
 
 
 
  '''End'''
 
  '''End'''
 
   
 
   
  '''Private''' Function CreaClient(srcdst As Byte[])
+
  '''Public''' Procedure ProgramChange(flg As Byte, tick As Integer, canale As Byte, non_usato As Byte, value As Integer)
 
   
 
   
   Dim rit As Integer
+
   Dim err As Integer
   
+
   Dim ev_midi As New Snd_seq_event_t
   rit = snd_seq_open(VarPtr(sndseq), "default", SND_SEQ_OPEN_OUTPUT, 0)
 
  If rit < 0 Then Error.Raise("Impossibile aprire il sub-sistema 'seq' di ALSA: " & snd_strerror(rit))
 
 
 
  srcdst[0] = snd_seq_client_id(sndseq)
 
   
 
  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)
 
  If srcdst[1] < 0 Then Error.Raise("Impossibile creare la porta di Uscita dei dati ALSA !")
 
 
 
  Fluidsynth(srcdst)
 
   
 
<FONT Color=gray>' ''Connette questo Client-Gambas a un altro Client (per esempio "QSynth"):''</font>
 
  rit = snd_seq_connect_to(sndseq, srcdst[1], srcdst[2], srcdst[3])
 
  If rit < 0 Then Error.Raise("Impossivbile 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_midi
 
 
   With ev_prch
 
<FONT Color=gray>' ''Specifica che è un Messaggio Midi "Program Change":''</font>
 
 
     .type = SND_SEQ_EVENT_PGMCHANGE
 
     .type = SND_SEQ_EVENT_PGMCHANGE
<FONT Color=gray>' ''Assegna il valore dello strumento musicale secondo lo standard "General Midi":''</font>
+
    .flags = flg
     .value = strum
+
    .tick_o_tv_sec = tick
 +
    .queue = que
 +
    .source_client = id
 +
    .source_port = s_port
 +
    .dest_client = dclient
 +
    .dest_port = dport
 +
    .channel = canale
 +
     .value = value
 
   End With
 
   End With
     
+
   InviaEvento(ev_prch)
+
   err = snd_seq_event_output_buffer(handle, ev_midi)
   
+
  If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))
 +
 
  '''End'''
 
  '''End'''
 
   
 
   
  '''Private''' Procedure Note_On(ev_nton As Snd_seq_event_t, nota As Byte)
+
  '''Public''' Procedure NoteON(flg As Byte, tick As Integer, canale As Byte, nota As Byte, vel As Byte)
 +
 +
  Dim ev_midi As New Snd_seq_event_t
 +
  Dim err As Integer
 
   
 
   
   With ev_nton
+
   With ev_midi
<FONT Color=gray>' ''Specifica che è un Messaggio Midi "Note ON":''</font>
 
 
     .type = SND_SEQ_EVENT_NOTEON
 
     .type = SND_SEQ_EVENT_NOTEON
<FONT Color=gray>' ''Specifica il numero della nota MIDI da eseguire:''</font>
+
    .flags = flg
 +
    .tick_o_tv_sec = tick
 +
    .queue = que
 +
    .source_client = id
 +
    .source_port = s_port
 +
    .dest_client = dclient
 +
    .dest_port = dport
 +
    .channel = canale
 
     .note = nota
 
     .note = nota
<FONT Color=gray>' ''Specifica il valore della "Velocity":''</font>
+
     .velocity = vel
     .velocity = 64
 
 
   End With
 
   End With
    
+
   InviaEvento(ev_nton)
+
   err = snd_seq_event_output_buffer(handle, ev_midi)
 
+
   If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))
 +
 
  '''End'''
 
  '''End'''
 
   
 
   
  '''Private''' Procedure Note_Off(ev_ntoff As Snd_seq_event_t, nota As Byte)
+
  '''Public''' Procedure NoteOFF(flg As Byte, tick As Integer, canale As Byte, nota As Byte, vel As Byte)
 +
 +
  Dim ev_midi As New Snd_seq_event_t
 +
  Dim err As Integer
 
   
 
   
   With ev_ntoff
+
   With ev_midi
<FONT Color=gray>' ''Specifica che è un Messaggio Midi "Note OFF":''</font>
 
 
     .type = SND_SEQ_EVENT_NOTEOFF
 
     .type = SND_SEQ_EVENT_NOTEOFF
<FONT Color=gray>' ''Specifica il numero della nota MIDI da eseguire:''</font>
+
    .flags = flg
 +
    .tick_o_tv_sec = tick
 +
    .queue = que
 +
    .source_client = id
 +
    .source_port = s_port
 +
    .dest_client = dclient
 +
    .dest_port = dport
 +
    .channel = canale
 
     .note = nota
 
     .note = nota
<FONT Color=gray>' ''Specifica il valore della "Velocity":''</font>
+
     .velocity = vel
     .velocity = 0
 
 
   End With
 
   End With
    
+
   InviaEvento(ev_ntoff)
+
   err = snd_seq_event_output_buffer(handle, ev_midi)
 
+
   If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))
 +
 
  '''End'''
 
  '''End'''
 
   
 
   
  '''Private''' Procedure InviaEvento(evento As Snd_seq_event_t)
+
  '''Public''' Procedure Flush()
 +
 +
  Dim err As Integer
 +
 +
  err = snd_seq_drain_output(handle)
 +
  If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))
 
   
 
   
  snd_seq_event_output(sndseq, evento)
 
 
 
  snd_seq_drain_output(sndseq)
 
 
 
 
  '''End'''
 
  '''End'''
 
   
 
   
  '''Private''' Function Fluidsynth(sd As Byte[])
+
  '''Public''' Procedure StopCoda()
 
   
 
   
   Dim s As String
+
   Dim err As Integer
  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
 
 
    
 
    
 +
  err = snd_seq_control_queue(handle, que, SND_SEQ_EVENT_STOP, 0, 0)
 +
  If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))
 +
 +
  snd_seq_close(handle)
 +
 
  '''End'''
 
  '''End'''
  
Di seguito un programma simile al primo sopra già visto, ma ora proposto in versione grafica con l'uso di uno ''Slider'' per agire sul Messaggio Midi ''Pitch Bend'':
+
====Invio Eventi Midi con temporizzazione in "real time"====
   Library "libasound:2"
+
La temporizzazione in ALSA nella modalità in "<B>real time</b>" (temporizzazione in formato orario) è attivata assegnando al membro ''flags'' della [[Alsa_e_Gambas_-_Dai_Messaggi_Midi_standard_all'Evento_Midi_di_ALSA#Confronto_fra_la_rappresentazione_in_C_e_quella_in_Gambas_della_Struttura_di_ALSA_.22seq_event_t.22|snd_seq_event_t]] la Costante "SND_SEQ_TIME_STAMP_REAL" di ALSA.
 +
<BR>Ricordiamo che questa modalità di temporizzazione degli ''Eventi Midi'' di ALSA adotta il sistema di misura dell'orario standard (ore, minuti, secondi), assegnando il valore in "secondi" al membro ''tv_sec'' e in "nanosecondi"  al membro ''tv_nsec'' della sotto-Struttura ''snd_seq_real_time_t'' del membro ''time'' della Struttura principale [[Alsa_e_Gambas_-_Dai_Messaggi_Midi_standard_all'Evento_Midi_di_ALSA#Confronto_fra_la_rappresentazione_in_C_e_quella_in_Gambas_della_Struttura_di_ALSA_.22seq_event_t.22|snd_seq_event_t]] costitutiva.
 +
 
 +
Mostriamo un esempio identico al precedente, ma con impostazione della temporizzazione in ''Clock'' Midi (modalità "orario") espressa in secondi:
 +
<BR>Dunque avremo la Classe principale "FMain.Class":
 +
Private Const id_dev As Integer = 128  <FONT Color=gray>' ''14 = midi out oppure  128 (solitamente) = softsynth''</font>
 +
Private Const p_dev As Integer = 0      <FONT Color=gray>' ''porta del Client destinatario dei dati Midi: solitamente 0''</font>
 +
Public alsa As CAlsa                    <FONT Color=gray>' ''Classe che incapsula le funzioni ALSA''</font>
 +
 +
 
 +
'''Public''' Sub Form_Open()
 +
 +
<FONT Color=gray>' ''Crea ("istanzia") un Oggetto della Classe "CAlsa" per poterla usare:''</font>
 +
  With alsa = New CAlsa As "Alsa"
 +
<FONT Color=gray>' ''Apre ALSA e assegna un nome al Client:''</font>
 +
    Me.Title = "|| Client ALSA " & .AlsaIni("Programma di prova")
 +
<FONT Color=gray>' ''Sceglie la periferica (Softsynth) su cui suonare:''</font>
 +
    .ImpostaDispositivo(id_dev, p_dev)
 +
  End With
 +
 +
'''End'''
 +
 +
'''Public''' Sub ToggleButton1_Click()
 +
 +
If ToggleButton1.Value Then
 +
    With alsa
 +
<FONT Color=gray>' ''Avvia la "Coda" degli Eventi Midi ALSA:''</font>
 +
      .AvvioCoda()
 +
<FONT Color=gray>' ''Imposta il Volume gestendo l'Evento Midi "Control Change":''</font>
 +
      .ControlChange(<FONT Color=#B222222>1</font>, 0, 0, 7, 50)
 +
<FONT Color=gray>' ''Imposta il suono dello strumento musicale gestendo l'Evento Midi "Program Change":''</font>
 +
      .ProgramChange(<FONT Color=#B222222>1</font>, 0, 0, 0, 44)
 +
<FONT Color=gray>' ''Imposta la prima nota Midi da eseguire:''</font>
 +
      .NoteON(<FONT Color=#B222222>1</font>, 0, 0, 64, 100)
 +
<FONT Color=gray>' ''Imposta la nota Midi da silenziare dopo 1 secondo dall'inizio della "Coda":''</font>
 +
      .NoteOFF(<FONT Color=#B222222>1</font>, <FONT Color=#B222222>1</font>, 0, 64, 0)
 +
 +
<FONT Color=gray>' ''Imposta il suono di un altro strumento musicale dopo 2 secondi dall'inizio della "Coda" (quindi crea una pausa):''</font>
 +
      .ProgramChange(<FONT Color=#B222222>1</font>, <FONT Color=#B222222>2</font>, 0, 0, 18)
 +
<FONT Color=gray>' ''Imposta la nota Midi da eseguire dopo 2 secondi dall'inizio della "Coda":''</font>
 +
      .NoteON(<FONT Color=#B222222>1</font>, <FONT Color=#B222222>2</font>, 0, 66, 100)
 +
<FONT Color=gray>' ''Imposta la nota Midi da silenziare dopo 3 secondi dall'inizio della "Coda":''</font>
 +
      .NoteOFF(<FONT Color=#B222222>1</font>, <FONT Color=#B222222>3</font>, 0, 66, 0)
 +
 +
<FONT Color=gray>' ''Imposta il suono di un altro strumento musicale dopo 3 secondi dall'inizio della "Coda":''</font>
 +
      .ProgramChange(<FONT Color=#B222222>1</font>, <FONT Color=#B222222>3</font>, 0, 0, 18)
 +
<FONT Color=gray>' ''Imposta la nota Midi da eseguire dopo 3 secondi dall'inizio della "Coda":''</font>
 +
      .NoteON(<FONT Color=#B222222>1</font>, <FONT Color=#B222222>3</font>, 0, 68, 100)
 +
<FONT Color=gray>' ''Imposta la nota Midi da silenziare dopo 5 secondi dall'inizio della "Coda":''</font>
 +
      .NoteOFF(<FONT Color=#B222222>1</font>, <FONT Color=#B222222>5</font>, 0, 68, 0)
 +
<FONT Color=gray>' ''Dispone infine l'invio di tutti gli Eventi Midi bufferizzati nella "Coda":''</font>
 +
      .Flush()
 +
    End With
 +
  Else
 +
    alsa.StopCoda()
 +
    Me.Close
 +
   Endif
 +
 +
'''End'''
 +
Nella Classe secondaria, chiamata "CAlsa", vi sarà invece il seguente codice:
 +
Private handle As Pointer
 +
Private id As Integer
 +
Private s_port As Integer
 +
Private que As Integer
 +
Private dclient As Byte
 +
Private dport As Byte
 
   
 
   
  '''Public''' Struct <Font Color= #B22222>EventoMidiAlsa</font>
+
   type As Byte             <Font Color= #006400>' '' byte indice n° 0''</font>
+
Library "libasound:2.0.0"
   flag As Byte
+
 +
  '''Public''' Struct <Font Color= #B22222>snd_seq_event_t</font>
 +
   type As Byte
 +
   flags As Byte
 
   tag As Byte
 
   tag As Byte
 
   queue As Byte
 
   queue As Byte
 
   tick_o_tv_sec As Integer
 
   tick_o_tv_sec As Integer
 
   tv_nsec As Integer
 
   tv_nsec As Integer
   source_id As Byte
+
   source_client As Byte
   source_porta As Byte
+
   source_port As Byte
   dest_id As Byte
+
   dest_client As Byte
   dest_porta As Byte       <Font Color= #006400>' ''byte n° 15''</font>
+
   dest_port As Byte
    channel As Byte           <Font Color= #006400>' ''byte n° 16''</font>
+
  channel As Byte
    note As Byte             <Font Color= #006400>' ''byte n° 17''</font>
+
  note As Byte
    velocity As Byte         <Font Color= #006400>' ''byte n° 18''</font>
+
  velocity As Byte
    off_velocity As Byte     <Font Color= #006400>' ''byte n° 19''</font>
+
  off_velocity As Byte
      param As Integer         <Font Color= #006400>' ''byte n° 20''</font>
+
  param As Integer
      value As Integer         <Font Color= #006400>' ''byte n° 24''</font>
+
  value As Integer
 
  '''End''' Struct
 
  '''End''' Struct
 
   
 
   
  Private Const SND_SEQ_OPEN_DUPLEX As Integer = 3
+
  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_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 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,
 
  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
+
               SND_SEQ_EVENT_CONTROLLER = 10, SND_SEQ_EVENT_PGMCHANGE, SND_SEQ_EVENT_CHANPRESS,
 +
              SND_SEQ_EVENT_PITCHBEND, SND_SEQ_EVENT_START = 30, SND_SEQ_EVENT_STOP = 32
 
   
 
   
 
  <FONT Color=gray>' ''int snd_seq_open (snd_seq_t **seqp, const char * name, int streams, int mode)''
 
  <FONT Color=gray>' ''int snd_seq_open (snd_seq_t **seqp, const char * name, int streams, int mode)''
Riga 487: Riga 1 008:
 
  ' ''Set client name.''</font>
 
  ' ''Set client name.''</font>
 
  Private Extern snd_seq_set_client_name(seq As Pointer, name As String) As Integer
 
  Private Extern snd_seq_set_client_name(seq As Pointer, name As String) As Integer
 
<FONT Color=gray>' ''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.''</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_client_id (snd_seq_t * seq)''
 
  <FONT Color=gray>' ''int snd_seq_client_id (snd_seq_t * seq)''
Riga 496: Riga 1 013:
 
  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_connect_to ( snd_seq_t * seq, int myport, int dest_client, int dest_port )''
+
  <FONT Color=gray>' ''int snd_seq_create_simple_port (snd_seq_t* seq, const char* name, unsigned int caps, unsigned int type)''
  ' ''Connect from the given receiver port in the current client to the given destination client:port.''</font>
+
  ' ''Creates a port with the given capability and type bits.''</font>
  Private Extern snd_seq_connect_to(seq As Pointer, myport As Integer, dest_client As Integer, dest_port As Integer) As Integer
+
  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_named_queue (snd_seq_t * seq, CONST char * name)''
+
  <FONT Color=gray>' ''int snd_seq_alloc_queue (snd_seq_t * seq)''
 
  ' ''Allocate a queue.''</font>
 
  ' ''Allocate a queue.''</font>
  Private Extern snd_seq_alloc_queue(seq As Pointer, name As String) As Integer
+
  Private Extern snd_seq_alloc_queue(seq As Pointer) As Integer
 +
 +
<FONT Color=gray>' ''int snd_seq_control_queue(snd_seq_t * seq, int q, int type, int value, snd_seq_event_t * ev)''
 +
' ''Queue controls - start/stop/continue.''</font>
 +
Private Extern snd_seq_control_queue(seq As Pointer, q As Integer, type As Integer, value As Integer, ev As Pointer) As Integer
 
    
 
    
  <FONT Color=gray>' ''int snd_seq_event_output (snd_seq_t * seq, snd_seq_event_t *ev)''
+
  <FONT Color=gray>' ''int snd_seq_event_output_buffer (snd_seq_t * seq, snd_seq_event_t *ev)''
 
  ' ''Output an event.''</font>
 
  ' ''Output an event.''</font>
  Private Extern snd_seq_event_output(seq As Pointer, ev As EventoMidiAlsa) As Integer
+
  Private Extern snd_seq_event_output_buffer(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)''
Riga 521: Riga 1 042:
 
   
 
   
 
   
 
   
 +
'''Public''' Function AlsaIni(nome As String) As String
 +
 +
  Dim err As Integer
 +
 +
  err = snd_seq_open(VarPtr(handle), "default", SND_SEQ_OPEN_OUTPUT, 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, nome)
 +
  id = snd_seq_client_id(handle)
 +
  Print "Alsa ClientID = "; id
 +
 +
  s_port = snd_seq_create_simple_port(handle, "Uscita", SND_SEQ_PORT_CAP_READ, SND_SEQ_PORT_TYPE_MIDI_GENERIC + SND_SEQ_PORT_TYPE_APPLICATION)
 +
  Print "Porta del programma = "; s_port
 +
  If s_port < 0 Then error.Raise("Errore: " & snd_strerror(s_port))
 +
 +
  que = snd_seq_alloc_queue(handle)
 +
  If que < 0 Then error.Raise("Errore: " & snd_strerror(que))
 +
 +
  Return CStr(id) & ":" & CStr(s_port)
 +
 +
'''End'''
 +
 +
'''Public''' Procedure ImpostaDispositivo(client As Integer, port As Integer)
 +
 +
  dclient = client
 +
  dport = port
 +
 
 +
'''End'''
 +
 +
'''Public''' Procedure AvvioCoda()
 +
 +
  Dim err As Integer
 +
 +
  err = snd_seq_control_queue(handle, que, SND_SEQ_EVENT_START, 0, 0)
 +
  If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))
 +
 +
'''End'''
 +
 +
'''Public''' Procedure ControlChange(flg As Byte, sec As Integer, canale As Byte, prm As Integer, value As Integer)
 +
 +
  Dim ev_midi As New Snd_seq_event_t
 +
  Dim err As Integer
 +
 +
  With ev_midi
 +
    .type = SND_SEQ_EVENT_CONTROLLER
 +
    .flags = flg
 +
    .tick_o_<Font Color= #B22222>tv_sec</font> = sec
 +
    .queue = que
 +
    .source_client = id
 +
    .source_port = s_port
 +
    .dest_client = dclient
 +
    .dest_port = dport
 +
    .channel = canale
 +
    .param = prm
 +
    .value = value
 +
  End With
 +
 +
  err = snd_seq_event_output_buffer(handle, ev_midi)
 +
  If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))
 +
 +
'''End'''
 +
 +
'''Public''' Procedure ProgramChange(flg As Byte, sec As Integer, canale As Byte, non_usato As Byte, value As Integer)
 +
 +
  Dim err As Integer
 +
  Dim ev_midi As New Snd_seq_event_t
 +
 +
  With ev_midi
 +
    .type = SND_SEQ_EVENT_PGMCHANGE
 +
    .flags = flg
 +
    .tick_o_<Font Color= #B22222>tv_sec</font> = sec
 +
    .queue = que
 +
    .source_client = id
 +
    .source_port = s_port
 +
    .dest_client = dclient
 +
    .dest_port = dport
 +
    .channel = canale
 +
    .value = value
 +
  End With
 +
 +
  err = snd_seq_event_output_buffer(handle, ev_midi)
 +
  If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))
 +
 +
'''End'''
 +
 +
'''Public''' Procedure NoteON(flg As Byte, sec As Integer, canale As Byte, nota As Byte, vel As Byte)
 +
 +
  Dim ev_midi As New Snd_seq_event_t
 +
  Dim err As Integer
 +
 +
  With ev_midi
 +
    .type = SND_SEQ_EVENT_NOTEON
 +
    .flags = flg
 +
    .tick_o_<Font Color= #B22222>tv_sec</font> = sec
 +
    .queue = que
 +
    .source_client = id
 +
    .source_port = s_port
 +
    .dest_client = dclient
 +
    .dest_port = dport
 +
    .channel = canale
 +
    .note = nota
 +
    .velocity = vel
 +
  End With
 +
 +
  err = snd_seq_event_output_buffer(handle, ev_midi)
 +
  If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))
 +
 +
'''End'''
 +
 +
'''Public''' Procedure NoteOFF(flg As Byte, sec As Integer, canale As Byte, nota As Byte, vel As Byte)
 +
 +
  Dim ev_midi As New Snd_seq_event_t
 +
  Dim err As Integer
 +
 +
  With ev_midi
 +
    .type = SND_SEQ_EVENT_NOTEOFF
 +
    .flags = flg
 +
    .tick_o_<Font Color= #B22222>tv_sec</font> = sec
 +
    .queue = que
 +
    .source_client = id
 +
    .source_port = s_port
 +
    .dest_client = dclient
 +
    .dest_port = dport
 +
    .channel = canale
 +
    .note = nota
 +
    .velocity = vel
 +
  End With
 +
 +
  err = snd_seq_event_output_buffer(handle, ev_midi)
 +
  If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))
 +
 +
'''End'''
 +
 +
'''Public''' Procedure Flush()
 +
 +
  Dim err As Integer
 +
 +
  err = snd_seq_drain_output(handle)
 +
  If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))
 +
 +
'''End'''
 +
 +
'''Public''' Procedure StopCoda()
 +
 +
  Dim err As Integer
 +
 
 +
  err = snd_seq_control_queue(handle, que, SND_SEQ_EVENT_STOP, 0, 0)
 +
  If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))
 +
 +
  snd_seq_close(handle)
 +
 +
'''End'''
 +
 +
====Esempio in modalità temporizzazione in formato orario usando anche i nanosecondi====
 +
Di seguito ostriamo un semplice esempio simile al precedente, ma usando anche il membro ".tv_nsec" della Struttura ''snd_seq_event_t''.
 +
<BR>La Classe principale sarà la seguente:
 +
Private Const id_dev As Integer = 128  ' 14 = midi out oppure  128 (solitamente) = softsynth
 +
Private Const p_dev As Integer = 0      ' porta del Client destinatario dei dati Midi: solitamente 0
 +
Public alsa As CAlsa                    ' Classe che incapsula le funzioni ALSA
 +
 +
 +
'''Public''' Sub Form_Open()
 +
 +
  With alsa = New CAlsa As "Alsa"
 +
    Me.Title = "|| Client ALSA " & .AlsaIni("Programma di prova")
 +
    .ImpostaDispositivo(id_dev, p_dev)
 +
  End With
 +
 +
'''End'''
 +
 +
'''Public''' Sub ToggleButton1_Click()
 +
 +
  Dim note As Byte[] = [60, 62, 64, 65, 67, 69, 71, 72]
 +
  Dim b As Byte
 +
  Dim ns As Integer
 +
 +
  If ToggleButton1.Value Then
 +
    With alsa
 +
      .AvvioCoda()
 +
      .ControlChange(1, 0, 7, 50)
 +
      .ProgramChange(1, 0, 44)
 +
      For b = 0 To note.Max
 +
        If b == 3 Then ns = 750000000
 +
<FONT Color=gray>' ''Imposta la nota Midi da eseguire.''
 +
' ''Aggiunge 750000000 nanosecondi.''</font>
 +
        .NoteON(1, b, ns, note[b], 100)
 +
        ns = 0
 +
<FONT Color=gray>' ''Imposta la nota Midi da silenziare dall'inizio della "Coda".''</font>
 +
' ''Alla terza nota aggiunge 750000000 nanosecondi.''</font>
 +
        If b == 2 Then ns = 750000000
 +
        .NoteOFF(1, b + 1, ns, note[b], 0)
 +
        ns = 0
 +
      Next
 +
      .Flush()
 +
    End With
 +
  Else
 +
    alsa.StopCoda()
 +
    Me.Close
 +
  Endif
 +
 +
'''End'''
 +
La Classe secondaria per la gestione delle risorse di ALSA:
 
  Private handle As Pointer
 
  Private handle As Pointer
Private ev_midi As New EventoMidiAlsa    <FONT Color=gray>' ''Dichiara la variabile del tipo della Struttura "EventoMidiAlsa"''</font>
 
 
  Private id As Integer
 
  Private id As Integer
  Private porta As Integer
+
  Private s_port As Integer
 
  Private que As Integer
 
  Private que As Integer
 +
Private dclient As Byte
 +
Private dport As Byte
 +
 +
 +
Library "libasound:2.0.0"
 +
 +
'''Public''' Struct <Font Color= #B22222>snd_seq_event_t</font>
 +
  type As Byte
 +
  flags As Byte
 +
  tag As Byte
 +
  queue As Byte
 +
  tick_o_tv_sec As Integer
 +
  <Font Color= #B22222>tv_nsec</font> 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, SND_SEQ_EVENT_CHANPRESS,
 +
              SND_SEQ_EVENT_PITCHBEND, SND_SEQ_EVENT_START = 30, SND_SEQ_EVENT_STOP = 32
 
   
 
   
  '''Public''' Sub _new()
+
  <FONT Color=gray>' ''int snd_seq_open (snd_seq_t **seqp, const char * name, int streams, int mode)''
 +
' ''Open the ALSA sequencer.''</font>
 +
Private Extern snd_seq_open(seqp As Pointer, name As String, streams As Integer, mode As Integer) As Integer
 
   
 
   
   With Slider1
+
<FONT Color=gray>' ''int snd_seq_set_client_name (snd_seq_t* seq, const char* name)''
    .MinValue = -8192
+
' ''Set client name.''</font>
    .MaxValue = 8192
+
Private Extern snd_seq_set_client_name(seq As Pointer, name As String) As Integer
  End With
+
 +
<FONT Color=gray>' ''int snd_seq_client_id (snd_seq_t * seq)''
 +
' ''Get the client id.''</font>
 +
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)''
 +
' ''Creates a port with the given capability and type bits.''</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 * seq)''
 +
' ''Allocate a queue.''</font>
 +
Private Extern snd_seq_alloc_queue(seq As Pointer) As Integer
 +
 +
<FONT Color=gray>' ''int snd_seq_control_queue(snd_seq_t * seq, int q, int type, int value, snd_seq_event_t * ev)''
 +
' ''Queue controls - start/stop/continue.''</font>
 +
Private Extern snd_seq_control_queue(seq As Pointer, q As Integer, type As Integer, value As Integer, ev As Pointer) As Integer
 +
    
 +
<FONT Color=gray>' ''int snd_seq_event_output_buffer (snd_seq_t * seq, snd_seq_event_t *ev)''
 +
' ''Output an event.''</font>
 +
Private Extern snd_seq_event_output_buffer(seq As Pointer, ev As Snd_seq_event_t) As Integer
 +
 +
<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>' ''const char * snd_strerror (int errnum)''
 +
' ''Returns the message for an error code.''</font>
 +
Private Extern snd_strerror(errnum As Integer) As String
 +
 +
<FONT Color=gray>' ''int snd_seq_close (snd_seq_t * seq)''
 +
' ''Close the sequencer.''</font>
 +
Private Extern snd_seq_close(seq As Pointer) As Integer
 
   
 
   
'''End'''
 
 
   
 
   
  '''Public''' Sub Button1_Click()
+
  '''Public''' Function AlsaIni(nome As String) As String
 
   
 
   
 
   Dim err As Integer
 
   Dim err As Integer
 
   
 
   
   err = snd_seq_open(VarPtr(handle), "default", SND_SEQ_OPEN_DUPLEX, 0)
+
   err = snd_seq_open(VarPtr(handle), "default", SND_SEQ_OPEN_OUTPUT, 0)
   Print "Apertura del subsistema 'seq' di ALSA = "; err
+
   Print "Apertura del subsistema 'seq' di ALSA = "; IIf(err == 0, "corretta !", "errata !")
 
   If err < 0 Then error.RAISE("Errore: " & snd_strerror(err))
 
   If err < 0 Then error.RAISE("Errore: " & snd_strerror(err))
 
   
 
   
   snd_seq_set_client_name(handle, "Test")
+
   snd_seq_set_client_name(handle, nome)
 
   id = snd_seq_client_id(handle)
 
   id = snd_seq_client_id(handle)
 
   Print "Alsa ClientID = "; id
 
   Print "Alsa ClientID = "; id
 
   
 
   
   porta = snd_seq_create_simple_port(handle, "Uscita", 0, SND_SEQ_PORT_TYPE_MIDI_GENERIC + SND_SEQ_PORT_TYPE_APPLICATION)  
+
   s_port = snd_seq_create_simple_port(handle, "Uscita", SND_SEQ_PORT_CAP_READ, SND_SEQ_PORT_TYPE_MIDI_GENERIC + SND_SEQ_PORT_TYPE_APPLICATION)  
   Print "Porta del programma = "; porta
+
   Print "Porta del programma = "; s_port
   If porta < 0 Then error.Raise("Errore: " & snd_strerror(porta))
+
   If s_port < 0 Then error.Raise("Errore: " & snd_strerror(s_port))
 
   
 
   
   que = snd_seq_alloc_queue(handle, "queue")
+
   que = snd_seq_alloc_queue(handle)
 
   If que < 0 Then error.Raise("Errore: " & snd_strerror(que))
 
   If que < 0 Then error.Raise("Errore: " & snd_strerror(que))
 
   
 
   
  <FONT Color=gray>' ''Assume che l'ID/porta dell'altro Client (Softsynth) sia 128:0''</font>
+
  Return CStr(id) & ":" & CStr(s_port)
   err = snd_seq_connect_to(handle, porta, 128, 0)
+
   If err < 0 Then error.Raise("Errore: " & snd_strerror(err))
+
'''End'''
 +
 +
  '''Public''' Procedure ImpostaDispositivo(client As Integer, port As Integer)
 +
 +
  dclient = client
 +
  dport = port
 +
 
 +
'''End'''
 +
 +
'''Public''' Procedure AvvioCoda()
 +
 +
  Dim err As Integer
 +
 +
   err = snd_seq_control_queue(handle, que, SND_SEQ_EVENT_START, 0, 0)
 +
   If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))
 +
 +
'''End'''
 +
 +
'''Public''' Procedure ControlChange(flg As Byte, sec As Integer, prm As Integer, value As Integer)
 
   
 
   
   Slider1.Enabled = True
+
   Dim ev_midi As New Snd_seq_event_t
 +
  Dim err As Integer
 
   
 
   
<FONT Color=gray>' '''Program Change'''</font>
+
   With ev_midi
   ev_midi.type = SND_SEQ_EVENT_PGMCHANGE
+
    .type = SND_SEQ_EVENT_CONTROLLER
  ev_midi.flag = 0
+
    .flags = flg
  ev_midi.tag = 0
+
    .tick_o_tv_sec = sec
  ev_midi.queue = que
+
    .queue = que
  ev_midi.tick o tv_sec = 0
+
    .source_client = id
  ev_midi.source_id = id
+
    .source_port = s_port
  ev_midi.source_porta = porta
+
    .dest_client = dclient
  ev_midi.dest_id = SND_SEQ_ADDRESS_SUBSCRIBERS
+
    .dest_port = dport
  ev_midi.dest_porta = SND_SEQ_ADDRESS_UNKNOWN
+
    .param = prm
  ev_midi.channel = 0
+
    .value = value
   ev_midi.value = 44    <FONT Color=gray>' ''Imposta il suono dello strumento musicale da usare''</font>
+
   End With
 
   
 
   
   InviaEventoMidi()
+
   err = snd_seq_event_output_buffer(handle, ev_midi)
 +
  If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))
 
   
 
   
  <FONT Color=gray>' '''Note ON'''</font>
+
  '''End'''
  ev_midi.type = SND_SEQ_EVENT_NOTEON
 
  ev_midi.flag = 0
 
  ev_midi.tag = 0
 
  ev_midi.queue = que
 
  ev_midi.tick o tv_sec = 0
 
  ev_midi.source_id = id
 
  ev_midi.source_porta = porta
 
  ev_midi.dest_id = SND_SEQ_ADDRESS_SUBSCRIBERS
 
  ev_midi.dest_porta = 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>
 
 
 
  InviaEventoMidi()
 
 
   
 
   
  <FONT Color=gray>' ''Imposta la durata dell'esecuzione della nota:''</font>
+
  '''Public''' Procedure ProgramChange(flg As Byte, sec As Integer, value As Integer)
  Wait 10
 
 
   
 
   
  <FONT Color=gray>' '''Note OFF'''</font>
+
  Dim err As Integer
   ev_midi.type = SND_SEQ_EVENT_NOTEOFF
+
  Dim ev_midi As New Snd_seq_event_t
  ev_midi.flag = 0
+
   
  ev_midi.tag = 0
+
   With ev_midi
  ev_midi.queue = que
+
    .type = SND_SEQ_EVENT_PGMCHANGE
  ev_midi.tick o tv_sec = 0
+
    .flags = flg
  ev_midi.source_id = id
+
    .tick_o_tv_sec = sec
  ev_midi.source_porta = porta
+
    .queue = que
  ev_midi.dest_id = SND_SEQ_ADDRESS_SUBSCRIBERS
+
    .source_client = id
  ev_midi.dest_porta = SND_SEQ_ADDRESS_UNKNOWN
+
    .source_port = s_port
  ev_midi.channel = 0
+
    .dest_client = dclient
  ev_midi.note = 64      <FONT Color=gray>' ''Imposta una nota Midi da eseguire''</font>
+
    .dest_port = dport
  ev_midi.velocity = 0    <FONT Color=gray>' ''Imposta la velocità di tocco''</font>
+
    .value = value
 
+
   End With
   InviaEventoMidi()
 
 
   
 
   
<FONT Color=gray>' ''Va in Chiusura:''</font>
+
  err = snd_seq_event_output_buffer(handle, ev_midi)
   Slider1.Value = 0
+
   If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))
  Slider1.Enabled = False
 
  snd_seq_close(handle)
 
 
   
 
   
 
  '''End'''
 
  '''End'''
 
   
 
   
  '''Public''' Sub Slider1_Change()  <FONT Color=gray>' ''Agendo sullo "Slider" si modifica il "Pitch-Bend" con effetto del glissato''</font>
+
  '''Public''' Procedure NoteON(flg As Byte, sec As Integer, ns As Integer, nota As Byte, vel As Byte)
 +
 +
   Dim ev_midi As New Snd_seq_event_t
 +
  Dim err As Integer
 +
 +
  With ev_midi
 +
    .type = SND_SEQ_EVENT_NOTEON
 +
    .flags = flg
 +
    .tick_o_tv_sec = sec
 +
    <FONT Color=#B22222>.tv_nsec = ns</font>
 +
    .queue = que
 +
    .source_client = id
 +
    .source_port = s_port
 +
    .dest_client = dclient
 +
    .dest_port = dport
 +
    .note = nota
 +
    .velocity = vel
 +
  End With
 
   
 
   
<FONT Color=gray>' '''Pitch-Bend'''</font>
+
   err = snd_seq_event_output_buffer(handle, ev_midi)
   ev_midi.type = SND_SEQ_EVENT_PITCHBEND
+
   If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))
  ev_midi.flag = 0
 
   ev_midi.tag = 0
 
  ev_midi.queue = que
 
  ev_midi.tick o tv_sec = 0
 
  ev_midi.source_id = id
 
  ev_midi.source_porta = porta
 
  ev_midi.dest_id = SND_SEQ_ADDRESS_SUBSCRIBERS
 
  ev_midi.dest_porta = SND_SEQ_ADDRESS_UNKNOWN
 
  ev_midi.channel = 0
 
  ev_midi.param = 0
 
  ev_midi.value = Slider1.Value    <FONT Color=gray>' ''Imposta i valori del "Pitch-Bend"''</font>
 
 
 
  InviaEventoMidi()
 
 
   
 
   
 
  '''End'''
 
  '''End'''
 
   
 
   
  '''Private''' Procedure InviaEventoMidi()
+
  '''Public''' Procedure NoteOFF(flg As Byte, sec As Integer, ns As Integer, nota As Byte, vel As Byte)
 
   
 
   
 +
  Dim ev_midi As New Snd_seq_event_t
 
   Dim err As Integer
 
   Dim err As Integer
 
   
 
   
   err = snd_seq_event_output(handle, ev_Midi)
+
  With ev_midi
   If err < 0 Then error.Raise("Errore: " & snd_strerror(err))
+
    .type = SND_SEQ_EVENT_NOTEOFF
 +
    .flags = flg
 +
    .tick_o_tv_sec = sec
 +
    <FONT Color=#B22222>.tv_nsec = ns</font>
 +
    .queue = que
 +
    .source_client = id
 +
    .source_port = s_port
 +
    .dest_client = dclient
 +
    .dest_port = dport
 +
    .note = nota
 +
    .velocity = vel
 +
  End With
 +
 +
   err = snd_seq_event_output_buffer(handle, ev_midi)
 +
   If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))
 +
 +
'''End'''
 +
 +
'''Public''' Procedure Flush()
 +
 +
  Dim err As Integer
 
   
 
   
 
   err = snd_seq_drain_output(handle)
 
   err = snd_seq_drain_output(handle)
   If err < 0 Then error.Raise("Errore: " & snd_strerror(err))
+
   If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))
 +
 +
'''End'''
 +
 +
'''Public''' Procedure StopCoda()
 +
 +
  Dim err As Integer
 +
 +
  err = snd_seq_control_queue(handle, que, SND_SEQ_EVENT_STOP, 0, 0)
 +
  If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))
 +
 +
  snd_seq_close(handle)
 
   
 
   
 
  '''End'''
 
  '''End'''

Versione attuale delle 08:10, 25 feb 2022

Preambolo

In questa pagina verrà preso in considerazione l'uso delle Strutture per l'invio degli Eventi Midi ad ALSA.
Le Strutture, introdotte in Gambas con la versione 3.0, possono essere utilizzate per scrivere e gestire i messaggi Midi. Infatti dichiarando una Struttura (Struct) ci si riserva una quantità di memoria, definita dal tipo (byte, short, integer, etc.) dei valori dichiarati in ciascun membro della Struttura.
Dichiarando poi una variabile del tipo di quella Struttura, si ha automaticamente una zona di memoria utilizzabile.

Scrittura dei dati dei messaggi Midi nelle Strutture

Inseriremo una Struttura modello all'inizio della classe secondaria CAlsa.class. Porremo quindi, quali campi della struttura-tipo, i dati necessari per la definizione degli eventi Midi, come richiesti da ALSA, attribuendo a ciascun campo il tipo di dato desiderato da ALSA nel rispetto dell'ordine previsto dei dati, nonché del numero del byte al quale ciascun dato dovrà essere assegnato. Inseriremo non solo i dati comuni per tutti i messaggi Midi, ma anche quelli previsti per gli specifici messaggi:

In rosso sono rappresentati i membri che contengono dati identici per qualunque Evento Midi ALSA;
In verde sono rappresentati i membri che contengono dati relativi allo specifico Evento Midi ALSA.

struct snd_seq_event_t {                           Public Struct Snd_seq_event_t

snd_seq_event_type_t type                            type As Byte          byte di indice 0
unsigned char flags                                  flags As Byte
unsigned char tag                                    tag As Byte
unsigned char queue                                  queue As Byte
snd_seq_timestamp_t time
   ↳ snd_seq_tick_time_t / snd_seq_real_time_t    
        ↳ tick                ↳ tv_sec               tick_o_tv_sec As Integer
                              ↳ tv_nsec              tv_nsec As Integer
snd_seq_addr_t source
   ↳ unsigned char client                            source_client As Byte
   ↳ unsigned char port                              source_port As Byte
snd_seq_addr_t dest
   ↳ unsigned char client                            dest_client As Byte
   ↳ unsigned char port                              dest_port As Byte
snd_seq_ev_note_t note
   ↳ unsigned char channel                           channel As Byte       byte di indice 16
   ↳ unsigned char note                              note As Byte          byte di indice 17
   ↳ unsigned char velocity                          velocity As Byte      byte di indice 18
   ↳ unsigned char off_velocity                      off_velocity As Byte  byte di indice 19
unsigned int param                                   param As Integer      byte di indice 20
signed int value                                     value As Integer      byte di indice 24

}                                                  End Struct

I dati appartenenti agli specifici Messaggi Midi non vengono utilizzati tutti insieme da ciascun Evento Midi ALSA, ma solo alcuni di essi a seconda dello specifico Messaggio. Ovviamente nella Struttura-tipo essi saranno comunque posti e dichiarati, al fine di consentirne l'uso.

In Gambas si istanzierà una variabile del tipo della Struttura snd_seq_event_t, sopra descritta, con la quale gestire gli specifici Eventi Midi di ALSA, assegnando a ciascun membro (sia quelli comuni, sia quelli specifici di ogni tipo di Evento Midi ALSA) il relativo valore. Ovviamente si potrà evitare di richiamare i membri che eventualmente non devono essere lasciati con valore zero.


Riassunto delle fasi di creazione, allocamento e invio degli Eventi Midi ad ALSA

Prima di mostrare dei codici esemplificativi sulle varie modalità di invio degli'Eventi Midi al sistema ALSA, è opportuno ricordare quanto descritto nela capitolo precedente "ALSA e Gambas - Gestione dei Messaggi Midi standard".

Dunque possiamo elencare per sommicapi le seguenti fasi da seguire:
1) comporre l'Evento Midi ALSA nei membri essenziali della sua Struttura snd_seq_event_t, ed in particolare attribuire la temporizzazione mediante il "Timestamp" in "tick Midi" o in "real time";
2) allocare la Coda degli Eventi Midi ALSA con la funzione esterna snd_seq_alloc_queue();
3) avviare il controllo della Coda degli Eventi Midi ALSA con la funzione esterna snd_seq_control_queue(seq, q, type, value, 0, ev), passandole come 3° argomento la Costante di ALSA "SND_SEQ_EVENT_START";
4) accodare gli Eventi Midi nel buffer mediante la funzione esterna snd_seq_event_output_buffer();
5) inviare al sistema ALSA mediante la funzione esterna snd_seq_drain_output() l'intera Coda degli Eventi Midi, memorizzati nel buffer, affinche siano processati;
6) arrestare la Coda degli Eventi Midi ALSA richiamando la funzione esterna snd_seq_control_queue(seq, q, type, value, 0, ev), passandole però questa volta come suo 3° argomento la Costante di ALSA "SND_SEQ_EVENT_STOP".


Esempi di codice

1° esempio pratico

Nel seguente esempio in ambiente grafico avremo la Classe principale "FMain.Class" e una Classe secondaria, che incapsula le risorse di ALSA da utilizzare.
Dichiareremo una Struttura, chiamata snd_seq_event_t, che farà da modello alla particolare variabile di quel tipo di Struttura da usare per la organizzazione di ciascun Evento Midi ALSA.
In questo esempio sarà effettuato un invio diretto e immediato di un singolo Evento Midi per volta: cliccando su un qualunque tasto della tastiera del computer, si invierà un Evento Midi "NoteON"; rilasciando il tasto si invierà un Evento Midi "NoteOFF", per far cesssare l'esecuzione della nota Midi prima avviata.
Saranno gestiti comunque in apertura del programma anche gli Eventi Midi "Control Change" e "Program Change".
Riguardo al Timestamp di ogni Evento Midi ALSA inviato, trattandosi di invio singolo e diretto con esecuzione immediata è indifferente la modalità scelta: il membro time della predetta Struttura snd_seq_event_t relativo al Timestamp del singolo Evento Midi sarà posto a zero.

Dunque avremo la Classe principale "FMain.Class":

Private Const id_dev As Integer = 128   ' 14 = midi out oppure  128 (solitamente) = softsynth
Private Const p_dev As Integer = 0      ' porta del Client destinatario dei dati Midi: solitamente 0
Public alsa As CAlsa                    ' Classe che incapsula le funzioni ALSA
Private bo As Boolean

Public Sub Form_Open()

' Crea ("istanzia") un Oggetto della Classe "CAlsa" per poterla usare:
 With alsa = New CAlsa As "Alsa"
' Apre ALSA e assegna un nome al Client:
   Me.Title = "|| Client ALSA " & .AlsaIni("Programma di prova")
' Sceglie la periferica (Softsynth) su cui suonare:
   .ImpostaDispositivo(id_dev, p_dev)
 End With

' Imposta il Volume gestendo l'Evento Midi "Control Change":
 alsa.ControlChange(0, 7, 64)
' Imposta il suono dello strumento musicale gestendo l'Evento Midi "Program Change":
 alsa.ProgramChange(0, 0, 44)

End

Public Sub Form_KeyPress()

 If bo Then Return 
' Imposta la nota midi da eseguire gestendo l'Evento Midi "Note-ON":
 alsa.NoteON(0, 64, 100)
 alsa.Flush()
 bo = True

End 

Public Sub Form_KeyRelease()

' Imposta la nota midi da eseguire gestendo l'Evento Midi "Note-OFF":
 alsa.NoteOFF(0, 64, 0)
 alsa.Flush()
 bo = False

End

Public Sub Form_Close()

 alsa.Chiude()

End

Nella Classe secondaria, chiamata "CAlsa", avremo il seguente codice:

Private handle As Pointer
Private id As Integer
Private s_port As Integer
Private que As Integer
Private dclient As Byte
Private dport As Byte


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, 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_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)
' 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_alloc_queue (snd_seq_t * seq)
' Allocate a queue.
Private Extern snd_seq_alloc_queue(seq As Pointer) As Integer
 
' int snd_seq_event_output_buffer (snd_seq_t * seq, snd_seq_event_t *ev)
' Output an event.
Private Extern snd_seq_event_output_buffer(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 Function AlsaIni(nome As String) As String

 Dim err As Integer

 err = snd_seq_open(VarPtr(handle), "default", SND_SEQ_OPEN_OUTPUT, 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, nome)
 id = snd_seq_client_id(handle)
 Print "Alsa ClientID = "; id

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

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

 Return CStr(id) & ":" & CStr(s_port)

End

Public Procedure ImpostaDispositivo(client As Integer, port As Integer)

 dclient = client
 dport = port
 
End

Public Procedure ControlChange(canale As Byte, prm As Integer, value As Integer)

 Dim ev_midi As New Snd_seq_event_t
 Dim err As Integer

 With ev_midi
   .type = SND_SEQ_EVENT_CONTROLLER
   .queue = que
   .source_client = id
   .source_port = s_port
   .dest_client = dclient
   .dest_port = dport
   .channel = canale
   .param = prm
   .value = value
 End With

 err = snd_seq_event_output_buffer(handle, ev_midi)
 If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))

End

Public Procedure ProgramChange(canale As Byte, non_usato As Byte, value As Integer)

 Dim err As Integer
 Dim ev_midi As New Snd_seq_event_t

 With ev_midi
   .type = SND_SEQ_EVENT_PGMCHANGE
   .queue = que
   .source_client = id
   .source_port = s_port
   .dest_client = dclient
   .dest_port = dport
   .channel = canale
   .value = value
 End With

 err = snd_seq_event_output_buffer(handle, ev_midi)
 If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))

End

Public Procedure NoteON(canale As Byte, nota As Byte, vel As Byte)

 Dim ev_midi As New Snd_seq_event_t
 Dim err As Integer

 With ev_midi
   .type = SND_SEQ_EVENT_NOTEON
   .queue = que
   .source_client = id
   .source_port = s_port
   .dest_client = dclient
   .dest_port = dport
   .channel = canale
   .note = nota
   .velocity = vel
 End With

 err = snd_seq_event_output_buffer(handle, ev_midi)
 If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))

End

Public Procedure NoteOFF(canale As Byte, nota As Byte, vel As Byte)

 Dim ev_midi As New Snd_seq_event_t
 Dim err As Integer

 With ev_midi
   .type = SND_SEQ_EVENT_NOTEOFF
   .queue = que
   .source_client = id
   .source_port = s_port
   .dest_client = dclient
   .dest_port = dport
   .channel = canale
   .note = nota
   .velocity = vel
 End With

 err = snd_seq_event_output_buffer(handle, ev_midi)
 If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))

End

Public Procedure Flush()

 Dim err As Integer

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

End

Public Procedure Chiude()

 snd_seq_close(handle) 

End


2° esempio pratico

Di seguito un programma simile al precedente, nel quale si farà uso di uno Slider per agire sul Messaggio Midi Pitch Bend.

Nella Classe principale avremo il seguente codice:

Private Const id_dev As Integer = 128   ' 14 = midi out oppure  128 (solitamente) = softsynth
Private Const p_dev As Integer = 0       ' porta del Client destinatario dei dati Midi: solitamente 0
Public alsa As CAlsa                     ' Classe che incapsula le funzioni ALSA
Private bo As Boolean

Public Sub Form_Open()

 With Slider1
   .MinValue = -8192
   .MaxValue = 8192
 End With

' Crea ("istanzia") un Oggetto della Classe "CAlsa" per poterla usare:
 With alsa = New CAlsa As "Alsa"
' Apre ALSA e assegna un nome al Client:
   Me.Title = "|| Client ALSA " & .AlsaIni("Programma di prova")
' Sceglie la periferica (Softsynth) su cui suonare:
   .ImpostaDispositivo(id_dev, p_dev)
 End With

' Imposta il Volume gestendo l'Evento Midi "Control Change":
 alsa.ControlChange(0, 7, 64)
' Imposta il suono dello strumento musicale gestendo l'Evento Midi "Program Change":
 alsa.ProgramChange(0, 0, 44)

End

Public Sub Form_KeyPress()

 If bo Then Return 
' Imposta la nota midi da eseguire gestendo l'Evento Midi "Note-ON":
 alsa.NoteON(0, 64, 100)
 alsa.Flush()
 bo = True

End 

Public Sub Form_KeyRelease()

' Imposta la nota midi da eseguire gestendo l'Evento Midi "Note-OFF":
 alsa.NoteOFF(0, 64, 0)
 alsa.Flush()
 bo = False

End

Public Sub Slider1_Change()

 alsa.PitchBend(0, 0, Slider1.Value)
 alsa.Flush()

End

Public Sub Form_Close()

 alsa.Chiude()

End

Nella Classe secondaria, chiamata "CAlsa", avremo il seguente codice:

Private handle As Pointer
Private id As Integer
Private s_port As Integer
Private que As Integer
Private dclient As Byte
Private dport As Byte


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, 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_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)
' 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_alloc_queue (snd_seq_t * seq)
' Allocate a queue.
Private Extern snd_seq_alloc_queue(seq As Pointer) As Integer
 
' int snd_seq_event_output_buffer (snd_seq_t * seq, snd_seq_event_t *ev)
' Output an event.
Private Extern snd_seq_event_output_buffer(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 Function AlsaIni(nome As String) As String

 Dim err As Integer

 err = snd_seq_open(VarPtr(handle), "default", SND_SEQ_OPEN_OUTPUT, 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, nome)
 id = snd_seq_client_id(handle)
 Print "Alsa ClientID = "; id

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

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

 Return CStr(id) & ":" & CStr(s_port)

End

Public Procedure ImpostaDispositivo(client As Integer, port As Integer)

 dclient = client
 dport = port
 
End

Public Procedure ControlChange(canale As Byte, prm As Integer, value As Integer)

 Dim ev_midi As New Snd_seq_event_t
 Dim err As Integer

 With ev_midi
   .type = SND_SEQ_EVENT_CONTROLLER
   .queue = que
   .source_client = id
   .source_port = s_port
   .dest_client = dclient
   .dest_port = dport
   .channel = canale
   .param = prm
   .value = value
 End With

 err = snd_seq_event_output_buffer(handle, ev_midi)
 If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))

End

Public Procedure ProgramChange(canale As Byte, non_usato As Byte, value As Integer)

 Dim err As Integer
 Dim ev_midi As New Snd_seq_event_t

 With ev_midi
   .type = SND_SEQ_EVENT_PGMCHANGE
   .queue = que
   .source_client = id
   .source_port = s_port
   .dest_client = dclient
   .dest_port = dport
   .channel = canale
   .value = value
 End With

 err = snd_seq_event_output_buffer(handle, ev_midi)
 If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))

End

Public Procedure NoteON(canale As Byte, nota As Byte, vel As Byte)

 Dim ev_midi As New Snd_seq_event_t
 Dim err As Integer

 With ev_midi
   .type = SND_SEQ_EVENT_NOTEON
   .queue = que
   .source_client = id
   .source_port = s_port
   .dest_client = dclient
   .dest_port = dport
   .channel = canale
   .note = nota
   .velocity = vel
 End With

 err = snd_seq_event_output_buffer(handle, ev_midi)
 If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))

End

Public Procedure NoteOFF(canale As Byte, nota As Byte, vel As Byte)

 Dim ev_midi As New Snd_seq_event_t
 Dim err As Integer

 With ev_midi
   .type = SND_SEQ_EVENT_NOTEOFF
   .queue = que
   .source_client = id
   .source_port = s_port
   .dest_client = dclient
   .dest_port = dport
   .channel = canale
   .note = nota
   .velocity = vel
 End With

 err = snd_seq_event_output_buffer(handle, ev_midi)
 If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))

End

Public Procedure Flush()

 Dim err As Integer

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

End

Public Procedure PitchBend(canale As Byte, prm As Integer, val As Integer)

 Dim ev_midi As New Snd_seq_event_t
 Dim err As Integer

 With ev_midi
   .type = SND_SEQ_EVENT_PITCHBEND
   .queue = que
   .source_client = id
   .source_port = s_port
   .dest_client = dclient
   .dest_port = dport
   .channel = canale
   .param = prm
   .value = val
 End With

 err = snd_seq_event_output_buffer(handle, ev_midi)
 If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))
 
End

Public Procedure Stop()

 snd_seq_close(handle) 

End


Esempi con invio di più Eventi Midi ALSA temporizzati in sequenza

Vedremo di seguito due semplici esempi sull'uso del membro flags della Struttura snd_seq_event_t e della temporizzazione in "tick time" e in "real time".

Invio Eventi Midi con temporizzazione in "tick time"

Ricordiamo che la temporizzazione in ALSA nella modalità in "tick" Midi è attivata assegnando al membro flags della snd_seq_event_t la Costante "SND_SEQ_TIME_STAMP_TICK" di ALSA, mentre i valori temporali sono espressi appunto in "tick" Midi al membro tick del membro snd_seq_timestamp_t time della predetta Struttura.

Mostriamo un esempio pratico in ambiente grafico, nel quale cliccando su un "ToggleButton" si avvierà l'invio ad ALSA degli Eventi Midi e il loro processamento. Essi saranno eseguiti secondo la propria temporizzazione impostata nella Struttura snd_seq_event_t costitutiva.
Dunque avremo la Classe principale "FMain.Class":

Private Const id_dev As Integer = 128   ' 14 = midi out oppure  128 (solitamente) = softsynth
Private Const p_dev As Integer = 0      ' porta del Client destinatario dei dati Midi: solitamente 0
Public alsa As CAlsa                    ' Classe che incapsula le funzioni ALSA

 
Public Sub Form_Open()

' Crea ("istanzia") un Oggetto della Classe "CAlsa" per poterla usare:
 With alsa = New CAlsa As "Alsa"
' Apre ALSA e assegna un nome al Client:
   Me.Title = "|| Client ALSA " & .AlsaIni("Programma di prova")
' Sceglie la periferica (Softsynth) su cui suonare:
   .ImpostaDispositivo(id_dev, p_dev)
 End With

End

Public Sub ToggleButton1_Click()

If ToggleButton1.Value Then
   With alsa
' Avvia la "Coda" degli Eventi Midi ALSA:
     .AvvioCoda()
' Imposta il Volume gestendo l'Evento Midi "Control Change":
     .ControlChange(0, 0, 0, 7, 50)
' Imposta il suono dello strumento musicale gestendo l'Evento Midi "Program Change":
     .ProgramChange(0, 0, 0, 0, 44)
' Imposta la prima nota Midi da eseguire:
     .NoteON(0, 0, 0, 64, 100)
' Imposta la nota Midi da silenziare dopo 96 tick dall'inizio della "Coda":
     .NoteOFF(0, 96, 0, 64, 0)

' Imposta il suono di un altro strumento musicale dopo 150 tick dall'inizio della "Coda" (quindi crea una pausa):
     .ProgramChange(0, 182, 0, 0, 18)
' Imposta la nota Midi da eseguire dopo 182 tick dall'inizio della "Coda":
     .NoteON(0, 182, 0, 66, 100)
' Imposta la nota Midi da silenziare dopo 300 tick dall'inizio della "Coda":
     .NoteOFF(0, 300, 0, 66, 0)

' Imposta il suono di un altro strumento musicale dopo 300 tick dall'inizio della "Coda":
     .ProgramChange(0, 300, 0, 0, 18)
' Imposta la nota Midi da eseguire dopo 300 tick dall'inizio della "Coda":
     .NoteON(0, 300, 0, 68, 100)
' Imposta la nota Midi da silenziare dopo 500 tick dall'inizio della "Coda":
     .NoteOFF(0, 500, 0, 68, 0)
' Dispone infine l'invio di tutti gli Eventi Midi bufferizzati nella "Coda":
     .Flush()
   End With
 Else
   alsa.StopCoda()
   Me.Close
 Endif

End

Nella Classe secondaria, chiamata "CAlsa", vi sarà invece il seguente codice:

Private handle As Pointer
Private id As Integer
Private s_port As Integer
Private que As Integer
Private dclient As Byte
Private dport As Byte


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, SND_SEQ_EVENT_CHANPRESS,
             SND_SEQ_EVENT_PITCHBEND, SND_SEQ_EVENT_START = 30, SND_SEQ_EVENT_STOP = 32

' 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_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)
' 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_alloc_queue (snd_seq_t * seq)
' Allocate a queue.
Private Extern snd_seq_alloc_queue(seq As Pointer) As Integer

' int snd_seq_control_queue(snd_seq_t * seq, int q, int type, int value, snd_seq_event_t * ev)
' Queue controls - start/stop/continue.
Private Extern snd_seq_control_queue(seq As Pointer, q As Integer, type As Integer, value As Integer, ev As Pointer) As Integer
 
' int snd_seq_event_output_buffer (snd_seq_t * seq, snd_seq_event_t *ev)
' Output an event.
Private Extern snd_seq_event_output_buffer(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 Function AlsaIni(nome As String) As String

 Dim err As Integer

 err = snd_seq_open(VarPtr(handle), "default", SND_SEQ_OPEN_OUTPUT, 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, nome)
 id = snd_seq_client_id(handle)
 Print "Alsa ClientID = "; id

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

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

 Return CStr(id) & ":" & CStr(s_port)

End

Public Procedure ImpostaDispositivo(client As Integer, port As Integer)

 dclient = client
 dport = port
 
End

Public Procedure AvvioCoda()

 Dim err As Integer

 err = snd_seq_control_queue(handle, que, SND_SEQ_EVENT_START, 0, 0)
 If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))

End

Public Procedure ControlChange(flg As Byte, tick As Integer, canale As Byte, prm As Integer, value As Integer)

 Dim ev_midi As New Snd_seq_event_t
 Dim err As Integer

 With ev_midi
   .type = SND_SEQ_EVENT_CONTROLLER
   .flags = flg
   .tick_o_tv_sec = tick
   .queue = que
   .source_client = id
   .source_port = s_port
   .dest_client = dclient
   .dest_port = dport
   .channel = canale
   .param = prm
   .value = value
 End With

 err = snd_seq_event_output_buffer(handle, ev_midi)
 If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))

End

Public Procedure ProgramChange(flg As Byte, tick As Integer, canale As Byte, non_usato As Byte, value As Integer)

 Dim err As Integer
 Dim ev_midi As New Snd_seq_event_t

 With ev_midi
   .type = SND_SEQ_EVENT_PGMCHANGE
   .flags = flg
   .tick_o_tv_sec = tick
   .queue = que
   .source_client = id
   .source_port = s_port
   .dest_client = dclient
   .dest_port = dport
   .channel = canale
   .value = value
 End With

 err = snd_seq_event_output_buffer(handle, ev_midi)
 If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))

End

Public Procedure NoteON(flg As Byte, tick As Integer, canale As Byte, nota As Byte, vel As Byte)

 Dim ev_midi As New Snd_seq_event_t
 Dim err As Integer

 With ev_midi
   .type = SND_SEQ_EVENT_NOTEON
   .flags = flg
   .tick_o_tv_sec = tick
   .queue = que
   .source_client = id
   .source_port = s_port
   .dest_client = dclient
   .dest_port = dport
   .channel = canale
   .note = nota
   .velocity = vel
 End With

 err = snd_seq_event_output_buffer(handle, ev_midi)
 If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))

End

Public Procedure NoteOFF(flg As Byte, tick As Integer, canale As Byte, nota As Byte, vel As Byte)

 Dim ev_midi As New Snd_seq_event_t
 Dim err As Integer

 With ev_midi
   .type = SND_SEQ_EVENT_NOTEOFF
   .flags = flg
   .tick_o_tv_sec = tick
   .queue = que
   .source_client = id
   .source_port = s_port
   .dest_client = dclient
   .dest_port = dport
   .channel = canale
   .note = nota
   .velocity = vel
 End With

 err = snd_seq_event_output_buffer(handle, ev_midi)
 If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))

End

Public Procedure Flush()

 Dim err As Integer

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

End

Public Procedure StopCoda()

 Dim err As Integer
 
 err = snd_seq_control_queue(handle, que, SND_SEQ_EVENT_STOP, 0, 0)
 If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))

 snd_seq_close(handle)

End

Invio Eventi Midi con temporizzazione in "real time"

La temporizzazione in ALSA nella modalità in "real time" (temporizzazione in formato orario) è attivata assegnando al membro flags della snd_seq_event_t la Costante "SND_SEQ_TIME_STAMP_REAL" di ALSA.
Ricordiamo che questa modalità di temporizzazione degli Eventi Midi di ALSA adotta il sistema di misura dell'orario standard (ore, minuti, secondi), assegnando il valore in "secondi" al membro tv_sec e in "nanosecondi" al membro tv_nsec della sotto-Struttura snd_seq_real_time_t del membro time della Struttura principale snd_seq_event_t costitutiva.

Mostriamo un esempio identico al precedente, ma con impostazione della temporizzazione in Clock Midi (modalità "orario") espressa in secondi:
Dunque avremo la Classe principale "FMain.Class":

Private Const id_dev As Integer = 128   ' 14 = midi out oppure  128 (solitamente) = softsynth
Private Const p_dev As Integer = 0      ' porta del Client destinatario dei dati Midi: solitamente 0
Public alsa As CAlsa                    ' Classe che incapsula le funzioni ALSA

 
Public Sub Form_Open()

' Crea ("istanzia") un Oggetto della Classe "CAlsa" per poterla usare:
 With alsa = New CAlsa As "Alsa"
' Apre ALSA e assegna un nome al Client:
   Me.Title = "|| Client ALSA " & .AlsaIni("Programma di prova")
' Sceglie la periferica (Softsynth) su cui suonare:
   .ImpostaDispositivo(id_dev, p_dev)
 End With

End

Public Sub ToggleButton1_Click()

If ToggleButton1.Value Then
   With alsa
' Avvia la "Coda" degli Eventi Midi ALSA:
     .AvvioCoda()
' Imposta il Volume gestendo l'Evento Midi "Control Change":
     .ControlChange(1, 0, 0, 7, 50)
' Imposta il suono dello strumento musicale gestendo l'Evento Midi "Program Change":
     .ProgramChange(1, 0, 0, 0, 44)
' Imposta la prima nota Midi da eseguire:
     .NoteON(1, 0, 0, 64, 100)
' Imposta la nota Midi da silenziare dopo 1 secondo dall'inizio della "Coda":
     .NoteOFF(1, 1, 0, 64, 0)

' Imposta il suono di un altro strumento musicale dopo 2 secondi dall'inizio della "Coda" (quindi crea una pausa):
     .ProgramChange(1, 2, 0, 0, 18)
' Imposta la nota Midi da eseguire dopo 2 secondi dall'inizio della "Coda":
     .NoteON(1, 2, 0, 66, 100)
' Imposta la nota Midi da silenziare dopo 3 secondi dall'inizio della "Coda":
     .NoteOFF(1, 3, 0, 66, 0)

' Imposta il suono di un altro strumento musicale dopo 3 secondi dall'inizio della "Coda":
     .ProgramChange(1, 3, 0, 0, 18)
' Imposta la nota Midi da eseguire dopo 3 secondi dall'inizio della "Coda":
     .NoteON(1, 3, 0, 68, 100)
' Imposta la nota Midi da silenziare dopo 5 secondi dall'inizio della "Coda":
     .NoteOFF(1, 5, 0, 68, 0)
' Dispone infine l'invio di tutti gli Eventi Midi bufferizzati nella "Coda":
     .Flush()
   End With
 Else
   alsa.StopCoda()
   Me.Close
 Endif

End

Nella Classe secondaria, chiamata "CAlsa", vi sarà invece il seguente codice:

Private handle As Pointer
Private id As Integer
Private s_port As Integer
Private que As Integer
Private dclient As Byte
Private dport As Byte


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, SND_SEQ_EVENT_CHANPRESS,
             SND_SEQ_EVENT_PITCHBEND, SND_SEQ_EVENT_START = 30, SND_SEQ_EVENT_STOP = 32

' 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_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)
' 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_alloc_queue (snd_seq_t * seq)
' Allocate a queue.
Private Extern snd_seq_alloc_queue(seq As Pointer) As Integer

' int snd_seq_control_queue(snd_seq_t * seq, int q, int type, int value, snd_seq_event_t * ev)
' Queue controls - start/stop/continue.
Private Extern snd_seq_control_queue(seq As Pointer, q As Integer, type As Integer, value As Integer, ev As Pointer) As Integer
 
' int snd_seq_event_output_buffer (snd_seq_t * seq, snd_seq_event_t *ev)
' Output an event.
Private Extern snd_seq_event_output_buffer(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 Function AlsaIni(nome As String) As String

 Dim err As Integer

 err = snd_seq_open(VarPtr(handle), "default", SND_SEQ_OPEN_OUTPUT, 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, nome)
 id = snd_seq_client_id(handle)
 Print "Alsa ClientID = "; id

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

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

 Return CStr(id) & ":" & CStr(s_port)

End

Public Procedure ImpostaDispositivo(client As Integer, port As Integer)

 dclient = client
 dport = port
 
End

Public Procedure AvvioCoda()

 Dim err As Integer

 err = snd_seq_control_queue(handle, que, SND_SEQ_EVENT_START, 0, 0)
 If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))

End

Public Procedure ControlChange(flg As Byte, sec As Integer, canale As Byte, prm As Integer, value As Integer)

 Dim ev_midi As New Snd_seq_event_t
 Dim err As Integer

 With ev_midi
   .type = SND_SEQ_EVENT_CONTROLLER
   .flags = flg
   .tick_o_tv_sec = sec
   .queue = que
   .source_client = id
   .source_port = s_port
   .dest_client = dclient
   .dest_port = dport
   .channel = canale
   .param = prm
   .value = value
 End With

 err = snd_seq_event_output_buffer(handle, ev_midi)
 If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))

End

Public Procedure ProgramChange(flg As Byte, sec As Integer, canale As Byte, non_usato As Byte, value As Integer)

 Dim err As Integer
 Dim ev_midi As New Snd_seq_event_t

 With ev_midi
   .type = SND_SEQ_EVENT_PGMCHANGE
   .flags = flg
   .tick_o_tv_sec = sec
   .queue = que
   .source_client = id
   .source_port = s_port
   .dest_client = dclient
   .dest_port = dport
   .channel = canale
   .value = value
 End With

 err = snd_seq_event_output_buffer(handle, ev_midi)
 If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))

End

Public Procedure NoteON(flg As Byte, sec As Integer, canale As Byte, nota As Byte, vel As Byte)

 Dim ev_midi As New Snd_seq_event_t
 Dim err As Integer

 With ev_midi
   .type = SND_SEQ_EVENT_NOTEON
   .flags = flg
   .tick_o_tv_sec = sec
   .queue = que
   .source_client = id
   .source_port = s_port
   .dest_client = dclient
   .dest_port = dport
   .channel = canale
   .note = nota
   .velocity = vel
 End With

 err = snd_seq_event_output_buffer(handle, ev_midi)
 If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))

End

Public Procedure NoteOFF(flg As Byte, sec As Integer, canale As Byte, nota As Byte, vel As Byte)

 Dim ev_midi As New Snd_seq_event_t
 Dim err As Integer

 With ev_midi
   .type = SND_SEQ_EVENT_NOTEOFF
   .flags = flg
   .tick_o_tv_sec = sec
   .queue = que
   .source_client = id
   .source_port = s_port
   .dest_client = dclient
   .dest_port = dport
   .channel = canale
   .note = nota
   .velocity = vel
 End With

 err = snd_seq_event_output_buffer(handle, ev_midi)
 If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))

End

Public Procedure Flush()

 Dim err As Integer

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

End

Public Procedure StopCoda()

 Dim err As Integer
 
 err = snd_seq_control_queue(handle, que, SND_SEQ_EVENT_STOP, 0, 0)
 If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))

 snd_seq_close(handle)

End

Esempio in modalità temporizzazione in formato orario usando anche i nanosecondi

Di seguito ostriamo un semplice esempio simile al precedente, ma usando anche il membro ".tv_nsec" della Struttura snd_seq_event_t.
La Classe principale sarà la seguente:

Private Const id_dev As Integer = 128   ' 14 = midi out oppure  128 (solitamente) = softsynth
Private Const p_dev As Integer = 0      ' porta del Client destinatario dei dati Midi: solitamente 0
Public alsa As CAlsa                    ' Classe che incapsula le funzioni ALSA


Public Sub Form_Open()

 With alsa = New CAlsa As "Alsa"
   Me.Title = "|| Client ALSA " & .AlsaIni("Programma di prova")
   .ImpostaDispositivo(id_dev, p_dev)
 End With

End

Public Sub ToggleButton1_Click()

 Dim note As Byte[] = [60, 62, 64, 65, 67, 69, 71, 72]
 Dim b As Byte
 Dim ns As Integer

 If ToggleButton1.Value Then
   With alsa
     .AvvioCoda()
     .ControlChange(1, 0, 7, 50)
     .ProgramChange(1, 0, 44)
     For b = 0 To note.Max
       If b == 3 Then ns = 750000000
' Imposta la nota Midi da eseguire.
' Aggiunge 750000000 nanosecondi.
       .NoteON(1, b, ns, note[b], 100)
       ns = 0
' Imposta la nota Midi da silenziare dall'inizio della "Coda".
' Alla terza nota aggiunge 750000000 nanosecondi.</font>
       If b == 2 Then ns = 750000000
       .NoteOFF(1, b + 1, ns, note[b], 0)
       ns = 0
     Next 
     .Flush()
   End With
 Else
   alsa.StopCoda()
   Me.Close
 Endif

End

La Classe secondaria per la gestione delle risorse di ALSA:

Private handle As Pointer
Private id As Integer
Private s_port As Integer
Private que As Integer
Private dclient As Byte
Private dport As Byte


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, SND_SEQ_EVENT_CHANPRESS,
             SND_SEQ_EVENT_PITCHBEND, SND_SEQ_EVENT_START = 30, SND_SEQ_EVENT_STOP = 32

' 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_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)
' 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_alloc_queue (snd_seq_t * seq)
' Allocate a queue.
Private Extern snd_seq_alloc_queue(seq As Pointer) As Integer

' int snd_seq_control_queue(snd_seq_t * seq, int q, int type, int value, snd_seq_event_t * ev)
' Queue controls - start/stop/continue.
Private Extern snd_seq_control_queue(seq As Pointer, q As Integer, type As Integer, value As Integer, ev As Pointer) As Integer
 
' int snd_seq_event_output_buffer (snd_seq_t * seq, snd_seq_event_t *ev)
' Output an event.
Private Extern snd_seq_event_output_buffer(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 Function AlsaIni(nome As String) As String

 Dim err As Integer

 err = snd_seq_open(VarPtr(handle), "default", SND_SEQ_OPEN_OUTPUT, 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, nome)
 id = snd_seq_client_id(handle)
 Print "Alsa ClientID = "; id

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

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

 Return CStr(id) & ":" & CStr(s_port)

End

Public Procedure ImpostaDispositivo(client As Integer, port As Integer)

 dclient = client
 dport = port
 
End

Public Procedure AvvioCoda()

 Dim err As Integer

 err = snd_seq_control_queue(handle, que, SND_SEQ_EVENT_START, 0, 0)
 If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))

End

Public Procedure ControlChange(flg As Byte, sec As Integer, prm As Integer, value As Integer)

 Dim ev_midi As New Snd_seq_event_t
 Dim err As Integer

 With ev_midi
   .type = SND_SEQ_EVENT_CONTROLLER
   .flags = flg
   .tick_o_tv_sec = sec
   .queue = que
   .source_client = id
   .source_port = s_port
   .dest_client = dclient
   .dest_port = dport
   .param = prm
   .value = value
 End With

 err = snd_seq_event_output_buffer(handle, ev_midi)
 If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))

End

Public Procedure ProgramChange(flg As Byte, sec As Integer, value As Integer)

 Dim err As Integer
 Dim ev_midi As New Snd_seq_event_t

 With ev_midi
   .type = SND_SEQ_EVENT_PGMCHANGE
   .flags = flg
   .tick_o_tv_sec = sec
   .queue = que
   .source_client = id
   .source_port = s_port
   .dest_client = dclient
   .dest_port = dport
   .value = value
 End With

 err = snd_seq_event_output_buffer(handle, ev_midi)
 If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))

End

Public Procedure NoteON(flg As Byte, sec As Integer, ns As Integer, nota As Byte, vel As Byte)

 Dim ev_midi As New Snd_seq_event_t
 Dim err As Integer

 With ev_midi
   .type = SND_SEQ_EVENT_NOTEON
   .flags = flg
   .tick_o_tv_sec = sec
   .tv_nsec = ns
   .queue = que
   .source_client = id
   .source_port = s_port
   .dest_client = dclient
   .dest_port = dport
   .note = nota
   .velocity = vel
 End With

 err = snd_seq_event_output_buffer(handle, ev_midi)
 If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))

End

Public Procedure NoteOFF(flg As Byte, sec As Integer, ns As Integer, nota As Byte, vel As Byte)

 Dim ev_midi As New Snd_seq_event_t
 Dim err As Integer

 With ev_midi
   .type = SND_SEQ_EVENT_NOTEOFF
   .flags = flg
   .tick_o_tv_sec = sec
   .tv_nsec = ns
   .queue = que
   .source_client = id
   .source_port = s_port
   .dest_client = dclient
   .dest_port = dport
   .note = nota
   .velocity = vel
 End With

 err = snd_seq_event_output_buffer(handle, ev_midi)
 If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))

End

Public Procedure Flush()

 Dim err As Integer

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

End

Public Procedure StopCoda()

 Dim err As Integer

 err = snd_seq_control_queue(handle, que, SND_SEQ_EVENT_STOP, 0, 0)
 If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))

 snd_seq_close(handle)

End