ALSA e Gambas - La marcatura temporale degli eventi: il Timestamp

Da Gambas-it.org - Wikipedia.

Preambolo

Nelle due sezioni precedenti abbiamo studiato le fondamenta della gestione del Midi con Gambas in ambiente ALSA: l'Invio e la Ricezione dei messaggi Midi in modo immediato.

Ora possiamo passare ad un livello superiore e più complesso: possiamo definirlo come quello dell'invio dei messaggi Midi pre-ordinato nel futuro.

Ciò avviene attraverso la loro temporizzazione, che vuol dire pre-disporre l'invio di uno o più messaggi Midi secondo un ordine temporale. Essa insomma ci dice quando un messaggio sarà inviato.
Mentre nelle due sezioni precedenti i dati Midi venivano inviati ad libitum, i dati cioè venivano inviati immediatamente, di volta in volta quando lo decidevamo noi; ora si tratta invece di stabilire preliminarmente quando saranno inviati - uno dopo l'altro (in SEQUENZA) - i messaggi Midi senza dover più intervenire direttamente per il loro invio.
Per ottenere questi eventi temporali (l'invio preordinato, prestabilito nel tempo dei messaggi Midi) dovrà essere usato il marcatore temporale: Timestamp.

Questo ci permetterà di creare un applicativo capace di svolgere le funzioni di sequencer Midi; ossia - appunto - capace di inviare in sequenza temporale i vari messaggi Midi, secondo un ordine prestabilito da chi ha creato la sequenza medesima di quei messaggi, costitutivi del brano musicale.

"Lo scopo [nella realizzazione di un sequencer] è quello di produrre un flusso di eventi che sarà eseguito in maniera successiva. Dobbiamo preparare un gruppo di eventi in anticipo in modo che l'hardware ha dei dati su cui lavorare. Ma [un sequecer] può durare anche per un lungo periodo e noi non possiamo effettuare un buffer di tutti gli eventi - dobbiamo selezionarne un certo numero, né troppi e né pochi. Il "puntatore" interno [del sequencer] è sempre di una misura musicale più avanti di ciò che stiamo ascoltando. Noi non possiamo prevedere in maniera precisa quando i nuovi dati saranno necessari, poiché il sequencer solleva gli eventi basandosi su una temporizzazione che può essere diversa dalla nostra. Senza un feedback del sequencer è quasi impossibile rimanere in sincronia con esso." [nota 1]


Introduzione al Timestamp

Nella programmazione Midi l'uso di una marcatura temporale (Timestamp) risulta utile in via generale se è richiesta la verifica dell'avvenimento di uno o più eventi. Gli eventi Midi così temporizzati vengono "accodati" e gestiti l'uno dopo l'altro (in sequenza) secondo l'ordine temporale della loro attivazione imposto dal marcatore temporale.
Come già accennato all'inizio di questa guida, il subsistema seq di ALSA gestisce gli Eventi Midi temporizzati [nota 2], i cui molti dati costitutivi sono organizzati internamente in modo - come abbiamo visto - molto preciso, arrivando ad occupare una zona di memoria riservata pari a 28 byte, e rappresentata dalla Struttura snd_seq_event_t.


Caratteristiche del Timestamp

All'interno della predetta Struttura dei dati degli Eventi Midi ALSA, snd_seq_event_t, vi sono due parametri adatti a determinare il Timestamp: il campo Flags ed il campo Time.
Il campo Flags serve per poter impostare il tipo di timestamp (Timestamp mode) che si intende utilizzare. Il Timestamp può essere impostato in:

  • tick-time, corrisponde ai tick del Midi.
  • real-time, corrisponde all'orologio, e la sua risoluzione è in secondi e nanosecondi;

Il Timestamp può inoltre essere:

  • assoluto, quando il tempo è determinato dal momento in cui la coda di eventi ha inizio;
  • relativo, quando il tempo è determinato dal momento in cui l'evento della coda è stato inviato.

I valori da assegnare al Timestamp sono definiti dal campo Time della citata Struttura snd_seq_event_t.
Riguardo alla dimensione di tale campo la documentazione presente nel file seq_event.h dice che esso è rappresentato da una Union:

typedef struct snd_seq_real_time {
             unsigned int tv_sec;            
             unsigned int tv_nsec;           
     } snd_seq_real_time_t;

     typedef unsigned int snd_seq_tick_time_t;

 typedef union snd_seq_timestamp {
             snd_seq_tick_time_t tick;       
             struct snd_seq_real_time time;  
     } snd_seq_timestamp_t;

Come possiamo vedere, snd_seq_timestamp è una Union (Unione) [nota 3] di dimensione pari a 8 byte, poiché il più lungo dei suoi elementi, "real_time", è composto da due unsigned int, ossia da "due" Interi.
La union "snd_seq_timestamp_t" può contenere, in alternativa, il valore di un "tick_time" (che è 1 integer) oppure di un "real_time", che a sua volta è composto da 2 integer. Per un determinato evento i due dati sono sovrapposti - essendo una Union - e non possono essere validi entrambi contemporaneamente. Infatti, un bit nel campo flags indica quale dei due casi si sta trattando.
Se il campo flags è impostato a 0 (zero), allora vuol dire che è stata scelta la modalità "tick time" e sarà valorizzato così soltanto il campo "snd_seq_tick_time_t tick" (ossia il primo membro) della Union di ALSA "snd_seq_timestamp_t". In breve, nel caso di "tick_time", scriviamo il primo membro della Union e impostiamo a zero il secondo.
Se, invece, al campo flags è stato assegnato il valore corrispondente al "real time", allora entrambi i campi (che sono due Integer) della union sono significativi: il primo di essi specifica i secondi e l'altro i nanosecondi (miliardesimi di secondo).


Determinazione ed impostazione del Timestamp

Per determinare il "Timestamp Mode", al fine di individuare il tipo di Timestamp da usare, si utilizzeranno le seguenti costanti, definite sempre in seq_event.h:

* #define SND_SEQ_TIME_STAMP_TICK(0<<0)/**< timestamp in clock ticks */
* #define SND_SEQ_TIME_STAMP_REAL(1<<0)/**< timestamp in real time */
* #define SND_SEQ_TIME_MODE_ABS(0<<1)/**< absolute timestamp */
* #define SND_SEQ_TIME_MODE_REL(1<<1)/**< relative to current time */

Pertanto, in Gambas scriveremo:

Private Const SND_SEQ_TIME_STAMP_TICK As Integer = 0
Private Const SND_SEQ_TIME_STAMP_REAL As Integer = 1
Private Const SND_SEQ_TIME_MODE_ABS As Integer = 0
Private Const SND_SEQ_TIME_MODE_REL As Integer = 2


Note

[1] Vedi D. Blengino: Una Drum Machine in Gambas

[2] Il sub-sistema di ALSA chiamato rawmidi, invece, gestisce un flusso di messaggi Midi nei loro valori essenziali secondo le specifiche standard Midi.

[3] Nel linguaggio C una Union è "l'alternativa" di più elementi, che sono sovrapposti. La lunghezza in byte di una Union è pari alla lunghezza del più lungo dei suoi elementi.