В этом материале описывается воспроизведение звукового сигнала в высокодоступном рендерере (HAR). Audio предоставляет доступ к AudioManager приложению HAR, которое управляет воспроизведением звукового сигнала.
Для минимизации задержки потоки воспроизведения работают на протяжении всего времени существования приложения, переходя в режим ожидания и уступая место, когда звук не воспроизводится.
Терминология
- объект
-
AudioAssetотносится к воспроизводимому аудио. Ресурсы (assets) общеизвестны и существуют во среде выполнения приложения. - устройство
-
AudioDeviceобозначает отдельную шину для воспроизведения звука. Это устройство является наиболее детализированной единицей, относящейся к аппаратному обеспечению, к которому обращается система. В стандартной реализации SDVMAudioDeviceобозначает отдельный PCM-модуль Advanced Linux Sound Architecture (ALSA). - транслировать
- Воспроизведение ресурса на устройстве. Потоковая передача продолжается с момента планирования до завершения, отмены или ошибки.
Компоненты
На рисунке 1 представлена схема компонентов колокольчика:
Рисунок 1. Схема компонентов.
Аудиоустройство и PCM
Конфигурация аудиооборудования соответствует стандартной архитектуре уровня абстракции платформы HAR, и она содержится в har-platform-api .
Крейт HAR Audio определяет новую структуру для AudioDevice , которая задает поля для всех структур данных, влияющих на внутренний крейт HAR Audio и воспроизведение. AudioDevice также использует обобщения для инкапсуляции потенциальных дополнительных параметров, специфичных для платформы. В случае tinyalsa , PlatformAudioDevice содержит дескрипторы и свойства ALSA PCM.
/// NOTE: The following code is a sample definition to help understanding, it is not a
/// representation of the final code/implementation.
AudioDevice<PlatformAudioDevice> {
/// Internal HAR Identifier for the device.
AudioDeviceID,
/// The size (in bytes) for chunks of audio data to stream to the device.
ChunkSize,
/// Properties necessary to control volume (details in "Mixer control" section).
VolumeControl,
/// Properties necessary to control spatialization (details in "Mixer control"
/// section).
SpatialControl,
/// Platform specific data for the AudioDevice.
/// E.g. ALSA properties and reference to opened PCM.
PlatformAudioDevice
}
/// Elaboration of the previously mentioned VolumeControl
VolumeControl {
/// Identifier for the control used to change volume.
ControlID,
/// Mapping between Decibel and control values. (see Mixer control section)
VolumeOutputIndex
}
Аудиоматериалы
В этом разделе описывается, как настраиваются и используются аудиоресурсы.
Конфигурация
Первоначальная реализация HAR audio поддерживает статически сконфигурированные аудиоресурсы. Конфигурация в формате JSON определяет, какие ресурсы доступны, а какие определены как WAV-файлы.
Данная реализация также поддерживает синтезированные и потоковые аудиофайлы, используя более универсальную реализацию, которая принимает функцию для генерации аудиоданных.
Выполнение
Реализуйте ресурсы, используя две отдельные конструкции: AudioAsset и AudioStream .
AudioAsset определяет статические свойства ресурса и контейнер для потенциальных внутренних данных, связанных с ресурсом. Из AudioAsset AudioStream , который представляет собой единственный экземпляр ресурса, доступный для потоковой передачи. AudioStream содержит внутреннее состояние, связанное с воспроизведением единственного потока.
/// NOTE: The following code is a sample definition to help understanding, it is not a
/// representation of the final code/implementation.
/// Static properties and definition of an Asset.
AudioAsset {
/// Perform optional initialization steps, e.g. load bytes from file into memory.
/// Can also define lazy loading, to load data at first playback instead.
fn initialize(LazyLoad);
/// Create a new AudioStream from the asset.
fn create_stream() -> AudioStream;
/// More functions for metadata etc. of the asset.
...
}
/// Single streamable instance of an AudioAsset
AudioStream {
/// Gets the next bytes to play from the Asset together with if the current chunk of
/// bytes contains any control signals (e.g. fade-out).
fn get_playback(num_bytes: usize) -> ([u8], ControlSignals);
/// Gets playback Mode details used to handle special states of playback
/// e.g. when a chime gets is interrupted and put in "fade-out" mode.
fn playback_mode() -> PlaybackMode;
/// [0.0, 1.0] indication of how much of the stream was played.
fn progress() -> f32;
/// Reset the stream, e.g. if it should play again.
fn reset();
/// Time of which the stream was created.
fn created_at() -> Instant;
/// Additional metadata etc. for the stream.
...
}
Воспроизведение звонка
В этом разделе описывается API и процедура воспроизведения звукового сигнала. Воспроизведение одного звукового сигнала называется потоком .
Жизненный цикл ручья
На рисунке 2 показан жизненный цикл ручья:
Рисунок 2. Воспроизведение потока и события.
На рисунке 2 описаны эти шаги:
Воспроизвести: Запланируйте трансляцию для воспроизведения.
Приоритизация: Приоритизация воспроизведения определяет, следует ли:
- Воспроизвести звуковой сигнал сейчас (событие началось с момента получения первых байтов)
- Воспроизвести звуковой сигнал позже (событие приостановлено или возобновлено)
- Снизить приоритет звукового сигнала (отмененного события)
Элементы управления микшером: При необходимости обновите элементы управления микшером в соответствии с настроенным поведением.
Запись байтов: Запись блока байтов в
AudioDevice.Дополнительные данные: Если в потоке содержится больше данных, вернитесь к шагу 2.
Повтор: Если поток необходимо повторить, сбросьте настройки и вернитесь к шагу 2 (перезапуск события).
Завершено: Поток успешно завершен (событие
FinishedSuccessfully).
Звуковой сигнал можно прервать, приостановив, возобновив или остановив воспроизведение в любой момент.
Приоритеты звонка
Эта логика устанавливает приоритеты для звуковых сигналов:
Режим воспроизведения имеет приоритет. Например, звуковому сигналу в режиме затухания всегда отдается наивысший приоритет до завершения затухания.
Указан приоритет.
Если принцип равного приоритета возник сравнительно недавно, то первым звучит колокольчик.
Когда сигналы имеют одинаковый приоритет, создается экземпляр AudioManager со значением enum .
API
События
Если при запуске звукового сигнала указан канал событий, HAR Audio генерирует ряд событий во время воспроизведения. Поддерживаемые события показаны в этом примере:
/// NOTE: The following code is a sample definition to help understanding, it is not a
/// representation of the final code/implementation.
StreamBehaviors<PlatformStreamBehaviors> {
/// What should happen if the stream is interrupted for a higher priority stream.
/// e.g. pause-and-resume or cancel, will also define preference for fade-out.
OverrunBehavior,
/// Urgency, if interrupted streams are allowed to "fade-out", or if the stream should
/// urgently disrupt any other playback.
Optional<Urgency>,
/// Priority for the stream (or minimum if not specified).
Optional<StreamPriority>
/// Descriptor if a stream should be played on repeat.
Optional<RepeatBehavior>
/// Volume, if the stream should play at a specific volume.
Optional<Volume>
/// Spatialization, if the stream should play with specific spatialization.
Optional<Spatialization>
/// Optional generic for future expandability of the API, or pass-through of platform
/// specific Stream Behaviors
Optional<PlatformStreamBehaviors>
}
/// Plays a chime on specified device with given behaviors. StreamEvents are delivered
/// using the provided event transmitter. This method won't wait for any events.
fn play(AudioDeviceID, AssetID, StreamBehaviors, Option<EventTransmitter>) -> StreamController
/// Object used to control a Stream.
StreamController {
/// Gets the current state/metadata of a stream (e.g. ID, progress, playback_state).
fn metadata() -> StreamMetadata
/// Stops the stream.
fn stop()
/// Pauses a given stream, if the specified duration expires the stream is cancelled.
/// Timeout is required to make sure there are no paused streams left indefinitely
/// pending resumption.
fn pause(TimeoutDuration)
/// Resumes a paused stream.
fn resume()
/// Updates the spatialization of a playing stream.
fn set_spatialization(Spatialization)
/// Updates the volume of a playing stream.
fn set_volume(Volume)
}
Управление миксером
В этом разделе описывается, как осуществляется управление объемом и пространственным расположением.
Объем
HAR определяет громкость в миллибелах. Крейт har-platform-api обрабатывает преобразование из миллибел в управляющий сигнал.
Зависимость между уровнем в миллибелах и выходной мощностью оборудования имеет логарифмический характер и сильно варьируется в зависимости от используемого оборудования и конфигурации акустических систем. Поэтому необходимо задавать параметры конфигурации в рамках параметра AudioDevice ( Audio Device и PCM ), а преобразование должно выполняться до вызова уровня платформы.
В результате реализация в API PAL определяет две функции.
fn set_volume_millibel(AudioDeviceID, Millibel) {
/// Default implementation with conversion using DeviceConfig.
}
fn set_volume_control(AudioDeviceID, ControlValue);
Реализация по умолчанию для set_volume_millibel использует конфигурацию, предоставленную для AudioDevice , включая набор пар ключ-значение для эталонного значения millibel - control, преобразует значение millibel в значение control, а затем вызывает функцию set_volume_control с преобразованным значением.
Данная конструкция обеспечивает значение по умолчанию и позволяет последующим реализациям переопределять сопоставление по умолчанию.
Рисунок 3. Аудиопоток HAR.
Пространственная организация
API аудио предоставляет функциональность для управления тем, в каком пространстве должны воспроизводиться аудиоданные. Эти параметры передаются на уровень PAL и применяются на последующих этапах с помощью аппаратных средств управления. Параметры определяются как часть API PAL следующим образом:
/// NOTE: The following code is a sample definition to help understanding, it is not a
/// representation of the final code/implementation.
enum Spatialization {
Front,
FrontLeft,
FrontRight,
Center, // No spatialization
Rear,
RearLeft,
RearRight,
Right,
Left
}
Уровни управления микшером
Вы можете задать объем и пространственное распределение для объекта и для потока. Если вы зададите приоритет потока, поток будет иметь приоритет над элементами управления, определенными объектом.
Управление потоками
Менеджер аудио поддерживает один поток для каждого экземпляра AudioDevice . Каждый поток работает независимо. Взаимодействие между AudioManager и потоком воспроизведения осуществляется с помощью общей очереди потоков, отсортированной по приоритету.
В вызовах ALSA используется асинхронная запись с опросом для определения момента обработки данных.

Рисунок 4. Последовательность управления потоками.
Управляющие сигналы во время опроса
В ожидании обработки байтов звуковой картой могут подаваться управляющие сигналы. Например, для изменения затухания или пространственного звучания. Опрос состояния аудиоустройства либо настраивается на уровне AudioManager , либо по умолчанию составляет 1 миллисекунду. После каждого цикла опроса поток воспроизведения обрабатывает данные и выдает соответствующие команды управления.
Управление буфером
Для минимизации задержки прерывания размеры буферов, записываемых в устройство, остаются небольшими. При использовании TinyALSA по умолчанию размер буфера устанавливается равным параметру startup_threshold . TinyALSA определяет значение по умолчанию как весь выделенный буфер устройства, деленный на два.
Прерывание потока
При прерывании потоков приоритет потоков сохраняется до тех пор, пока не будут исчерпаны все данные, записанные на карту. В результате возникает переходный период между прерыванием и запуском нового потока.
Например, если в аудиосэмпле в HAR используется:
- Размер 3072
- Ставка 48 000
- Размер выборки: два
Размер буфера ожидания составляет 3072 и 6144 кадра, что приводит к задержке прерывания от 64 до 128 миллисекунд. В производственной среде потребуется меньший буфер.
Управление ошибками и рисками
В этом разделе описывается порядок обработки ошибок и потенциальные риски.
Застоявшиеся потоки и нехватка времени в очередях
Учитывая, что AudioStream можно приостановить, и поскольку воспроизведение может происходить только из экземпляра AudioStream с наивысшим приоритетом, возникает риск того, что растущая очередь будет лишать потоки с низким приоритетом необходимого доступа.
Чтобы избежать этого, размер каждой очереди ограничен настраиваемым значением. При превышении этого значения поток с самым низким приоритетом отбрасывается.
Мониторинг и оповещение
В процессе производства система контроля безопасности отслеживает параметры звука, чтобы убедиться, что воспроизведение происходит должным образом.
AudioManager отслеживает внутреннюю статистику, касающуюся задержек, и флаг, определяющий производительность логирования. После установки этих пороговых значений для всех отладочных сборок генерируются предупреждающие сообщения в следующих случаях:
- Промежуток времени между планированием и началом воспроизведения превышает
xмиллисекунд. - (При непрерывном потоке) длина ресурса и время воспроизведения различаются более чем на
yпроцентов.
Устройство заблокировано
Всегда существует небольшой риск того, что аудиоустройство перестанет отвечать, например, если оно выделяется и в него записывается данные другим процессом в системе. Учитывая, что воспроизведение происходит асинхронно в отдельных потоках, и что мелодии могут быть поставлены в очередь для воспроизведения позже, это совершенно незаметно для вызывающего приложения.
Для обнаружения этого при каждом запланированном воспроизведении нового звукового сигнала выполняется проверка работоспособности потока, которая возвращает ошибку, если поток воспроизведения имеет заполненную очередь и не обработал ни одного нового байта за последнюю секунду.
В будущем может потребоваться попытка перезапуска/включения устройств, но на начальном этапе внедрения ошибки не должны оставаться незаметными.
Структура кода
В общих чертах, код, отвечающий за воспроизведение колокольчиков, находится в следующих библиотеках:
CRATE: display-safety/crates/(harry-app|harry)
Существующее приложение HAR, которое отправляет команды для воспроизведения колокольчиков.
NEW CRATE: display-safety/crates/audio
НОВИНКА: Crate для управления воспроизведением звука (здесь сосредоточена большая часть функциональности).
CRATE: display-safety/crates/har-platform-api/audio
Формат PAL, включая все системные вызовы, необходимые для передачи звука.
CRATE: display-safety/crates/har-platform-(android|linux)/audio
Для воспроизведения с использованием TinyALSA используются вызовы tinyalsa-rs . Поддержка QNX в первоначальном решении не реализована, и она будет добавлена по мере поддержки других платформ.
TINYALSA PAL: display-safety/crates/tinyalsa-audio
Специфический для TinyALSA код для воспроизведения. Он используется в реализациях для платформ Android и Linux.
CRATE: display-safety/crates/tinyalsa-rs
Привязки Rust для реализации TinyALSA на языке C.
Детали реализации на Rust
Некоторые конкретные детали реализации:
- Все функции API возвращают
Result<X, AudioError>гдеX— это либо (), либо возвращаемое значение. - Никакие функции API не помечены как
unsafe. - Механизмы мьютекса и синхронизации являются внутренними и не отображаются в API
AudioManager.
Модель владения и AudioManager
Все взаимодействие приложения с аудиосистемой происходит через
AudioManagerили объекты, возвращаемыеAudioManager.AudioManagerявляется потокобезопасным.В приложении HARry
AudioManagerсоздается один раз иMoved, чтобыLooperполучил права собственности.AudioManagerиспользует токенtokio_util::CancellationTokenдля управления запущенными потоками воспроизведения, гарантируя завершение потоков и освобождение ресурсов, еслиAudioManagerDropped.AudioManagerявно не препятствует созданию нескольких экземпляров. Если существует более одного экземпляра, он выводитwarnв лог.
Совместная собственность
Ряд объектов имеют совместное владение, обернутое и синхронизированное с эксклюзивным доступом. Эти механизмы не представлены в API AudioManager , а являются внутренними для реализаций аудио и PAL.
AudioDevice— Каждый открытый (имеющий дескриптор) аппаратный модуль (например, TinyALSA PCM) имеет эксклюзивный доступ. См. проектирование SMP .После планирования воспроизведения экземпляры
AudioStreamполучают эксклюзивный доступ, поскольку ими может управлять приложение, и одновременно к ним может обращаться поток воспроизведения.Поток воспроизведения не удерживает блокировки во время воспроизведения, а создает неизменяемый снимок следующего буфера для воспроизведения и не учитывает изменения до тех пор, пока не будет обработан следующий буфер.
Каждый поток воспроизведения имеет очередь воспроизведения, которая является общей ссылкой между
AudioManagerи потоком воспроизведения. В результате потоку необходим эксклюзивный доступ для внесения изменений.Потоки, в которых отсутствуют потоки данных, переходят в состояние ожидания с помощью переменной
Condvar, которая получает события пробуждения при обнаружении новых данных. Этот механизм использует совместное владение.
Зависимости
Крейты и аудиокрейт предназначены для уменьшения зависимостей от крейтов, которые не одобрены для сборки в исходном коде Android. См. этот список включенных крейтов .
Реализации на платформах Android и Linux зависят от TinyALSA и существующего крейта tinyalsa-rs обеспечивающего безопасность дисплея.
Качественные характеристики
Надежность
Хотя воспроизведение звука имеет критически важное значение для безопасности, данная конструкция не предусматривает реализацию системы мониторинга безопасности. Реализуйте её в отдельном проекте, чтобы проверить надёжность воспроизведения звука на аппаратном уровне и в процессе эксплуатации.
Масштабируемость
Подход с использованием одного потока на устройство предназначен для масштабирования под различные аппаратные конфигурации. Учитывая, что каждый поток в основном простаивает, ожидая данных или ожидая, пока устройство обработает записанные данные, он не должен создавать чрезмерную нагрузку на процессор или ресурсоемкость системы.
Решение о воспроизведении данных только на одно устройство в сочетании с командами управления микшером для всего дальнейшего управления выводом гарантирует, что звуковое оборудование будет обрабатывать именно тот выходной сигнал, который необходим, и должно масштабироваться для будущих систем.
Задержка
Задержка имеет решающее значение для аудиосистемы, поэтому после внедрения определяется набор целевых показателей уровня обслуживания (SLO) для задержки системы. Для непрерывного мониторинга состояния задержки в системных журналах во всех отладочных сборках отслеживается несоответствие определенным SLO.
В производственных версиях данные мониторинга передаются во внешнюю систему, не связанную с аудиосистемой, вместо использования логов.
Тестирование и стратегия тестирования
Библиотеки и аудиобиблиотека разработаны с учетом тестового покрытия. Мы добавили имитацию реализации на конкретной платформе, чтобы подтвердить тестирование всех возможностей.
Сложность аппаратного обеспечения и используемых привязок исключает возможность обширного тестирования реализаций на разных платформах. Мы предоставляем примеры реализаций для ручного тестирования решения на аппаратном обеспечении и на эмуляторе Cuttlefish.
Документация
Файл README.md в папке Audio crates/audio описывает, как использовать AudioManager . В crates/audio/examples содержатся примеры для:
- Внедрить платформу.
- Создайте экземпляр
AudioManager. - Воспроизвести
WavAsset. - Воспроизводить пользовательский функциональный ресурс по кругу в режиме повтора.
- Записывать события воспроизведения в журнал.