Задержка звука

Задержка — это время, необходимое сигналу для прохождения через систему. Вот распространенные типы задержки, связанные с аудиоприложениями:

  • Задержка вывода звука — это время между сэмплом, создаваемым приложением, и сэмплом, воспроизводимым через разъем для наушников или встроенный динамик.
  • Задержка аудиовхода — это время между получением аудиосигнала аудиовходом устройства, например микрофоном, и появлением тех же аудиоданных, доступных приложению.
  • Задержка туда и обратно — это сумма задержки ввода, времени обработки приложения и задержки вывода.

  • Задержка касания — это время между прикосновением пользователя к экрану и получением события касания приложением.
  • Задержка прогрева — это время, необходимое для запуска аудиоконвейера при первой постановке данных в буфер.

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

Измерение задержки

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

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

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

Поскольку наименьшая задержка достигается на аудиопутях с минимальной обработкой сигнала, вы также можете использовать ключ Audio Loopback Dongle , который позволяет запускать тест через разъем гарнитуры.

Рекомендации по минимизации задержки

Проверка качества звука

Документ определения совместимости Android (CDD) перечисляет требования к аппаратному и программному обеспечению совместимого устройства Android. См. «Совместимость Android» для получения дополнительной информации об общей программе совместимости и CDD для фактического документа CDD.

В CDD задержка туда и обратно указана как 20 мс или ниже (хотя музыкантам обычно требуется 10 мс). Это связано с тем, что существуют важные варианты использования, которые включаются через 20 мс.

В настоящее время не существует API для определения задержки звука по любому пути на устройстве Android во время выполнения. Однако вы можете использовать следующие флаги аппаратных функций, чтобы узнать, дает ли устройство какие-либо гарантии задержки:

Критерии сообщения об этих флагах определены в CDD в разделах 5.6 Задержка звука и 5.10 Профессиональное аудио .

Вот как проверить наличие этих функций в Java:

Котлин

val hasLowLatencyFeature: Boolean =
        packageManager.hasSystemFeature(PackageManager.FEATURE_AUDIO_LOW_LATENCY)

val hasProFeature: Boolean =
        packageManager.hasSystemFeature(PackageManager.FEATURE_AUDIO_PRO)

Ява

boolean hasLowLatencyFeature =
    getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUDIO_LOW_LATENCY);

boolean hasProFeature =
    getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUDIO_PRO);

Что касается взаимосвязи функций звука, функция android.hardware.audio.low_latency является обязательным условием для android.hardware.audio.pro . Устройство может реализовать android.hardware.audio.low_latency , а не android.hardware.audio.pro , но не наоборот.

Не делайте предположений о качестве звука

Остерегайтесь следующих предположений, которые помогут избежать проблем с задержкой:

  • Не думайте, что динамики и микрофоны, используемые в мобильных устройствах, обычно имеют хорошую акустику. Из-за их небольшого размера акустика, как правило, плохая, поэтому для улучшения качества звука добавляется обработка сигнала. Такая обработка сигнала приводит к задержке.
  • Не предполагайте, что ваши входные и выходные обратные вызовы синхронизированы. Для одновременного ввода и вывода для каждой стороны используются отдельные обработчики завершения очереди буфера. Нет никакой гарантии относительного порядка этих обратных вызовов или синхронизации аудиосигналов, даже если обе стороны используют одинаковую частоту дискретизации. Ваше приложение должно буферизовать данные с правильной синхронизацией буферов.
  • Не предполагайте, что фактическая частота дискретизации точно соответствует номинальной частоте дискретизации. Например, если номинальная частота дискретизации составляет 48 000 Гц, это нормально, что аудиосинхронизация движется немного с другой скоростью, чем скорость операционной системы CLOCK_MONOTONIC . Это связано с тем, что звуковые и системные часы могут получаться от разных кристаллов.
  • Не предполагайте, что фактическая частота дискретизации воспроизведения точно соответствует фактической частоте дискретизации захвата, особенно если конечные точки находятся на разных путях. Например, если вы захватываете звук с микрофона на устройстве с номинальной частотой дискретизации 48 000 Гц и воспроизводите аудио через USB с номинальной частотой дискретизации 48 000 Гц, фактические частоты дискретизации, скорее всего, будут немного отличаться друг от друга.

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

Минимизируйте задержку ввода

В этом разделе представлены рекомендации, которые помогут снизить задержку ввода звука при записи с помощью встроенного микрофона или микрофона внешней гарнитуры.

  • Если ваше приложение отслеживает ввод, предложите пользователям использовать гарнитуру (например, отобразив экран «Лучше всего с наушниками» при первом запуске). Обратите внимание: простое использование гарнитуры не гарантирует минимально возможной задержки. Возможно, вам придется выполнить другие действия, чтобы удалить нежелательную обработку сигнала из аудиотракта, например, используя предустановку VOICE_RECOGNITION при записи.
  • Будьте готовы обрабатывать номинальные частоты дискретизации 44 100 и 48 000 Гц, как сообщает getProperty(String) для PROPERTY_OUTPUT_SAMPLE_RATE . Другие частоты дискретизации возможны, но редки.
  • Будьте готовы обработать размер буфера, сообщаемый getProperty(String) для PROPERTY_OUTPUT_FRAMES_PER_BUFFER . Типичные размеры буфера включают 96, 128, 160, 192, 240, 256 или 512 кадров, но возможны и другие значения.

Минимизируйте задержку вывода

Используйте оптимальную частоту дискретизации при создании аудиоплеера

Чтобы получить минимальную задержку, необходимо предоставить аудиоданные, соответствующие оптимальной частоте дискретизации и размеру буфера устройства. Дополнительные сведения см. в разделе Проектирование с уменьшенной задержкой .

В Java вы можете получить оптимальную частоту дискретизации с помощью AudioManager, как показано в следующем примере кода:

Котлин

val am = getSystemService(Context.AUDIO_SERVICE) as AudioManager
val sampleRateStr: String? = am.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE)
var sampleRate: Int = sampleRateStr?.let { str ->
    Integer.parseInt(str).takeUnless { it == 0 }
} ?: 44100 // Use a default value if property not found

Ява

AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
String sampleRateStr = am.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE);
int sampleRate = Integer.parseInt(sampleRateStr);
if (sampleRate == 0) sampleRate = 44100; // Use a default value if property not found

Зная оптимальную частоту дискретизации, вы можете указать ее при создании проигрывателя. В этом примере используется OpenSL ES :

// create buffer queue audio player
void Java_com_example_audio_generatetone_MainActivity_createBufferQueueAudioPlayer
        (JNIEnv* env, jclass clazz, jint sampleRate, jint framesPerBuffer)
{
   ...
   // specify the audio source format
   SLDataFormat_PCM format_pcm;
   format_pcm.numChannels = 2;
   format_pcm.samplesPerSec = (SLuint32) sampleRate * 1000;
   ...
}

Примечание. samplesPerSec означает частоту дискретизации на канал в миллигерцах (1 Гц = 1000 МГц).

Используйте оптимальный размер буфера для постановки в очередь аудиоданных

Вы можете получить оптимальный размер буфера аналогично оптимальной частоте дискретизации, используя API AudioManager:

Котлин

val am = getSystemService(Context.AUDIO_SERVICE) as AudioManager
val framesPerBuffer: String? = am.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER)
var framesPerBufferInt: Int = framesPerBuffer?.let { str ->
    Integer.parseInt(str).takeUnless { it == 0 }
} ?: 256 // Use default

Ява

AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
String framesPerBuffer = am.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER);
int framesPerBufferInt = Integer.parseInt(framesPerBuffer);
if (framesPerBufferInt == 0) framesPerBufferInt = 256; // Use default

Свойство PROPERTY_OUTPUT_FRAMES_PER_BUFFER указывает количество аудиокадров, которые может хранить буфер HAL (уровень аппаратной абстракции). Вам следует сконструировать свои аудиобуферы так, чтобы они содержали число, кратное этому числу. Если вы используете правильное количество аудиокадров, обратные вызовы выполняются через регулярные промежутки времени, что снижает джиттер.

Важно использовать API для определения размера буфера, а не использовать жестко запрограммированное значение, поскольку размеры буфера HAL различаются на разных устройствах и в разных сборках Android.

Не добавляйте выходные интерфейсы, требующие обработки сигналов.

Быстрый микшер поддерживает только эти интерфейсы:

  • SL_IID_ANDROIDSIMPLEBUFFERQUEUE
  • SL_IID_VOLUME
  • SL_IID_MUTESOLO

Эти интерфейсы не разрешены, поскольку они включают обработку сигналов и могут привести к отклонению вашего запроса на ускоренное рассмотрение:

  • SL_IID_BASSBOOST
  • SL_IID_EFFECTSEND
  • SL_IID_ENVIRONMENTALREVERB
  • SL_IID_EQUALIZER
  • SL_IID_PLAYBACKRATE
  • SL_IID_PRESETREVERB
  • SL_IID_VIRTUALIZER
  • SL_IID_ANDROIDEFFECT
  • SL_IID_ANDROIDEFFECTSEND

При создании проигрывателя убедитесь, что вы добавляете только быстрые интерфейсы, как показано в следующем примере:

const SLInterfaceID interface_ids[2] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_VOLUME };

Убедитесь, что вы используете трек с низкой задержкой

Выполните следующие действия, чтобы убедиться, что вы успешно получили трек с низкой задержкой:

  1. Запустите приложение и затем выполните следующую команду:
  2. adb shell ps | grep your_app_name
    
  3. Запишите идентификатор процесса вашего приложения.
  4. Теперь воспроизведите звук из вашего приложения. У вас есть примерно три секунды, чтобы выполнить следующую команду из терминала:
  5. adb shell dumpsys media.audio_flinger
    
  6. Сканируйте свой идентификатор процесса. Если вы видите букву F в столбце «Имя» , это означает, что используется дорожка с низкой задержкой (F означает « быстрая дорожка »).

Минимизируйте задержку прогрева

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

#define CHANNELS 1
static short* silenceBuffer;
int numSamples = frames * CHANNELS;
silenceBuffer = malloc(sizeof(*silenceBuffer) * numSamples);
    for (i = 0; i<numSamples; i++) {
        silenceBuffer[i] = 0;
    }

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

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

Дополнительный пример кода

Чтобы загрузить пример приложения, демонстрирующего задержку звука, см. раздел «Образцы NDK» .

Для получения дополнительной информации

  1. Задержка звука для разработчиков приложений
  2. Причины задержки звука
  3. Измерение задержки звука
  4. Аудио разминка
  5. Задержка (аудио)
  6. Время задержки туда и обратно