Udostępnij przez


Synchronizacja zegara

Krytycznym zadaniem dla odbiornika fal jest rozwiązanie dryfu czasu między zegarem odniesienia a kryształami zegara próbnego. Robi to za pomocą oprogramowania będącego odpowiednikiem pętli synchronizowanej fazowo.

Ujście fali śledzi liczbę próbek w buforze, do której może zapisywać dane. Nawet jeśli wie, że jest przy próbce 20, ujście fali nadal musi sprawdzić zegar główny, aby uzyskać czas odniesienia. Ma wątek, który budzi się co około 20 milisekund i pyta zegara głównego o bieżący czas. Zegar główny może zgłaszać, że bieżący czas (w milisekundach) wynosi na przykład 420.

Odbiornik fali utrzymuje również zegar opóźnienia, który pokazuje przesunięcie między bieżącym czasem zgodnie z zegarem głównym a czasem próbkowania. Używa tych informacji do obliczenia oczekiwanego czasu zegara głównego i porównuje to z rzeczywistym odczytem zegara głównego, aby sprawdzić, czy dwa zegary rozjechały się.

Układ synchronizacji fazy fali używa pętli fazowej do dostosowania czasu próbkowania. Podczas sprawdzania dryfu obiekt falowy nie dostosowuje się w pełnym zakresie, ponieważ odczyty zawierają pewne drgania. Zamiast tego przesuwa zegar próbki o ułamek odległości w kierunku zegara głównego. W ten sposób odbiornik fali wygładza błędy drgań, pozostając w przybliżeniu zsynchronizowane. Zajmuje również ten czas i konwertuje go na czas opóźnienia w odniesieniu do zegara głównego. Jest to ważne, ponieważ aplikacja może wymagać znajomości miejsca renderowania syntetyzatora w dowolnym momencie.

Zegar opóźnienia informuje aplikację o najwcześniejszym czasie, w którym można zaplanować odtwarzanie nowej notatki. Czas zegara opóźnienia to czas zegara głównego oraz przesunięcie, które reprezentuje opóźnienie syntetyzatora. To opóźnienie reprezentuje minimalne opóźnienie od momentu, w jaki aplikacja przesyła nową notatkę do czasu, w jaki syntetyzator rzeczywiście odtwarza notatkę. W dowolnym momencie aplikacja może zaplanować odtworzenie nuty, która ma być zagrana w momencie lub po obecnym czasie opóźnienia zegara, ale nie wcześniej.

Na przykład, jeśli zegar główny pokazuje czas 420, a aplikacja ma nutę, którą chce odtworzyć tak szybko, jak to możliwe, zegar opóźnienia informuje ją, kiedy najwcześniej może być odtworzona. Jeśli syntetyzator oprogramowania ma opóźnienie 100 milisekund, następnym razem, gdy będzie można odtworzyć nutę, to czas 520.

Załóżmy, że zdarzenie jest oznaczone do odtwarzania o godzinie 520 według czasu odniesienia. Syntetyzator wykonuje swoją pracę przez renderowanie notatek w próbkach i wykonywanie wszystkich obliczeń w czasie próby. Musi zatem wiedzieć, jak czas odniesienia 520 jest przeliczany na czas próbkowania. W trybie użytkownika odbiornik fal zapewnia dwie funkcje wykorzystywane przez syntezator:

IDirectMusicSynthSink::SampleToRefTime i IDirectMusicSynthSink::RefTimeToSample**

Aby wykonać konwersję w tym przypadku, synth wywołuje element IDirectMusicSynthSink::RefTimeToSample na ujściu fali.

Następnie wyjście falowe zwraca czas próbkowania (na przykład 600). Notatka, o której mowa, zostaje renderowana przy czasie próbkowania 600. Następnie, gdy metoda synth IDirectMusicSynth::Render jest wywoływana przez ujście fali w celu renderowania następnej części strumienia (na przykład od czasu próbki 600 do 800), nuta jest renderowana w buforze w czasie próbki 600.

Uwaga Czas próbki jest zachowywany jako liczba 64-bitowa, aby uniknąć przepływu liczb. (Wartość DWORD przewraca się w ciągu 27 godzin).

Podsumowując, synth wykonuje całą wewnętrzną matematykę w czasie próby, a ujście fali wykonuje konwersję na czas próbkowania z czasu odniesienia i odwrotnie. Odbiornik fali zarządza również synchronizacją z zegarem głównym i dostarcza informacje o opóźnieniach. Ukrycie tej funkcjonalności w odbiorniku fali ułatwia tworzenie syntezatora.