صوتی فضایی

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

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

اگر محتوای شما از یک فرمت صوتی پشتیبانی‌شده استفاده می‌کند، می‌توانید از اندروید ۱۳ (سطح API ۳۳) صدای مکانی را به برنامه خود اضافه کنید.

استعلام قابلیت‌ها

از کلاس 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 موجود است.

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

رفتار پیش‌فرض فضاسازی در اندروید شامل رفتارهای زیر است که ممکن است توسط تولیدکنندگان اصلی تجهیزات (OEM) سفارشی‌سازی شوند:

  • فقط محتوای چند کاناله فضاسازی می‌شود، نه محتوای استریو. اگر از 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)