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

Da Gambas-it.org - Wikipedia.

Preambolo

ALSA gestisce i Messaggi Midi attraverso una propria Struttura (snd_seq_event_t ), i cui dati contenuti rappresentano diverse informazioni.

Un Evento è un'entità che si crea, si realizza e termina nel tempo: accade in un certo momento. L'Evento è essenzialmente sottoposto all'ordine del tempo.

La Struttura dell'Evento Midi di ALSA contiene dati che consentono il determinarsi temporale del'Evento: l'essere inviato in modo pre-ordinato nel futuro.

La temporizzazione (Timestamp ) dell'Evento Midi di ALSA, che vuol dire pre-disporre l'invio di uno o più messaggi Midi secondo un ordine temporale, stabilisce quando l'Evento Midi sarà inviato.
Ciò consente di stabilire preliminarmente l'ordine temporale dell'invio di ciascun Evento Midi di ALSA: uno dopo l'altro (in SEQUENZA).

Per ottenere questi eventi temporali (l'invio preordinato, prestabilito nel tempo dei messaggi Midi) dovrà essere usato il marcatore temporale, presente come membro identificato con "snd_seq_timestamp_t time" all'interno della Struttura snd_seq_event_t.
Questo membro della Struttura ci permetterà di creare un applicativo capace di svolgere le funzioni di sequencer Midi; ossia capace di inviare in sequenza temporale i vari Eventi Midi secondo un ordine prestabilito dall'utente.


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 1], i cui molti dati costitutivi sono organizzati internamente in modo 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 preposti a determinare il Timestamp:

  • il membro flags
  • il membro time.

Uso del membro "flags"

In particolare il membro flags serve per poter impostare il tipo di timestamp (Timestamp mode) che si intende utilizzare. [nota 2]
Il Timestamp può essere impostato in:

  • tick time, corrisponde ai tick del Midi (il valore assegnato è espresso in "tick" Midi);
  • real time, corrisponde all'orologio, e la sua risoluzione è in secondi e nanosecondi.

Inoltre il Timestamp può essere:

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

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 nel già citato file header 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

La Costante prescelta, fra quelle qui sopra mostrate, andrà assegnata al campo flags dell'area di memoria ricreata in Gambas, conforme alla citata Struttura snd_seq_event_t.


Uso del membro "time"

I valori effettivi della temporizzazione da assegnare al Timestamp sono invece definiti dal membro Time della citata Struttura snd_seq_event_t.
Riguardo alla dimensione di tale membro la documentazione presente nel file header seq_event.h dice che esso è rappresentato da una Union:</p>

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_t è 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 Midi ALSA 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.

Campo "flags" impostato a zero

Se il campo flags è impostato a 0 (SND_SEQ_TIME_STAMP_TICK ), 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 ovviamente a zero il secondo.

In modo predefinito la temporizzazione degli Eventi Midi ALSA è impostata in modalità "Assoluta".
In tal caso il valore - espresso in tick Midi - da assegnare al membro "tick" della Struttura "snd_seq_tick_time_t" va considerato dall'inizio della Coda degli Eventi Midi allocati, ossia da un momento iniziale pari a "tick" zero.
Se ad esempio si intende far eseguire un Evento Midi di "NoteON" all'inizio dell'esecuzione degli Eventi Midi allocati nella Coda, si porrà il suo Timestamp a zero:

.tick = 0

Se poi si intende far durare tale Evento Midi ALSA per 96 tick, si invierà ad ALSA un Evento Midi di "NoteOFF" con "Timestamp" pari a 96:

.tick = 96

Se si intende far eseguire un altro Evento Midi di "NoteON" immediatamente dopo l'esecuzione del precedente "NoteOFF", allora si porrà a 96 il "Timestamp" di detto Evento Midi "NoteON":

.tick = 96

Se poi si intende far durare tale Evento Midi ALSA per 182 tick, si invierà ad ALSA un Evento Midi di "NoteOFF" con "Timestamp" pari alla somma dei tick impostati in tutti gli Eventi Midi, sino ad allora allocati nella Coda, più 182 tick.
In sostanza:

.tick = 96 + 182

Quindi, l'Evento Midi di "NoteOFF" sarà eseguito dopo 96 + 182 = 278 tick dall'inizio dell'accodamento degli Eventi Midi, e così via.

0 96 278
| | |
NoteON NoteOFF/NoteON NoteOFF

Potremmo riassumere quanto sopra descritto con questa formula:

temp = tot_ticks + accadimento

laddove:
- "temp" è il valore in tick da assegnare al membro ".tick" della sotto-Struttura snd_seq_timestamp_t dell'Evento Midi in considerazione che si sta costruendo;
- "tot_ticks" è la quantità di tick dall'inizio della Coda sino all'Evento Midi precedente a quello in considerazione che si sta costruendo;
- "accadimento" è la quantità di tick dopo la quale (più precisamente e ovviamente dall'Evento Midi precedente a quello in considerazione) si intende far eseguire l'Evento Midi che si sta costruendo.

Campo "flags" impostato a 1

Se, invece, al campo flags è stato assegnato il valore corrispondente al "real time" (SND_SEQ_TIME_STAMP_REAL ), allora entrambi i campi (che sono due Integer) della Union sono significativi:

  • tv_sec specifica i secondi;
  • tv_nsec specifica i nanosecondi (miliardesimi di secondo).


Anche in questo caso il valore di temporizzazione - esprimibile in secondi e/o in nanosecondi - degli Eventi Midi ALSA è impostata in modalità "Assoluta", e l'assegnazione dei valori a uno o a entrambi i membri ("tv_sec" e "tv_nsec") della Struttura snd_seq_real_time_t va fatta con la logica già vista sopra per l'impostazione in "tick time", sebbene ovviamente i valori debbano essere espressi in secondi, esempio 2 secondi:

.tv_sec = 2

o in nanosecondi, esempio 500 nanosecondi:

.tv_nsec = 500000000


Note

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

[2] Va comunque ricordato che l'esecuzione audio degli Eventi Midi avviene - salvo diversa impostazione mediante le apposite funzioni fornite dal sub-sietma "seq" - con un Tempo Metronomico predefinito di ALSA pari a 120 BpM.

[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.