این محتوا پخش زنگ را در رندرکننده با دسترسی بالا (HAR) توصیف میکند. یک جعبه Audio AudioManager در اختیار برنامه HAR قرار میدهد که پخش زنگ را کنترل میکند.
برای پایین نگه داشتن تأخیر، رشتههای پخش در طول عمر برنامه اجرا میشوند، در حالت سکون قرار میگیرند و وقتی صدایی پخش نمیشود، متوقف میشوند.
اصطلاحات
- دارایی
-
AudioAssetمربوط به صدای قابل پخش است. این صداها معمولاً شناخته شده هستند و در زمان اجرای برنامه وجود دارند. - دستگاه
-
AudioDeviceبه یک گذرگاه جداگانه برای پخش صدا اشاره دارد. این دستگاه، جزئیترین واحد مربوط به سختافزاری است که سیستم به آن دسترسی دارد. در پیادهسازی استاندارد SDVM،AudioDeviceبه یک PCM واحد معماری صدای پیشرفته لینوکس (ALSA) اشاره دارد. - جریان
- نمونهای از پخش یک فایل روی یک دستگاه. استریمها از لحظه برنامهریزی تا زمان تکمیل، لغو یا پایان یافتن به دلیل خطا ادامه مییابند.
قطعات
شکل 1 نمودار اجزای مربوط به chime را نشان میدهد:
شکل ۱. نمودار اجزا.
دستگاه صوتی و 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 از فایلهای صوتی پیکربندیشدهی استاتیک پشتیبانی میکند. پیکربندی 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 و رویه پخش یک زنگ را شرح میدهد. پخش یک زنگ به صورت تکی، یک جریان (stream) نامیده میشود.
چرخه حیات یک جریان
شکل ۲ چرخه حیات یک جریان را نشان میدهد:
شکل ۲. پخش جریانی و رویدادها.
شکل ۲ این مراحل را شرح میدهد:
پخش: پخش جریانی را برای پخش برنامهریزی کنید.
اولویتبندی: اولویتبندی پخش تصمیم میگیرد که آیا:
- همین حالا زنگ را پخش کن (رویداد از زمان اولین بایتها آغاز شد)
- پخش زنگ در فرصتی دیگر (رویداد متوقف یا از سر گرفته شد)
- اولویتبندی مجدد زنگ (رویداد لغو شده)
کنترلهای میکسر: در صورت نیاز، کنترلهای میکسر را بر اساس رفتارهای پیکربندیشده بهروزرسانی کنید.
نوشتن بایتها: یک تکه از بایتها را در
AudioDeviceبنویسید.دادههای بیشتر: اگر جریان دادههای بیشتری دارد، به مرحله ۲ برگردید.
تکرار: اگر قرار است استریم تکرار شود، آن را ریست کنید و به مرحله ۲ (رویداد از سرگیری شده) برگردید.
تکمیلشده: پخش زنده با موفقیت انجام شد (رویداد
FinishedSuccessfully).
زنگ را میتوان در هر زمانی با مکث، از سرگیری یا توقف تماسها قطع کرد.
اولویتهای چیم
این منطق اولویتهای زنگها را تعیین میکند:
حالت پخش لغو میشود. برای مثال، یک زنگ در حالت محو شدن صدا (fade out) همیشه تا زمان تکمیل محو شدن صدا (fade out) در اولویت اول قرار دارد.
اولویت مشخص شده.
اگر اولویت مساوی جدیدتر باشد، زنگ اول پخش میشود.
وقتی زنگها اولویت یکسانی دارند، AudioManager با یک مقدار enum نمونهسازی میشود.
رابط برنامهنویسی کاربردی
رویدادها
اگر هنگام شروع پخش صدا، یک کانال رویداد ارائه شود، 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 ( دستگاه صوتی و 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 را به مقادیر کنترل تبدیل میکند و سپس تابع set_volume_control را با مقدار تبدیل شده فراخوانی میکند.
این طراحی یک نگاشت پیشفرض ارائه میدهد و پیادهسازیهای بعدی را قادر میسازد تا نگاشت پیشفرض را لغو کنند.
شکل 3. جریان صوتی HAR.
فضاسازی
رابط برنامهنویسی کاربردی صدا (Audio API) قابلیتهایی را برای کنترل دادههای صوتی در محدودهی مکانی که باید پخش شوند، ارائه میدهد. این پارامترها به لایهی PAL منتقل میشوند و با استفاده از کنترلهای سختافزاری در پاییندست اعمال میشوند. گزینهها به عنوان بخشی از رابط برنامهنویسی کاربردی 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 یک رشته (thread) را نگهداری میکند. هر رشته به طور مستقل عمل میکند. تعامل بین AudioManager و رشته پخش از یک صف استریم مشترک که بر اساس اولویت مرتب شده است، استفاده میکند.
فراخوانیهای ALSA از نوشتنهای ASYNC به همراه نمونهبرداری برای تعیین زمان خلاصهسازی دادهها استفاده میکنند.

شکل 4. توالی مدیریت نخ.
سیگنالهای کنترلی در طول رایگیری
هنگام انتظار کارت صدا برای هضم بایتها، میتوان سیگنالهای کنترلی صادر کرد. به عنوان مثال، برای تغییر محو شدن یا فضاسازی صدا. نمونهبرداری برای دریافت وضعیت دستگاه صوتی یا در سطح AudioManager پیکربندی شده است یا به طور پیشفرض روی ۱ میلیثانیه تنظیم شده است. پس از هر چرخه نمونهبرداری، رشته پخش، هر دستور کنترلی زمانبندی شده را هضم و صادر میکند.
مدیریت بافر
برای به حداقل رساندن تأخیر وقفه، اندازه بافر نوشته شده در دستگاه کوچک نگه داشته میشود. هنگام استفاده از TinyALSA به عنوان پیشفرض، اندازه بافر پیکربندی شده برابر با پارامتر startup_threshold است. TinyALSA پیشفرض را به عنوان کل بافر اختصاص داده شده به دستگاه تقسیم بر دو تعریف میکند .
وقفه در جریان
وقتی جریانها قطع میشوند، اولویت نخها تا زمانی که دادههایی که روی کارت نوشتهاند تمام شود، حفظ میشود. در نتیجه، یک دوره گذار بین وقفه و جریان جدید رخ میدهد.
برای مثال، اگر یک نمونه صوتی در HAR از موارد زیر استفاده کند:
- اندازه ۳,۰۷۲
- نرخ ۴۸۰۰۰
- حجم نمونه دو
بافر در حال انتظار به صورت 3072 و 6144 فریم محاسبه میشود که منجر به تأخیر وقفه 64 تا 128 میلیثانیه میشود. پیادهسازی در محیط عملیاتی به بافر کوچکتری نیاز دارد.
مدیریت خطا و ریسکها
این بخش نحوه مدیریت خطاها و خطرات احتمالی را شرح میدهد.
استریمهای قدیمی و قحطی صف
با توجه به اینکه میتوان AudioStream را متوقف کرد، و از آنجا که پخش فقط از نمونه AudioStream با اولویت بالا امکانپذیر است، خطر ایجاد یک صف رو به رشد که استریمهای با اولویت پایین را از دست میدهد، وجود دارد.
برای جلوگیری از این اتفاق، هر صف به اندازه قابل تنظیمی محدود میشود. وقتی از این مقدار تجاوز شود، جریان با کمترین اولویت حذف میشود.
نظارت و هشدار
در مرحله تولید، مانیتور ایمنی ویژگیهای صوتی را ردیابی میکند تا مطمئن شود که پخش صدا طبق انتظار انجام میشود.
AudioManager آمارهای داخلی مربوط به تأخیرها و پرچمی را که عملکرد ثبت وقایع را تعریف میکند، رصد میکند. پس از تنظیم این آستانهها، گزارشهای هشدار برای همه اشکالزداییهای ساخته شده در موارد زیر ایجاد میشوند:
- مدت زمان بین زمانبندی و شروع پخش بیش از
xمیلیثانیه است. - (برای یک پخش زندهی بدون وقفه) طول فایل و زمان پخش بیش از
yدرصد با هم تفاوت دارند.
دستگاه مسدود شده است
همیشه خطر کمی برای از کار افتادن یک دستگاه صوتی وجود دارد، برای مثال، اگر توسط فرآیند دیگری در سیستم تخصیص داده شده و در آن نوشته شده باشد. با توجه به اینکه پخش به صورت ناهمزمان در رشتههای جداگانه اجرا میشود و زنگها میتوانند برای پخش بعدی در صف قرار گیرند، این موضوع برای برنامه فراخوانی کاملاً شفاف است.
برای تشخیص این موضوع، هر زمان که قرار است یک زنگ جدید پخش شود، بررسی سلامت نخ انجام میشود و اگر نخ پخش دارای صف پر از بایت باشد و در آخرین ثانیه هیچ بایت جدیدی را هضم نکرده باشد، خطا برمیگرداند.
برای اهداف آینده ممکن است لازم باشد دستگاهها را مجدداً راهاندازی/باز کنید، اما برای پیادهسازی اولیه، خطاها نباید نامرئی باشند.
ساختار کد
در سطح بالا، کد مربوط به پخش زنگها در جعبههای زیر وجود دارد:
CRATE: display-safety/crates/(harry-app|harry)
برنامه HAR موجود، که تماسهایی را برای پخش زنگها صادر میکند.
NEW CRATE: display-safety/crates/audio
جدید: جعبهای برای مدیریت کنترل صدا و پخش (بیشتر قابلیتها در اینجا وجود دارند).
CRATE: display-safety/crates/har-platform-api/audio
PAL شامل تمام فراخوانیهای سیستمی مورد نیاز برای صدا.
CRATE: display-safety/crates/har-platform-(android|linux)/audio
فراخوانی tinyalsa-rs برای پخش با استفاده از TinyALSA. پشتیبانی از QNX در راهحل اولیه پیادهسازی نشده است و با پشتیبانی از پلتفرمهای بیشتر، این پشتیبانی افزایش خواهد یافت.
TINYALSA PAL: display-safety/crates/tinyalsa-audio
کد مخصوص TinyALSA برای پخش. این کد توسط پیادهسازیهای پلتفرم اندروید و لینوکس استفاده میشود.
CRATE: display-safety/crates/tinyalsa-rs
اتصالات Rust برای پیادهسازی TinyALSA به زبان C
جزئیات پیادهسازی Rust
برخی از جزئیات پیادهسازی خاص:
- همه توابع API
Result<X, AudioError>را برمیگردانند که در آنXیا () است یا یک مقدار بازگشتی. - هیچ تابع API به عنوان
unsafeعلامتگذاری نشده است. - مکانیزمهای Mutex و همگامسازی داخلی هستند و در رابط برنامهنویسی کاربردی
AudioManagerنمایش داده نمیشوند.
مدل مالکیت و AudioManager
تمام تعاملات برنامه با سیستم صوتی از طریق
AudioManagerیا اشیاء برگردانده شده ازAudioManagerانجام میشود.AudioManagerاز نظر thread safe است.AudioManagerیک بار در برنامه HARry نمونهسازی میشود وMovedتاLooperمالکیت آن را داشته باشد.AudioManagerاز توکنtokio_util::CancellationTokenبرای مدیریت رشتههای پخش آغاز شده خود استفاده میکند و تضمین میکند که در صورتDroppedAudioManagerرشتهها خاتمه یافته و منابع آزاد میشوند.AudioManagerصراحتاً از ایجاد چندین نمونه جلوگیری نمیکند. اگر بیش از یک نمونه وجود داشته باشد، با سطحwarnثبت میشود.
مالکیت مشترک
تعدادی از اشیاء دارای مالکیت مشترک و دسترسی انحصاری هستند. این مکانیسمها در رابط برنامهنویسی کاربردی AudioManager نمایش داده نمیشوند، اما در پیادهسازیهای صوتی و PAL داخلی هستند.
AudioDevice- هر مرجع سختافزاری (برای مثال، TinyALSA PCM) که باز شود (دارای یک دسته باشد) دسترسی انحصاری دارد. به SMP Design مراجعه کنید.نمونههای
AudioStreamپس از زمانبندی برای پخش، دسترسی انحصاری دارند، زیرا میتوانند توسط برنامه کنترل شوند و همزمان توسط رشته پخش قابل دسترسی باشند.رشتهی پخش در طول پخش قفل نگه نمیدارد، اما یک تصویر لحظهای تغییرناپذیر از بافر بعدی برای پخش ایجاد میکند و تا زمانی که بافر بعدی هضم نشود، تغییرات را در نظر نمیگیرد.
هر نخ پخش دارای یک صف پخش است که یک مرجع مشترک بین
AudioManagerو نخ پخش است. در نتیجه، این نخ برای جهشها به دسترسی انحصاری نیاز دارد.نخهایی که هیچ جریانی ندارند، با متغیر
Condvarبیکار میشوند تا هنگام شناسایی دادههای جدید، رویدادهای بیدارباش دریافت کنند. این مکانیسم مالکیت مشترک دارد.
وابستگیها
جعبهها و جعبه صوتی به گونهای طراحی شدهاند که وابستگی به جعبههایی را که برای ساخت در درخت منبع اندروید تأیید نشدهاند، کاهش دهند. به این لیست از جعبههای گنجانده شده مراجعه کنید.
پیادهسازیهای پلتفرمهای پاییندستی برای اندروید و لینوکس به TinyALSA و جعبهی ایمنی نمایشگر موجود tinyalsa-rs بستگی دارد.
ویژگیهای کیفی
قابلیت اطمینان
اگرچه پخش صدا از نظر ایمنی بسیار مهم است، اما این طرح، پیادهسازی نظارت ایمنی را پوشش نمیدهد. این مورد را در یک تلاش جداگانه پیادهسازی کنید تا قابلیت اطمینان پخش صدا را روی سختافزار و در مرحله تولید تأیید کنید.
مقیاسپذیری
رویکرد یک رشته در هر دستگاه برای مقیاسپذیری با تنظیمات سختافزاری مختلف در نظر گرفته شده است. با توجه به اینکه هر رشته در درجه اول بیکار، منتظر داده یا منتظر هضم داده نوشته شده توسط دستگاه است، نباید فشار زیادی به پردازنده یا عملکرد سیستم وارد کند.
تصمیم طراحی مبنی بر پخش دادهها فقط در یک دستگاه واحد، همراه با دستورات کنترل میکسر برای کنترل تمام خروجیهای بیشتر، تضمین میکند که خروجی دقیق توسط سختافزار صدا مدیریت میشود و باید برای سیستمهای آینده نیز قابل استفاده باشد.
تأخیر
تأخیر برای سیستم صوتی بسیار مهم است، بنابراین پس از پیادهسازی، مجموعهای از اهداف سطح سرویس (SLO) برای تأخیر سیستم تعریف میشوند. برای نظارت مداوم بر سلامت تأخیر، سیستم نظارتی، عدم تطابق SLOهای تعریفشده را در تمام نسخههای اشکالزدایی ثبت میکند.
برای نسخههای تولیدی، دادههای نظارتی به جای تکیه بر گزارشها، به سیستمی خارج از پیادهسازی صوتی منتقل میشوند.
استراتژی تست و آزمایش
جعبهها و جعبه صوتی با پوشش آزمایشی طراحی شدهاند. ما یک پیادهسازی پلتفرم آزمایشی اضافه کردهایم تا تأیید کنیم که همه قابلیتها آزمایش شدهاند.
پیچیدگی سختافزار و اتصالات مانع از پوشش گسترده تست برای پیادهسازیهای پلتفرم میشود. ما پیادهسازیهای نمونه را برای آزمایش دستی راهحل روی سختافزار و روی شبیهساز Cuttlefish ارائه میدهیم.
مستندات
فایل README.md در Audio crates/audio نحوه استفاده AudioManager را شرح میدهد. crates/audio/examples شامل مثالهایی برای موارد زیر است:
- یک پلتفرم را پیادهسازی کنید.
- یک نمونه از
AudioManagerایجاد کنید. -
WavAssetرا بازی کنید. - یک تابع سفارشی را به صورت مکرر پخش کنید.
- رویدادهای پخش را ثبت کنید.