صدای فضایی

«صدای فراگیر» تجربه صوتی فراگیری است که کاربران را در مرکز کنش قرار می‌دهد و باعث می‌شود محتوای شما واقعی‌تر به‌نظر برسد. صدا «فضایی» می‌شود تا جلوه چندبلندگویی ایجاد کند، شبیه به راه‌اندازی صدای فراگیر، اما ازطریق هدفون.

برای مثال، در فیلم، صدای خودرو ممکن است از پشت سر کاربر شروع شود، به‌سمت جلو حرکت کند، و در دوردست محو شود. در گپ تصویری، صداها را می‌توان از هم جدا کرد و در اطراف کاربر قرار داد تا شناسایی گویندگان آسان‌تر شود.

اگر محتوایتان از قالب صوتی پشتیبانی‌شده‌ای استفاده می‌کند، می‌توانید از Android 13 (سطح میانای برنامه‌سازی کاربردی ۳۳) به بعد، صدای فضایی را به برنامه‌تان اضافه کنید.

پُرسمان برای قابلیت‌ها

از کلاس Spatializer برای پرس‌وجو درباره قابلیت‌ها و رفتار فضایی‌سازی دستگاه استفاده کنید. با بازیابی نمونه‌ای از Spatializer از AudioManager شروع کنید:

کاتلین

val spatializer = audioManager.spatializer

جاوا

Spatializer spatializer = AudioManager.getSpatializer();

پس‌از دریافت Spatializer، چهار شرطی را که باید برای خروجی صدای فضایی‌شده دستگاه برقرار باشد بررسی کنید:

معیارها بررسی کردن
آیا دستگاه از فضایی‌سازی پشتیبانی می‌کند؟ getImmersiveAudioLevel() SPATIALIZER_IMMERSIVE_LEVEL_NONE نیست
آیا فضاسازی دردسترس است؟
دردسترس بودن به سازگاری با مسیریابی خروجی صوتی فعلی بستگی دارد.
‫isAvailable() true است
آیا فضاسازی فعال است؟ ‫isEnabled() true است
آیا قطعه صوتی با پارامترهای داده‌شده می‌تواند فضایی شود؟ ‫canBeSpatialized() true است

ممکن است این شرایط برآورده نشود، برای مثال، اگر فضایی‌سازی برای قطعه صوتی فعلی دردسترس نباشد یا کلاً در دستگاه خروجی صدا غیرفعال باشد.

ردیابی سر

با هدست‌های پشتیبانی‌شده، پلاتفرم می‌تواند فضایی‌سازی صدا را براساس موقعیت سر کاربر تنظیم کند. برای بررسی اینکه آیا ردیاب سر برای مسیریابی خروجی صوتی فعلی دردسترس است یا نه، isHeadTrackerAvailable() را فراخوانی کنید.

محتوای سازگار

Spatializer.canBeSpatialized() نشان می‌دهد که آیا صدا با ویژگی‌های داده‌شده می‌تواند با مسیریابی دستگاه خروجی فعلی فضایی شود یا نه. این روش یک AudioAttributes و یک AudioFormat می‌گیرد که هر دو در زیر با جزئیات بیشتر شرح داده شده‌اند.

AudioAttributes

یک شیء AudioAttributes استفاده از جاری‌سازی صوتی (برای مثال، صدای بازی یا رسانه استاندارد)، به‌همراه رفتارهای بازپخش و نوع محتوای آن را توصیف می‌کند.

هنگام فراخوانی canBeSpatialized()، از همان نمونه AudioAttributes استفاده کنید که برای Player شما تنظیم شده است. برای مثال، اگر از کتابخانه Jetpack Media3 استفاده می‌کنید و AudioAttributes را سفارشی‌سازی نکرده‌اید، از AudioAttributes.DEFAULT استفاده کنید.

درحال غیرفعال کردن صدای فضایی

برای نشان دادن اینکه محتوایتان قبلاً فضایی شده است، setIsContentSpatialized(true) را فراخوانی کنید تا صدا دوباره پردازش نشود. یا با فراخوانی setSpatializationBehavior(AudioAttributes.SPATIALIZATION_BEHAVIOR_NEVER)، رفتار فضایی‌سازی را طوری تنظیم کنید که فضایی‌سازی به‌طور کامل غیرفعال شود.

AudioFormat

شیء AudioFormat جزئیات مربوط به قالب و پیکربندی کانال قطعه صوتی را شرح می‌دهد.

هنگام نمونه‌سازی AudioFormat برای ارسال به canBeSpatialized()، کدبندی را روی همان قالب خروجی موردانتظار از رمزگشا تنظیم کنید. همچنین باید ماسک کانالی تنظیم کنید که با پیکربندی کانال محتوایتان مطابقت داشته باشد. برای راهنمایی درباره مقادیر خاصی که باید استفاده شود، به بخش رفتار فضایی‌سازی پیش‌فرض مراجعه کنید.

گوش دادن به تغییرات Spatializer

برای گوش دادن به تغییرات در وضعیت Spatializer، می‌توانید شنونده‌ای با Spatializer.addOnSpatializerStateChangedListener() اضافه کنید. به‌طور مشابه، برای گوش دادن به تغییرات در دردسترس بودن ردیاب سر، Spatializer.addOnHeadTrackerAvailableListener() را فراخوانی کنید.

این ویژگی زمانی مفید است که بخواهید انتخاب قطعه را درطول بازپخش بااستفاده از فراخوانی‌های شنونده تنظیم کنید. برای مثال، وقتی کاربر هدست خود را به دستگاه متصل یا از آن جدا می‌کند، فراخوانی برگشتی onSpatializerAvailableChanged نشان می‌دهد که آیا جلوه فضاساز برای مسیریابی خروجی صوتی جدید دردسترس است یا نه. در این مرحله، می‌توانید منطق انتخاب قطعه پخش‌کننده‌تان را به‌روز کنید تا با قابلیت‌های جدید دستگاه مطابقت داشته باشد. برای جزئیات بیشتر درباره رفتار انتخاب قطعه ExoPlayer، به بخش ExoPlayer و صدای فضایی مراجعه کنید.

ExoPlayer و صدای فضایی

نسخه‌های اخیر ExoPlayer استفاده از صدای فراگیر را آسان‌تر می‌کند. اگر از کتابخانه ExoPlayer مستقل (نام بسته com.google.android.exoplayer2) استفاده می‌کنید، نسخه ۲.۱۷ پلاتفرم را برای خروجی صدای فضایی پیکربندی می‌کند و نسخه ۲.۱۸ محدودیت‌های تعداد کانال صوتی را معرفی می‌کند. اگر از ماژول ExoPlayer از کتابخانه Media3 (نام بسته androidx.media3) استفاده می‌کنید، نسخه‌های 1.0.0-beta01 و جدیدتر شامل همین به‌روزرسانی‌ها هستند.

پس‌از به‌روزرسانی وابستگی ExoPlayer به جدیدترین نسخه، برنامه‌تان فقط باید محتوایی را دربرگیرد که بتواند فضایی شود.

محدودیت‌های تعداد کانال صوتی

وقتی هر چهار شرط برای صدای فضایی برآورده شود، ExoPlayer قطعه صوتی چندکاناله را انتخاب می‌کند. اگر این‌طور نباشد، ExoPlayer درعوض قطعه استریو را انتخاب می‌کند. اگر ویژگی‌های Spatializer تغییر کند، ExoPlayer انتخاب قطعه جدیدی را راه‌اندازی می‌کند تا قطعه صوتی‌ای را انتخاب کند که با ویژگی‌های فعلی مطابقت داشته باشد. توجه داشته باشید که این انتخاب قطعه جدید ممکن است باعث ایجاد یک دوره کوتاه بافرینگ مجدد شود.

برای غیرفعال کردن محدودیت‌های تعداد کانال صوتی، پارامترهای انتخاب قطعه را در پخش‌کننده به‌صورت زیر تنظیم کنید:

کاتلین

exoPlayer.trackSelectionParameters = DefaultTrackSelector.Parameters.Builder(context)
  .setConstrainAudioChannelCountToDeviceCapabilities(false)
  .build()

جاوا

exoPlayer.setTrackSelectionParameters(
  new DefaultTrackSelector.Parameters.Builder(context)
    .setConstrainAudioChannelCountToDeviceCapabilities(false)
    .build()
);

به‌طور مشابه، می‌توانید پارامترهای انتخابگر قطعه موجود را به‌روز کنید تا محدودیت‌های تعداد کانال صوتی را به‌صورت زیر غیرفعال کنید:

کاتلین

val trackSelector = DefaultTrackSelector(context)
...
trackSelector.parameters = trackSelector.buildUponParameters()
  .setConstrainAudioChannelCountToDeviceCapabilities(false)
  .build()

جاوا

DefaultTrackSelector trackSelector = new DefaultTrackSelector(context);
...
trackSelector.setParameters(
  trackSelector
    .buildUponParameters()
    .setConstrainAudioChannelCountToDeviceCapabilities(false)
    .build()
);

وقتی محدودیت‌های تعداد کانال صوتی غیرفعال باشد، اگر محتوا چندین قطعه صوتی داشته باشد، ExoPlayer ابتدا قطعه‌ای را انتخاب می‌کند که بیشترین تعداد کانال را دارد و از دستگاه قابل‌پخش است. برای مثال، اگر محتوا حاوی یک قطعه صوتی چندکاناله و یک قطعه صوتی استریو باشد و دستگاه از بازپخش هر دو پشتیبانی کند، ExoPlayer قطعه چندکاناله را انتخاب می‌کند. برای جزئیات بیشتر درباره نحوه سفارشی‌سازی این رفتار، انتخاب قطعه صوتی را ببینید.

انتخاب قطعه صوتی

وقتی رفتار محدودیت‌های تعداد کانال صوتی ExoPlayer غیرفعال باشد، ExoPlayer به‌طور خودکار قطعه صوتی‌ای را که با ویژگی‌های فضاساز دستگاه مطابقت داشته باشد انتخاب نمی‌کند. درعوض، می‌توانید منطق انتخاب قطعه ExoPlayer را با تنظیم پارامترهای انتخاب قطعه قبل‌از بازپخش یا درطول آن سفارشی‌سازی کنید. به‌طور پیش‌فرض، ExoPlayer قطعه‌های صوتی‌ای را انتخاب می‌کند که ازنظر نوع MIME (کدبندی)، تعداد کانال، و نرخ نمونه‌برداری با قطعه اولیه یکسان باشند.

درحال تغییر پارامترهای انتخاب قطعه

برای تغییر پارامترهای انتخاب قطعه ExoPlayer، از Player.setTrackSelectionParameters() استفاده کنید. به‌همین ترتیب، می‌توانید پارامترهای فعلی ExoPlayer را با Player.getTrackSelectionParameters() دریافت کنید. برای مثال، برای انتخاب قطعه صوتی استریو در حین بازپخش:

کاتلین

exoPlayer.trackSelectionParameters = exoPlayer.trackSelectionParameters
  .buildUpon()
  .setMaxAudioChannelCount(2)
  .build()

جاوا

exoPlayer.setTrackSelectionParameters(
  exoPlayer.getTrackSelectionParameters()
    .buildUpon()
    .setMaxAudioChannelCount(2)
    .build()
);

توجه داشته باشید که تغییر پارامترهای انتخاب قطعه در اواسط بازپخش ممکن است باعث ایجاد وقفه در بازپخش شود. اطلاعات بیشتر درباره تنظیم پارامترهای انتخاب قطعه پخش‌کننده در بخش انتخاب قطعه در اسناد ExoPlayer دردسترس است.

رفتار فضایی‌سازی پیش‌فرض

رفتار فضاسازی پیش‌فرض در Android شامل رفتارهای زیر است که ممکن است توسط سازندگان اصلی محصول سفارشی‌سازی شود:

  • فقط محتوای چندکاناله فضایی می‌شود، نه محتوای استریو. اگر از ExoPlayer استفاده نمی‌کنید، بسته به قالب محتوای صوتی چندکاناله، ممکن است لازم باشد حداکثر تعداد کانال‌هایی را که می‌توانند توسط رمزگشای صوتی به تعداد زیادی خروجی داده شوند پیکربندی کنید. این کار تضمین می‌کند که رمزگشای صوتی، PCM چندکاناله را برای فضایی‌سازی پلاتفرم خروجی دهد.

    کاتلین

    val mediaFormat = MediaFormat()
    mediaFormat.setInteger(MediaFormat.KEY_MAX_OUTPUT_CHANNEL_COUNT, 99)

    جاوا

    MediaFormat mediaFormat = new MediaFormat();
    mediaFormat.setInteger(MediaFormat.KEY_MAX_OUTPUT_CHANNEL_COUNT, 99);

    برای مشاهده نمونه‌ای در عمل، MediaCodecAudioRenderer.java ExoPlayer را ببینید. برای خاموش کردن فضاسازی توسط خودتان، صرف‌نظر از سفارشی‌سازی OEM، به غیرفعال کردن صدای فضایی مراجعه کنید.

  • AudioAttributes: اگر usage روی USAGE_MEDIA یا USAGE_GAME تنظیم شده باشد، صدا برای فضایی‌سازی واجدشرایط است.

  • AudioFormat: از ماسک کانالی استفاده کنید که حداقل حاوی کانال‌های AudioFormat.CHANNEL_OUT_QUAD (جلو-چپ، جلو-راست، عقب-چپ، و عقب-راست) باشد تا صدا برای فضایی‌سازی واجدشرایط باشد. در مثال زیر، از AudioFormat.CHANNEL_OUT_5POINT1 برای قطعه صوتی ۵.۱ استفاده می‌کنیم. برای قطعه موسیقی استریو، از AudioFormat.CHANNEL_OUT_STEREO استفاده کنید.

    اگر از Media3 استفاده می‌کنید، می‌توانید از Util.getAudioTrackChannelConfig(int channelCount) برای تبدیل تعداد کانال به ماسک کانال استفاده کنید.

    علاوه‌براین، اگر رمزگشا را برای خروجی PCM چندکاناله پیکربندی کرده‌اید، رمزگذاری را روی AudioFormat.ENCODING_PCM_16BIT تنظیم کنید.

    کاتلین

    val audioFormat = AudioFormat.Builder()
      .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
      .setChannelMask(AudioFormat.CHANNEL_OUT_5POINT1)
      .build()

    جاوا

    AudioFormat audioFormat = new AudioFormat.Builder()
      .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
      .setChannelMask(AudioFormat.CHANNEL_OUT_5POINT1)
      .build();

آزمایش صدای فضایی

مطمئن شوید که صدای فضایی در دستگاه آزمایشی شما فعال است:

  • برای هدست‌های سیمی، به تنظیمات سیستم > صدا و لرزش > صدای فراگیر بروید.
  • برای هدست‌های بی‌سیم، به تنظیمات سیستم > دستگاه‌های متصل > نماد چرخ‌دنده برای دستگاه بی‌سیم > صدای فضایی بروید.

برای بررسی دردسترس بودن «صدای فضایی» برای مسیریابی فعلی، فرمان adb shell dumpsys audio را در دستگاهتان اجرا کنید. وقتی بازپخش فعال است، باید پارامترهای زیر را در خروجی ببینید:

Spatial audio:
mHasSpatializerEffect:true (effect present)
isSpatializerEnabled:true (routing dependent)