Differenze tra le versioni di "Alsa e Gambas: Invio dati con l'uso di un'area di memoria allocata"

Da Gambas-it.org - Wikipedia.
 
(10 versioni intermedie di uno stesso utente non sono mostrate)
Riga 3: Riga 3:
  
 
==Riservare innanzitutto memoria per il puntatore==
 
==Riservare innanzitutto memoria per il puntatore==
Utilizzeremo quindi la funzione "Alloc()" per riservare una porzione di memoria e porla a disposizione del pointer, che chiameremo: &quot;ev". Più precisamente con ''Alloc()'' si prende una parte di memoria dal sistema e la si assegna al programma. A questo punto è possibile operare con il pointer, a patto che si vada a scrivere ''esclusivamente'' dentro quella porzione di memoria che è stata appositamente riservata per il Puntatore <SUP>&#091;[[#Note|nota 1]]&#093;</sup>
+
Utilizzeremo quindi la funzione "Alloc()" per riservare una porzione di memoria e porla a disposizione di una variabile di tipo ''Puntatore''. Quindi con "Alloc()" si prende una parte di memoria dal sistema e la si assegna al programma.
 +
<BR>A questo punto è possibile operare con il ''Puntatore'', purché si vada a scrivere <SPAN Style="text-decoration:underline">esclusivamente</span> dentro quella porzione di memoria che è stata appositamente riservata per il ''Puntatore''. <SUP>&#091;[[#Note|nota 1]]&#093;</sup>
  
Assegneremo un'adeguata porzione pari a 28 byte:
+
Assegneremo un'adeguata porzione pari a 28 byte (tanti quanto è la dimensione della ''Struttura'' "[https://www.alsa-project.org/alsa-doc/alsa-lib/structsnd__seq__event__t.html snd_seq_event_t]" usata da ALSA per creare i suoi Eventi Midi):
  ev = Alloc(SizeOf(gb.Byte), 28)
+
  Puntatore = Alloc(SizeOf(gb.Byte), 28)
  
  
 
==Uso dei Memory Stream per scrivere nell'area di memoria allocata==
 
==Uso dei Memory Stream per scrivere nell'area di memoria allocata==
Per scrivere i valori arbitrari dentro la predetta area di memoria, necessari alla definizione dell'evento Midi, useremo i ''[[Definizione_ed_uso_dei_Memory_Stream|Memory Stream]]'' mediante l'istruzione ''Memory''. In questo modo, dopo aver allocato la quantità di memoria adeguata per i dati necessari all'Evento Midi di ALSA (28 byte), agganceremo l'istruzione ''Memory'' a detta zona di memoria attraverso una variabile di tipo ''Stream''. Il flusso di memoria di 28 byte sarà aperto in ''Scrittura'' e gestito similmente a un file.  
+
Per scrivere i valori arbitrari dentro la predetta area di memoria, necessari alla definizione dell'evento Midi, useremo i ''[[Definizione_ed_uso_dei_Memory_Stream|Memory Stream]]'' mediante l'istruzione ''Memory''. In questo modo, dopo aver allocato la quantità di memoria adeguata per i dati necessari all'Evento Midi di ALSA (28 byte), agganceremo l'istruzione ''Memory'' a detta zona di memoria attraverso una variabile di tipo ''Stream''. Il flusso di memoria di 28 byte sarà aperto in ''Scrittura'', per scrivervi i dati dell'Evento Midi ALSA, e gestito similmente a un file.  
  Private st As Stream
+
  Stream = Memory Puntatore For Write
......
+
Viene così creato lo ''stream'' di memoria, e il suo puntatore interno, implicito, è posto automaticamente all'inizio dello stream medesimo (''offset'' 0).
st = Memory Puntatore For Write
+
<BR>Esso all'atto della scrittura:
Viene così creato lo ''stream'' di memoria, e il suo puntatore interno, implicito, è posto automaticamente all'inizio dello stream medesimo (''offset'' 0). Esso all'atto della scrittura:
 
 
  WRITE #''stream'', ''espressione'' As ''Tipo_di_Dati''
 
  WRITE #''stream'', ''espressione'' As ''Tipo_di_Dati''
 
automaticamente viene incrementato di una quantità di byte pari a quella occupata dal [https://gambaswiki.org/wiki/cat/datatypes?l=it tipo di dati] scritto. <SUP>&#091;[[#Note|nota 2]]&#093;</sup>
 
automaticamente viene incrementato di una quantità di byte pari a quella occupata dal [https://gambaswiki.org/wiki/cat/datatypes?l=it tipo di dati] scritto. <SUP>&#091;[[#Note|nota 2]]&#093;</sup>
Riga 20: Riga 20:
  
 
===La struttura degli eventi Midi trascritta in Gambas con i ''Memory Stream''===
 
===La struttura degli eventi Midi trascritta in Gambas con i ''Memory Stream''===
Inseriamo dunque nel nostro codice la suddetta area di memoria allocata all'interno di una sub-routine generale per la preparazione degli Eventi Midi di ALSA, la quale sarà richiamata da ciascun Evento Midi.
+
Inseriamo dunque nel nostro codice la suddetta area di memoria allocata per la preparazione degli Eventi Midi di ALSA, che sarà richiamata da ciascun ''Evento Midi'' di ALSA per potervi scrivere i dati costitutivi dell'Evento.
 
+
  <Font Color= #006400>' ''==== Inizio area dell'Evento Midi secondo il protocollo ALSA ====''</font>
Inseriremo fra le dichiarazioni iniziali due costanti relative agli indirizzi del sorgente e della destinazione degli eventi, che nella documentazione di ALSA sono così descritte:
 
* #define SND_SEQ_ADDRESS_SUBSCRIBERS 254<BR>''send event to all subscribed ports''
 
 
 
* #define SND_SEQ_ADDRESS_UNKNOWN 253<BR>''special client (port) ids unknown source''
 
 
 
Const SND_SEQ_ADDRESS_UNKNOWN As Byte = 253
 
Const SND_SEQ_ADDRESS_SUBSCRIBERS As Byte = 254
 
 
 
Ricordiamo che il nostro scopo iniziale era di creare un semplice applicativo capace di inviare unici eventi Midi. Pertanto, escluderemo i campi relativi al ''Timestamp'' e più in generale al Tempo (Time), ossia i campi denominati: ''flags'', ''tag'' e ''time'' (li riportiamo comunque scritti per opportuna memoria):
 
  Private Sub PreparaEvento(type As Byte)  <Font Color= #006400>' ''Inseriamo solo il valore del campo ''Type'',''
 
                                          ' ''perché ''in questo nostro caso'' è l'unico che ci serve !''</font>
 
  Dim i As Integer
 
<Font Color= #006400>' Dim flags, tag As Byte
 
 
    
 
    
  ' ''Pulisce opportunamente l'area di memoria allocata, partendo dal 1° byte, ossia dall'indice 0:''</font>
+
  <FONT Color=gray>' ''Imposta i valori per ciascun Evento Midi inviato ad ALSA:''</font>
  Seek #st, 0
+
  Write #st, type As Byte
  For i = 1 To SIZE_OF_SEQEV
 
    Write #st, 0 As Byte
 
  Next
 
 
<Font Color= #006400>' ''==== Inizio area dell'Evento Midi secondo il protocollo ALSA ====''
 
 
'</font> <Font Color= "blue">'''Type'''</font>
 
  Seek #st, 0
 
  Write #st, type As Byte
 
 
   
 
   
<Font Color= #006400>'</font> <Font Color= "blue">'''Flags'''</font>
+
  Write #st, flags As Byte
<Font Color= #006400>' Write #st, flags As Byte
 
 
   
 
   
'</font> <Font Color= "blue">'''Tag'''</font>
+
  Write #st, tag As Byte
<Font Color= #006400>' Write #st, tag As Byte</font>
 
 
   
 
   
   <Font Color= #006400>' ''Non scrivento nei byte di memoria precedenti, il puntatore implicito dello stream non avanza, ovviamente.''
+
   Write #st, queue As Byte
  ' ''Pertanto, bisogna porre il puntatore sul byte della zona di memoria opportuno, cioè quello richiesto da ALSA.''</font>
 
  Seek #st, 3
 
<Font Color= #006400>'</font> <Font Color= "blue">'''Queue'''</font>
 
  Write #st, outq As Byte
 
 
   
 
   
<Font Color= #006400>'</font> <Font Color= "blue">'''Time'''</font>  
+
  Write #st, tick_o_tv_sec As Integer  <FONT Color=gray>' ''"tick time" o "real time"''</font>
<Font Color= #006400>' Write #st, ts As Integer        ' ''timestamp''
+
  Write #st, tv_nsec As Integer         <FONT Color=gray>' ''solo "real time"''</font>
' Write #st, ts As Integer       ' ''2^ parte (realtime event)''</font>
 
 
   
 
   
   Seek #st, 12                    <Font Color= #006400>' ''Sposta il puntatore dello stream al byte di indice 12 (ossia al 13°)''
+
   Write #st, source_client As Byte      <FONT Color=gray>' ''l'ID Client del programma sorgente: il programma che invia gli Eventi Midi ad ALSA''</font>
'</font> <Font Color= "blue">'''Source'''</font>
+
   Write #st, source_port As Byte
  Write #st, id As Byte
 
    
 
  Write #st, outport As Byte
 
 
   
 
   
<Font Color= #006400>'</font> <Font Color= "blue">'''Dest'''</font>
+
  Write #st, dest_client As Byte        <FONT Color=gray>' ''l'ID del Programma (Softsynth) che riceverà gli Eventi Midi ad ALSA''</font>
  Write #st, SND_SEQ_ADDRESS_SUBSCRIBERS As Byte   <Font Color= #006400>' ''254/dclient''</font>
+
  Write #st, dest_port As Byte
 
   
 
   
  Write #st, SND_SEQ_ADDRESS_UNKNOWN As Byte       <Font Color= #006400>' ''253/dport''</font>
+
  Write #st, channel As Byte
 +
  Write #st, note As Byte
 +
  Write #st, velocity As Byte
 +
  Write #st, off_velocity As Byte
 
   
 
   
'''End'''
+
  Write #st, param As Integer
 
+
  Write #st, value As Integer
 
 
==Il codice completo==
 
Mostriamo di seguito il codice definitivo sin qui descritto, proposto per un'applicazione in ambiente grafico, ed avente un'apposita Classe destinata a contenenre le funzioni e risorse per la gestione di ALSA e degli eventi Midi:
 
  
CLASSE: FMain.class
+
==Codici Esemplificativi==
 +
Vedremo di seguito due semplici esempi che fanno uso appunto di un'area di memoria riservata per disporre adeguatamente i dati necessari a creare un Evento Midi da inviare ad ALSA.
  
Public alsa as CAlsa      <Font Color= #006400>' ''classe che incapsula le funzioni ALSA''</font>
+
====1° esempio pratico====
  Public gross As Integer   <Font Color= #006400>' dovrebbe ricevere il valore da uno ''Slider''</font>
+
Il primo esempio pratico imposta il 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]] per usare la [[ALSA_e_Gambas_-_La_marcatura_temporale_degli_eventi:_il_Timestamp|temporizzazione in "tick time"]], definita dalla Costante di ALSA:
 +
#define SND_SEQ_TIME_STAMP_TICK    (0<<0)
 +
Dunque avremo la Classe principale "FMain.Class":
 +
  Private strum As String[] = ["Acustic Grand Piano", "Bright Acustic Piano", "Electric Grand Piano", "Honky-tonk",
 +
  "Electric Piano 1", "Electric Piano 2", "Harpsichord", "Clavinet", "Celesta", "Glockenspiel", "Music Box", "Vibraphone",
 +
  "Marimba", "Xylophone", "Tubular Bells", "Dulcimer", "Hammond Organ", "Percussive Organ", "Rock Organ", "Church Organ",
 +
  "Reed Organ", "Accordion", "Harmonica", "Tango Accordion", "Acoustic Guitar (nylon)", "Acoustic Guitar (steel)",
 +
  "Electric Guitar (jazz)", "Electric Guitar (clean)", "Electric Guitar(muted)", "Overdriven Guitar", "Distortion Guitar",
 +
  "Guitar Harmonics", "Acoustic Bass", "Electric Bass (finger)", "Electric Bass (pick)", "Fretless Bass", "Slap Bass 1",
 +
  "Slap Bass 2", "Synth Bass 1", "Synth Bass 2", "Violin", "Viola", "Cello", "Contrabass", "Tremolo Strings",
 +
  "Pizzicato Strings", "Orchestral Harp", "Timpani", "String Ensemble 1", "String Ensemble 2", "SynthStrings 1",
 +
  "SynthStrings 2", "Choir Aahs", "Voice Oohs", "Synth Voice", "Orchestra Hit", "Trumpet", "Trombone", "Tuba", "Muted Trumpet",
 +
  "French Horn", "Brass Section", "Synth Brass 1", "Synth Brass 2", "Soprano Sax", "Alto Sax", "Tenor Sax", "Baritone Sax",
 +
  "Oboe", "English Horn", "Basson", "Clarinet", "Piccolo", "Flute", "Recorder", "Pan Flute", "Bottle Blow", "Shakuhachi",
 +
  "Whistle", "Ocarina", "Lead 1 (square)", "Lead 2 (sawtooth)", "Lead 3 (caliope lead)", "Lead 4 (chiff lead)",
 +
  "Lead 5 (charang)", "Lead 6 (voice)", "Lead 7 (fifths)", "Lead 8(brass+lead)", "Pad 1 (new age)", "Pad 2 (warm)",
 +
  "Pad 3 (polysynth)", "Pad 4 (choir)", "Pad 5 (bowed)", "Pad 6 (metallic)", "Pad 7 (halo)", "Pad 8 (sweep)", "FX 1 (rain)",
 +
  "FX 2 (soundtrack)", "FX 3 (crystal)", "FX 4 (atmosphere)", "FX 5 (brightness)", "FX 6 (goblins)", "FX 7 (echoes)",
 +
  "FX 8 (sci-fi)", "Sitar", "Banjo", "Shamisen", "Koto", "Kalimba", "Bagpipe", "Fiddle", "Shanai", "Tinkle Bell", "Agogo",
 +
  "Steel Drums", "Woodblock", "Taiko Drum", "Melodic Tom", "Synth Drum", "Reverse Cymbal", "Guitar Fret Noise", "Breath Noise",
 +
   "Seashore", "Bird Tweet", "Telephone Ring", "Helicopter", "Applause", "Gunshot"]
 
   
 
   
 +
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()
 
  '''Public''' Sub Form_Open()
 
   
 
   
  <Font Color= #006400>' ''Crea ("istanzia") la classe per poterla usare''</font>
+
  <FONT Color=gray>' ''Crea ("istanzia") un Oggetto della Classe "CAlsa" per poterla usare:''</font>
alsa = NEW CAlsa as "alsa"
+
  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= #006400>' ''Apre alsa e gli assegna un nome''</font>
+
  '''End'''
alsa.alsa_open("Progetto sequencer in Gambas-3")<Font Color= #006400> ' ''La stringa fra virgolette sarà il nome del nostro Client''</font>
 
 
   
 
   
  <Font Color= #006400>' ''Sceglie la periferica su cui suonare.''
+
  '''Public''' Sub ToggleButton1_Click()
' ''Ident.vo numerico e num. porta di altro dispositivo/client (ad esempio: QSynth), al quale il client si connetterà.''</font>
 
alsa.setdevice(128, 0)
 
 
'''End'''
 
 
   
 
   
 +
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, strum.Find("Violin"))
 +
<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)
 
   
 
   
  '''Public''' Sub noteOn_Click()          <Font Color= #006400>' '''Note ON'''</font>
+
  <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, strum.Find("Reed Organ"))
  <Font Color= #006400>' ''chiama la subroutine "noteon" nella classe secondaria CAlsa.class;''
+
<FONT Color=gray>' ''Imposta la nota Midi da eseguire dopo 182 tick dall'inizio della "Coda":''</font>
  ' ''poniamo come esempio la nota da suonare num. 60 (il DO centrale) e velocità 100.''</font>
+
      .NoteON(0, <FONT Color=#B222222>182</font>, 0, 66, 100)
  alsa.noteon(0, 60, 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)
 
   
 
   
   alsa.flush()
+
<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, strum.Find("Flute"))
 +
<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'''
 
  '''End'''
 +
Nella Classe secondaria, chiamata "CAlsa", vi sarà invece il seguente codice:
 
   
 
   
 +
Private ev As Pointer
 +
Private st As Stream
 +
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''' Sub noteOff_Click()          <Font Color= #006400>' '''Note OFF'''</font>
+
  Library "libasound:2"
 
 
  <Font Color= #006400>' ''chiama la subroutine "noteoff" nella classe secondaria CAlsa.class;''
 
  ' ''poniamo come esempio la nota da far cessare di suonare num. 60 (il DO centrale) e velocità 0.''</font>
 
  alsa.noteoff(0, 60, 0)
 
 
   
 
   
  alsa.flush()
+
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  <FONT Color=gray>'  ''(1<<20)''</font>
 +
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
 
   
 
   
  '''End'''
+
  <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
 
   
 
   
  '''Public''' Sub polypho_Click()          <Font Color= #006400>' '''Polyphonic Aftertouch (Key pressure)'''</font>
+
  <FONT Color=gray>' ''int snd_seq_client_id (snd_seq_t * seq)''
   
+
' ''Get the client id.''</font>
  <FONT Color= #006400>' ''chiama la subroutine, che chiameremo "polypho", nella classe secondaria CAlsa.class;''</font>
+
Private Extern snd_seq_client_id(seq As Pointer) As Integer
  <FONT Color= #006400>' ''poniamo come esempio alla nota 50 un valore di pressione aggiuntiva di 110.''</font>
 
  alsa.polypho(0, 60, 110)
 
 
   
 
   
  alsa.flush()
+
<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
 
   
 
   
  '''End'''
+
  <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
 
    
 
    
  '''Public''' Sub controller_Click()          <Font Color= #006400>' '''Control Change'''</font>
+
  <FONT Color=gray>' ''int snd_seq_event_output_buffer (snd_seq_t * seq, snd_seq_event_t *ev)''
   
+
' ''Output an event.''</font>
  <FONT Color= #006400>' ''chiama la subroutine, che chiameremo "controller", nella classe secondaria CAlsa.class;''</font>
+
Private Extern snd_seq_event_output_buffer(seq As Pointer, ev As Snd_seq_event_t) As Integer
  <FONT Color= #006400>' ''poniamo come esempio il tipo di controller num. 7 (Volume). Il terzo valore sarà posto a 100.''</font>
 
  alsa.controller(0, 7, 100)
 
 
   
 
   
  alsa.flush()
+
<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
 
   
 
   
  '''End'''
+
  <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''' Sub pgmchange_Click()          <Font Color= #006400>' '''Program Change'''</font>
 
   
 
  ' ''chiama la subroutine, che chiameremo "pgmchange", nella classe secondaria CAlsa.class;''</font>
 
  <FONT Color= #006400>' ''poniamo come esempio lo strumento musicale num. 71 (Fagotto).''</font>
 
  alsa.pgmchange(0, 71)
 
 
   
 
   
  alsa.flush()
+
'''Public''' Function AlsaIni(nome As String) As String
 
   
 
   
'''End'''
+
  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))
 
   
 
   
'''Public''' Sub chanpress_Click()         <Font Color= #006400>' '''Channel Aftertouch (Channel Pressure)'''</font>
+
  snd_seq_set_client_name(handle, nome)
   
+
  id = snd_seq_client_id(handle)
  <FONT Color= #006400>' ''chiama la subroutine, che chiameremo "chanpress", nella classe secondaria CAlsa.class;''</font>
+
  Print "Alsa ClientID = "; id
  <FONT Color= #006400>' ''poniamo come esempio un valore di pressione aggiuntiva di 110.''</font>
 
  alsa.chanpress(0, 110)
 
 
   
 
   
  alsa.flush()
+
  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))
 
   
 
   
'''End'''
+
  que = snd_seq_alloc_queue(handle)
 +
  If que < 0 Then error.Raise("Errore: " & snd_strerror(que))
 
   
 
   
 +
  ev = Alloc(SizeOf(gb.Byte), 28)
 +
  st = Memory ev For Write
 
   
 
   
'''Public''' Sub SliderPB_Change()         <Font Color= #006400>' '''Pitch Bend (Pitch Wheel)'''</font>
+
  Return CStr(id) & ":" & CStr(s_port)
   
 
  gross = SliderPB.Value
 
 
  <FONT Color= #006400>' ''chiama la subroutine, che chiameremo "pitchbend", nella classe secondaria CAlsa.class;''</font>
 
  alsa.pitchbend(0, 0, gross)
 
 
  alsa.flush()
 
 
   
 
   
 
  '''End'''
 
  '''End'''
 
 
----
 
 
 
CLASSE: CAlsa.class
 
 
Private handle As Pointer
 
Private id As Integer
 
Private outport As Integer
 
Private outq As Integer
 
 
   
 
   
  Public dclient As Byte
+
  '''Public''' Procedure ImpostaDispositivo(client As Integer, port As Integer)
Public dport As Byte
 
 
   
 
   
Private ev As Pointer
+
   dclient = client
Private p As Stream
+
   dport = port  
 
Const SND_SEQ_OPEN_DUPLEX As Integer = 3
 
Const SND_SEQ_PORT_TYPE_MIDI_GENERIC As Integer = 2
 
Const SND_SEQ_PORT_TYPE_APPLICATION As Integer = 1048576  <Font Color= #006400>'  (1<<20)</font>
 
 
Const SND_SEQ_ADDRESS_UNKNOWN As Byte = 253
 
Const SND_SEQ_ADDRESS_SUBSCRIBERS As Byte = 254
 
 
Const SND_SEQ_EVENT_NOTEON As Byte = 6
 
Const SND_SEQ_EVENT_NOTEOFF As Byte = 7
 
Const SND_SEQ_EVENT_KEYPRESS As Byte = 8
 
Const SND_SEQ_EVENT_CONTROLLER As Byte = 10
 
Const SND_SEQ_EVENT_PGMCHANGE As Byte = 11
 
Const SND_SEQ_EVENT_CHANPRESS As Byte = 12
 
Const SND_SEQ_EVENT_PITCHBEND As Byte = 13
 
 
 
Private Const SIZE_OF_SEQEV As Integer = 64
 
 
Library "libasound:2"
 
 
<Font Color= #006400>''''---- Parte relativa alla creazione del Client e delle sue porte ----'''
 
 
' ''int snd_seq_open(snd_seq_t **seqp, const char * name, Int streams, Int mode);'' </font>
 
Private Extern snd_seq_open(handle As Pointer, name As String, streams As Integer, mode As Integer) As Integer
 
 
<Font Color= #006400>' ''int snd_seq_set_client_name(snd_seq_t* seq, const char* name)''</font>
 
Private Extern snd_seq_set_client_name(handle As Pointer, name As String) As Integer
 
 
<Font Color= #006400>' ''int snd_seq_create_simple_port(snd_seq_t* seq, const char* name, unsigned int caps, unsigned int type)''</font>
 
Private Extern snd_seq_create_simple_port(handle As Pointer, name As String, caps As Integer, type As Integer) As Integer
 
 
<Font Color= #006400>' ''int snd_seq_client_id(snd_seq_t * seq)''</font>
 
Private Extern snd_seq_client_id(handle As Pointer) As Integer
 
 
<Font Color= #006400>' ''int snd_seq_connect_to(seq as pointer, myport as integer, src_client as integer, src_port as integer)''</font>
 
Private Extern snd_seq_connect_to(seq As Pointer, myport As Integer, src_client As Integer, src_port As Integer) As Integer
 
 
<Font Color= #006400>' ''int snd_seq_alloc_named_queue(snd_seq_t * seq, CONST char * name)''</font>
 
Private Extern snd_seq_alloc_queue(seq As Pointer, name As String) As Integer
 
    
 
' ''int snd_seq_event_output (snd_seq_t *handle, snd_seq_event_t *ev)''</font>
 
Private Extern snd_seq_event_output(handle As Pointer, ev As Pointer) As Integer
 
 
 
  <Font Color= #006400>' ''int snd_seq_drain_output (snd_seq_t * handle)''</font>
 
Private Extern snd_seq_drain_output(handle As Pointer) As Integer
 
 
 
'''Public''' Sub alsa_open(myname As String)
 
  Dim err As Integer
 
 
  err = snd_seq_open(VarPtr(handle), "default", SND_SEQ_OPEN_DUPLEX, 0)
 
  Print "Opening alsa="; err
 
  If err < 0 Then error.RAISE("Error opening alsa")
 
 
 
  snd_seq_set_client_name(handle, myname)
 
  id = snd_seq_client_id(handle)
 
  Print "Alsa ClientID="; id
 
 
 
   err = snd_seq_create_simple_port(handle, "Seq-Out", 0, SND_SEQ_PORT_TYPE_MIDI_GENERIC + SND_SEQ_PORT_TYPE_APPLICATION)
 
  Print "My alsa client port="; err
 
  If err < 0 Then error.Raise("Error creating alsa port")
 
  outport = err
 
 
 
  err = snd_seq_alloc_queue(handle, "outqueue")        <Font Color= #006400>' ''per creare una coda di eventi''</font>
 
  printerr("Creating queue", err)
 
  If err < 0 Then error.Raise("Error creating out queue")
 
  outq = err
 
 
 
<Font Color= #006400>' ''alloca un evento nella zona di memoria riservata per lavorarci.''
 
' ''E' globale per evitare allocazioni/deallocazioni onerose.''</font>
 
  ev = Alloc(SIZE_OF_SEQEV)
 
 
 
  p = Memory ev For Write
 
 
   
 
   
 
  '''End'''
 
  '''End'''
 
   
 
   
 +
'''Public Procedure ImpostaDispositivo(client As Integer, port As Integer)
 
   
 
   
'''Public''' Sub setdevice(client As Integer, port As Integer)
 
  Dim err As Integer
 
 
 
 
   dclient = client
 
   dclient = client
 
   dport = port
 
   dport = port
  err = snd_seq_connect_to(handle, outport, dclient, dport)
 
  printerr("Subscribe", err)
 
  If err < 0 Then error.Raise("Error subscribe output device")
 
 
   
 
   
 
  '''End'''
 
  '''End'''
 
   
 
   
 +
'''Public''' Procedure AvvioCoda()
 
   
 
   
<Font Color= #006400>''''---- Parte relativa alla gestione degli errori ----'''</font>
+
  Dim err As Integer
 
   
 
   
Private Extern snd_strerror(err As Integer) As Pointer
+
  err = snd_seq_control_queue(handle, que, SND_SEQ_EVENT_START, 0, 0)
 +
  If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))
 
   
 
   
  '''Public''' Sub errmsg(err As Integer) As String
+
  '''End'''
  Return String@(snd_strerror(err))
 
 
   
 
   
  '''End'''
+
  '''Public''' Procedure ControlChange(flags As Byte, tick As Integer, channel As Byte, param_ As Integer, value As Integer)
 
   
 
   
 +
  Dim err, tv_nsec As Integer
 +
  Dim tag, note, velocity, off_velocity As Byte
 
   
 
   
'''Private''' Sub printerr(operation As String, err As Integer)
+
  Write #st, SND_SEQ_EVENT_CONTROLLER As Byte
 +
  Write #st, flags As Byte
 +
  Write #st, tag As Byte 
 +
  Write #st, que As Byte
 +
  Write #st, tick As Integer
 +
  Write #st, tv_nsec As Integer
 +
  Write #st, id As Byte
 +
  Write #st, s_port As Byte
 +
  Write #st, dclient As Byte
 +
  Write #st, dport As Byte
 +
  Write #st, channel As Byte
 +
  Write #st, note As Byte
 +
  Write #st, velocity As Byte
 +
  Write #st, off_velocity As Byte
 +
  Write #st, param_ As Integer
 +
  Write #st, value As Integer
 
   
 
   
   If err < 0 Then Print operation; ": err ="; err; " ("; errmsg(err); ")"
+
  err = snd_seq_event_output_buffer(handle, ev)
 +
   If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))
 
   
 
   
 
  '''End'''
 
  '''End'''
 
   
 
   
 +
'''Public''' Procedure ProgramChange(flags As Byte, tick As Integer, channel As Byte, non_usato As Byte, value As Integer)
 
   
 
   
<Font Color= #006400>''''---- Parte relativa all’invio degli eventi Midi ----'''
+
  Dim err, tv_nsec, param_ As Integer
    
+
   Dim tag, note, velocity, off_velocity As Byte
' '''Note ON'''</font>
 
'''Public''' Sub noteon(channel As Byte, note As Byte, velocity As Byte)
 
 
   
 
   
   Dim err As Integer
+
<FONT Color=gray>' ''Riporta il puntatore dello Stream all'inizio dell'area di memoria puntata dalla variabile "ev":''</font>
    
+
   Seek #st, 0
prepareev(SND_SEQ_EVENT_NOTEON)
+
<FONT Color=gray>' ''Esegue una opportuna ripulitura dell'area di memoria:''</font>
 +
  Repeat
 +
    Write #st, 0 As Byte
 +
   Until Seek(st) == 28
 
   
 
   
   Write #p, channel As Byte
+
<FONT Color=gray>' ''Riporta il puntatore dello Stream all'inizio dell'area di memoria puntata dalla variabile "ev":''</font>
 +
  Seek #st, 0
 +
  Write #st, SND_SEQ_EVENT_PGMCHANGE As Byte
 +
  Write #st, flags As Byte
 +
  Write #st, tag As Byte 
 +
  Write #st, que As Byte
 +
  Write #st, tick As Integer
 +
  Write #st, tv_nsec As Integer
 +
  Write #st, id As Byte
 +
  Write #st, s_port As Byte
 +
  Write #st, dclient As Byte
 +
   Write #st, dport As Byte
 +
  Write #st, channel As Byte
 +
  Write #st, note As Byte
 +
  Write #st, velocity As Byte
 +
  Write #st, off_velocity As Byte
 +
  Write #st, param_ As Integer
 +
  Write #st, value As Integer
 
   
 
   
  Write #p, note As Byte
+
   err = snd_seq_event_output_buffer(handle, ev)
+
   If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))
  Write #p, velocity As Byte
 
 
 
   err = snd_seq_event_output(handle, ev)
 
   printerr("Noteon = ", err)
 
 
   
 
   
 
  '''End'''
 
  '''End'''
 
   
 
   
 +
'''Public''' Procedure NoteON(flags As Byte, tick As Integer, channel As Byte, note As Byte, velocity As Byte)
 
   
 
   
<FONT Color= #006400>' '''Note OFF'''</font>
+
   Dim err, tv_nsec, param_, value As Integer
'''Public''' Sub noteoff(channel As Byte, note As Byte, velocity As Byte)
+
   Dim tag, off_velocity As Byte
 
   Dim err As Integer
 
    
 
prepareev(SND_SEQ_EVENT_NOTEOFF)
 
 
  Write #p, channel As Byte
 
 
  Write #p, note As Byte
 
 
   
 
   
   Write #p, velocity As Byte
+
<FONT Color=gray>' ''Riporta il puntatore dello Stream all'inizio dell'area di memoria puntata dalla variabile "ev":''</font>
 +
   Seek #st, 0
 +
<FONT Color=gray>' ''Esegue una opportuna ripulitura dell'area di memoria:''</font>
 +
  Repeat
 +
    Write #st, 0 As Byte
 +
  Until Seek(st) == 28
 
   
 
   
 +
<FONT Color=gray>' ''Riporta il puntatore dello Stream all'inizio dell'area di memoria puntata dalla variabile "ev":''</font>
 +
  Seek #st, 0
 +
  Write #st, SND_SEQ_EVENT_NOTEON As Byte
 +
  Write #st, flags As Byte
 +
  Write #st, tag As Byte 
 +
  Write #st, que As Byte
 +
  Write #st, tick As Integer
 +
  Write #st, tv_nsec As Integer
 +
  Write #st, id As Byte
 +
  Write #st, s_port As Byte
 +
  Write #st, dclient As Byte
 +
  Write #st, dport As Byte
 +
  Write #st, channel As Byte
 +
  Write #st, note As Byte
 +
  Write #st, velocity As Byte
 +
  Write #st, off_velocity As Byte
 +
  Write #st, param_ As Integer
 +
  Write #st, value As Integer
 
   
 
   
   err = snd_seq_event_output(handle, ev)
+
   err = snd_seq_event_output_buffer(handle, ev)
   printerr("NoteOFF = ", err)
+
   If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))
 
   
 
   
 
  '''End'''
 
  '''End'''
 
   
 
   
 +
'''Public''' Procedure NoteOFF(flags As Byte, tick As Integer, channel As Byte, note As Byte, velocity As Byte)
 
   
 
   
<FONT Color= #006400>' '''Polyphonic Aftertouch (Key Pressure)'''</font>
+
   Dim err, tv_nsec, param_, value As Integer
   '''Public''' Sub polypho(channel As Byte, note As Byte, velocity As Byte)
+
   Dim tag, off_velocity As Byte
 
 
   Dim err As Integer
 
 
<FONT Color= #006400>' ''Chiama la subroutine prepareev''</font>
 
  prepareev(SND_SEQ_EVENT_KEYPRESS)
 
 
 
  Write #p, channel As Byte
 
 
  Write #p, note As Byte
 
 
   
 
   
  Write #p, poly As Byte
+
<FONT Color=gray>' ''Riporta il puntatore dello Stream all'inizio dell'area di memoria puntata dalla variabile "ev":''</font>
 +
  Seek #st, 0
 +
<FONT Color=gray>' ''Esegue una opportuna ripulitura dell'area di memoria:''</font>
 +
  Repeat
 +
    Write #st, 0 As Byte
 +
  Until Seek(st) == 28
 
   
 
   
 +
<FONT Color=gray>' ''Riporta il puntatore dello Stream all'inizio dell'area di memoria puntata dalla variabile "ev":''</font>
 +
  Seek #st, 0 Write #st, SND_SEQ_EVENT_NOTEON As Byte
 +
  Write #st, flags As Byte
 +
  Write #st, tag As Byte 
 +
  Write #st, que As Byte
 +
  Write #st, tick As Integer
 +
  Write #st, tv_nsec As Integer
 +
  Write #st, id As Byte
 +
  Write #st, s_port As Byte
 +
  Write #st, dclient As Byte
 +
  Write #st, dport As Byte
 +
  Write #st, channel As Byte
 +
  Write #st, note As Byte
 +
  Write #st, velocity As Byte
 +
  Write #st, off_velocity As Byte
 +
  Write #st, param_ As Integer
 +
  Write #st, value As Integer
 
   
 
   
  err = snd_seq_event_output(handle, ev)       <FONT Color= #006400>' ''output an event</font>
+
  err = snd_seq_event_output_buffer(handle, ev)
  printerr("Polyphonic = ", err)
+
  If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))
 
   
 
   
 
  '''End'''
 
  '''End'''
 
   
 
   
   
+
  '''Public''' Procedure Flush()
  <FONT Color= #006400>' '''Control Change'''</font>
 
  '''Public''' Sub controller(channel As integer, ctrl As integer, valCtr as integer)
 
 
   
 
   
 
   Dim err As Integer
 
   Dim err As Integer
 
 
  <FONT Color= #006400>' ''Chiama la subroutine prepareev''</font>
 
  prepareev(SND_SEQ_EVENT_CONTROLLER)
 
 
 
    Write #p, channel As Integer
 
 
    Write #p, ctrl As Integer
 
 
    Write #p, valCtr As Integer
 
 
   
 
   
  err = snd_seq_event_output(handle, ev)       <FONT Color= #006400>' ''output an event</font>
+
  err = snd_seq_drain_output(handle)
  printerr("Controller = ", err)
+
  If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))
 
   
 
   
 
  '''End'''
 
  '''End'''
 
   
 
   
 +
'''Public''' Procedure StopCoda()
 
   
 
   
 
<FONT Color= #006400>' '''Program Change'''</font>
 
'''Public''' Sub pgmchange(channel As Byte, strum As Integer)
 
 
 
 
   Dim err As Integer
 
   Dim err As Integer
 
 
  <FONT Color= #006400>' ''Chiama la subroutine prepareev''</font>
 
  prepareev(SND_SEQ_EVENT_PGMCHANGE)
 
 
 
  Write #p, channel As Byte
 
  Seek #p, 24                            <FONT Color= #006400>' ''Spostiamo il puntatore dello stream sul byte num. 24''</font>
 
  Write #p, strum As Integer
 
 
   
 
   
  err = snd_seq_event_output(handle, ev)       <FONT Color= #006400>' ''output an event</font>
+
  err = snd_seq_control_queue(handle, que, SND_SEQ_EVENT_STOP, 0, 0)
  printerr("Pgmchange = ", err)
+
  If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))
 
 
'''End'''
 
 
 
<FONT Color= #006400>' '''Channel Aftertouch (Channel Pressure)'''</font>
 
  '''Public''' Sub chanpress(channel As Byte, press As Integer)
 
 
   
 
   
   Dim err As Integer
+
   snd_seq_close(handle)
    
+
   st.Close
  <FONT Color= #006400>' ''Chiama la subroutine prepareev''</font>
+
  <FONT Color=gray>' ''Dealloca l'area di memoria precedentemente riservata e si assicura che il Puntatore non punti ad un indirizzo rilevante:''</font>
  prepareev(SND_SEQ_EVENT_CHANPRESS)
+
  Free(ev
 
+
  ev = 0
  Write #p, channel As Byte
 
  Seek #p, 24                            <FONT Color= #006400>' ''Spostiamo il puntatore dello stream sul byte num. 24''</font>
 
  Write #p, press As Integer
 
 
 
  err = snd_seq_event_output(handle, ev)        <FONT Color= #006400>' ''output an event</font>
 
  printerr("ChanPress = ", err)
 
 
   
 
   
 
  '''End'''
 
  '''End'''
 +
 +
====2° esempio pratico====
 +
Il secondo esempio pratico imposta il 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]] per usare la [[ALSA_e_Gambas_-_La_marcatura_temporale_degli_eventi:_il_Timestamp|temporizzazione in "real time"]] (ossia usando il sistema di misura dell'orario), definita dalla Costante di ALSA:
 +
#define SND_SEQ_TIME_STAMP_REAL    (1<<0)
 +
In particolare a ciascun ''Evento Midi'' di ALSA sarà passato un valore, espresso in ''secondi'', da assegnare al primo membro "tv_sec" della Struttura ''snd_seq_real_time_t'' appartenente al membro ''time'' della predetta Struttura ''[https://www.alsa-project.org/alsa-doc/alsa-lib/structsnd__seq__event__t.html snd_seq_event_t]''.
 +
<BR>Dunque avremo la Classe principale "FMain.Class":
 +
Private strum As String[] = ["Acustic Grand Piano", "Bright Acustic Piano", "Electric Grand Piano", "Honky-tonk",
 +
  "Electric Piano 1", "Electric Piano 2", "Harpsichord", "Clavinet", "Celesta", "Glockenspiel", "Music Box", "Vibraphone",
 +
  "Marimba", "Xylophone", "Tubular Bells", "Dulcimer", "Hammond Organ", "Percussive Organ", "Rock Organ", "Church Organ",
 +
  "Reed Organ", "Accordion", "Harmonica", "Tango Accordion", "Acoustic Guitar (nylon)", "Acoustic Guitar (steel)",
 +
  "Electric Guitar (jazz)", "Electric Guitar (clean)", "Electric Guitar(muted)", "Overdriven Guitar", "Distortion Guitar",
 +
  "Guitar Harmonics", "Acoustic Bass", "Electric Bass (finger)", "Electric Bass (pick)", "Fretless Bass", "Slap Bass 1",
 +
  "Slap Bass 2", "Synth Bass 1", "Synth Bass 2", "Violin", "Viola", "Cello", "Contrabass", "Tremolo Strings",
 +
  "Pizzicato Strings", "Orchestral Harp", "Timpani", "String Ensemble 1", "String Ensemble 2", "SynthStrings 1",
 +
  "SynthStrings 2", "Choir Aahs", "Voice Oohs", "Synth Voice", "Orchestra Hit", "Trumpet", "Trombone", "Tuba", "Muted Trumpet",
 +
  "French Horn", "Brass Section", "Synth Brass 1", "Synth Brass 2", "Soprano Sax", "Alto Sax", "Tenor Sax", "Baritone Sax",
 +
  "Oboe", "English Horn", "Basson", "Clarinet", "Piccolo", "Flute", "Recorder", "Pan Flute", "Bottle Blow", "Shakuhachi",
 +
  "Whistle", "Ocarina", "Lead 1 (square)", "Lead 2 (sawtooth)", "Lead 3 (caliope lead)", "Lead 4 (chiff lead)",
 +
  "Lead 5 (charang)", "Lead 6 (voice)", "Lead 7 (fifths)", "Lead 8(brass+lead)", "Pad 1 (new age)", "Pad 2 (warm)",
 +
  "Pad 3 (polysynth)", "Pad 4 (choir)", "Pad 5 (bowed)", "Pad 6 (metallic)", "Pad 7 (halo)", "Pad 8 (sweep)", "FX 1 (rain)",
 +
  "FX 2 (soundtrack)", "FX 3 (crystal)", "FX 4 (atmosphere)", "FX 5 (brightness)", "FX 6 (goblins)", "FX 7 (echoes)",
 +
  "FX 8 (sci-fi)", "Sitar", "Banjo", "Shamisen", "Koto", "Kalimba", "Bagpipe", "Fiddle", "Shanai", "Tinkle Bell", "Agogo",
 +
  "Steel Drums", "Woodblock", "Taiko Drum", "Melodic Tom", "Synth Drum", "Reverse Cymbal", "Guitar Fret Noise", "Breath Noise",
 +
  "Seashore", "Bird Tweet", "Telephone Ring", "Helicopter", "Applause", "Gunshot"]
 
   
 
   
 +
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>
 
   
 
   
<FONT Color= #006400>' '''Pitch Bend (Pitch Wheel)'''</font> 
 
  '''Public''' Sub pitchbend(channel As Integer, valorePB1 As Integer, gross As Integer)
 
 
  Dim err As Integer
 
 
    
 
    
  <FONT Color= #006400>' ''Chiama la subroutine prepareev''</font>
+
'''Public''' Sub Form_Open()
  prepareev(SND_SEQ_EVENT_PITCHBEND)
 
 
 
  Write #p, channel As Integer
 
 
 
  Write #p, valorePB1 As Integer          <FONT Color= #006400>' ''questo valore resta a 0''</font>
 
 
 
  Write #p, gross As Integer
 
 
 
  err = snd_seq_event_output(handle, ev)   <FONT Color= #006400>' ''output an event''</font>
 
  printerr("PitchBend = ", err)
 
 
 
'''End'''
 
 
   
 
   
 +
<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= #006400>'' ---> Scrittura dei dati generali per tutti i messaggi Midi <--- ''</font>
+
  '''End'''
 
   
 
   
  '''Private''' Sub prepareev(type As Byte)
+
  '''Public''' Sub ToggleButton1_Click()
 
   
 
   
  Dim i As Integer
+
If ToggleButton1.Value Then
  Dim flags, tag As Byte
+
    With alsa
 
+
<FONT Color=gray>' ''Avvia la "Coda" degli Eventi Midi ALSA:''</font>
  <Font Color= #006400>' ''ripulisce la zona di memoria riservata per la definizione dell’evento''</font>
+
      .AvvioCoda()
  Seek #p, 0
+
<FONT Color=gray>' ''Imposta il Volume gestendo l'Evento Midi "Control Change":''</font>
  For i = 1 To SIZE_OF_SEQEV
+
      .ControlChange(<FONT Color=#B222222>1</font>, 0, 0, 7, 50)
    Write #p, 0 As Byte
+
<FONT Color=gray>' ''Imposta il suono dello strumento musicale gestendo l'Evento Midi "Program Change":''</font>
  Next
+
      .ProgramChange(<FONT Color=#B222222>1</font>, 0, 0, 0, strum.Find("Violin"))
 +
  <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)
 
   
 
   
  Seek #p, 0
+
<FONT Color=gray>' ''Imposta il suono di un altro strumento musicale dopo 2 secondi dall'inizio della "Coda" (quindi crea una pausa):''</font>
  Write #p, type As Byte
+
      .ProgramChange(<FONT Color=#B222222>1</font>, <FONT Color=#B222222>2</font>, 0, 0, strum.Find("Reed Organ"))
 
+
<FONT Color=gray>' ''Imposta la nota Midi da eseguire dopo 2 secondi dall'inizio della "Coda":''</font>
<Font Color= #006400>' Write #p, flags As Byte
+
      .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>
  ' Write #p, tag As Byte</font>
+
      .NoteOFF(<FONT Color=#B222222>1</font>, <FONT Color=#B222222>3</font>, 0, 66, 0)
 
   
 
   
  Seek #p, 3
+
  <FONT Color=gray>' ''Imposta il suono di un altro strumento musicale dopo 3 secondi dall'inizio della "Coda":''</font>
  Write #p, outq As Byte
+
      .ProgramChange(<FONT Color=#B222222>1</font>, <FONT Color=#B222222>3</font>, 0, 0, strum.Find("Flute"))
+
  <FONT Color=gray>' ''Imposta la nota Midi da eseguire dopo 3 secondi dall'inizio della "Coda":''</font>
  <Font Color= #006400>' Write #p, ts As Integer  ' ''timestamp''
+
      .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>
' Write #p, ts As Integer  ' ''2^ parte (realtime event)''</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>
  Seek #p, 12
+
      .Flush()
<Font Color= #006400>' ''source''</font>
+
    End With
  Write #p, id As Byte
+
  Else
 
+
    alsa.StopCoda()
  Write #p, outport As Byte
+
    Me.Close
 
+
  Endif
  <Font Color= #006400>' ''dest''</font>
 
  Write #p, SND_SEQ_ADDRESS_SUBSCRIBERS As Byte  <Font Color= #006400>' ''254/dclient''</font>
 
 
 
  Write #p, SND_SEQ_ADDRESS_UNKNOWN As Byte  <Font Color= #006400>' ''253/dport''</font>
 
   
 
'''End'''
 
 
 
<Font Color= #006400>' ''Svuota il buffer in ALSA''</font>
 
 
 
'''Public''' Sub flush()
 
  Dim err As Integer
 
   
 
  err = snd_seq_drain_output(handle)    <Font Color= #006400>' ''drain output buffer to sequencer''</font>
 
  Printerr("Flush", err)
 
 
   
 
   
 
  '''End'''
 
  '''End'''
+
Nella Classe secondaria, chiamata "CAlsa", vi sarà invece il medesimo codice impostato nel primo esempio sopra.
 
<Font Color= #006400>' ENUM snd_seq_event_type {
 
'    SND_SEQ_EVENT_SYSTEM = 0, SND_SEQ_EVENT_RESULT, SND_SEQ_EVENT_NOTE = 5, SND_SEQ_EVENT_NOTEON = 6,
 
'    SND_SEQ_EVENT_NOTEOFF = 7, SND_SEQ_EVENT_KEYPRESS = 8, SND_SEQ_EVENT_CONTROLLER = 10, SND_SEQ_EVENT_PGMCHANGE = 11,
 
'    SND_SEQ_EVENT_CHANPRESS = 12, SND_SEQ_EVENT_PITCHBEND = 13, SND_SEQ_EVENT_CONTROL14, SND_SEQ_EVENT_NONREGPARAM,
 
'    SND_SEQ_EVENT_REGPARAM, SND_SEQ_EVENT_SONGPOS = 20, SND_SEQ_EVENT_SONGSEL, SND_SEQ_EVENT_QFRAME,
 
'    SND_SEQ_EVENT_TIMESIGN, SND_SEQ_EVENT_KEYSIGN, SND_SEQ_EVENT_START = 30, SND_SEQ_EVENT_CONTINUE,
 
'    SND_SEQ_EVENT_STOP, SND_SEQ_EVENT_SETPOS_TICK, SND_SEQ_EVENT_SETPOS_TIME, SND_SEQ_EVENT_TEMPO,
 
'    SND_SEQ_EVENT_CLOCK, SND_SEQ_EVENT_TICK, SND_SEQ_EVENT_QUEUE_SKEW, SND_SEQ_EVENT_SYNC_POS,
 
'    SND_SEQ_EVENT_TUNE_REQUEST = 40, SND_SEQ_EVENT_RESET, SND_SEQ_EVENT_SENSING, SND_SEQ_EVENT_ECHO = 50,
 
'    SND_SEQ_EVENT_OSS, SND_SEQ_EVENT_CLIENT_START = 60, SND_SEQ_EVENT_CLIENT_EXIT, SND_SEQ_EVENT_CLIENT_CHANGE,
 
'    SND_SEQ_EVENT_PORT_START, SND_SEQ_EVENT_PORT_EXIT, SND_SEQ_EVENT_PORT_CHANGE, SND_SEQ_EVENT_PORT_SUBSCRIBED,
 
'    SND_SEQ_EVENT_PORT_UNSUBSCRIBED, SND_SEQ_EVENT_USR0 = 90, SND_SEQ_EVENT_USR1, SND_SEQ_EVENT_USR2,
 
'    SND_SEQ_EVENT_USR3, SND_SEQ_EVENT_USR4, SND_SEQ_EVENT_USR5, SND_SEQ_EVENT_USR6,
 
'    SND_SEQ_EVENT_USR7, SND_SEQ_EVENT_USR8, SND_SEQ_EVENT_USR9, SND_SEQ_EVENT_SYSEX = 130,
 
'    SND_SEQ_EVENT_BOUNCE, SND_SEQ_EVENT_USR_VAR0 = 135, SND_SEQ_EVENT_USR_VAR1, SND_SEQ_EVENT_USR_VAR2,
 
'    SND_SEQ_EVENT_USR_VAR3, SND_SEQ_EVENT_USR_VAR4, SND_SEQ_EVENT_NONE = 255
 
'  }</font>
 
  
  

Versione attuale delle 22:34, 25 gen 2022

Una modalità per creare ed inviare eventi Midi ad Alsa è quella mediante l'allocazione di memoria puntata da una variabile di tipo Puntatore.


Riservare innanzitutto memoria per il puntatore

Utilizzeremo quindi la funzione "Alloc()" per riservare una porzione di memoria e porla a disposizione di una variabile di tipo Puntatore. Quindi con "Alloc()" si prende una parte di memoria dal sistema e la si assegna al programma.
A questo punto è possibile operare con il Puntatore, purché si vada a scrivere esclusivamente dentro quella porzione di memoria che è stata appositamente riservata per il Puntatore. [nota 1]

Assegneremo un'adeguata porzione pari a 28 byte (tanti quanto è la dimensione della Struttura "snd_seq_event_t" usata da ALSA per creare i suoi Eventi Midi):

Puntatore = Alloc(SizeOf(gb.Byte), 28)


Uso dei Memory Stream per scrivere nell'area di memoria allocata

Per scrivere i valori arbitrari dentro la predetta area di memoria, necessari alla definizione dell'evento Midi, useremo i Memory Stream mediante l'istruzione Memory. In questo modo, dopo aver allocato la quantità di memoria adeguata per i dati necessari all'Evento Midi di ALSA (28 byte), agganceremo l'istruzione Memory a detta zona di memoria attraverso una variabile di tipo Stream. Il flusso di memoria di 28 byte sarà aperto in Scrittura, per scrivervi i dati dell'Evento Midi ALSA, e gestito similmente a un file.

Stream = Memory Puntatore For Write

Viene così creato lo stream di memoria, e il suo puntatore interno, implicito, è posto automaticamente all'inizio dello stream medesimo (offset 0).
Esso all'atto della scrittura:

WRITE #stream, espressione As Tipo_di_Dati

automaticamente viene incrementato di una quantità di byte pari a quella occupata dal tipo di dati scritto. [nota 2]


La struttura degli eventi Midi trascritta in Gambas con i Memory Stream

Inseriamo dunque nel nostro codice la suddetta area di memoria allocata per la preparazione degli Eventi Midi di ALSA, che sarà richiamata da ciascun Evento Midi di ALSA per potervi scrivere i dati costitutivi dell'Evento.

' ==== Inizio area dell'Evento Midi secondo il protocollo ALSA ====
 
' Imposta i valori per ciascun Evento Midi inviato ad ALSA:
 Write #st, type As Byte

 Write #st, flags As Byte

 Write #st, tag As Byte

 Write #st, queue As Byte

 Write #st, tick_o_tv_sec As Integer   ' "tick time" o "real time"
 Write #st, tv_nsec As Integer         ' solo "real time"

 Write #st, source_client As Byte      ' l'ID Client del programma sorgente: il programma che invia gli Eventi Midi ad ALSA
 Write #st, source_port As Byte

 Write #st, dest_client As Byte        ' l'ID del Programma (Softsynth) che riceverà gli Eventi Midi ad ALSA
 Write #st, dest_port As Byte

 Write #st, channel As Byte
 Write #st, note As Byte
 Write #st, velocity As Byte
 Write #st, off_velocity As Byte

 Write #st, param As Integer
 Write #st, value As Integer

Codici Esemplificativi

Vedremo di seguito due semplici esempi che fanno uso appunto di un'area di memoria riservata per disporre adeguatamente i dati necessari a creare un Evento Midi da inviare ad ALSA.

1° esempio pratico

Il primo esempio pratico imposta il membro flags della Struttura snd_seq_event_t per usare la temporizzazione in "tick time", definita dalla Costante di ALSA:

#define SND_SEQ_TIME_STAMP_TICK     (0<<0)

Dunque avremo la Classe principale "FMain.Class":

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

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, strum.Find("Violin"))
' 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, strum.Find("Reed Organ"))
' 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, strum.Find("Flute"))
' 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 ev As Pointer
Private st As Stream
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"

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  '  (1<<20)
Private Enum SND_SEQ_EVENT_NOTEON = 6, SND_SEQ_EVENT_NOTEOFF, SND_SEQ_EVENT_KEYPRESS,
             SND_SEQ_EVENT_CONTROLLER = 10, SND_SEQ_EVENT_PGMCHANGE, 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_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))

 ev = Alloc(SizeOf(gb.Byte), 28)
 st = Memory ev For Write

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

End

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

 dclient = client
 dport = 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(flags As Byte, tick As Integer, channel As Byte, param_ As Integer, value As Integer)

 Dim err, tv_nsec As Integer
 Dim tag, note, velocity, off_velocity As Byte

 Write #st, SND_SEQ_EVENT_CONTROLLER As Byte
 Write #st, flags As Byte
 Write #st, tag As Byte   
 Write #st, que As Byte
 Write #st, tick As Integer
 Write #st, tv_nsec As Integer
 Write #st, id As Byte
 Write #st, s_port As Byte
 Write #st, dclient As Byte
 Write #st, dport As Byte
 Write #st, channel As Byte
 Write #st, note As Byte
 Write #st, velocity As Byte
 Write #st, off_velocity As Byte
 Write #st, param_ As Integer
 Write #st, value As Integer

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

End

Public Procedure ProgramChange(flags As Byte, tick As Integer, channel As Byte, non_usato As Byte, value As Integer)

 Dim err, tv_nsec, param_ As Integer
 Dim tag, note, velocity, off_velocity As Byte

' Riporta il puntatore dello Stream all'inizio dell'area di memoria puntata dalla variabile "ev":
 Seek #st, 0
' Esegue una opportuna ripulitura dell'area di memoria:
 Repeat 
   Write #st, 0 As Byte
 Until Seek(st) == 28

' Riporta il puntatore dello Stream all'inizio dell'area di memoria puntata dalla variabile "ev":
 Seek #st, 0
 Write #st, SND_SEQ_EVENT_PGMCHANGE As Byte
 Write #st, flags As Byte
 Write #st, tag As Byte   
 Write #st, que As Byte
 Write #st, tick As Integer
 Write #st, tv_nsec As Integer
 Write #st, id As Byte
 Write #st, s_port As Byte
 Write #st, dclient As Byte
 Write #st, dport As Byte
 Write #st, channel As Byte
 Write #st, note As Byte
 Write #st, velocity As Byte
 Write #st, off_velocity As Byte
 Write #st, param_ As Integer
 Write #st, value As Integer

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

End

Public Procedure NoteON(flags As Byte, tick As Integer, channel As Byte, note As Byte, velocity As Byte)

 Dim err, tv_nsec, param_, value As Integer
 Dim tag, off_velocity As Byte

' Riporta il puntatore dello Stream all'inizio dell'area di memoria puntata dalla variabile "ev":
 Seek #st, 0
' Esegue una opportuna ripulitura dell'area di memoria:
 Repeat 
   Write #st, 0 As Byte
 Until Seek(st) == 28

' Riporta il puntatore dello Stream all'inizio dell'area di memoria puntata dalla variabile "ev":
 Seek #st, 0
 Write #st, SND_SEQ_EVENT_NOTEON As Byte
 Write #st, flags As Byte
 Write #st, tag As Byte   
 Write #st, que As Byte
 Write #st, tick As Integer
 Write #st, tv_nsec As Integer
 Write #st, id As Byte
 Write #st, s_port As Byte
 Write #st, dclient As Byte
 Write #st, dport As Byte
 Write #st, channel As Byte
 Write #st, note As Byte
 Write #st, velocity As Byte
 Write #st, off_velocity As Byte
 Write #st, param_ As Integer
 Write #st, value As Integer

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

End

Public Procedure NoteOFF(flags As Byte, tick As Integer, channel As Byte, note As Byte, velocity As Byte)

 Dim err, tv_nsec, param_, value As Integer
 Dim tag, off_velocity As Byte

' Riporta il puntatore dello Stream all'inizio dell'area di memoria puntata dalla variabile "ev":
 Seek #st, 0
' Esegue una opportuna ripulitura dell'area di memoria:
 Repeat 
   Write #st, 0 As Byte
 Until Seek(st) == 28

' Riporta il puntatore dello Stream all'inizio dell'area di memoria puntata dalla variabile "ev":
 Seek #st, 0 Write #st, SND_SEQ_EVENT_NOTEON As Byte
 Write #st, flags As Byte
 Write #st, tag As Byte   
 Write #st, que As Byte
 Write #st, tick As Integer
 Write #st, tv_nsec As Integer
 Write #st, id As Byte
 Write #st, s_port As Byte
 Write #st, dclient As Byte
 Write #st, dport As Byte
 Write #st, channel As Byte
 Write #st, note As Byte
 Write #st, velocity As Byte
 Write #st, off_velocity As Byte
 Write #st, param_ As Integer
 Write #st, value As Integer

 err = snd_seq_event_output_buffer(handle, ev)
 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)
 st.Close
' Dealloca l'area di memoria precedentemente riservata e si assicura che il Puntatore non punti ad un indirizzo rilevante:
 Free(ev
 ev = 0

End

2° esempio pratico

Il secondo esempio pratico imposta il membro flags della Struttura snd_seq_event_t per usare la temporizzazione in "real time" (ossia usando il sistema di misura dell'orario), definita dalla Costante di ALSA:

#define SND_SEQ_TIME_STAMP_REAL     (1<<0)

In particolare a ciascun Evento Midi di ALSA sarà passato un valore, espresso in secondi, da assegnare al primo membro "tv_sec" della Struttura snd_seq_real_time_t appartenente al membro time della predetta Struttura snd_seq_event_t.
Dunque avremo la Classe principale "FMain.Class":

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

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, strum.Find("Violin"))
' 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, strum.Find("Reed Organ"))
' 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, strum.Find("Flute"))
' 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 medesimo codice impostato nel primo esempio sopra.


Note

[1] Quando è dichiarato un Pointer, Gambas riserva 8 byte tutti per lui (in un sistema a 64 bit), che rappresentano l'indirizzo di memoria contenuto dal Pointer. Quando viene poi manipolato, vengono toccati quegli otto byte. Ma l'unica operazione utile da compiere su un Pointer è la dereferenziazione, che si riferisce ad un'altra area di memoria, alla quale si accede attraverso quell'indirizzo di memoria individuato - come abbiamo detto - dai predetti 8 byte.

Il Pointer risulta così legato a due aree distinte di memoria operative:
- una dove sono scritti otto byte dell'indirizzo di memoria (cioè l'altra) contenente i byte del valore solitamente contenuto da una variabile;
- l'altra ove, come appena detto, sono realmente stipati i byte costituenti il valore contenuto da una variabile.

[2] Qualora volessimo spostare il puntatore interno dello stream ad uno specifico byte, dovremo utilizzare la funzione Seek, la quale esegue - come è noto - uno spiazzamento assoluto puntando dritto al byte di numero definito nella funzione medesima.