Задержка аудио

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

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

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

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

Измерьте задержку

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

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

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

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

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

Проверка эффективности аудиообъявлений

В документе Android Compatibility Definition Document (CDD) перечислены требования к аппаратному и программному обеспечению совместимых устройств Android. Дополнительную информацию о программе совместимости можно найти в статье Совместимость Android, а сам документ CDD – в статье Документ определения совместимости (CDD).

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

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

Критерии для передачи этих флагов определены в CDD в разделах 5.6 Audio Latency и 5.10 Professional Audio.

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

Kotlin

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

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

Java

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-аудиоустройство с такой же номинальной частотой дискретизации, фактические частоты дискретизации могут немного отличаться.

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

Минимизация задержки ввода

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

  • Если ваше приложение отслеживает входной сигнал, предложите пользователям использовать гарнитуру (например, покажите экран Лучше использовать наушники при первом запуске). Обратите внимание, что использование гарнитуры не гарантирует минимальную задержку. Возможно, вам потребуется выполнить другие действия, чтобы удалить нежелательную обработку сигнала из аудиопути, например использовать набор настроек 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, как показано в следующем примере кода:

Kotlin

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

Java

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 мГц).

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

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

Kotlin

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

Java

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. Найдите идентификатор процесса. Если в столбце Name (Название) есть буква F, значит это версия с низкой задержкой (F означает fast track).

Минимизация задержки при разогреве

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

#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. Время задержки при передаче данных в обе стороны