ALSA e Gambas - Il Tempo della coda

Da Gambas-it.org - Wikipedia.

Introduzione

Come è facilmente immaginabile, un brano potrà eventualmente prevedere una o più variazioni del Tempo metronomico durante la sua esecuzione. Si ha questo caso, quando tali variazioni del Tempo sono previste e stabilite all'interno del File Midi. L'impostazione del valore del nuovo Tempo metronomico avviene mediante la previsione di apposito Meta-evento (FF 51 03 nn nn nn ); laddove i tre dati nn nn nn esprimono il valore del Tempo metronomico in microsecondi.
Inoltre, il cambio del Tempo metronomico può essere effettuato anche manualmente a valontà.

ALSA dispone di varie funzioni per poter gestire il Tempo metronomico di un brano musicale, ossia la velocità di una coda di eventi.
Il Tempo Metronomico pianificato degli Eventi Midi è definito in:

  • Tempo Midi che determina il tempo in microsecondi;
  • Tick (PPQN - pulse per quarter note) che determina il Tempo per tick. [nota 1]

Il Tempo Metronomico di un brano musicale viene gestito in microsecondi [nota 2] per movimenti (beat), ossia per nota da un quarto (semiminima), mediante la funzione di ALSA "snd_seq_queue_tempo_set_tempo()". Solitamente, si preferisce impostare il Tempo in battute per minuto (bpm - beat per minute).

L'impostazione del Tempo in Tick (PPQN) definisce la risoluzione dei tick, ed avviene mediante la funzione "snd_seq_queue_tempo_set_ppq()". Esso è impostato di default a 96 tick per nota da un quarto; ossia per ogni nota da un quarto (corrispondente al valore temporale in musica di una semiminima) vi sono 96 tick.
Inoltre, ALSA imposta di default il valore dei movimenti per minuto (bpm - beat per minute) a 120, che è pari a 500000 microsecondi = (60 * 1000000) / 120.
Da notare che l'impostazione in PPQN non può essere modificata mentre la Coda sta scorrendo, pertanto quel parametro deve essere impostato prima della partenza della Coda.

E' invece possibile modificare il tempo metronomico della Coda degli Eventi Midi ALSA, mentre sta scorrendo, o inviando un Evento Midi del tipo del Tempo Metronomino con il nuovo valore in microsecondi dei BpM, oppure usando la funzione di ALSA "snd_seq_set_queue_tempo()".


La scrittura del codice in Gambas

Si devono innanzitutto distinguere due possibilità di impostazione del Tempo metronomico:

  • quello diretto con l'invio di un Evento Midi "Tempo" di ALSA;
  • quello effettuato mediante apposite funzioni esterne di ALSA.

Impostazione del Tempo metronomico con l'invio di un evento Midi Tempo Alsa

In questo caso il valore del Tempo metronomico avviene attraverso l'invio ad Alsa di uno specifico evento con valore Type uguale a 35, corrispondente alla costante di Alsa:

SND_SEQ_EVENT_TEMPO = 35

Successivamente sulla base del suo Tempo Delta assoluto (ossia posto correttamente nell'ordine crescente della coda di eventi Midi da inviare), invieremo ad ALSA l'Evento Tempo metronomico come un qualsiasi Evento Midi di ALSA, avendo l'accortezza, però, di attribuirgli e di accompagnarlo con i seguenti valori:

* type = SND_SEQ_EVENT_TEMPO (questo valore è una Costante di Alsa uguale a 35);
* flags = "tick time" oppure "real time";
* tag  = 0
* queue = il numero della coda ottenuto nel modo consueto;
* tick_o_tv_sec = il "Timestamp" di questo Evento Midi;
* tv_nsec = idem;
* source_client = identificato nel modo consueto;
* source_port = identificata nel modo consueto;
* dest_client = SND_SEQ_CLIENT_SYSTEM (questo valore è una Costante di ALSA uguale a 0);
* dest_port = SND_SEQ_PORT_SYSTEM_TIMER (questo valore è una Costante di Alsa uguale a 0);
* channel = 0;
* note = 0;
* velocity = 0;
* off_velocity = 0;
* param = il valore del Tempo metronomico qui è sempre e solo espresso in microsecondi;
* value = 0.

Calcolo per ottenere il valore da passare all'Evento Midi ALSA del Tempo Metronomico

Per il calcolo del valore da passare al campo param della Struttura dell'Evento Midi di ALSA bisogna distinguere:

se l'impostazione del membro flags è in tick Midi, allora il valore da passare all'Evento Midi ALSA del Tempo Metronomico sarà ottenuto:

valore = (60 * 1000000) / bpm

Se l'impostazione del membro flags è invece in real time (tempo orario), allora non è possibile cambiare il Tempo metronomico della Coda di ALSA, restando necessario così agire sulla temporizzazione dei singoli Eventi Midi.

Impostazione del Tempo metronomico mediante apposite funzioni esterne di ALSA

In quest'altro caso l'impostazione del Tempo metronomico avviene mediante una serie di apposite funzioni esterne di Alsa.

Come si è detto sopra, la risoluzione del Tempo Delta di ALSA è predefinita sul valore di 96 tick per quarto (semiminima) di battura (Misura).
E' possibile modificare questo valore mediante la funzione esterna "snd_seq_queue_tempo_set_ppq()". Ciò è richiesto in particolare nel caso di elaborazione di dati provenienti da file Midi, i quali contengono alla fine del proprio "Blocco d'intestazione" (MThd) due byte per l'impostazione appunto della risoluzione del Tempo Delta.

Qualora invece non si debba apportare una modifica del valore della risoluzione del Tempo Delta, ovviamente si lascerà il valore predefinito di ALSA.

Calcolo per ottenere il valore da passare all'Evento Midi ALSA del Tempo Metronomico

Se l'impostazione del membro flags è in tick Midi, allora il valore da passare all'Evento Midi ALSA del Tempo Metronomico dovrà essere ottenuto attraverso la trasformazione del Tempo da BpM in "microsecondi" (ossia quanti microsecondi dura una nota da un quarto), adottando una delle seguenti modalità:

 tempo_definitivo = Round(240000000 / (bmp * (risoluzione_TD / 24)))

oppure:

tempo_definitivo = CInt(6000000000 / (bmp * risoluzione_TD))

oppure:

tempo_definitivo = CInt((6e7 / (bmp * risoluzione_TD)) * risoluzione_TD)

Un esempio astratto

' int snd_seq_queue_tempo_malloc (snd_seq_queue_tempo_t ** ptr)
Private Extern snd_seq_queue_tempo_malloc(ptr As Pointer) As Integer
' void snd_seq_queue_tempo_set_ppq (snd_seq_queue_tempo_t * info, int ppq)
Private Extern snd_seq_queue_tempo_set_ppq(info As Pointer, ppq As Integer) As Integer
' void snd_seq_queue_tempo_set_tempo (snd_seq_queue_tempo_t * info, unsigned int tempo)
Private Extern snd_seq_queue_tempo_set_tempo(info As Pointer, tempo As Integer) As Integer
' int snd_seq_set_queue_tempo (snd_seq_t * seq, int q, snd_seq_queue_tempo_t * tempo)
Private Extern snd_seq_set_queue_tempo(seq As Pointer, q As Integer, tempo As Pointer) As Integer
' void snd_seq_queue_tempo_free (snd_seq_queue_tempo_t * obj)
Private Extern snd_seq_queue_tempo_free(obj As Pointer) As Integer

Public Sub ImpostaTempoMetronomico(bpm As Integer)
 
 Dim qtempo As Pointer
 Dim tempo_definitivo As Integer

 snd_seq_queue_tempo_malloc(varPtr(qtempo))

 tempo_definitivo = CInt((6e7 / (bmp * risoluzione_TD)) * risoluzione_TD)
 
 snd_seq_queue_tempo_set_tempo(qtempo, tempo_definitivo)
 
 snd_seq_queue_tempo_set_ppq(qTempo, risoluzione_TD)

 snd_seq_set_queue_tempo(handle, queue, qtempo)

 snd_seq_queue_tempo_free(qtempo)
 
End

La funzione di ALSA "snd_seq_queue_tempo_malloc(snd_seq_queue_tempo_t ** ptr)" riserva della memoria per la gestione del tempo da parte delle funzioni di ALSA, e ritorna un puntatore. In questo caso ALSA riserva la memoria necessaria per la funzione, e ritorna un puntatore (segnato dal secondo dei due asterischi). Il primo asterisco è necessario perché ALSA deve sapere dove scrivere il suo puntatore per passarlo poi all'applicativo. Quindi avremo un puntatore ad un puntatore. Infatti, ogni volta che una funzione C scrive qualcosa, è necessario passargli un puntatore. Se ALSA deve riempire un puntatore, bisognerà passarle un puntatore a questo puntatore. Per passare, dunque, un indirizzo del puntatore (cioè un puntatore ad un puntatore) ad ALSA, useremo la funzione di Gambas "VarPtr() ".

La funzione "snd_seq_queue_tempo_set_ppq()" imposta il valore della risoluzione del Tempo Delta alla Coda degli Eventi Midi Alsa.

La funzione "snd_seq_set_queue_tempo(snd_seq_t * seq, int q, snd_seq_queue_tempo_t * tempo)" imposta il tempo di una coda.
Laddove:

  • seq è l'handle del sequencer ALSA;
  • q è è il numero identificativo della Coda, di cui si deve cambiare il Tempo Metronomico;
  • tempo è un puntatore ed un'informazione relativa al Tempo Metronomico.

La funzione "snd_seq_queue_tempo_free()" libera l'area di memoria precedentemente allocata.


Note

[1] Il Tick è l'unità di misura più piccola nella risoluzione del sequencer.

[2] Il microsecondo rappresenta un milionesimo di secondo.