Conversione del Tempo Delta fra due eventi Midi dai secondi nel valore esadecimale in formato "a lunghezza variabile"

Da Gambas-it.org - Wikipedia.

Il Tempo Delta, che divide un evento Midi dall'evento immediatamente precedente, viene espresso nel file Midi standard in formato a lunghezza variabile. Più specificatamente il Tempo Delta, così come contenuto nel file Midi standard, assume la rappresentazione esadecimale del valore espresso in tick preliminarmente convertito in formato a lunghezza variabile. [nota 1]

Il formato "a lunghezza variabile" rappresenta i numeri usando solo sette bit per byte, mentre il bit più significativo è impostato ad un valore tale da poterlo riconoscere. Se la rappresentazione contiene più byte per esprimere un numero, il bit più significativo dei primi byte sarà posto sempre ad 1, mentre quello dell'ultimo byte sarà sempre posto a 0. Tale impostazione sta ad indicare che, quando il bit più significativo è posto ad 1, tale byte non è l'ultimo della rappresentazione esadecimale, ma che dopo di esso v'è un altro byte. Se il bit più significativo è invece posto a 0, allora vuol dire che il byte, al quale quel bit appartiene, è l'ultimo della rappresentazione. [nota 2]


Di seguito verrà mostrato un algoritmo per ottenere di un Tempo Delta, espresso in secondi, la conversione da tale unità di misura in tick reali fra due eventi Midi e successivamente in tick in formato "a lunghezza variabile", in rappresentazione esadecimale e in ordine Big-Endian. [nota 3]

Innanzitutto va detto che per passare dal valore temporale espresso in secondi al valore espresso in tick reali, bisognerà adottare questa formula:

(secondi * 1000000) / ((60000000 / bpm) / td) = tick_reali

Laddove:

  • secondi: è il valore temporale espresso in secondi che intercorre fra un Evento-Midi e quello immediatamente successivo;
  • bmp: è il tempo metronomico (battute per minuto);
  • td: è la risoluzione di clock per nota da 1/4 (PPQN), che nel file Midi è espressa in Big-Endian congiuntamente dal 13° e dal 14* byte (gli ultimi due byte della traccia MThd.

Fatto ciò, si potrà passare all'ultima fase, ossia convertire il valore dei tick reali nel corrispondente valore in formato "a lunghezza variabile", in rappresentazione esadecimale e in ordine Big-Endian.

Public Sub Main()
 
 Dim tick_reali, intero As Integer
 Dim b, c As Byte
 Dim lv As New Byte[]
  
 tick_reali = 151808    ' Poniamo questo valore dei "tick reali" a mero scopo esemplificativo:
  
 lv.Push(tick_reali Mod 128)
 intero = Fix(tick_reali / 128)
  
 While intero > 0
   b = CByte(intero Mod 128)
   lv.Push(b Or 128)
   intero = Fix(intero / 128)
 Wend
 
 lv.Reverse()
 
' Mostra il valore dei tick in formato "a lunghezza variabile" e in rappresentazione esadecimale:
 For Each c In lv
   Print Hex(c, 2);
 Next
 
End


Note

[1] Vedere anche la pagina: Conoscere la durata in secondi del Tempo Delta fra due Eventi Midi

[2] Il Tempo Delta (TΔ), che intercorre fra un Evento-Midi e quello immediatamente successivo, può essere espresso nel file Midi da uno a quattro byte, a seconda ovviamente della lunghezza della durata. Se sono utilizzati due o più byte, l'ordine adottato nella memorizzazione del valore del Tempo Delta è sempre in Big-Endian.
Il valore del Tempo Delta memorizzato nel file non è espressamente proprio quello corrispondente alla quantità di secondi. Infatti non è adoperata direttamente l'unità di misura temporale dei secondi, bensì questa viene prima convertita in tick reali, ossia in impulsi per nota da 1/4 (PPQN). Successivamente si procede alla conversione del valore da tick reali in un valore in rappresentazione a lunghezza variabile.
La rappresentazione a lunghezza variabile nel Midi prevede che in ogni byte, facente parte nel file Midi del valore del Tempo Delta, sono utili a fini della definizione di tale valore soltanto sette bit, e segnatamente quelli meno significativi (dunque dal 1° bit a destra, che esprime per convenzione il valore 1, fino al penultimo bit a sinistra, che esprime a sua volta il valore 64). Il bit più significativo del Byte nel Midi non assume il valore 128, bensì può rispondere a due possibilità:
1) se il byte di appartenenza è l'unico o l'ultimo byte del gruppo che rappresenta il valore del Tempo Delta, allora quel bit assume il valore 0 (zero);
2) se il byte di appartenenza non è l'ultimo byte del gruppo che rappresenta il valore del Tempo Delta, allora quel bit assume il valore 1 (uno). Quindi se un byte, che costituisce nel file Midi la rappresentazione in ordine Big-Endia e in rappresentazione a lunghezza variabile, possiede il suo bit più significativo posto a 1 (uno), significa che esso non è l'unico, ma fa parte di un gruppo di due o quattro bye, e che alla sua destra c'è almeno un byte.
Comprensibilmente ci si domanderà quale fine faccia il valore 128 nella logica dei bit. Ebbene, il bit di valore 128, non si perde, ma, non potendo essere rappresentato da alcun bit all'interno di quello che di norma sarebbe stato il suo byte, si sposta sul bit meno significativo (quello più a destra) del byte successivo. Pertanto, da quel che s'è detto, sarà necessario, qualora si debba esprimere un valore uguale o superiore a 128, utilizzare un secondo byte.
Come ben si comprende, questa circostanza comporta a catena che anche nel secondo byte utilizzato i valori, espressi dai suoi bit, subiranno uno spostamento a sinistra a causa della forzata entrata del bit 128 dall'altro byte.
Si deve aggiungere che il secondo byte, non essendo ovviamente l'ultimo del gruppo dei byte (in tal caso 2) che devono esprimere il valore del Tempo Delta nel file Midi, vedrà il proprio bit più significativo (il primo da sinistra) assumere il valore 1 (uno). A questo punto, dovendosi rappresentare in lunghezza variabile il valore del Tempo Delta pari a 128 tick reali, avremo una situazione di questo genere:

10000001 00000000

(in rosso è colorato il bit che è stato oggetto di spostamento nel byte posto a sinistra di quello, al quale il bit originariamente apparteneva)che sarà uguale in esadecimale a: 81 00. Quindi porremo nel file Midi non il valore &80 (ossia 128 in decimale dei tick reali), bensì il valore &8100, che è &80 espresso in rappresentazione a lunghezza variabile.

Per concludere l'esposizione in questa nota, prendiamo il valore in tick reali 45312 (&B100, pari a 118 secondi), che in rappresentazione binaria è così rappresentato da due byte: 10110001 00000000.
Per convertire tale valore in rappresentazione a lunghezza variabile nel Midi, accade quanto segue:
- dovendo tenere conto dello spostamento del valore 128 dal primo byte all'interno del secondo byte, tutti i valori naturali dei bit, appartenenti al secondo byte, si sposteranno a sinistra di un posto. Questo fa sì che il bit più significativo del secondo byte passerà al posto del bit meno significativo di un terzo ulteriore byte, che per l'ordine in Big-Endian si porrà a sinistra del gruppo di byte, spostando così di un posto i bit all'interno di questo terzo byte. Però, poiché il posto del bit più significativo del secondo byte non può essere occupato dal bit alla sua destra, dato che questo posto deve servire per comunicare che il byte non è solitario, e neppure è l'ultimo del gruppo (e pertanto assumerà il numero 1), il bit che stava alla sua destra deve anch'esso passare al terzo Byte, spostando ulteriormente di un posto i bit all'interno di quel terzo byte.
A questo punto, dovendosi rappresentare in lunghezza variabile il valore del Tempo Delta pari a 45312 (&B100) tick reali, avremo una situazione di questo genere:

10000010 11100010 00000000

(in rosso sono colorati i bit che sono stati oggetto di spostamento nel byte posto a sinistra di quello al quale essi originariamente appartenevano) che sarà uguale in esadecimale a: 82 E2 00. Quindi porremo nel file Midi non il valore &B100 (ossia 45312 in decimale dei tick reali), bensì il valore &82E200, che insomma è &B100 espresso in formato a lunghezza variabile.

SOMMARE DUE VALORI IN FORMATO A "LUNGHEZZA VARIABILE"
Ovviamente anche nell'addizione di due valori in formato a lunghezza variabile bisogna tenere considerare che sono utilizzabili, ai fini della definizione del valore risultante (anch'esso in formato a lunghezza variabile), soltanto 7 bit in ciascun byte adoperato.
Praticamente, di un addendo ogni bit posto a 1 va confrontato mediante un operatore AND con il corrispondente bit dell'altro addendo. Se il risultato è vero (e quindi entrambi i bit confrontati sono posti a 1), il bit corrispondente risultante da scrivere è 0 (zero), ma, poiché la somma dei valori rappresentati dai due bit confrontati, posti entrambi a 1, è pari valore rappresentato dal bit posto immediatamente a sinistra, nel byte risultante tale bit a sinistra andrà posto a 1.
Ovviamente, se il bit da porre a 1 è quello più significativo all'interno del byte, allora - come da regola per il formato a lunghezza variabile - il posto del bit salta a quello del bit meno significativo di un ulteriore byte da utilizzare e più significativo (quindi posto a sinistra) del byte originario.

Esempio di addizione: 96+96:

        01100000
        01100000
        / /
1000000101000000

In rosso i bit non utilizzabili ai fini della definizione del valore in formato a lunghezza variabile.
Il risultato dell'addizione è: &8140

Esempio di addizione: &8140+&8140:

1000000101000000
1000000101000000
       //   
1000001100000000

Il risultato dell'addizione è: &8300

[3] In linguaggio C si può avere il seguente codice per ottenere un valore a lunghezza variabile:

void ScriveVarLen(unsigned long value) {

  unsigned long buffer;

  buffer = value & 0x7f;

  while((value >>= 7) > 0) {
    buffer <<= 8;
    buffer |= 0x80;
    buffer += (value & 0x7f);
  }

   while(1) {
     printf("%x", (unsigned)(buffer & 0xff));
     if(buffer & 0x80)
       buffer >>= 8;
     else
       return;
   }

  printf("\n");

}