Differenze tra le versioni di "Alsa e Gambas: Il Tempo della coda"
Riga 26: | Riga 26: | ||
PRIVATE EXTERN snd_seq_queue_tempo_malloc(queue_tempo As Pointer) As Integer | PRIVATE EXTERN snd_seq_queue_tempo_malloc(queue_tempo As Pointer) As Integer | ||
− | + | PRIVATE EXTERN snd_seq_queue_tempo_set_ppq(queue_tempo As Pointer, tempo_per_quarti As Integer) As Integer | |
PRIVATE EXTERN snd_seq_queue_tempo_set_tempo(qtempo As Pointer, quarti_per_minuto As Integer) As Integer | PRIVATE EXTERN snd_seq_queue_tempo_set_tempo(qtempo As Pointer, quarti_per_minuto As Integer) As Integer | ||
PRIVATE EXTERN snd_seq_set_queue_tempo(seq As Pointer, outq As Integer, qtempo As Pointer) As Integer | PRIVATE EXTERN snd_seq_set_queue_tempo(seq As Pointer, outq As Integer, qtempo As Pointer) As Integer | ||
Riga 36: | Riga 36: | ||
snd_seq_queue_tempo_malloc(varPtr(qtempo)) | snd_seq_queue_tempo_malloc(varPtr(qtempo)) | ||
+ | |||
+ | <Font Color= #006400>' ''Calcoliamo il Tempo in BpM (ossia quanti microsecondi dura una nota da un quarto):</font>'' | ||
+ | tempoDefinitivo = Round(240000000 / (modificaTempo * (risoluzioneTD / 24))) | ||
− | + | snd_seq_queue_tempo_set_tempo(qtempo, tempoDefinitivo) | |
− | |||
− | |||
− | |||
− | |||
− | + | snd_seq_queue_tempo_set_ppq(qTempo, risoluzioneTD) | |
snd_seq_set_queue_tempo(handle, outq, qtempo) | snd_seq_set_queue_tempo(handle, outq, qtempo) | ||
Riga 93: | Riga 92: | ||
[2] Il ''microsecondo'' rappresenta un milionesimo di secondo. | [2] Il ''microsecondo'' rappresenta un milionesimo di secondo. | ||
− | |||
− |
Versione delle 23:21, 3 lug 2013
Indice
[nascondi]Introduzione
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 pianificato degli eventi è definito in:
- Tempo Midi che determina il tempo in microsecondi;
- Tick (PPQN - pulse per quarter note) che determina il Tempo per tick {1}.
Il Tempo di un brano musicale viene gestito in microsecondi {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 movimenti 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 quarto; ossia per ogni 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 50.000 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. Per modificare, invece, il tempo della coda, mentre sta scorrendo, si dovrà operare mediante la funzione di ALSA snd_seq_set_queue_tempo().
Nel caso la coda sia stata impostata in real-time, allora la risoluzione, come sappiamo, è definita in nanosecondi.
La scrittura del codice in Gambas
Si devono innanzitutto distiguere tre casi di impostazione del Tempo metronomico:
- quello effettuato manualmente;
- quello iniziale di default;
- quello secondo le istruzioni contenute ed impostate dal file Midi.
Impostazione del Tempo metronomico effettuato manualmente o come tempo iniziale di default
Nel nostro applicativo in Gambas procederemo in tali due casi innanzitutto inserendo uno Slider nella classe principale FMain.class, con il quale variare manualmente ed in qualsiasi momento il valore del tempo desiderato, e che richiama nella classe CAlsa.class la seguente routine:
PRIVATE EXTERN snd_seq_queue_tempo_malloc(queue_tempo As Pointer) As Integer PRIVATE EXTERN snd_seq_queue_tempo_set_ppq(queue_tempo As Pointer, tempo_per_quarti As Integer) As Integer PRIVATE EXTERN snd_seq_queue_tempo_set_tempo(qtempo As Pointer, quarti_per_minuto As Integer) As Integer PRIVATE EXTERN snd_seq_set_queue_tempo(seq As Pointer, outq As Integer, qtempo As Pointer) As Integer PRIVATE EXTERN snd_seq_queue_tempo_free(qtempo As Pointer) As Integer Public Sub impostaTempo(modificaTempo As Integer) Dim qtempo As Pointer Dim tempoDefinitivo As Integer snd_seq_queue_tempo_malloc(varPtr(qtempo)) ' Calcoliamo il Tempo in BpM (ossia quanti microsecondi dura una nota da un quarto): tempoDefinitivo = Round(240000000 / (modificaTempo * (risoluzioneTD / 24))) snd_seq_queue_tempo_set_tempo(qtempo, tempoDefinitivo) snd_seq_queue_tempo_set_ppq(qTempo, risoluzioneTD) snd_seq_set_queue_tempo(handle, outq, 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 int 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;
- tempo è un puntatore ed un'informazione relativa al tempo.
La funzione snd_seq_queue_tempo_free(snd_seq_queue_tempo_t * obj) libera l'area di memoria precedentemente allocata.
Anche per la modifica del Tempo sarà necessario il passaggio finale attraverso la funzione esterna, che già conosciamo, snd_seq_drain_output().
Se vorremo impostare un valore di default iniziale del Tempo (al di là se verrà successivamente variato o meno), si dovrà inizializzare la variabile modificaTempo attribuendole il valore - espresso in microsecondi - desiderato.
Impostazione del Tempo metronomico secondo le istruzioni contenute ed impostate dal file Midi
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.
In questo caso non ci serviremo più delle funzioni e dell'algoritmo, visto nel precedente paragrafo, bensì di un processo più complesso.
Innanzitutto assumeremo dal file Midi il Tempo Delta (sommando il valore del Tempo Delta indicato nel file per il Meta-evento del Tempo in questione a tutti i valori del Tempi Delta che eventualmente lo precedono: distanza temporale assoluta del Meta-evento dall'inizio del brano che è uguale a zero), e il valore del Tempo espresso in microsecondi.
Successivamente sulla base del suo Tempo Delta assoluto (ossia ponendolo correttamente nell'ordine crescente della coda di eventi Midi da inviare), invieremo ad Alsa il Meta-evento Tempo metronomico come un qualsiasi Evento Midi, avendo l'accortezza, però, di attribuirgli e di accompagnarlo con i seguenti valori:
- Type = SND_SEQ_EVENT_TEMPO (questo valore è una costante prevista da Alsa, e dovrà essere nel nostro codice impostata a 35);
- Flag = 0
- Tag = 0
- Queue = il numero della coda ottenuto nel modo consueto;
- Timestamp 1 = la somma dei Tempi Delta di tutti gli eventi sino al Meta-evento Tempo in questione;
- Timestamp 2 = 0
- Id Client sorgente = identificato nel modo consueto;
- Porta Client sorgente = identificata nel modo consueto;
- Id Client destinatario = SND_SEQ_CLIENT_SYSTEM (questo valore è una costante prevista da Alsa, e dovrà essere nel nostro codice impostata a 0);
- Porta Client destinatario = SND_SEQ_PORT_SYSTEM_TIMER (questo valore è una costante prevista da Alsa, e dovrà essere nel nostro codice impostata a 0);
- Queue = nuovamente il numero della coda ottenuto nel modo consueto (nel manuale di Alsa è indicato come: unsigned char queue);
- altro campo = 0 (corrisponde sostanzialmente alla posizione in byte del .note nell'invio dell'evento del NoteOn) (nel manuale di Alsa è indicato come: unsigned char unused [3]);
- altro campo = 0 (corrisponde sostanzialmente alla posizione in byte del .velocity nell'invio dell'evento del NoteOn);
- primo Valore = il valore del Tempo metronomico espresso in microsecondi (nel manuale di Alsa è indicato come: signed int value);
- secondo Valore = 0
Note
[1] Il Tick è l'unità di misura più piccola nella risoluzione del sequencer.
[2] Il microsecondo rappresenta un milionesimo di secondo.