Konten ini menjelaskan pemutaran bel di perender ketersediaan tinggi (HAR).
Crate Audio mengekspos AudioManager ke aplikasi HAR, yang mengontrol pemutaran
bel.
Untuk menjaga latensi tetap rendah, thread pemutaran berjalan selama masa aktif aplikasi, menganggur dan melepaskan saat tidak ada audio yang diputar.
Terminologi
- aset
AudioAssetberkaitan dengan audio yang dapat diputar. Aset biasanya diketahui dan ada di runtime aplikasi.- perangkat
AudioDevicemengacu pada bus terpisah untuk pemutaran audio. Perangkat adalah unit paling terperinci yang terkait dengan hardware yang diakses oleh sistem. Dalam penerapan SDVM standar,AudioDevicemerujuk ke satu PCM Advanced Linux Sound Architecture (ALSA).- aliran
- Instance pemutaran aset di perangkat. Aliran tetap ada sejak dijadwalkan hingga selesai, dibatalkan, atau berakhir karena error.
Komponen
Gambar 1 menampilkan diagram komponen untuk chime:
Gambar 1. Diagram komponen.
Perangkat audio dan PCM
Konfigurasi hardware audio mengikuti desain lapisan abstraksi platform HAR standar, dan har-platform-api memuatnya.
Crate HAR Audio menentukan struktur baru untuk AudioDevice, yang menentukan
kolom untuk semua struktur data yang memengaruhi crate Audio HAR internal
dan pemutaran. AudioDevice juga menggunakan generik untuk membungkus parameter tambahan khusus platform yang potensial. Dalam kasus tinyalsa,
PlatformAudioDevice berisi deskripsi dan properti PCM ALSA.
/// 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
}
Aset audio
Bagian ini menjelaskan cara aset audio dikonfigurasi dan diterapkan.
Konfigurasi
Implementasi audio HAR awal mendukung aset audio yang dikonfigurasi secara statis. Konfigurasi JSON menentukan aset mana yang tersedia dan aset mana yang ditentukan sebagai file WAV.
Implementasi ini juga mendukung aset audio yang disintesis dan di-streaming melalui implementasi aset yang lebih umum, yang menerima fungsi untuk menghasilkan data audio.
Penerapan
Terapkan aset menggunakan dua konstruksi terpisah, AudioAsset dan AudioStream.
AudioAsset menentukan properti statis aset, dan penampung untuk
potensi data internal yang terkait dengan aset. Dapat diturunkan dari AudioAsset AudioStream, yang merupakan satu instance aset yang dapat di-streaming. AudioStream
berisi status internal yang terkait dengan pemutaran streaming tunggal.
/// 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.
...
}
Pemutaran bel
Bagian ini menjelaskan API dan prosedur untuk pemutaran bel. Pemutaran lonceng tunggal disebut sebagai stream.
Siklus proses streaming
Gambar 2 menggambarkan siklus proses aliran:
Gambar 2. Pemutaran dan acara streaming.
Gambar 2 menjelaskan langkah-langkah ini:
Putar: Jadwalkan streaming untuk diputar.
Prioritaskan: Prioritas pemutaran memutuskan apakah akan:
- Putar bunyi lonceng sekarang (memulai acara saat byte pertama)
- Putar bunyi bel nanti (acara dijeda atau dilanjutkan)
- Membatalkan prioritas bel (acara dibatalkan)
Kontrol Mixer: Jika diperlukan, perbarui kontrol Mixer berdasarkan perilaku yang dikonfigurasi.
Menulis byte: Menulis chunk byte ke
AudioDevice.Data lainnya: Jika aliran memiliki lebih banyak data, kembali ke Langkah 2.
Ulangi: Jika streaming harus diulang, reset dan kembali ke Langkah 2 (peristiwa dimulai ulang).
Selesai: Streaming berhasil diselesaikan (peristiwa
FinishedSuccessfully).
Bel dapat dihentikan dengan panggilan jeda, lanjutkan, atau hentikan kapan saja.
Prioritas bunyi bel
Logika ini menetapkan prioritas bunyi lonceng:
Penggantian mode pemutaran. Misalnya, bunyi bel dalam mode memudar selalu diberi prioritas utama hingga memudar selesai.
Prioritas yang ditentukan.
Jika prioritas yang sama lebih baru, bel akan berbunyi terlebih dahulu.
Jika bel memiliki prioritas yang sama, AudioManager akan di-instansiasi dengan nilai
enum.
API
Acara
Jika saluran peristiwa disediakan saat bunyi lonceng dimulai, HAR Audio akan memancarkan sejumlah peristiwa selama pemutaran. Peristiwa yang didukung ditampilkan dalam contoh ini:
/// 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)
}
Kontrol mixer
Bagian ini menjelaskan cara mengontrol volume dan spasialisasi.
Volume
HAR menentukan volume secara konsisten dalam milibel. Crate har-platform-api
menangani konversi dari milibel ke sinyal kontrol.
Hubungan antara milibel dan output daya hardware bersifat logaritmik, dan sangat bervariasi antara berbagai hardware dan konfigurasi speaker. Akibatnya,
berikan konfigurasi antara nilai sebagai bagian dari konfigurasi AudioDevice
(Perangkat Audio dan PCM), dan konversi harus dilakukan sebelum
memanggil lapisan platform.
Akibatnya, penerapan di PAL API menentukan dua fungsi.
fn set_volume_millibel(AudioDeviceID, Millibel) {
/// Default implementation with conversion using DeviceConfig.
}
fn set_volume_control(AudioDeviceID, ControlValue);
Implementasi default untuk set_volume_millibel menggunakan konfigurasi yang disediakan untuk AudioDevice, termasuk serangkaian key-value pair untuk referensi millibel - kontrol, mengubah millibel menjadi nilai kontrol, lalu memanggil fungsi set_volume_control dengan nilai yang dikonversi.
Desain ini menyediakan default dan memungkinkan penerapan berikutnya untuk mengganti pemetaan default.
Gambar 3. Alur audio HAR.
Spasialisasi
Audio API mengekspos fungsi untuk mengontrol area spasial tempat data audio harus diputar. Parameter ini diteruskan ke lapisan PAL, dan diterapkan di hilir menggunakan kontrol hardware. Opsi ditentukan sebagai bagian dari PAL API sebagai:
/// 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
}
Tingkat kontrol mixer
Anda dapat menentukan volume dan spasialisasi pada aset dan untuk streaming. Jika Anda menentukan prioritas streaming, streaming akan menggantikan kontrol yang ditentukan oleh aset.
Pengelolaan thread
Pengelola audio mempertahankan satu thread per instance AudioDevice. Setiap thread
beroperasi secara independen. Interaksi antara AudioManager dan thread pemutaran
menggunakan antrean streaming bersama yang diurutkan berdasarkan prioritas.
Panggilan ALSA menggunakan penulisan ASYNC dengan polling untuk menentukan kapan data diproses.
Gambar 4. Urutan pengelolaan thread.
Sinyal kontrol selama polling
Saat menunggu kartu suara mencerna byte, sinyal kontrol dapat dikeluarkan. Misalnya, untuk mengubah pudar atau spasialisasi audio. Polling untuk mendapatkan status perangkat audio dikonfigurasi pada level AudioManager atau
secara default 1 milidetik. Setelah setiap siklus polling, thread pemutaran
mencerna dan mengeluarkan perintah kontrol berwaktu.
Pengelolaan buffer
Untuk meminimalkan latensi gangguan, ukuran buffer yang ditulis ke perangkat
dijaga agar tetap kecil. Saat menggunakan TinyALSA sebagai default, ukuran buffer dikonfigurasi agar sama dengan parameter startup_threshold.
TinyALSA menentukan default sebagai seluruh buffer perangkat yang dialokasikan dibagi dua.
Gangguan streaming
Saat aliran terganggu, aliran mempertahankan prioritas thread hingga data yang telah ditulis ke kartu dikosongkan. Akibatnya, periode transisi terjadi antara gangguan dan streaming baru.
Misalnya, jika sampel audio di HAR menggunakan:
- Ukuran 3.072
- Kecepatan 48.000
- Ukuran sampel dua
Buffer yang tertunda dihitung sebagai 3.072 dan 6.144 frame, yang menghasilkan penundaan gangguan 64 hingga 128 milidetik. Penerapan produksi akan memerlukan buffer yang lebih kecil.
Pengelolaan error dan risiko
Bagian ini menjelaskan cara pengelolaan error dan potensi risiko.
Streaming yang tidak aktif dan kekurangan antrean
Mengingat AudioStream dapat dijeda, dan karena pemutaran hanya dapat terjadi dari instance AudioStream dengan prioritas teratas, risiko munculnya antrean yang terus bertambah akan membuat streaming dengan prioritas rendah tidak dapat diputar.
Untuk menghindari kejadian ini, setiap antrean dibatasi pada ukuran yang dapat dikonfigurasi. Jika nilai ini terlampaui, aliran dengan prioritas terendah akan dibuang.
Memantau dan mengingatkan
Dalam produksi, monitor keamanan melacak fitur audio untuk melacak pemutaran yang berlangsung seperti yang diharapkan.
AudioManager memantau statistik internal khusus untuk latensi dan tanda
yang menentukan performa logging. Setelah menetapkan nilai minimum ini, log peringatan
dibuat untuk semua build debug saat:
- Durasi antara penjadwalan dan memulai pemutaran melebihi
xmilidetik. - (Untuk streaming yang tidak terganggu) panjang aset dan waktu pemutaran berbeda lebih dari
ypersen.
Perangkat diblokir
Selalu ada sedikit risiko perangkat audio menjadi tidak responsif, misalnya, jika dialokasikan dan ditulis oleh proses lain dalam sistem. Mengingat pemutaran berjalan secara asinkron di thread terpisah, dan bunyi lonceng dapat diantrekan untuk diputar nanti, hal ini sepenuhnya transparan bagi aplikasi yang memanggil.
Untuk mendeteksinya, pemeriksaan kondisi thread dilakukan setiap kali bel baru dijadwalkan untuk diputar, yang akan menampilkan error jika thread pemutaran memiliki antrean yang terisi, dan belum memproses byte baru selama satu detik terakhir.
Untuk tujuan mendatang, mungkin perlu dicoba untuk memulai ulang / membuka perangkat, tetapi untuk penerapan awal, error tidak boleh tidak terlihat.
Struktur kode
Secara umum, kode yang terkait dengan pemutaran bel ada di seluruh crate berikut:
CRATE: display-safety/crates/(harry-app|harry)
Aplikasi HAR yang ada, yang mengeluarkan panggilan untuk memutar bunyi lonceng.
NEW CRATE: display-safety/crates/audio
BARU: Crate untuk mengelola kontrol dan pemutaran audio (di sinilah sebagian besar fungsionalitas berada).
CRATE: display-safety/crates/har-platform-api/audio
PAL termasuk semua panggilan sistem yang diperlukan untuk audio.
CRATE: display-safety/crates/har-platform-(android|linux)/audio
Panggilan ke tinyalsa-rs untuk pemutaran menggunakan TinyALSA. Dukungan QNX tidak diterapkan dalam solusi awal, dan dukungan ini akan berkembang seiring dengan makin banyaknya platform yang didukung.
TINYALSA PAL: display-safety/crates/tinyalsa-audio
Kode khusus TinyALSA untuk pemutaran. Ini digunakan oleh implementasi platform Android dan Linux.
CRATE: display-safety/crates/tinyalsa-rs
Binding Rust untuk penerapan C TinyALSA
Detail penerapan Rust
Beberapa detail penerapan khusus:
- Semua fungsi API menampilkan
Result<X, AudioError>denganXadalah () atau nilai yang ditampilkan. - Tidak ada fungsi API yang ditandai sebagai
unsafe. - Mekanisme mutex dan sinkronisasi bersifat internal dan tidak diekspos di API
AudioManager.
Model kepemilikan dan AudioManager
Semua interaksi aplikasi dengan sistem audio terjadi melalui
AudioManageratau objek yang ditampilkan dariAudioManager.AudioManageraman untuk thread.AudioManagerdi-instantiate satu kali di aplikasi HARry, danMoved, agarLoopermemiliki kepemilikan.AudioManagermenggunakan tokentokio_util::CancellationTokenuntuk mengelola thread pemutaran yang dimulai, memastikan thread dihentikan dan resource dilepaskan jikaAudioManageradalahDropped.AudioManagertidak secara eksplisit mencegah pembuatan beberapa instance. Jika ada lebih dari satu instance, instance tersebut akan dicatat dengan tingkatwarn.
Kepemilikan bersama
Sejumlah objek memiliki kepemilikan bersama yang di-wrap dan disinkronkan dengan
akses eksklusif. Mekanisme ini tidak diekspos di AudioManager API, tetapi
bersifat internal untuk implementasi audio dan PAL.
AudioDevice- Setiap referensi hardware (misalnya, TinyALSA PCM) yang dibuka (memiliki handle) memiliki akses eksklusif. Lihat Desain SMP.Instance
AudioStreammemiliki akses eksklusif setelah dijadwalkan untuk pemutaran karena dapat dikontrol oleh aplikasi dan diakses secara bersamaan oleh thread pemutaran.Thread pemutaran tidak menahan kunci selama pemutaran, tetapi membuat snapshot buffer berikutnya yang tidak dapat diubah untuk diputar, dan tidak mempertimbangkan perubahan hingga buffer berikutnya diproses.
Setiap thread pemutaran memiliki antrean pemutaran, referensi bersama antara
AudioManagerdan thread pemutaran. Akibatnya, thread memerlukan akses eksklusif untuk mutasi.Thread tanpa aliran akan menjadi tidak aktif dengan variabel
Condvarmenjadi menerima peristiwa aktif saat data baru terdeteksi. Mekanisme ini memiliki kepemilikan bersama.
Dependensi
Crate dan crate audio didesain untuk mengurangi dependensi pada crate yang tidak disetujui untuk dibangun di pohon sumber Android. Lihat daftar peti yang disertakan di sini.
Implementasi platform downstream untuk Android dan Linux bergantung pada
TinyALSA dan crate keamanan layar tinyalsa-rs yang ada.
Atribut kualitas
Keandalan
Meskipun pemutaran audio sangat penting untuk keselamatan, desain ini tidak mencakup penerapan pemantauan keselamatan. Terapkan ini dalam upaya terpisah untuk memverifikasi keandalan pemutaran audio di hardware dan dalam produksi.
Skalabilitas
Pendekatan satu thread per perangkat dimaksudkan untuk menskalakan ke berbagai konfigurasi hardware. Mengingat setiap thread sebagian besar dalam kondisi tidak ada aktivitas, menunggu data, atau menunggu perangkat mencerna data yang ditulis, thread tersebut tidak akan membebani prosesor atau performa sistem.
Keputusan desain untuk hanya memutar data ke satu perangkat, yang dikombinasikan dengan perintah kontrol mixer untuk semua kontrol output lebih lanjut, memastikan output yang tepat ditangani oleh hardware suara, dan harus diskalakan untuk sistem mendatang.
Latensi
Latensi sangat penting untuk sistem audio, jadi setelah penerapan, serangkaian tujuan tingkat layanan (SLO) ditentukan untuk latensi sistem. Untuk terus memantau kondisi latensi, pemantauan di log sistem tidak memenuhi SLO yang ditentukan di semua build debug.
Untuk versi produksi, data pemantauan diteruskan ke beberapa sistem eksternal ke implementasi audio, bukan mengandalkan log.
Pengujian dan strategi pengujian
Crate dan crate audio dirancang dengan cakupan pengujian. Kami menambahkan penerapan platform tiruan untuk mengonfirmasi bahwa semua kemampuan diuji.
Kompleksitas hardware dan binding menghalangi cakupan pengujian yang luas untuk implementasi platform. Kami menyediakan contoh penerapan untuk menguji solusi secara manual di hardware dan di emulator Cuttlefish.
Dokumentasi
File README.md di Audio crates/audio menjelaskan cara menggunakan
AudioManager. crates/audio/examples berisi contoh untuk:
- Terapkan platform.
- Buat instance
AudioManager. - Putar
WavAsset. - Memutar aset fungsi kustom berulang kali.
- Mencatat peristiwa pemutaran ke dalam log.