Alsa e Gambas: Invio dati con l'uso di una Classe specifica
Indice
Preambolo
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 Struttura. [nota 1]
Scrittura dei dati dei messaggi Midi nella Classe specifica dei dati Midi
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.
' Definisce le Proprietà della Classe specifica: Property type As Byte Property flags As Byte Property tag As Byte Property queue As Byte Property ticktime As Integer Property realtime As Integer Property sourceclient As Byte Property sourceport As Byte Property destclient As Byte Property destport 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 $ticktime As Integer Private $realtime As Integer Private $sourceclient As Byte Private $sourceport As Byte Private $destclient As Byte Private $destport 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 ticktime_Read() As Integer Return $ticktime End Private Sub ticktime_Write(Value As Integer) $ticktime = Value End Private Function realtime_Read() As Integer Return $realtime End Private Sub realtime_Write(Value As Integer) $realtime = Value End Private Function sourceclient_Read() As Byte Return $sourceclient End Private Sub sourceclient_Write(Value As Byte) $sourceclient = Value End Private Function sourceport_Read() As Byte Return $sourceport End Private Sub sourceport_Write(Value As Byte) $sourceport = Value End Private Function destclient_Read() As Byte Return $destclient End Private Sub destclient_Write(Value As Byte) $destclient = Value End Private Function destport_Read() As Byte Return $destport End Private Sub destport_Write(Value As Byte) $destport = 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
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:
Private Extern snd_seq_event_output(handle As Pointer, vC As Cmidi) As Integer
mentre all'interno delle routine, quando essa viene chiamata, va inserito il nome della variabile-classe specifica. Ad esempio:
err = snd_seq_event_output(handle, ev_OnOffPoly)
Gruppo messaggi: NoteON - NoteOFF - Polyphonic Aftertouch
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.
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:
ev_OnOffPoly As New CMidi
Ora la nostra variabile ev_OnOffPoly è operativa e può essere utilizzata per la gestione ed il successivo invio dei messaggi Midi ad ALSA.
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.
Quindi dalla classe principale chiameremo la routine specifica per i tre eventi qui considerati, passandogli anche tre valori necessari:
Public Sub eventoOnOffPoly(def_Evento As Byte, nota as byte, velocitas As Byte) Dim err As Integer With ev_OnOffPoly .type = def_Evento ' = 6, 7 oppure 8 .queue = outq .source_id = id .source_porta = outport .dest_id = SND_SEQ_ADDRESS_SUBSCRIBERS .dest_porta = SND_SEQ_ADDRESS_UNKNOWN .channel = 0 .note = 60 .velocity = velocitas End With err = snd_seq_event_output(handle, ev_OnOffPoly) ' si passano ad ALSA i valori presenti nella variabile-Classe: ev_OnOffPoly printerr("OnOffPoly = ", err) End
Gruppo messaggi: Program Change - Channel Aftertouch (Key Pressure)
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.
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:
ev_PgmKeyPress As New CMidi
Ora la nostra variabile ev_PgmKeyPress è operativa e può essere utilizzata per la gestione ed il successivo invio dei messaggi Midi ad ALSA.
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.
Quindi dalla classe principale chiameremo la routine specifica per i due eventi qui considerati, passandogli anche due valori necessari:
Public Sub eventoPgmKeyPress(def_Evento As Byte, sec_Valore As Integer) Dim err As Integer With ev_PgmKeyPress .type = def_Evento ' = 11 oppure 12 .queue = outq .source_id = id .source_porta = outport .dest_id = SND_SEQ_ADDRESS_SUBSCRIBERS .dest_porta = SND_SEQ_ADDRESS_UNKNOWN .channel = 0 .value = sec_Valore ' impostato nella classe principale End With ' Si passano ad ALSA i valori presenti nella variabile-Classe: ev_PgmKeyPress: err = snd_seq_event_output(handle, ev_PgmKeyPress) printerr("PgmKeyPress = ", err) End
Gruppo messaggi: Control Change - Pitch Bend (Pitch Wheel)
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.
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:
ev_CtrlPitBnd As New CMidi
Ora la nostra variabile-Classe ev_CtrlPitBnd è operativa e può essere utilizzata per la gestione ed il successivo invio dei messaggi Midi ad ALSA.
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.
Quindi dalla classe principale chiameremo la routine specifica per i due eventi qui considerati, passandogli anche tre valori necessari:
Public Sub eventoPgmKeyPress(def_Evento As Byte, prm_Valore As Integer, sec_Valore As Integer) Dim err As Integer With ev_CtrlPitBnd .type = def_Evento ' = 10 oppure 13 .queue = outq .source_id = id .source_porta = outport .dest_id = SND_SEQ_ADDRESS_SUBSCRIBERS .dest_porta = SND_SEQ_ADDRESS_UNKNOWN .channel = 0 .param = prm_Valore ' impostato nella classe principale .value = sec_Valore ' impostato nella classe principale End With err = snd_seq_event_output(handle, ev_CtrlPitBnd) ' si passano ad ALSA i valori presenti nella variabile-Classe: CtrlPitBnd printerr("CtrlPitBnd = ", err) End
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 ' Dichiara la variabile del tipo della Classe "EventoMidiAlsa" Library "libasound:2" Private Const SND_SEQ_OPEN_DUPLEX As Integer = 3 Private Const SND_SEQ_PORT_TYPE_MIDI_GENERIC As Integer = 2 Private Const SND_SEQ_PORT_TYPE_APPLICATION As Integer = 1048576 Private Const SND_SEQ_ADDRESS_UNKNOWN As Byte = 253 Private Const SND_SEQ_ADDRESS_SUBSCRIBERS As Byte = 254 Private Enum SND_SEQ_EVENT_NOTEON = 6, SND_SEQ_EVENT_NOTEOFF, SND_SEQ_EVENT_KEYPRESS, SND_SEQ_EVENT_CONTROLLER = 10, SND_SEQ_EVENT_PGMCHANGE, SND_SEQ_EVENT_CHANPRESS, SND_SEQ_EVENT_PITCHBEND ' int snd_seq_open (snd_seq_t **seqp, const char * name, int streams, int mode) ' Open the ALSA sequencer. Private Extern snd_seq_open(seqp As Pointer, name As String, streams As Integer, mode As Integer) As Integer ' int snd_seq_set_client_name (snd_seq_t* seq, const char* name) ' Set client name. Private Extern snd_seq_set_client_name(seq As Pointer, name As String) As Integer ' int snd_seq_create_simple_port (snd_seq_t* seq, const char* name, unsigned int caps, unsigned int type) ' Creates a port with the given capability and type bits. Private Extern snd_seq_create_simple_port(seq As Pointer, name As String, caps As Integer, type As Integer) As Integer ' int snd_seq_client_id (snd_seq_t * seq) ' Get the client id. Private Extern snd_seq_client_id(seq As Pointer) As Integer ' int snd_seq_connect_to ( snd_seq_t * seq, int myport, int dest_client, int dest_port ) ' Connect from the given receiver port in the current client to the given destination client:port. Private Extern snd_seq_connect_to(seq As Pointer, myport As Integer, dest_client As Integer, dest_port As Integer) As Integer ' int snd_seq_alloc_named_queue (snd_seq_t * seq, CONST char * name) ' Allocate a queue. Private Extern snd_seq_alloc_queue(seq As Pointer, name As String) As Integer ' int snd_seq_event_output (snd_seq_t * seq, snd_seq_event_t *ev) ' Output an event. Private Extern snd_seq_event_output(seq As Pointer, ev As EventoMidiAlsa) As Integer ' int snd_seq_drain_output (snd_seq_t * seq) ' Drain output buffer to sequencer. Private Extern snd_seq_drain_output(seq As Pointer) As Integer ' const char * snd_strerror (int errnum) ' Returns the message for an error code. Private Extern snd_strerror(errnum As Integer) As String ' int snd_seq_close (snd_seq_t * seq) ' Close the sequencer. Private Extern snd_seq_close(seq As Pointer) As Integer Public Sub Main() Dim handle As Pointer Dim err, id, porta, que As Integer ' Crea un'istanza della Classe specifica "EventoMidiAlsa", con la quale saranno richiamate e gestite le Proprietà della Classe: 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)) snd_seq_set_client_name(handle, "Test") id = snd_seq_client_id(handle) Print "Alsa ClientID = "; id porta = snd_seq_create_simple_port(handle, "Uscita", 0, SND_SEQ_PORT_TYPE_MIDI_GENERIC + SND_SEQ_PORT_TYPE_APPLICATION) Print "Porta del programma = "; porta If porta < 0 Then error.Raise("Errore: " & snd_strerror(porta)) que = snd_seq_alloc_queue(handle, "queue") If que < 0 Then error.Raise("Errore: " & snd_strerror(que)) ' Assume che l'ID/porta dell'altro Client (Softsynth) sia 128:0 err = snd_seq_connect_to(handle, porta, 128, 0) If err < 0 Then error.Raise("Errore: " & snd_strerror(err)) ' Program Change With ev_midi .type = SND_SEQ_EVENT_PGMCHANGE .flags = 0 .tag = 0 .queue = que .ticktime = 0 .realtime = 0 .sourceclient = id .sourceport = porta .destclient = SND_SEQ_ADDRESS_SUBSCRIBERS .destport = SND_SEQ_ADDRESS_UNKNOWN .channel = 0 .value = 44 ' Imposta il suono dello strumento musicale da usare End With InviaEventoMidi(handle, ev_midi) ' Note ON With ev_midi .type = SND_SEQ_EVENT_NOTEON .flags = 0 .tag = 0 .queue = que .ticktime = 0 .realtime = 0 .sourceclient = id .sourceport = porta .destclient = SND_SEQ_ADDRESS_SUBSCRIBERS .destport = SND_SEQ_ADDRESS_UNKNOWN .channel = 0 .note = 64 ' Imposta una nota Midi da eseguire .velocity = 100 ' Imposta la velocità di tocco End With InviaEventoMidi(handle, ev_midi) ' Imposta la durata dell'esecuzione della nota: Wait 3 ' Note OFF With ev_midi .type = SND_SEQ_EVENT_NOTEOFF .flags = 0 .tag = 0 .queue = que .ticktime = 0 .realtime = 0 .sourceclient = id .sourceport = porta .destclient = SND_SEQ_ADDRESS_SUBSCRIBERS .destport = SND_SEQ_ADDRESS_UNKNOWN .channel = 0 .note = 64 ' Imposta una nota Midi da eseguire .velocity = 0 ' Imposta la velocità di tocco End With InviaEventoMidi(handle, ev_midi) ' Va in Chiusura: snd_seq_close(handle) End Private Procedure InviaEventoMidi(p As Pointer, ev_midi As EventoMidiAlsa) Dim err As Integer err = snd_seq_event_output(p, ev_midi) If err < 0 Then error.Raise("Errore: " & snd_strerror(err)) err = snd_seq_drain_output(p) If err < 0 Then error.Raise("Errore: " & snd_strerror(err)) End
Note
[1] In Gambas una "Struttura " può anche essere sostituita con una Classe avente solo Proprietà (dunque senza Metodi né Eventi).