Есть ли согласованный способ отслеживать положение в клипе (или аудиофайле) в Java? - PullRequest
0 голосов
/ 02 мая 2019

Я пытаюсь закодировать ритмическую игру на Java.Я играю аудиофайл с javax.sound.sampled.Clip и хочу узнать точное положение, в котором я нахожусь.

После моих исследований хорошей практикой для ритм-игры является отслеживание времени с помощьюположение в воспроизводимой музыке вместо системного времени (чтобы избежать сдвига между звуком и игрой).

Я уже закодировал основной цикл, который использует System.nanoTime() для поддержания постоянной частоты кадров.Но когда я пытаюсь получить время клипа, я возвращаю одно и то же время для нескольких кадров.Вот отрывок из моего кода:

while (running) {
    // Get a consistent frame rate
    now = System.nanoTime();
    delta += (now - lastTime) / timePerTick;
    timer += now - lastTime;
    lastTime = now;
    if (delta >= 1) {
        if(!paused) {
            tick((long) Math.floor(delta * timePerTick / 1000000));
        }
        display.render();
        ticks++;
        delta--;
    }
    // Show FPS ...
}

// ...

private void tick(long frameTime) {
    // Doing some stuff ...
    System.out.println(clip.getMicrosecondPosition());
}

Я ожидал последовательного вывода (с шагом приблизительно 8333 мкс на кадр для 120 кадров в секунду), но получаю одно и то же время для нескольких кадров.

Возврат консоли:

0
0
748
748
748
10748
10748
10748
20748
20748
20748
30748

Как я могу последовательно и точно отслеживать положение, в котором я нахожусь в песне?Есть ли альтернатива javax Clip?

Ответы [ 2 ]

1 голос
/ 02 мая 2019

Есть ряд трудностей, с которыми приходится иметь дело.Во-первых, Java не предоставляет гарантий в реальном времени, поэтому момент, когда часть звуковых данных обрабатывается, не обязательно соответствует моменту их прослушивания.Во-вторых, легко запутаться из-за отсутствия детализации системных часов ОС, что, как я предполагаю, способствует тому, что вы видите, как время появляется несколько раз.У нас было много рассказов об этом на java-gaming.org несколько лет назад.[РЕДАКТИРОВАТЬ: теперь я думаю, что ваша проверка зацикливается быстрее, чем степень детализации размера буфера, используемого клипом.]

Единственный способ, который я знаю, чтобы получить действительно точную синхронизацию, - отказаться от использования.Clip в целом, и вместо этого подсчитывать кадры при выводе на SourceDataLine.У меня был хороший успех с этим.Это самая последняя точка перед выводом в собственный код рендеринга звука, и поэтому она имеет наилучшие временные характеристики.

При этом может быть возможно реализовать какой-то импульс (например, с помощью утилиты.Timer) через регулярные промежутки времени (соответствующие четвертным или шестнадцатым нотам) и TimerTask запускает воспроизведение Clip объектов.Я сам не пробовал, но этого может быть достаточно для ритм-игры.Для этого вы бы использовали Clip объектов, а не SourceDataLine.Но убедитесь, что каждый Clip полностью загружен и готов к работе во время воспроизведения.Не делайте типичную ошибку, загружая их заново для каждого воспроизведения.

Но когда я в последний раз пытался сделать что-то подобное, используя обычный игровой цикл, результат был не таким горячим.

JavaFX отлично подходит для написания игр!Я нахожу графику намного проще в обращении, чем AWT.Я написал учебник для начала работы с JavaFX для программирования игр.Это устарело, что касается настройки с Eclipse, если вы используете JavaFX 11. Но если вы используете Java 8, я думаю, что он все еще охватывает основы.

В JGO люди в пареразные лагеря.Многие любят использовать Libgdx, библиотеку, а некоторые из нас продолжают использовать JavaFX или AWT / Swing.Есть еще несколько библиотек, отстаиваемых различными участниками.

JavaFX имеет свой собственный звуковой выход, но я не играл с ним и не знаю, лучше ли это, чем Clip для ритмической точности.Все сигналы играют великолепно, но всегда будет трудно получить полезную информацию о синхронизации из сигналов, учитывая систему непрерывного переключения между потоками в Java.

Просто просматривал содержимое на JGO.Вы можете найти эту тему интересной, где ОП пытался сделать метроном.http://www.java -gaming.org / themes / java-audio-метроном-проблемы со временем и скоростью / 33591 / view.html

[РЕДАКТИРОВАТЬ: PS, я написалнадежный метроном и система воспроизведения событий, достаточно надежная, чтобы связать воедино музыкальное событие в бесшовном ритме, и слушатели, которые могут следить за такими вещами, как огибающие громкости нот, воспроизводимых в режиме реального времени.В конечном итоге это должно быть выполнимо, если вы хотите.]

0 голосов
/ 03 мая 2019

Проблема с clip.getMicrosecondPosition() заключается в том, что она может сообщать вам только позицию аудио, которая была отправлена ​​в собственную аудиосистему или соответствует буферу, который отображается .

Что это значит?

При воспроизведении аудио у вас обычно есть несколько сэмплов в памяти (как в Clip), которые вы отправляете в собственную аудиосистему для воспроизведения. Теперь отправка каждого образца по отдельности была бы супер неэффективной, поэтому вы всегда отправляете образцы в большом количестве. В терминах javax.sound.sampled вы записываете их в SourceDataLine , используя метод write (buf) . Теперь, когда вы записываете данные, они не отправляются непосредственно в динамик, а записываются в буфер. Собственная система затем берет сэмплы из этого буфера и отправляет их на динамик. Система может снова выбрать выборку из этого буфера навалом.

Поэтому, когда вы спрашиваете клип о его микросекундной позиции, он может не дать вам действительно то, что было воспроизведено на самом деле (которое воспроизводится на динамике), но то, что было записано с клип в памяти на буфер строки или на то, что было извлечено системой из внутреннего буфера строки.

Таким образом, разрешение clip.getMicrosecondPosition() может зависеть от размеров буфера.

Чтобы повлиять на размеры используемого буфера, вы можете попробовать что-то подобное при создании клипа:

int bufferSize = 1024;
AudioFormat format = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
    AudioSystem.NOT_SPECIFIED, 16, 2, 4, AudioSystem.NOT_SPECIFIED, true);
DataLine.Info info = new DataLine.Info(Clip.class, format, bufferSize);
Clip clip = (Clip) AudioSystem.getLine(info)

При воспроизведении клипа аудиосистема просит использовать буфер размером всего 1024 байта. При использовании формата с 16 битами на выборку, 2 каналами на кадр и частотой дискретизации 44100 Гц это будет соответствовать 256 кадрам (4 байта на кадр), т. Е. 256/44100 = 0,0058 секунды, очень короткий буфер.

Для высокого разрешения (аудио с малой задержкой) предпочтителен короткий буфер. Однако, если сделать буфер слишком коротким, вы можете испытывать переполнение буфера, что означает, что ваш звук будет «зависать» или «заикаться». Это очень раздражает пользователя. Имейте в виду, что Java использует сборку мусора. Поэтому вы можете просто не иметь возможности непрерывно записывать аудиосэмплы в систему, когда Java занята выбрасыванием мусора (отсутствие возможностей в реальном времени).

Надеюсь, это поможет. Удачи!

...