События стрельбы с микросекундным разрешением для миди секвенсора - PullRequest
12 голосов
/ 04 августа 2010

Есть ли способ инициировать события в C # с разрешением в несколько микросекунд?

Я создаю MIDI-секвенсор, и для каждого тика MIDI требуется событие, которое будет воспроизводить любую ноту, зарегистрированную в то время.

При 120 ударах в минуту и ​​разрешении 120 ppqn (импульсы на удар / четвертная нота) это событие должно срабатывать каждые 4,16666 миллисекунд. Современные секвенсоры имеют более высокие разрешения, такие как 768ppqn, что требует запуска этого события каждые 651 микросекунду.

Наилучшее разрешение для кратковременных событий, которое я нашел, составляет 1 миллисекунду. Как я могу пойти дальше?

Эта проблема должна быть уже решена любым C # MIDI-секвенсором или проигрывателем MIDI-файлов. Может быть, я просто не смотрю на проблему под прямым углом.

Спасибо за вашу помощь.

Ответы [ 4 ]

6 голосов
/ 04 августа 2010

Большинство миди-секвенсоров / миди-плееров либо преобразуют большие блоки времени в волновую форму (для воспроизведения через компьютерные колонки), либо принимают большой блок MIDI-инструкций (для внешнего устройства, подключенного к порту MIDI). В любом случае, блок данных копируется на звуковую карту, а звуковая карта заботится о точном времени.

Возможно, вы захотите взглянуть на API управления мультимедиа.

См. этот пост на дискуссионном форуме Microsoft

3 голосов
/ 04 августа 2010

Я думаю, вы вряд ли получите точно правильное разрешение от таймера.Лучшим подходом было бы использовать таймер с точностью до 1 мс, а при его запуске проверить, какие события MIDI ожидают, и запустить их.

Итак, события MIDI идут в отсортированной очереди, вы смотрите первыйодин, и установите таймер для стрельбы как можно ближе к этому времени.Когда таймер срабатывает, потребляйте все события из прошедшей очереди, пока не встретите будущее событие.Подсчитайте время до этого события.Перепланируйте таймер.

Конечно, если вы выводите на свою звуковую карту, подход в корне отличается, и вы должны считать сэмплы для всех ваших таймингов.

1 голос
/ 04 августа 2010

Невозможно точно запускать события с микросекундными интервалами в .NET.

Фактически, поскольку сама Windows не является операционной системой реального времени, выполнение чего-либо со 100% точностью до определенных микросекунд в программном обеспечении пользовательского режима практически невозможно.

Для получения дополнительной информации о том, почему это так сложно, см. Статью журнала MSDN: Реализация постоянно обновляемого поставщика времени с высоким разрешением для Windows . Хотя речь идет о Windows NT, в целом это относится и к более поздним версиям Windows.

Заключение этой статьи хорошо подводит итог:

Если вы сейчас думаете, что можете получить системное время с почти произвольная точность здесь, просто небольшое предупреждение: не забудьте приоритетность многозадачности система, такая как Windows NT. В лучшем случай, отметка времени, которую вы получите, выключена только время, которое требуется, чтобы прочитать счетчик производительности и преобразовать это чтение в абсолютное время. в в худшем случае, прошедшее время может легко быть порядка десятков миллисекунды.

Хотя это может указать, что вы прошли через все это ни за что, будьте уверены, что вы не сделал. Даже выполнение вызова к Win32 API GetSystemTimeAsFileTime (или gettimeofday под Unix) подлежит такие же условия, так что вы на самом деле делает не хуже этого. В В большинстве случаев вы будете иметь хорошие результаты. Просто не выступать все, что требует в режиме реального времени предсказуемость на основе времени штампы в Windows NT.

0 голосов
/ 04 августа 2010

вместо использования таймера

используйте секундомер

пример, в течение 10x 1 секунды

    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());
        }
    }

выведет:

Тики: 10005392 (что составляет 1.0005392 секунды)
Тики: 10004792
Тики: 10004376
Тики: 10005408
Тики: 10004398
Тики: 10004426
Тиков: 10004268
Тиков: 10004427
Тиков: 10005161
Тиков: 10004306

, которые кажутся нормальными

...