Differenze tra le versioni di "Alsa e Gambas: Invio dati con l'uso di una Classe specifica"

Da Gambas-it.org - Wikipedia.
 
(10 versioni intermedie di uno stesso utente non sono mostrate)
Riga 1: Riga 1:
===Preambolo===
+
===Introduzione===
 +
Per definire e gestire gli Eventi Midi di ALSA si può usare una Classe specifica, fornita di Proprietà in "lettura" e in "scrittura" che potranno essere usate per memorizzare i valori necessari dell'Evento Midi, secondo la Struttura ''[https://www.alsa-project.org/alsa-doc/alsa-lib/structsnd__seq__event.html snd_seq_event_t]'' prevista dal protocollo di ALSA.
 +
<BR>Dichiarando poi una variabile del tipo di quella Classe per ciascun Evento Midi ALSA, si avrà un'area di memoria utilizzabile.
 +
<BR>L'uso di tale Classe specifica sostituisce egregiamente e più dinamicamente la [[Strutture:_dichiarazione_ed_uso|Struttura]]. <SUP>&#091;[[#Note|nota 1]]&#093;</sup>
  
Prenderemo ora in considerazione l'uso di una Classe specifica, nella quale inseriremo le variabili che saranno successivamente riempite dai valori relativi ai dati Midi e necessari per la definizione dell'evento Midi particolare. Dichiarando poi una variabile del tipo di quella Classe per ciascun evento Midi si avrà un'area di memoria utilizzabile. L'uso di tale Classe specifica sostituisce egregiamente e più dinamicamente la [[Strutture:_dichiarazione_ed_uso|Struttura]]. <SUP>&#091;[[#Note|nota 1]]&#093;</sup>
 
  
 +
=Scrittura dei dati dei messaggi Midi nella Classe specifica dei dati Midi=
  
 
+
==Impostando e usando le Proprietà della Classe specifica==
=Scrittura dei dati dei messaggi Midi nella Classe specifica dei dati Midi=
+
La ''Classe'' modello specifica per la gestione delle risorse di ALSA, che chiameremo "CAlsa", va creata con tutte le Proprietà necessarie per la definizione di qualunque Evento Midi di ALSA e sarà così organizzata:
Viene innanzitutto creata ed impostata una ''Classe'' modello specifica che contiene, come già detto, tutte le Proprietà necessarie per la definizione di qualunque Evento Midi di ALSA.
 
 
  <FONT Color=#006400>' ''Definisce le Proprietà della Classe specifica:''</font>
 
  <FONT Color=#006400>' ''Definisce le Proprietà della Classe specifica:''</font>
  Property type As Byte
+
  Property Type As Byte
  Property flags As Byte
+
  Property Flags As Byte
  Property tag As Byte
+
  Property Tag As Byte
  Property queue As Byte
+
  Property Queue As Byte
  Property ticktime As Integer
+
  Property Tick_o_Tv_sec As Integer
  Property realtime As Integer
+
  Property Tv_nsec As Integer
  Property sourceclient As Byte
+
  Property Source_client As Byte
  Property sourceport As Byte
+
  Property Source_port As Byte
  Property destclient As Byte
+
  Property Dest_client As Byte
  Property destport As Byte
+
  Property Dest_port As Byte
  Property channel As Byte
+
  Property Channel As Byte
  Property note As Byte
+
  Property Note As Byte
  Property velocity As Byte
+
  Property Velocity As Byte
  Property off_velocity As Byte
+
  Property Off_velocity As Byte
  Property param As Integer
+
  Property Param As Integer
  Property value As Integer
+
  Property Value As Integer
 
   
 
   
 
  <FONT Color=#006400>' ''Definisce i simboli associati alle suddette Proprietà:''</font>
 
  <FONT Color=#006400>' ''Definisce i simboli associati alle suddette Proprietà:''</font>
  Private $type As Byte
+
  Private $Type As Byte
  Private $flags As Byte
+
  Private $Flags As Byte
  Private $tag As Byte
+
  Private $Tag As Byte
  Private $queue As Byte
+
  Private $Queue As Byte
  Private $ticktime As Integer
+
  Private $Tick_o_Tv_sec As Integer
  Private $realtime As Integer
+
  Private $Tv_nsec As Integer
  Private $sourceclient As Byte
+
  Private $Source_client As Byte
  Private $sourceport As Byte
+
  Private $Source_port As Byte
  Private $destclient As Byte
+
  Private $Dest_client As Byte
  Private $destport As Byte
+
  Private $Dest_port As Byte
  Private $channel As Byte
+
  Private $Channel As Byte
  Private $note As Byte
+
  Private $Note As Byte
  Private $velocity As Byte
+
  Private $Velocity As Byte
  Private $off_velocity As Byte
+
  Private $Off_velocity As Byte
  Private $param As Integer
+
  Private $Param As Integer
  Private $value As Integer
+
  Private $Value As Integer
 
   
 
   
  Private Function type_Read() As Byte
+
  Private Function Type_Read() As Byte
   Return $type
+
   Return $Type
 
  End
 
  End
 
   
 
   
  Private Sub type_Write(Value As Byte)
+
  Private Sub Type_Write(Value As Byte)
   $type = Value
+
   $Type = Value
 
  End
 
  End
 
   
 
   
  Private Function flags_Read() As Byte
+
  Private Function Flags_Read() As Byte
   Return $flags
+
   Return $Flags
 
  End
 
  End
 
   
 
   
  Private Sub flags_Write(Value As Byte)
+
  Private Sub Flags_Write(Value As Byte)
   $flags = Value
+
   $Flags = Value
 
  End
 
  End
 
   
 
   
  Private Function tag_Read() As Byte
+
  Private Function Tag_Read() As Byte
   Return $tag
+
   Return $Tag
 
  End
 
  End
 
   
 
   
  Private Sub tag_Write(Value As Byte)
+
  Private Sub Tag_Write(Value As Byte)
   $tag = Value
+
   $Tag = Value
 
  End
 
  End
 
   
 
   
  Private Function queue_Read() As Byte
+
  Private Function Queue_Read() As Byte
   Return $queue
+
   Return $Queue
 
  End
 
  End
 
   
 
   
  Private Sub queue_Write(Value As Byte)
+
  Private Sub Queue_Write(Value As Byte)
   $queue = Value
+
   $Queue = Value
 
  End
 
  End
 
   
 
   
  Private Function ticktime_Read() As Integer
+
  Private Function Tick_o_Tv_sec_Read() As Integer
   Return $ticktime
+
   Return $Tick_o_Tv_sec
 
  End
 
  End
 
   
 
   
  Private Sub ticktime_Write(Value As Integer)
+
  Private Sub Tick_o_Tv_sec_Write(Value As Integer)
   $ticktime = Value
+
   $Tick_o_Tv_sec = Value
 
  End
 
  End
 
   
 
   
  Private Function realtime_Read() As Integer
+
  Private Function Tv_nsec_Read() As Integer
   Return $realtime
+
   Return $Tv_nsec
 
  End
 
  End
 
   
 
   
  Private Sub realtime_Write(Value As Integer)
+
  Private Sub Tv_nsec_Write(Value As Integer)
   $realtime = Value
+
   $Tv_nsec = Value
 
  End
 
  End
 
   
 
   
  Private Function sourceclient_Read() As Byte
+
  Private Function Source_client_Read() As Byte
   Return $sourceclient
+
   Return $Source_client
 
  End
 
  End
 
   
 
   
  Private Sub sourceclient_Write(Value As Byte)
+
  Private Sub Source_client_Write(Value As Byte)
   $sourceclient = Value
+
   $Source_client = Value
 
  End
 
  End
 
   
 
   
  Private Function sourceport_Read() As Byte
+
  Private Function Source_port_Read() As Byte
   Return $sourceport
+
   Return $Source_port
 
  End
 
  End
 
   
 
   
  Private Sub sourceport_Write(Value As Byte)
+
  Private Sub Source_port_Write(Value As Byte)
   $sourceport = Value
+
   $Source_port = Value
 
  End
 
  End
 
   
 
   
  Private Function destclient_Read() As Byte
+
  Private Function Dest_client_Read() As Byte
   Return $destclient
+
   Return $Dest_client
 
  End
 
  End
 
   
 
   
  Private Sub destclient_Write(Value As Byte)
+
  Private Sub Dest_client_Write(Value As Byte)
   $destclient = Value
+
   $Dest_client = Value
 
  End
 
  End
 
   
 
   
  Private Function destport_Read() As Byte
+
  Private Function Dest_port_Read() As Byte
   Return $destport
+
   Return $Dest_port
 
  End
 
  End
 
   
 
   
  Private Sub destport_Write(Value As Byte)
+
  Private Sub Dest_port_Write(Value As Byte)
   $destport = Value
+
   $Dest_port = Value
 
  End
 
  End
 
   
 
   
  Private Function channel_Read() As Byte
+
  Private Function Channel_Read() As Byte
   Return $channel
+
   Return $Channel
 
  End
 
  End
 
   
 
   
  Private Sub channel_Write(Value As Byte)
+
  Private Sub Channel_Write(Value As Byte)
   $channel = Value
+
   $Channel = Value
 
  End
 
  End
 
   
 
   
  Private Function note_Read() As Byte
+
  Private Function Note_Read() As Byte
   Return $note
+
   Return $Note
 
  End
 
  End
 
   
 
   
  Private Sub note_Write(Value As Byte)
+
  Private Sub Note_Write(Value As Byte)
   $note = Value
+
   $Note = Value
 
  End
 
  End
 
   
 
   
  Private Function velocity_Read() As Byte
+
  Private Function Velocity_Read() As Byte
   Return $velocity
+
   Return $Velocity
 
  End
 
  End
 
   
 
   
  Private Sub velocity_Write(Value As Byte)
+
  Private Sub Velocity_Write(Value As Byte)
   $velocity = Value
+
   $Velocity = Value
 
  End
 
  End
 
   
 
   
  Private Function off_velocity_Read() As Byte
+
  Private Function Off_velocity_Read() As Byte
   Return $off_velocity
+
   Return $Off_velocity
 
  End
 
  End
 
   
 
   
  Private Sub off_velocity_Write(Value As Byte)
+
  Private Sub Off_velocity_Write(Value As Byte)
   $off_velocity = Value
+
   $Off_velocity = Value
 
  End
 
  End
 
   
 
   
  Private Function param_Read() As Integer
+
  Private Function Param_Read() As Integer
   Return $param
+
   Return $Param
 
  End
 
  End
 
   
 
   
  Private Sub param_Write(Value As Integer)
+
  Private Sub Param_Write(Value As Integer)
   $param = Value
+
   $Param = Value
 
  End
 
  End
 
   
 
   
  Private Function value_Read() As Integer
+
  Private Function Value_Read() As Integer
   Return $value
+
   Return $Value
 
  End
 
  End
 
   
 
   
  Private Sub value_Write(Value As Integer)
+
  Private Sub Value_Write(Value As Integer)
   $value = Value
+
   $Value = Value
 
  End
 
  End
  
=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, come già sappiamo, una disposizione all'interno della zona di memoria comune con altri messaggi, prevederemo - per ciascun gruppo di messaggi dalla struttura simile - delle variabili del tipo della Classe specifica ''CMidi'' apposite che vadano bene per i messaggi appartenenti a quel gruppo.
 
  
V'è da sottolineare che nella classe ''CAlsa.class'' la funzione ''snd_seq_event_output'' va modificata inserendo come secondo parametro nella sua dichiarazione con la funzione ''Extern'' il nome della Classe modello ''CMidi'':
+
==Esempio di codice==
Private Extern snd_seq_event_output(handle As Pointer, vC As <FONT color=#B22222>Cmidi</font>) As Integer
+
Mostriamo un codice in ambiente garfico che prevede tre Classi:
  
mentre all'interno delle routine, quando essa viene chiamata, va inserito il nome della variabile-classe specifica. Ad esempio:
+
- la prima è una Classe secondaria, che chiameremo '''''EventoMidi''''', ed è la Classe specifica per la gestione degli Eventi Midi di ALSA e sarà identica al modello di Classe specifica di un ''Evento Midi ALSA'' sopra descritto.
err = snd_seq_event_output(handle, <FONT color=#FF7000>ev_OnOffPoly</font>)
 
  
 +
- la seconda è un'altra Classe secondaria, che chiameremo '''''CAlsa''''', ed è la Classe per la gestione delle risorse di ALSA. In questa Classe saranno attribuiti alla variabile del tipo della Classe specifica della gestione degli ''Eventi Midi'' alcuni valori.
  
==Gruppo messaggi: <FONT Color= "red">NoteON - NoteOFF</font> - <FONT Color= "red">Polyphonic Aftertouch</font>==
+
- La terza Classe è quella principale e in essa saranno attribuiti alla variabile del tipo della Classe specifica della gestione degli ''Eventi Midi'' la maggior parte dei valori per il particolare ''Evento Midi'' in quel momento gestito.
Questi tre messaggi Midi hanno tra loro in comune tre medesimi specifici dati posti ai byte num. 16, 17 e 18 della Classe specifica modello, e che abbiamo chiamato: ''channel'', ''note'' e ''velocity''.
+
<BR>Il suo codice, che sarà il seguente, prevede che la ''temporizzazione'' degli ''Eventi Midi'', ivi gestiti, sia impostata in modalità ''tick time'' Midi:
 
+
Private strum As String[] = ["Acustic Grand Piano", "Bright Acustic Piano", "Electric Grand Piano", "Honky-tonk",
<P>Dichiareremo nella classe ''CAlsa.class'' una variabile ''ad hoc'' per questi tre messaggi Midi del tipo della Classe specifica modello ''CMidi'', della quale ultima assumerà così l'organizzazione interna delle particolari variabili ed il tipo dei valori:</p>
+
  "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",
  ev_OnOffPoly As New CMidi
+
  "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",
<P>Ora la nostra variabile ''ev_OnOffPoly'' è operativa e può essere utilizzata per la gestione ed il successivo invio dei messaggi Midi ad ALSA.</p>
+
  "Guitar Harmonics", "Acoustic Bass", "Electric Bass (finger)", "Electric Bass (pick)", "Fretless Bass", "Slap Bass 1",
<P>L'uso di tale variabile-Classe si ha quando si dovrà definire ''concretamente'' il messaggio Midi richiesto, fissando in ciascun elemento costituivo (le diverse variabili in essa contenute) i relativi valori. Si potrà evitare di richiamare le varibili particolari della Classe che eventualmente non servono.</p>
+
  "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",
<P>Quindi dalla classe principale chiameremo la routine specifica per i tre eventi qui considerati, passandogli anche tre valori necessari:</p>
+
  "SynthStrings 2", "Choir Aahs", "Voice Oohs", "Synth Voice", "Orchestra Hit", "Trumpet", "Trombone", "Tuba", "Muted Trumpet",
Public Sub eventoOnOffPoly(def_Evento As Byte, nota as byte, velocitas As Byte)
+
  "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>
 +
Private Const SND_SEQ_TIME_STAMP_TICK As Integer = 0
 +
Public alsa As CAlsa                              <FONT Color=gray>' ''Classe che incapsula le funzioni ALSA''</font>
 +
Public evmid As New Evento_Midi        <FONT Color=gray>' ''Classe delle Proprietà per gestire uno spcifico "Evento Midi" di 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
 +
<FONT Color=gray>' ''Avvia la "Coda" degli Eventi Midi ALSA:''</font>
 +
    alsa.AvvioCoda()
 +
<FONT Color=gray>' ''Imposta i valori specifici dell'Evento Midi "Control Change":''</font>
 +
    With evmid
 +
      .Flags = SND_SEQ_TIME_STAMP_TICK
 +
      .Tick_o_Tv_sec = 0
 +
      .Channel = 0
 +
      .Param = 7
 +
      .Value = 50
 +
    End With
 +
    alsa.ControlChange(evmid)
 
   
 
   
  Dim err As Integer
+
  <FONT Color=gray>' ''Imposta i valori specifici dell'Evento Midi "Program Change":''</font>
 +
    With evmid
 +
      .Flags = SND_SEQ_TIME_STAMP_TICK
 +
    .Tick_o_Tv_sec = 0
 +
    .Channel = 0
 +
    .Value = strum.Find("Violin")
 +
    End With
 +
    alsa.ProgramChange(evmid)
 
   
 
   
  With ev_OnOffPoly
+
<FONT Color=gray>' ''Imposta i valori specifici dell'Evento Midi "Midi-ON":''</font>
    .type = def_Evento          <FONT color=#006400>' ''= 6, 7 oppure 8''</font>
+
     With evmid
     .queue = outq
+
      .Flags = SND_SEQ_TIME_STAMP_TICK
    .source_id = id
+
      .Tick_o_Tv_sec = 0
    .source_porta = outport
+
      .Channel = 0
    .dest_id = SND_SEQ_ADDRESS_SUBSCRIBERS
+
      .Note = 64
    .dest_porta = SND_SEQ_ADDRESS_UNKNOWN
+
      .Velocity = 100
    .channel = 0
+
     End With
     .note = 60
+
     alsa.NoteON(evmid)
     .velocity = velocitas
 
  End With
 
 
   
 
   
  err = snd_seq_event_output(handle, <FONT color=#FF7000>ev_OnOffPoly</font>)    <FONT color=#006400>' ''si passano ad ALSA i valori presenti nella variabile-Classe: ev_OnOffPoly''</font>
+
<FONT Color=gray>' ''Imposta i valori specifici dell'Evento Midi "Midi-OFF":''</font>
  printerr("OnOffPoly = ", err)
+
    With evmid
 +
      .Flags = SND_SEQ_TIME_STAMP_TICK
 +
      .Tick_o_Tv_sec = 96
 +
      .Channel = 0
 +
      .Note = 64
 +
      .Velocity = 100
 +
    End With
 +
    alsa.NoteOFF(evmid)
 
   
 
   
  End
+
  <FONT Color=gray>' ''Imposta il suono di un altro strumento musicale dopo 150 tick dall'inizio della "Coda" (quindi crea una pausa):''</font>
 
+
    With evmid
 
+
      .Flags = SND_SEQ_TIME_STAMP_TICK
==Gruppo messaggi: <FONT Color= "red">Program Change</font> - <FONT Color= "red">Channel Aftertouch (Key Pressure)</font>==
+
      .Tick_o_Tv_sec = 150
Questi due messaggi Midi hanno tra loro in comune due medesimi specifici dati posti ai byte num. 16 e 24 della Classe ''CMidi.class'' modello, e che abbiamo chiamato: ''channel'' e ''valorePrimo''.
+
      .Channel = 0
 
+
      .Value = strum.Find("Reed Organ")
<P>Dichiareremo una variabile ''ad hoc'' anche per questi due messaggi Midi del tipo della Classe ''CMidi.class'' modello, della quale ultima assumerà così l'organizzazione interna delle particolari variabili ed il tipo dei valori:</p>
+
    End With
 
+
    alsa.ProgramChange(evmid)
ev_PgmKeyPress As New CMidi
 
 
 
<P>Ora la nostra variabile ''ev_PgmKeyPress'' è operativa e può essere utilizzata per la gestione ed il successivo invio dei messaggi Midi ad ALSA.</p>
 
<P>L'uso di tale variabile-Classe specifica si ha quando si dovrà definire ''concretamente'' il messaggio Midi richiesto, fissando in ciascun elemento costituivo (le diverse variabili in essa contenute) i relativi valori. Si potrà evitare di richiamare le varibili particolari della Classe che eventualmente non servono.</p>
 
 
 
<P>Quindi dalla classe principale chiameremo la routine specifica per i due eventi qui considerati, passandogli anche due valori necessari:</p>
 
 
 
Public Sub eventoPgmKeyPress(def_Evento As Byte, sec_Valore As Integer)
 
 
   
 
   
  Dim err As Integer
+
  <FONT Color=gray>' ''Imposta la nota Midi da eseguire dopo 182 tick dall'inizio della "Coda":''</font>
 +
    With evmid
 +
      .Flags = SND_SEQ_TIME_STAMP_TICK
 +
      .Tick_o_Tv_sec = 182
 +
      .Channel = 0
 +
      .Note = 66
 +
      .Velocity = 100
 +
    End With
 +
    alsa.NoteON(evmid)
 
   
 
   
  With ev_PgmKeyPress
+
<FONT Color=gray>' ''Imposta la nota Midi da silenziare dopo 300 tick dall'inizio della "Coda":''</font>
  .type = def_Evento          <FONT color=#006400>' ''= 11 oppure 12''</font>
+
    With evmid
  .queue = outq
+
      .Flags = SND_SEQ_TIME_STAMP_TICK
  .source_id = id
+
      .Tick_o_Tv_sec = 300
  .source_porta = outport
+
      .Channel = 0
  .dest_id = SND_SEQ_ADDRESS_SUBSCRIBERS
+
      .Note = 66
  .dest_porta = SND_SEQ_ADDRESS_UNKNOWN
+
      .Velocity = 0
  .channel = 0
+
    End With
  .value = sec_Valore        <FONT color=#006400>' ''impostato nella classe principale''</font>
+
    alsa.NoteOFF(evmid)
  End With
 
 
   
 
   
  <FONT color=#006400>' ''Si passano ad ALSA i valori presenti nella variabile-Classe: ev_PgmKeyPress:''</font>
+
  <FONT Color=gray>' ''Imposta il suono di un altro strumento musicale dopo 300 tick dall'inizio della "Coda":''</font>
  err = snd_seq_event_output(handle, <FONT color=#FF7000>ev_PgmKeyPress</font>)
+
    With evmid
  printerr("PgmKeyPress = ", err)
+
      .Flags = SND_SEQ_TIME_STAMP_TICK
 +
      .Tick_o_Tv_sec = 300
 +
      .Channel = 0
 +
      .Value = strum.Find("Flute")
 +
    End With
 +
    alsa.ProgramChange(evmid)
 
   
 
   
  End
+
  <FONT Color=gray>' ''Imposta la nota Midi da eseguire dopo 300 tick dall'inizio della "Coda":''</font>
 
+
    With evmid
 
+
      .Flags = SND_SEQ_TIME_STAMP_TICK
==Gruppo messaggi: <FONT Color= "red">Control Change</font> - <FONT Color= "red">Pitch Bend (Pitch Wheel)</font>==
+
      .Tick_o_Tv_sec = 300
Questi due messaggi Midi hanno tra loro in comune tre medesimi specifici dati posti ai byte num. 16, 20 e 24 della Classe ''CMidi.class'' modello, e che abbiamo chiamato: ''channel'', ''primoValore'' e ''secondoValore''.
+
      .Channel = 0
 
+
      .Note = 68
<P>Dichiareremo una variabile ''ad hoc'' anche per questi due messaggi Midi del tipo della Classe ''CMidi.class'' modello, della quale ultima assumerà così l'organizzazione interna delle particolari variabili ed il tipo dei valori:</p>
+
      .Velocity = 100
 
+
    End With
ev_CtrlPitBnd As New CMidi
+
    alsa.NoteON(evmid)
 
 
<P>Ora la nostra variabile-Classe ''ev_CtrlPitBnd'' è operativa e può essere utilizzata per la gestione ed il successivo invio dei messaggi Midi ad ALSA.</p>
 
<P>L'uso di tale variabile-Classe specifica si ha quando si dovrà definire ''concretamente'' il messaggio Midi richiesto, fissando in ciascun elemento costituivo (le diverse variabili in essa contenute) i relativi valori. Si potrà evitare di richiamare le varibili particolari della Classe che eventualmente non servono.</p>
 
 
 
<P>Quindi dalla classe principale chiameremo la routine specifica per i due eventi qui considerati, passandogli anche tre valori necessari:</p>
 
 
 
Public Sub eventoPgmKeyPress(def_Evento As Byte, prm_Valore As Integer, sec_Valore As Integer)
 
 
   
 
   
  Dim err As Integer
+
  <FONT Color=gray>' ''Imposta la nota Midi da silenziare dopo 500 tick dall'inizio della "Coda":''</font>
 +
    With evmid
 +
      .Flags = SND_SEQ_TIME_STAMP_TICK
 +
      .Tick_o_Tv_sec = 500
 +
      .Channel = 0
 +
      .Note = 68
 +
      .Velocity = 0
 +
    End With
 +
    alsa.NoteOFF(evmid)
 
   
 
   
  With ev_CtrlPitBnd
+
<FONT Color=gray>' ''Dispone infine l'invio di tutti gli Eventi Midi bufferizzati nella "Coda":''</font>
    .type = def_Evento          <FONT color=#006400>' ''= 10 oppure 13''</font>
+
     alsa.Flush()
     .queue = outq
+
  Else
    .source_id = id
+
     alsa.StopCoda()
    .source_porta = outport
+
     Me.Close
    .dest_id = SND_SEQ_ADDRESS_SUBSCRIBERS
+
   Endif
     .dest_porta = SND_SEQ_ADDRESS_UNKNOWN
 
     .channel = 0
 
    .param = prm_Valore        <FONT color=#006400>' ''impostato nella classe principale''</font>
 
    .value = sec_Valore        <FONT color=#006400>' ''impostato nella classe principale''</font>
 
   End With
 
 
   
 
   
  err = snd_seq_event_output(handle, <FONT color=#FF7000>ev_CtrlPitBnd</font>)    <FONT color=#006400>' ''si passano ad ALSA i valori presenti nella variabile-Classe: CtrlPitBnd''</font>
+
'''End'''
  printerr("CtrlPitBnd = ", err)
+
La Classe secondaria ''CAlsa'' avrà 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
 
   
 
   
End
 
<BR>Da notare che per il Pitch Bend il dato ''primoValore'' è ininfluente, e può essere quindi fissato a 0.
 
 
 
==Esempio di codice==
 
Mostriamo ora il codice che sarà inserito nel Modulo di avvio principale, dal quale si richiamerà la Classe specifica per la gestione degli Eventi Midi di ALSA, che qui sarà chiamata ''EventoMidiAlsa'':
 
Public ev_midi As EventoMidiAlsa    <FONT color=gray>' ''Dichiara la variabile del tipo della Classe "EventoMidiAlsa"''</font>
 
 
   
 
   
  Library "libasound:2"
+
  Library "libasound:2.0.0"
 
   
 
   
  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 306: Riga 358:
 
  ' ''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 315: Riga 363:
 
  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 EventoMidi) 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 340: Riga 392:
 
   
 
   
 
   
 
   
  '''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
+
    
+
   err = snd_seq_open(VarPtr(handle), "default", SND_SEQ_OPEN_OUTPUT, 0)
<FONT Color=gray>' ''Crea un'istanza della Classe specifica "EventoMidiAlsa", con la quale saranno richiamate e gestite le Proprietà della Classe:''</font>
+
   Print "Apertura del subsistema 'seq' di ALSA = "; IIf(err == 0, "corretta !", "errata !")
   ev_midi = New EventoMidiAlsa
 
 
   err = snd_seq_open(VarPtr(handle), "default", SND_SEQ_OPEN_DUPLEX, 0)
 
   Print "Apertura del subsistema 'seq' di ALSA = "; err
 
 
   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
 
   
 
   
  <FONT Color=gray>' '''Program Change'''</font>
+
  '''End'''
   With ev_midi
+
    .type = SND_SEQ_EVENT_PGMCHANGE
+
'''Public''' Procedure AvvioCoda()
    .flags = 0
+
    .tag = 0
+
   Dim err As Integer
    .queue = que
+
    .ticktime = 0
+
  err = snd_seq_control_queue(handle, que, SND_SEQ_EVENT_START, 0, 0)
    .realtime = 0
+
  If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))
     .sourceclient = id
+
 
     .sourceport = porta
+
'''End'''
     .destclient = SND_SEQ_ADDRESS_SUBSCRIBERS
+
     .destport = SND_SEQ_ADDRESS_UNKNOWN
+
'''Public''' Procedure ControlChange(mid As Evento_Midi)
     .channel = 0
+
     .value = 44    ' Imposta il suono dello strumento musicale da usare
+
  Dim err As Integer
 +
 +
  With mid
 +
     .Type = SND_SEQ_EVENT_CONTROLLER
 +
     .Queue = que
 +
     .Source_client = id
 +
     .Source_port = s_port
 +
     .Dest_client = dclient
 +
     .Dest_port = dport
 
   End With
 
   End With
 
   
 
   
   InviaEventoMidi(handle, ev_midi)
+
   err = snd_seq_event_output_buffer(handle, mid)
 +
  If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))
 
   
 
   
  <FONT Color=gray>' '''Note ON'''</font>
+
  '''End'''
   With ev_midi
+
    .type = SND_SEQ_EVENT_NOTEON
+
'''Public''' Procedure ProgramChange(mid As Evento_Midi)
    .flags = 0
+
     .tag = 0
+
   Dim err As Integer
     .queue = que
+
     .ticktime = 0
+
  With mid
    .realtime = 0
+
     .Type = SND_SEQ_EVENT_PGMCHANGE
    .sourceclient = id
+
     .Queue = que
     .sourceport = porta
+
     .Source_client = id
     .destclient = SND_SEQ_ADDRESS_SUBSCRIBERS
+
     .Source_port = s_port
     .destport = SND_SEQ_ADDRESS_UNKNOWN
+
     .Dest_client = dclient
    .channel = 0
+
     .Dest_port = dport
    .note = 64        <FONT Color=gray>' ''Imposta una nota Midi da eseguire''</font>
 
    .velocity = 100  <FONT Color=gray>' ''Imposta la velocità di tocco''</font>
 
 
   End With
 
   End With
 
   
 
   
   InviaEventoMidi(handle, ev_midi)
+
   err = snd_seq_event_output_buffer(handle, mid)
 +
  If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))
 +
 +
'''End'''
 
   
 
   
  <FONT Color=gray>' ''Imposta la durata dell'esecuzione della nota:''</font>
+
  '''Public''' Procedure NoteON(mid As Evento_Midi)
  Wait 3
 
 
   
 
   
  <FONT Color=gray>' '''Note OFF'''</font>
+
  Dim err As Integer
   With ev_midi
+
   
     .type = SND_SEQ_EVENT_NOTEOFF
+
   With mid
     .flags = 0
+
     .Type = SND_SEQ_EVENT_NOTEON
    .tag = 0
+
     .Queue = que
    .queue = que
+
     .Source_client = id
     .ticktime = 0
+
     .Source_port = s_port
    .realtime = 0
+
     .Dest_client = dclient
    .sourceclient = id
+
     .Dest_port = dport
     .sourceport = porta
 
     .destclient = SND_SEQ_ADDRESS_SUBSCRIBERS
 
     .destport = SND_SEQ_ADDRESS_UNKNOWN
 
    .channel = 0
 
    .note = 64        <FONT Color=gray>' ''Imposta una nota Midi da eseguire''</font>
 
    .velocity = 0    <FONT Color=gray>' ''Imposta la velocità di tocco''</font>
 
 
   End With
 
   End With
 
   
 
   
   InviaEventoMidi(handle, ev_midi)
+
   err = snd_seq_event_output_buffer(handle, mid)
 +
  If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))
 +
 +
'''End'''
 +
 +
'''Public''' Procedure NoteOFF(mid As Evento_Midi)
 +
 +
  Dim err As Integer
 +
 +
  With mid
 +
    .Type = SND_SEQ_EVENT_NOTEOFF
 +
    .Queue = que
 +
    .Source_client = id
 +
    .Source_port = s_port
 +
    .Dest_client = dclient
 +
    .Dest_port = dport
 +
  End With
 
   
 
   
<FONT Color=gray>' ''Va in Chiusura:''</font>
+
  err = snd_seq_event_output_buffer(handle, mid)
   snd_seq_close(handle)
+
   If err < 0 Then Error.Raise("Errore: " & snd_strerror(err))
 
   
 
   
 
  '''End'''
 
  '''End'''
 
   
 
   
  '''Private''' Procedure InviaEventoMidi(p As Pointer, ev_midi As EventoMidiAlsa)
+
  '''Public''' Procedure Flush()
 
   
 
   
 
   Dim err As Integer
 
   Dim err As Integer
 
   
 
   
   err = snd_seq_event_output(p, ev_midi)
+
   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'''
 
   
 
   
   err = snd_seq_drain_output(p)
+
'''Public''' Procedure StopCoda()
   If err < 0 Then error.Raise("Errore: " & snd_strerror(err))
+
 +
  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'''
 +
 +
 +
==Impostando e usando solo variabili ''Globali'' per ciascun dato della Classe specifica==
 +
La Classe specifica della gestione degli Eventi Midi di ALSA può anche solo contenere singole variabili di tipo ''Globale'', ciascuna delle quali rappresenta un membro:
 +
Public type As Byte
 +
Public flag As Byte
 +
Public tag As Byte
 +
Public queue As Byte
 +
Public tick_o_tv_sec As Integer
 +
Public tv_nsec As Integer
 +
Public source_client As Byte
 +
Public source_port As Byte
 +
Public dest_client As Byte
 +
Public dest_port As Byte
 +
Public channel As Byte
 +
Public note As Byte
 +
Public velocity As Byte
 +
Public off_velocity As Byte
 +
Public param As Integer
 +
Public value As Integer
 +
Ovviamente nel codice pricipale di Gambas si creerà un Oggetto (istanza) della Classe specifica degli Eventi Midi ALSA, così costituita, e le predette variabili ''globali'' saranno richiamate nel modo consueto attraverso la variabile identificatrice della Classe specifica.
  
  

Versione attuale delle 09:37, 4 feb 2022

Introduzione

Per definire e gestire gli Eventi Midi di ALSA si può usare una Classe specifica, fornita di Proprietà in "lettura" e in "scrittura" che potranno essere usate per memorizzare i valori necessari dell'Evento Midi, secondo la Struttura snd_seq_event_t prevista dal protocollo di ALSA.
Dichiarando poi una variabile del tipo di quella Classe per ciascun Evento Midi ALSA, si avrà un'area di memoria utilizzabile.
L'uso di tale Classe specifica sostituisce egregiamente e più dinamicamente la Struttura. [nota 1]


Scrittura dei dati dei messaggi Midi nella Classe specifica dei dati Midi

Impostando e usando le Proprietà della Classe specifica

La Classe modello specifica per la gestione delle risorse di ALSA, che chiameremo "CAlsa", va creata con tutte le Proprietà necessarie per la definizione di qualunque Evento Midi di ALSA e sarà così organizzata:

' Definisce le Proprietà della Classe specifica:
Property Type As Byte
Property Flags As Byte
Property Tag As Byte
Property Queue As Byte
Property Tick_o_Tv_sec As Integer
Property Tv_nsec As Integer
Property Source_client As Byte
Property Source_port As Byte
Property Dest_client As Byte
Property Dest_port As Byte
Property Channel As Byte
Property Note As Byte
Property Velocity As Byte
Property Off_velocity As Byte
Property Param As Integer
Property Value As Integer

' Definisce i simboli associati alle suddette Proprietà:
Private $Type As Byte
Private $Flags As Byte
Private $Tag As Byte
Private $Queue As Byte
Private $Tick_o_Tv_sec As Integer
Private $Tv_nsec As Integer
Private $Source_client As Byte
Private $Source_port As Byte
Private $Dest_client As Byte
Private $Dest_port As Byte
Private $Channel As Byte
Private $Note As Byte
Private $Velocity As Byte
Private $Off_velocity As Byte
Private $Param As Integer
Private $Value As Integer

Private Function Type_Read() As Byte
  Return $Type
End

Private Sub Type_Write(Value As Byte)
  $Type = Value
End

Private Function Flags_Read() As Byte
  Return $Flags
End

Private Sub Flags_Write(Value As Byte)
  $Flags = Value
End

Private Function Tag_Read() As Byte
  Return $Tag
End

Private Sub Tag_Write(Value As Byte)
  $Tag = Value
End

Private Function Queue_Read() As Byte
  Return $Queue
End

Private Sub Queue_Write(Value As Byte)
  $Queue = Value
End

Private Function Tick_o_Tv_sec_Read() As Integer
  Return $Tick_o_Tv_sec
End

Private Sub Tick_o_Tv_sec_Write(Value As Integer)
  $Tick_o_Tv_sec = Value
End

Private Function Tv_nsec_Read() As Integer
  Return $Tv_nsec
End

Private Sub Tv_nsec_Write(Value As Integer)
  $Tv_nsec = Value
End

Private Function Source_client_Read() As Byte
  Return $Source_client
End

Private Sub Source_client_Write(Value As Byte)
  $Source_client = Value
End

Private Function Source_port_Read() As Byte
  Return $Source_port
End

Private Sub Source_port_Write(Value As Byte)
  $Source_port = Value
End

Private Function Dest_client_Read() As Byte
  Return $Dest_client
End

Private Sub Dest_client_Write(Value As Byte)
  $Dest_client = Value
End

Private Function Dest_port_Read() As Byte
  Return $Dest_port
End

Private Sub Dest_port_Write(Value As Byte)
  $Dest_port = Value
End

Private Function Channel_Read() As Byte
  Return $Channel
End

Private Sub Channel_Write(Value As Byte)
  $Channel = Value
End

Private Function Note_Read() As Byte
  Return $Note
End

Private Sub Note_Write(Value As Byte)
  $Note = Value
End

Private Function Velocity_Read() As Byte
  Return $Velocity
End

Private Sub Velocity_Write(Value As Byte)
  $Velocity = Value
End

Private Function Off_velocity_Read() As Byte
  Return $Off_velocity
End

Private Sub Off_velocity_Write(Value As Byte)
  $Off_velocity = Value
End

Private Function Param_Read() As Integer
  Return $Param
End

Private Sub Param_Write(Value As Integer)
  $Param = Value
End

Private Function Value_Read() As Integer
  Return $Value
End

Private Sub Value_Write(Value As Integer)
  $Value = Value
End


Esempio di codice

Mostriamo un codice in ambiente garfico che prevede tre Classi:

- la prima è una Classe secondaria, che chiameremo EventoMidi, ed è la Classe specifica per la gestione degli Eventi Midi di ALSA e sarà identica al modello di Classe specifica di un Evento Midi ALSA sopra descritto.

- la seconda è un'altra Classe secondaria, che chiameremo CAlsa, ed è la Classe per la gestione delle risorse di ALSA. In questa Classe saranno attribuiti alla variabile del tipo della Classe specifica della gestione degli Eventi Midi alcuni valori.

- La terza Classe è quella principale e in essa saranno attribuiti alla variabile del tipo della Classe specifica della gestione degli Eventi Midi la maggior parte dei valori per il particolare Evento Midi in quel momento gestito.
Il suo codice, che sarà il seguente, prevede che la temporizzazione degli Eventi Midi, ivi gestiti, sia impostata in modalità tick time Midi:

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
Private Const SND_SEQ_TIME_STAMP_TICK As Integer = 0
Public alsa As CAlsa                              ' Classe che incapsula le funzioni ALSA
Public evmid As New Evento_Midi         ' Classe delle Proprietà per gestire uno spcifico "Evento Midi" di 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
' Avvia la "Coda" degli Eventi Midi ALSA:
   alsa.AvvioCoda()
' Imposta i valori specifici dell'Evento Midi "Control Change":
   With evmid
     .Flags = SND_SEQ_TIME_STAMP_TICK 
     .Tick_o_Tv_sec = 0
     .Channel = 0
     .Param = 7
     .Value = 50
   End With
   alsa.ControlChange(evmid)

' Imposta i valori specifici dell'Evento Midi "Program Change":
   With evmid
     .Flags = SND_SEQ_TIME_STAMP_TICK 
   .Tick_o_Tv_sec = 0
   .Channel = 0
   .Value = strum.Find("Violin")
   End With
   alsa.ProgramChange(evmid)

' Imposta i valori specifici dell'Evento Midi "Midi-ON":
   With evmid
     .Flags = SND_SEQ_TIME_STAMP_TICK 
     .Tick_o_Tv_sec = 0
     .Channel = 0
     .Note = 64
     .Velocity = 100
   End With
   alsa.NoteON(evmid)

' Imposta i valori specifici dell'Evento Midi "Midi-OFF":
   With evmid
     .Flags = SND_SEQ_TIME_STAMP_TICK 
     .Tick_o_Tv_sec = 96
     .Channel = 0
     .Note = 64
     .Velocity = 100
   End With
   alsa.NoteOFF(evmid)

' Imposta il suono di un altro strumento musicale dopo 150 tick dall'inizio della "Coda" (quindi crea una pausa):
   With evmid
     .Flags = SND_SEQ_TIME_STAMP_TICK 
     .Tick_o_Tv_sec = 150
     .Channel = 0
     .Value = strum.Find("Reed Organ")
   End With
   alsa.ProgramChange(evmid)

' Imposta la nota Midi da eseguire dopo 182 tick dall'inizio della "Coda":
   With evmid
     .Flags = SND_SEQ_TIME_STAMP_TICK 
     .Tick_o_Tv_sec = 182
     .Channel = 0
     .Note = 66
     .Velocity = 100
   End With
   alsa.NoteON(evmid)

' Imposta la nota Midi da silenziare dopo 300 tick dall'inizio della "Coda":
   With evmid
     .Flags = SND_SEQ_TIME_STAMP_TICK 
     .Tick_o_Tv_sec = 300
     .Channel = 0
     .Note = 66
     .Velocity = 0
   End With
   alsa.NoteOFF(evmid)

' Imposta il suono di un altro strumento musicale dopo 300 tick dall'inizio della "Coda":
   With evmid
     .Flags = SND_SEQ_TIME_STAMP_TICK 
     .Tick_o_Tv_sec = 300
     .Channel = 0
     .Value = strum.Find("Flute")
   End With
   alsa.ProgramChange(evmid)

' Imposta la nota Midi da eseguire dopo 300 tick dall'inizio della "Coda":
   With evmid
     .Flags = SND_SEQ_TIME_STAMP_TICK 
     .Tick_o_Tv_sec = 300
     .Channel = 0
     .Note = 68
     .Velocity = 100
   End With
   alsa.NoteON(evmid)

' Imposta la nota Midi da silenziare dopo 500 tick dall'inizio della "Coda":
   With evmid
     .Flags = SND_SEQ_TIME_STAMP_TICK 
     .Tick_o_Tv_sec = 500
     .Channel = 0
     .Note = 68
     .Velocity = 0
   End With
   alsa.NoteOFF(evmid)

' Dispone infine l'invio di tutti gli Eventi Midi bufferizzati nella "Coda":
   alsa.Flush()
 Else
   alsa.StopCoda()
   Me.Close
 Endif

End

La Classe secondaria CAlsa avrà 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"

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 EventoMidi) 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(mid As Evento_Midi)

 Dim err As Integer

 With mid
   .Type = SND_SEQ_EVENT_CONTROLLER
   .Queue = que
   .Source_client = id
   .Source_port = s_port
   .Dest_client = dclient
   .Dest_port = dport
 End With

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

End

Public Procedure ProgramChange(mid As Evento_Midi)

 Dim err As Integer

 With mid
   .Type = SND_SEQ_EVENT_PGMCHANGE
   .Queue = que
   .Source_client = id
   .Source_port = s_port
   .Dest_client = dclient
   .Dest_port = dport
 End With

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

End

Public Procedure NoteON(mid As Evento_Midi)

 Dim err As Integer

 With mid
   .Type = SND_SEQ_EVENT_NOTEON
   .Queue = que
   .Source_client = id
   .Source_port = s_port
   .Dest_client = dclient
   .Dest_port = dport
 End With

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

End

Public Procedure NoteOFF(mid As Evento_Midi)

 Dim err As Integer

 With mid
   .Type = SND_SEQ_EVENT_NOTEOFF
   .Queue = que
   .Source_client = id
   .Source_port = s_port
   .Dest_client = dclient
   .Dest_port = dport
 End With

 err = snd_seq_event_output_buffer(handle, mid)
 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


Impostando e usando solo variabili Globali per ciascun dato della Classe specifica

La Classe specifica della gestione degli Eventi Midi di ALSA può anche solo contenere singole variabili di tipo Globale, ciascuna delle quali rappresenta un membro:

Public type As Byte
Public flag As Byte
Public tag As Byte
Public queue As Byte
Public tick_o_tv_sec As Integer
Public tv_nsec As Integer
Public source_client As Byte
Public source_port As Byte
Public dest_client As Byte
Public dest_port As Byte
Public channel As Byte
Public note As Byte
Public velocity As Byte
Public off_velocity As Byte
Public param As Integer
Public value As Integer

Ovviamente nel codice pricipale di Gambas si creerà un Oggetto (istanza) della Classe specifica degli Eventi Midi ALSA, così costituita, e le predette variabili globali saranno richiamate nel modo consueto attraverso la variabile identificatrice della Classe specifica.


Note

[1] In Gambas una "Struttura " può anche essere sostituita con una Classe avente solo Proprietà (dunque senza Metodi né Eventi).