Русские Блоги
Аудиосистема Android: от AudioTrack до AudioFlinger
1. Обзор Android Audio Framework
Аудио — очень важная часть всей платформы Android, отвечающая за сбор и вывод аудиоданных, управление аудиопотоком, управление аудиоустройствами, регулировку громкости и т. Д. Она в основном включает в себя следующие части:
Платформа аудио приложений: Структура аудио приложений
AudioTrack: отвечает за вывод данных воспроизведения, принадлежит к классу API фреймворка Android.
AudioRecord: отвечает за сбор данных записи, принадлежащих классу API платформы приложения Android.
AudioSystem: отвечает за комплексное управление аудиофайлами, относится к классу API платформы приложений Android.
Аудио Native Framework: Аудио Native Framework
AudioTrack: отвечает за вывод данных воспроизведения, принадлежит к классу API собственной платформы Android.
AudioRecord: отвечает за сбор данных записи, принадлежащих классу API собственной платформы Android.
AudioSystem: отвечает за комплексное управление аудио делами, относится к классу API собственной платформы Android.
Аудио услуги — аудио услуги
AudioPolicyService: разработчик политик аудио, отвечающий за выбор стратегий переключения аудиоустройств, стратегий регулировки громкости и т. Д.
AudioFlinger: исполнитель аудиостратегии, отвечающий за управление устройствами потокового ввода и вывода, а также за обработку и передачу данных аудиопотока.
Audio HAL: уровень абстракции аудиооборудования, отвечающий за взаимодействие с аудиоустройствами, напрямую вызываемый AudioFlinger
С аудио тесно связана MultiMedia, которая отвечает за кодирование и декодирование аудио и видео,
MultiMedia выводит декодированные данные через AudioTrack, а данные записи, собранные AudioRecord, кодируются MultiMedia.
2. Обзор API AudioTrack
Для воспроизведения звуков вы можете использовать MediaPlayer и AudioTrack, оба из которых предоставляют API-интерфейсы Java для разработчиков приложений. Разница между ними:
MediaPlayer может воспроизводить аудиоисточники в нескольких форматах, таких как mp3, flac, wma, ogg, wav и т. д.
И AudioTrack может воспроизводить только декодированный поток данных PCM.
Из приведенной выше схемы архитектуры аудиосистемы Android:
MediaPlayer создаст соответствующий аудиодекодер и AudioTrack на собственном уровне, а декодированные данные будут выводиться AudioTrack.
Таким образом, MediaPlayer имеет более широкий диапазон сценариев приложений, и его, как правило, более удобно использовать; AudioTrack требуется только для некоторых сценариев приложений, которые требуют очень требовательной задержки звука.
2.1. AudioTrack Java API
AudioTrack Java API имеет два режима передачи данных:
Transfer Mode | Description |
---|---|
MODE_STATIC | Процесс приложения передает данные воспроизведения в AudioTrack за один раз, что подходит для сценариев с небольшим объемом данных и требованиями к высокой задержке. |
MODE_STREAM | Пользовательский процесс должен постоянно вызывать write () для записи данных в FIFO, и он может быть заблокирован при записи данных. (Ожидание, пока AudioFlinger :: PlaybackThread потребляет предыдущие данные), в основном применимо ко всем аудио сценариям |
Тип аудиопотока AudioTrack Java API:
Stream Type | Description |
---|---|
STREAM_VOICE_CALL | Телефонный голос |
STREAM_SYSTEM | Системный звук |
STREAM_RING | Мелодии звонков, например мелодии звонков, мелодии будильника и т. Д. |
STREAM_MUSIC | Музыкальный звук |
STREAM_ALARM | Предупреждающий сигнал |
STREAM_NOTIFICATION | Тон уведомления |
STREAM_DTMF | DTMF тон (тональный сигнал клавиатуры набора номера) |
Почему Android определяет так много типов потоков? Это связано со стратегией управления звуком Android, например:
① Управление громкостью аудиопотока, регулировка громкости одного типа аудиопотока без влияния на другие типы аудиопотоков
② Выберите соответствующее устройство вывода в соответствии с типом потока: Например, когда подключена проводная гарнитура, музыкальный звук (STREAM_MUSIC) будет выводиться только на проводную гарнитуру, а рингтон (STREAM_RING) будет выводиться на проводную гарнитуру и внешний усилитель одновременно.
Это содержимое AudioPolicyService. Разработчики приложений должны выбрать соответствующий тип потока в соответствии со сценарием приложения, чтобы система могла выбрать подходящее устройство вывода для этого потока.
Тестовый пример AudioTrack Java API (режим MODE_STREAM):
Подробное описание интерфейса getMinBufferSize (): буквально означает возвращение размера наименьшего буфера данных, что является минимальной гарантией нормального воспроизведения звука. Возвращаемое значение зависит от параметров функции от частоты дискретизации, глубины дискретизации и количества каналов. Атрибуты.
В режиме MODE_STREAM прикладная программа в основном обращается к своему возвращаемому значению, а затем определяет, сколько буфера данных выделить.
Если буфер данных выделен слишком маленьким, воспроизводимый звук часто будет испытывать опустошение.
underrun означает, что данные, предоставленные производителем (AudioTrack), не успевают за данными, потребляемыми потребителем (AudioFlinger :: PlaybackThread).
Реальный результат — прерывистая звуковая задержка, которая серьезно влияет на качество прослушивания.
Ниже приводится реализация кода getMinBufferSize ():
Видимый минимальный размер буфера = минимальное количество кадров * количество каналов * глубина выборки (глубина выборки в байтах)
К настоящему моменту у всех должно быть четкое понимание. Если количество кадров в видео слишком мало, изображение будет зависать. То же самое и со звуком. Как найти минимальное количество кадров, мы объясним нативному слою.
2.2. AudioTrack Native API
AudioTrack Native API четыре режима передачи данных:
Transfer Mode | Description |
---|---|
TRANSFER_CALLBACK | В потоке AudioTrackThread используйте функцию обратного вызова audioCallback для активного запроса данных из процесса приложения, ToneGenerator использует эту модель |
TRANSFER_OBTAIN | Процесс приложения должен вызвать receiveBuffer () / releaseBuffer () для заполнения данных. Я еще не видел фактического сценария использования. |
TRANSFER_SYNC | Процесс приложения должен постоянно вызывать write () для записи данных в FIFO, и он может быть заблокирован при записи данных. (Ожидание, пока AudioFlinger :: PlaybackThread потребляет предыдущие данные), в основном применимо ко всем сценариям звука; Соответствует режиму MODE_STREAM API AudioTrack Java |
TRANSFER_SHARED | Процесс приложения передает данные воспроизведения в AudioTrack за один раз, что подходит для сценариев с небольшим объемом данных и высокими требованиями к задержке; Соответствует режиму MODE_STATIC API AudioTrack Java |
Тип аудиопотока AudioTrack Native API:
Stream Type | Description |
---|---|
AUDIO_STREAM_VOICE_CALL | Телефонный голос |
AUDIO_STREAM_SYSTEM | Системный звук |
AUDIO_STREAM_RING | Мелодии звонков, например мелодии звонков, мелодии будильника и т. Д. |
AUDIO_STREAM_MUSIC | Музыкальный звук |
AUDIO_STREAM_ALARM | Предупреждающий сигнал |
AUDIO_STREAM_NOTIFICATION | Тон уведомления |
AUDIO_STREAM_DTMF | DTMF тон (тональный сигнал клавиатуры набора номера) |
Идентификация вывода AudioTrack Native API:
AUDIO_OUTPUT_FLAG | Description |
---|---|
AUDIO_OUTPUT_FLAG_DIRECT | Указывает, что аудиопоток напрямую выводится на аудиоустройство без программного микширования, Обычно используется для вывода звука устройства HDMI |
AUDIO_OUTPUT_FLAG_PRIMARY | Указывает, что аудиопоток необходимо вывести на основное устройство вывода, Обычно используется для рингтонов |
AUDIO_OUTPUT_FLAG_FAST | Указывает, что аудиопоток необходимо быстро вывести на аудиоустройство, Обычно используется в сценах с высокими требованиями к задержке, таких как звуки кнопок и фоновые звуки игры. |
AUDIO_OUTPUT_FLAG_DEEP_BUFFER | Указывает, что выходной аудиопоток может принимать большую задержку, Обычно используется в сценах, не требующих большой задержки, например при воспроизведении музыки и видео. |
AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD | Указывает, что аудиопоток не был декодирован программным обеспечением, Его необходимо вывести на аппаратный декодер и декодировать аппаратным декодером. |
Мы используем разные выходные идентификаторы в соответствии с разными сценариями воспроизведения,
Если для звука клавиш и фонового звука игры требуется большая задержка вывода, вам необходимо установить AUDIO_OUTPUT_FLAG_FAST,
Подробнее см. в ToneGenerator, SoundPool и OpenSL ES.
Тестовый пример AudioTrack Natvie API (режим MODE_STATIC / TRANSFER_SHARED),
Расположение файла кода: frameworks / base / media / tests / audiotests / shared_mem_test.cpp:
В последнем разделе все еще есть проблема: как AudioTrack :: getMinFrameCount () вычисляет минимальное количество кадров?
Источник
Android High Performance Audio APIs 🥁
Performance matters. It is especially true for audio performance, where you want the audio to be as smooth as possible.
In this article I will have a look at various available options you can use in your high performance audio app on Android.
But first, we need to understand the motivation behind high performance audio. Why is it important? What problems are we trying to solve? What apps are we actually talking about?
High performance audio app can be for example a karaoke app, a music production or learning app, a drum machine, a synthesizer… Frankly, it can be anything. But they all have all in common one goal — minimal latency and increased quality.
Latency is the time it takes for a signal to travel through a system. So in order to have a responsive user interface and good user experience, latency should be as low as possible. Best Android devices currently on the market can go as low as 20 ms for round-trip latency.
Round-trip latency is the sum of input latency, app processing time, and output latency.
This 20ms latency is called a PRO latency, there is also a LOW latency which guarantee 45ms latency and you can check at runtime if the currently used device supports one of these two features.
Lastly, before we take a look at the high performance audio options, a bare minimum of digital audio terminology:
PCM — Pulse-code modulation is a standard method in computer science to convert an analog signal to its digital representation. During PCM, continuous analog signal is regularly captured and converted into small audio samples.
Sampling rate —A speed at which individual samples are created. Usually it is 44,1 or 48 kHz. This means that PCM produces 44 100 or 48 000 samples per second from an analog signal.
Sample size — or a bit depth is a maximal amplitude of a sample represented in bits. For example an 8 bit depth, which can represent only 256 levels (2⁸) of an amplitude would have a much lower audio quality and resolution than a standard 16 bit depth which can hold 65536 (2¹⁶) levels of an amplitude.
Channel — Can be either mono (1 channel) or stereo (2 channels).
SoundPool
The SoundPool class can be used to create a collection of samples, from existing resources, that are pre-loaded into memory. This collection can then be played with low latency since there is no CPU load and decompressing during playback.
Apart from basic operations, SoundPool provides several nice-to-have features such as:
- Setting a maximum number of streams that can be played at a time from a single SoundPool
- Prioritisation of playback streams
- Playback rate customisation ranging from 0.5 to 2.0
- Pausing and resuming playback
SoundPool is a convenient tool if you need to play and manage short, often repeated sounds that already exists in your APK or in the filesystem. I would typically use SoundPool to play low-latency UI or game sounds.
AudioTrack/AudioRecord
AudioTrack and AudioRecord are the lowest level APIs you can access while still using Java or Kotlin. AudioTrack is used for audio playback and AudioRecord equivalently for recording audio. They were both added early in Android API level 3 and provide convenient way to work with raw audio data.
I will focus rather on AudioTrack since this article is mainly about audio playback, but feel free to explore AudioRecord also.
AudioTrack can operate under two modes:
Static mode is better for short sounds that need to played often and with minimal latency, similar to SoundPool. But compared to SoundPool, you can modify or enhance the raw audio data.
In streaming mode you can play sounds that are either too large to fit into memory, because of the length or characteristics of the audio to be played, or that are received or generated on the fly.
I think that AudioTrack is a perfect introductory API to start working with raw audio on Android. Having all the high performance features available, but remaining still in the Java layer..
OpenSL ES
OpenSL ES is a cross-platform, hardware-accelerated audio API tuned for embedded systems, such as mobile devices. Its Android specific implementation comes as a part of the Android NDK, but you need to be aware that there are some limitations compared to standard OpenSL ES specification and maybe optimise existing code a little bit if you are not writing it from scratch.
This API is not meant to be used to write pure C/C++ application, it is more like a one way API, because up-calls to code running in the Android runtime are not expected. But it is a full-featured API which can be used to facilitate the implementation of shared libraries, e.g with an iOS team.
But there is a small catch. According to the documentation, sole use of OpenSL ES on Android does not guarantee performance improvement compared to standard platform solutions. Why is that?
Although OpenSL ES can be used from Android API level 9, significant improvements for audio output latency were added later in Android API level 17 (Android 4.2) and these are only device-specific. Which means you have to check at runtime if the device supports low latency. Not ideal, but the number of low latency devices is growing steadily and to this day there is already a large number of devices on the market already supporting low latency.
So, OpenSL ES might be good for you if you need to share code between platforms, or you are already familiar with it.
AAudio
AAudio is a new Android C API introduced in the Android Oreo. It is a pure Android native API designed for high-performance audio applications that require low latency. Something that Android was missing for a long time.
You can write or read from AAudio audio streams in your application, and AAudio take care that the passed data moves between your application and audio inputs or outputs on your Android device.
A stream is defined by the following components:
- Audio device
- Sharing mode.
- Audio Format
Audio device can be either source or sink for a continuous stream of digital audio data. Don’t confuse this terminology with a real device like a phone or watch, that is running your application.
Sharing mode can be EXCLUSIVE or SHARED where exclusive means, that stream has exclusive access to its audio device. Whereas SHARED means that AAudio mixes all the streams assigned to the same device.
Audio format is a term for all the standard attributes used in digital audio, that a stream can have. Like sample rate, sample format and samples per frame.
By default audio stream has a default performance mode which balances latency and power savings. You can further improve low latency by setting AAUDIO_PERFORMANCE_MODE_LOW_LATENCY mode, which is useful for applications that are very interactive such as musical instruments.
I really liked to try out AAudio, but since the AAudio was introduced in Android Oreo and is not backwards compatible, I believe I wouldn’t be able to use it in a production application. Luckily, there is a solution.
Oboe is a new native library by Google that solves the problem with backwards compatibility of AAudio. It works with API 16 and above, which means it’s compatible witch over 99% of Android devices. Internally it uses AAudio on API 27 and above, but falling back to OpenSL ES on older APIs.
This simplified API is something Android had been lacking for a long time and I believe it will become a new native audio standard on Android.
Oboe provides similar features as single AAudio API, such as audio streams, sharing modes and performance modes, so if you are already familiar with AAudio, transition to Oboe should be straightforward.
So If you would like to try out Oboe, you can add it to your project manually like this.
First, clone the Oboe repository
and then edit your CMakeLists.txt, adding Oboe to the libraries your project depends on and providing a path to the folder, where you cloned the repository.
and finally don’t forget to index newly added Oboe library to start using it.
Источник