Как определить задержку звука (AudioTrack) на Android?

У меня есть приложение, в котором я использую AudioTrack в потоковом режиме для воспроизведения динамически генерируемого звука. Приложение не должно мгновенно реагировать на ввод, поэтому проблемы с задержкой не беспокоят меня в этой части программы.

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

Мое текущее решение дает мне большую часть пути — я подсчитываю количество кадров, которые я передал до AudioTrack, и сравниваю его с getPlaybackHeadPosition(). В основном это выглядит так:

long currentTimeInFrames = 0;
while(playingAudio) {
  currentTimeInFrames += numberOfFramesToWrite;
  long delayInFrames = (currentTimeInFrames - audioTrack.getPlaybackHeadPosition());
  audioTrack.write(frameBuffer,0,sampleSize);
  doAnimationAfterDelay(delayInFrames);
}

Тем не менее, все еще существует некоторая задержка, которую getPlaybackHeadPosition(), похоже, не учитывает и которая зависит от устройства.

Есть ли способ опросить систему на предмет задержки AudioTrack?


person jnpdx    schedule 04.04.2013    source источник


Ответы (4)


Учитывайте задержку драйвера. Для этого есть скрытая функция AudioManager.getOutputLatency(int).

Назовите это так:

AudioManager am = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
try{
   Method m = am.getClass().getMethod("getOutputLatency", int.class);
   latency = (Integer)m.invoke(am, AudioManager.STREAM_MUSIC);
}catch(Exception e){
}

Я получаю около 45-50 мс на разных устройствах. Используйте результат в своих расчетах.

person Pointer Null    schedule 22.04.2014
comment
Обратите внимание, что на некоторых устройствах выдает MethodNotFoundException - person zella; 10.09.2015
comment
Существует еще один скрытый метод AudioTrack.getLatency, который является более точным, поскольку он находится в экземпляре AudioTrack и, таким образом, также может учитывать задержку, связанную с оборудованием/маршрутом. Его можно вызвать аналогичным образом через отражение: developer.amazon.com/docs/fire-tv/ - person tmm1; 30.03.2018

Уровень API 19 добавляет в AudioTrack метод с именем получить отметку времени(). Из документации:

Опрос для временной метки по запросу.

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

Вы указываете объект AudioTimestamp в качестве параметра функции, и он заполнит самая последняя представленная позиция кадра вместе с его предполагаемой отметкой времени в наносекундах. Значение в наносекундах соответствует значению в миллисекундах, возвращаемому функцией SystemClock.uptimeMillis( ).

Затем вы можете определить задержку, выяснив, когда вы записали этот конкретный кадр в AudioTrack по сравнению с тем, когда getTimestamp() думает, что он действительно представлен. Я нашел этот метод более точным, чем другие методы, упомянутые выше.

Вы должны быть осторожны. В документации сказано, что getTimeStamp() поддерживается не на всех платформах и не на всех маршрутах. Вы можете определить, был ли вызов успешным, проверив возвращаемое значение boolean. На устройствах, которые я тестировал, я обнаружил, что функция возвращает false до тех пор, пока звук не начнет воспроизводиться, а затем последующие вызовы возвращают true. Я тестировал только с AudioTrack в режиме STREAM_MUSIC. Ваш пробег может отличаться.

person josmith42    schedule 04.06.2016

Вы должны учитывать размер буфера, который вы передали при создании AudioTrack.

final int minBufSize = AudioTrack.getMinBufferSize(Application.PLAYRATE,
AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT);

out=new AudioTrack(AudioManager.STREAM_MUSIC, Application.PLAYRATE, 
AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT, minBufSize, 
AudioTrack.MODE_STREAM);

extraLatencyFrames = minBufSize/4;
person Community    schedule 10.09.2013
comment
Хм.. теперь, когда я снова думаю об этом. Было бы странно, если бы этот минимальный размер буфера не учитывался в положении головы. - person ; 10.09.2013

Ладно, это ключ. Сначала вам нужно расширить класс Audiotrack, а затем использовать getNativeFrameCount, чтобы получить приблизительное представление о задержке, связанной с нативной стороной вещей.

class MyAudioTrack extends AudioTrack
{
    public MyAudioTrack(int streamType, int sampleRateInHz, int channelConfig,
            int audioFormat, int bufferSizeInBytes, int mode)
            throws IllegalArgumentException {
        super(streamType, sampleRateInHz, channelConfig, audioFormat,
                bufferSizeInBytes, mode);
        System.out.println("Native framecount "+getNativeFrameCount());
    }   
    public int getFrameCount()
    {
        return getNativeFrameCount();
    }
}
person Community    schedule 10.09.2013