Czasy delta w plikach MIDI
Po układzie ym2149 i komunikacji z zewnętrznym modułem MIDI, przyszedł czas na zmierzenie się z czasami delta, które są niezbędne do odgrywania sekwencji w plikach MIDI. W ramach przypomnienia, czas delta to relatywny czas w odniesieniu do poprzedniego zdarzenia w sekwencji. Mając tą wartość wiemy w jakim okresie czasu muszą być wysłane odpowiednie zdarzenia. np. z sekwencera do urządzenia midi (nuta włączona, wyłączona, zmiana instrumentu etc..).
Po lekturze specyfikacji formatu plików MIDI [typ 0,1,2] powstaje pytanie – jak długo trwa jednostka czasu delta i jak zaimplementować wysyłanie danych midi i zmianę tempa? Ok, najpierw musimy wiedzieć co interesującego związanego z tempem może zostać znalezione w pliku MID. Są dwie rzeczy:
1) Podział czasu (time division) w “MThd” zbitce (0x4D546864), wskazuje ilość tyknięć zegara w jednej ćwierć nucie (PPQN – pulses per quaternote). I jest to po prostu rozdzielczość w jakiej pulsy są zliczane w ćwierć nucie, która ma skończony okres (będzie o tym później). Więc jeżeli mamy podział czasu 96 PPQN, to oznacza to, że 96 tych tyknięć odmieża trwanie jednej ćwierć nuty.
Muzycy na przykład mierzą tempo w liczbach ćwierć nut na minutę (po prostu ilość ćwierć nut, która może być wyzwolona w jednej minucie co jakiś interwał), w komputerach i innych urządzeniach śledzenie czasu musi się odbywać w inny sposób. Podział czasu może być też zakodowany w formacie SMPTE, ale jest on rzadko spotykany i nie będę go tutaj opisywał..
2)Wartość “Ustaw tempo”(Set tempo), która zawiera ilość mikrosekund na ćwierć nutę. Może się znajdować w meta zdarzeniu ścieżki – fragment “MTrk” (0x4D54726B)
Jeżeli mamy te informacje możemy obliczyć częstotliwość z jaką występuje jedno tyknięcie zegara :
MSPM – mikrosekundy na minutę = 60 000 000 BPM – uderzenia na minutę MPQN – mikrosekundy na ćwierćnutę |
BPM = MSPM / MPQN MPQN = MSPM / BPM |
Jeżeli mamy 120 BPM (standardowe, domyślne tempo) to okres ćwierć nuty jest:
Następnym krokiem jest obliczenie częstotliwości tyknięcia zegara, gdy mamy okres ćwierć nuty reszta jest łatwa:
Z dwoma rzeczami powyżej programujemy tyknięcie z częstotliwością ~0,005208333333 Hz, e.g. dla rozdzielczości 96PPQN będzie 96 tyknięć tworzących trwanie ćwierć nuty (96 * 1 tyknięcie = 96 * 0,005208333333 Hz = 500 000 mikrosekund = 0,5s).
Teraz możemy, rozwiązać sprawę translacji czasu delta na czas rzeczywisty. Poniżej są trzy przykładowe wartości tempa:
Tempo 1 | Tempo 2 | Tempo 3 | |
BPM | 120 | 130 | 130 |
PPQN | 120 | 120 | 96 |
MPQN | 500000 | 461538 | 461538 |
QLS | 0,5 | 0,4615 | 0,46 |
TDPS | 0,0041666667 | 0,0038461500 | 0,0048076875 |
BPM – to uderzenia na minutę 60 000 000/MPQN
PPQN – pulsy na ćwierć nutę, rozdzielczość z pliku MIDI
MPQN – mikrosekundy na ćwierć nutę lub inaczej czas trwania ćwierć nuty, zakres 0-8355711 mikrosekund (zgodnie ze specyfikacją formatu)
QLS – sekund na ćwierć nutę (MPQN/1000000)
TDPS – sekund na tyknięcie (QLS/PPQN)
I tutaj tłumaczymy każdą wartość delta do czasu rzeczywistego dla tych trzech temp. Wszystkie zdarzenia są wyzwalane relatywnie do ostatniego zdarzenia. Tak więc pierwsze zdarzenia będą z delta 0, potem będzie potrzeba 15 tyknięć do wyzwolenia następnego zdarzenia, a potem musi minąć 45 tyknięć (w stosunku do zdarzenia poprzedniego) zanim będzie miało miejsce następne zdarzenie.
Jak nietrudno zauważyć kolumna z akumulowaną deltą odzwierciedla absolutną linię czasu zdarzeń.
Żeby otrzymać interesujący nas czas rzeczywisty w jakim zdarzenie ma nastąpić mnożymy TDPS*zakumulowana delta. Rezultaty są przedstawione w trzech kolumnach poniżej dla każdego z temp.
Jeżeli ktoś chce zobaczyć jak będą wyglądały inne tempa przygotowałem prosty arkusz w OpenOffice Calc, więc każdy może pozmieniać wartości i zobaczyć jaki mają skutek.
Tempo 1 | Tempo 2 | Tempo 3 | |||
lp | zakumulowana delta | delta | delta [s] | delta [s] | delta [s] |
1 | 0 | 0 | 0,00 | 0,00 | 0,00 |
2 | 15 | 15 | 0,06 | 0,06 | 0,07 |
3 | 60 | 45 | 0,25 | 0,23 | 0,29 |
4 | 70 | 10 | 0,29 | 0,27 | 0,34 |
5 | 90 | 20 | 0,38 | 0,35 | 0,43 |
6 | 100 | 10 | 0,42 | 0,38 | 0,48 |
7 | 120 | 20 | 0,50 | 0,46 | 0,58 |
8 | 135 | 15 | 0,56 | 0,52 | 0,65 |
9 | 180 | 45 | 0,75 | 0,69 | 0,87 |
10 | 195 | 15 | 0,81 | 0,75 | 0,94 |
11 | 240 | 45 | 1,00 | 0,92 | 1,15 |
12 | 250 | 10 | 1,04 | 0,96 | 1,20 |
13 | 270 | 20 | 1,13 | 1,04 | 1,30 |
14 | 280 | 10 | 1,17 | 1,08 | 1,35 |
15 | 300 | 20 | 1,25 | 1,15 | 1,44 |
16 | 315 | 15 | 1,31 | 1,21 | 1,51 |
17 | 360 | 45 | 1,50 | 1,38 | 1,73 |
18 | 480 | 120 | 2,00 | 1,85 | 2,31 |
19 | 555 | 75 | 2,31 | 2,13 | 2,67 |
20 | 570 | 15 | 2,38 | 2,19 | 2,74 |
21 | 615 | 45 | 2,56 | 2,37 | 2,96 |
22 | 630 | 15 | 2,63 | 2,42 | 3,03 |
23 | 675 | 45 | 2,81 | 2,60 | 3,25 |
24 | 735 | 60 | 3,06 | 2,83 | 3,53 |
25 | 765 | 30 | 3,19 | 2,94 | 3,68 |
26 | 780 | 15 | 3,25 | 3,00 | 3,75 |
27 | 795 | 15 | 3,31 | 3,06 | 3,82 |
28 | 810 | 15 | 3,38 | 3,12 | 3,89 |
29 | 855 | 45 | 3,56 | 3,29 | 4,11 |
30 | 955 | 100 | 3,98 | 3,67 | 4,59 |
31 | 975 | 20 | 4,06 | 3,75 | 4,69 |
32 | 990 | 15 | 4,13 | 3,81 | 4,76 |
33 | 1005 | 15 | 4,19 | 3,87 | 4,83 |
34 | 1020 | 15 | 4,25 | 3,92 | 4,90 |
35 | 1035 | 15 | 4,31 | 3,98 | 4,98 |
36 | 1155 | 120 | 4,81 | 4,44 | 5,55 |
Tak więc, żeby odtworzyć sekwencję z pliku MIDI typ 0 (dla uproszczenia tylko jedna ścieżka) inkrementujemy naszą deltę delta co okres TDPS. Sprawdzamy deltę aktualnego zdarzenia w sekwencji. Jeżeli delta zdarzenia jest równa ilości zliczonych tyknięć wyzwalamy zdarzenie, resetujemi licznik tyknięć i przechodzimy do następnego zdarzenia w sekwencji. W przeciwnym wypadku inkrementujemy licznik tyknięć i ponownie sprawdzamy deltę aktualnego zdarzenia.
Dla wielu ścieżek musimy utrzymywać jeden licznik tyknięć na ścieżkę i iterować przez wszystkie aktualne zdarzenia ścieżki.
Drugą opcją byłoby wyzwalanie zdarzeń zgodnie z obliczoną wcześniej linią czasową (bazującą na zakumulowanej delcie), ale obsługa byłaby skomplikowana dla plików wielościeżkowych.