2010-08-04 23 views
12

Czy istnieje sposób na wywoływanie zdarzeń w języku C# z rozdzielczością kilku mikrosekund?Zdarzenia wyzwalające w rozdzielczości mikrosekund dla sekwensera midi

Buduję sekwencer MIDI i wymaga, aby zdarzenie zostało odpalone przy każdym teście MIDI, który będzie odgrywał każdą nutę zarejestrowaną w tym czasie.

Przy 120 uderzeniach na minutę i przy rozdzielczości 120 ppqn (impulsy na uderzenie/ćwierćnuta), zdarzenie to powinno wystrzelać co 4.16666 milisekund. Nowoczesne sekwencery mają wyższe rozdzielczości, takie jak 768ppqn, co wymagałoby wyrzucania zdarzenia co 651 mikrosekund.

Najlepsza rozdzielczość dla krótkotrwałych zdarzeń, które znalazłem, wynosi 1 milisekundę. Jak mogę wyjść poza to?

Ten problem musiał już zostać rozwiązany przez dowolny sekwencer C# MIDI lub odtwarzacz plików MIDI. Może po prostu nie patrzę na problem pod właściwym kątem.

Dziękuję za pomoc.

+0

Nie podążam za twoją matematyką. Jeśli 120ppqn = 41,6666ms, z pewnością 768ppqn! = 651microsec, ale 768ppqn = 1000 * 41.6666 * 120/768 = 6510 microsec? – spender

+0

120 bpm = 1 uderzenie co 500 ms, 120 taktów na uderzenie = 500ms/120 = 4.166ms i 768 tick na uderzenie = 500ms/768 = 0.651ms Jest rzeczywiście literówka w moim pierwszym numerze, poprawię go teraz . Dziękuję Ci. – Brice

+0

Spójrz na moją zaktualizowaną odpowiedź – Fredou

Odpowiedz

6

Większość midi sekwencerów/midi będzie konwertować duże bloki czasu na kształt fali (do odtwarzania przez głośniki komputerowe) lub wziąć duży blok instrukcji MIDI (dla zewnętrznego urządzenia podłączonego do portu MIDI). Tak czy inaczej, blok danych jest kopiowany na kartę dźwiękową, a karta dźwiękowa zajmuje się dokładnym timingiem.

Być może warto przejrzeć interfejsy API kontroli multimediów.

Zobacz this post over at the Microsoft discussion forum

0

zamiast za pomocą czasowego

zastosowanie stopwatch

przykład 10x 1 sekundy

static void Main(string[] args) 
    { 
     Stopwatch masterSW; 
     Stopwatch sw=null; 
     int count; 

     sw = Stopwatch.StartNew(); 
     sw.Reset(); 

     for (int i = 0; i < 10; i++) 
     { 
      count = 0; 
      masterSW = Stopwatch.StartNew(); 
      while (count!=1536) //1537*651 microsecond is about a second (1.0005870 second) 
      { 
       if (!sw.IsRunning) 
        sw.Start(); 

       if (sw.Elapsed.Ticks >= 6510) 
       { 
        count++; 
        sw.Reset(); 
       } 
      } 

      Debug.WriteLine("Ticks: " + masterSW.Elapsed.Ticks.ToString()); 
     } 
    } 

wyprodukuje:

Kleszcze: 10005392 (który jest 1,0005392 sekundy)
Kleszcze: 10004792
Kleszcze: 10004376
Kleszcze: 10005408
Kleszcze: 10004398
Kleszcze: 10004426
Kleszcze: 10004268
Kleszcze: 10004427
Kleszcze: 10005161
Kleszcze: 10004306

które wydają się być w porządku,

+0

Dziękuję. Próbowałem używać stopera, ale z tego, co wiem, pozwala on tylko sprawdzić czas, jaki upłynął między stopwatch.start i stopwatch.stop, nie uruchamia zdarzeń w regularnych odstępach czasu – Brice

+0

Wykonuje 'count ++' w dość regularnych odstępach czasu.Dopóki absolutnie nic nie działa na maszynie, może to zadziałać. –

3

Myślę, że mało prawdopodobne jest et dokładnie poprawną rozdzielczość timera. Lepszym rozwiązaniem byłoby użycie dokładnego timera o czasie 1 ms, a po jego uruchomieniu sprawdzenie, które zdarzenia MIDI oczekują i ich zwolnienie.

Tak więc, zdarzenia MIDI są sortowane, zerknij na pierwszą i ustaw czas tak, aby strzelał jak najbliżej tego czasu. Po uruchomieniu licznika zużyj wszystkie zdarzenia z kolejki, która upłynęła, aż do wystąpienia przyszłego zdarzenia. Oblicz czas na to wydarzenie. Przełóż timer.

Oczywiście, jeśli odtwarzasz kartę dźwiękową, podejście jest zasadniczo różne i powinieneś liczyć próbki dla wszystkich ustawień czasowych.

1

Nie jest możliwe, aby zdarzenia dokładnie ostrzelali odstępach mikrosekund w .NET.

W rzeczywistości, ponieważ sam system Windows nie jest systemem działającym w czasie rzeczywistym, wykonywanie w 100% z dokładnością do mikrosekund, w trybie użytkownika, jest prawie niemożliwe.

Aby uzyskać więcej informacji o tym, dlaczego jest to tak trudne, zobacz artykuł na temat czasopisma MSDN: Implement a Continuously Updating, High-Resolution Time Provider for Windows. Chociaż mówi o systemie Windows NT, nadal ma to zastosowanie do późniejszych wersji systemu Windows.

Zawarcie tego artykułu podsumowuje to dobrze:

Jeśli teraz myślę, że można uzyskać czas systemowy z niemal dowolną dokładnością tu tylko nieznaczne ostrzeżenie: Nie zapomnij prewencyjność wielozadaniowego systemu , takiego jak Windows NT. W najlepszym przypadku znacznik czasu, który otrzymasz, jest wyłączony tylko przez czas potrzebny do odczytania licznika wydajności i przekształcenia tego odczytu w czas bezwzględny. W najgorszych przypadkach, , upływający czas może być rzędu łatwo rzędu dziesiątek milisekund.

Mimo że może to być , oznacza to, że przeszedłeś przez cały ten numer bez powodu, możesz mieć pewność, że nie zrobiłeś tego . Nawet wykonywanie połączenia z interfejsem Win32 API GetSystemTimeAsFileTime (lub gettimeofday w systemie Unix) podlega tym samym warunkom, co , więc w rzeczywistości nie robisz nic gorszego niż . W większości przypadków będziesz mieć dobre wyniki. Po prostu nie wykonuj żadnej czynności wymagającej w przewidywaniu czasu w czasie rzeczywistym na podstawie czasu w systemie Windows NT.

+0

To powinno brzmieć "w trybie użytkownika", a nie "w oprogramowaniu", ponieważ sterowniki jądra są również oprogramowaniem i na pewno są zdolne do mikrosekundowego czasu/opóźnienia. –