الصوت المكاني

"الصوت المكاني" هو تجربة صوتية غامرة تضع المستخدمين في قلب الحدث، ما يجعل الصوت في المحتوى أكثر واقعية. يتم "توزيع" الصوت لإنشاء تأثير مكبّرات صوت متعددة، على غرار إعداد الصوت المحيطي، ولكن من خلال سمّاعات الرأس بدلاً من ذلك.

على سبيل المثال، في فيلم، قد يبدأ صوت سيارة من خلف المستخدم، ثم يتحرّك إلى الأمام، ويتلاشى في المسافة البعيدة. في محادثة فيديو، يمكن فصل الأصوات وتوزيعها حول المستخدم، ما يسهّل التعرّف على المتحدّثين.

إذا كان المحتوى الخاص بك يستخدم تنسيق صوت متوافقًا، يمكنك إضافة ميزة "الصوت المكاني" إلى تطبيقك بدءًا من Android 13 (المستوى 33 لواجهة برمجة التطبيقات).

طلب البحث عن الإمكانات

استخدِم فئة Spatializer للاستعلام عن إمكانات الجهاز وسلوكه في ما يتعلّق بالصوت المكاني. ابدأ باسترداد نسخة من Spatializer من AudioManager:

Kotlin

val spatializer = audioManager.spatializer

Java

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)، يضبط الإصدار 2.17 النظام الأساسي لإخراج صوت مكاني، ويقدّم الإصدار 2.18 قيودًا على عدد القنوات الصوتية. إذا كنت تستخدم وحدة ExoPlayer من مكتبة Media3 (اسم الحزمة androidx.media3)، تتضمّن الإصدارات 1.0.0-beta01 والإصدارات الأحدث هذه التحديثات نفسها.

بعد تعديل تبعية ExoPlayer إلى أحدث إصدار، ما عليك سوى تضمين محتوى يمكن تحويله إلى صوت مكاني في تطبيقك.

قيود عدد القنوات الصوتية

عند استيفاء جميع الشروط الأربعة المتعلقة بالصوت المكاني، يختار ExoPlayer مقطعًا صوتيًا متعدد القنوات. إذا لم يكن متوفّرًا، يختار ExoPlayer مقطعًا صوتيًا مجسمًا بدلاً من ذلك. في حال تغيّر خصائص Spatializer، سيؤدي ذلك إلى أن يفعّل ExoPlayer عملية اختيار مقطع صوتي جديد لاختيار مقطع صوتي يتطابق مع الخصائص الحالية. يُرجى العِلم أنّ اختيار مسار صوتي جديد قد يؤدي إلى فترة إعادة تخزين مؤقت قصيرة.

لإيقاف قيود عدد قنوات الصوت، اضبط مَعلمات اختيار المسار على المشغّل كما هو موضّح أدناه:

Kotlin

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

Java

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

وبالمثل، يمكنك تعديل مَعلمات أداة اختيار مسار حالية لإيقاف قيود عدد قنوات الصوت على النحو التالي:

Kotlin

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

Java

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(). على سبيل المثال، لاختيار مقطع صوتي استريو أثناء التشغيل:

Kotlin

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

Java

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

يُرجى العِلم أنّ تغيير مَعلمات اختيار المقطع الصوتي أثناء التشغيل قد يؤدي إلى حدوث انقطاع في التشغيل. يمكنك الاطّلاع على مزيد من المعلومات حول ضبط مَعلمات اختيار المسار في قسم اختيار المسار في مستندات ExoPlayer.

السلوك التلقائي للصوت المكاني

يتضمّن السلوك التلقائي للتجسيم الصوتي في Android السلوكيات التالية التي يمكن لمصنّعي المعدات الأصلية تخصيصها:

  • يتم تحويل المحتوى المتعدد القنوات إلى صوت مكاني، وليس المحتوى المجسم. إذا كنت لا تستخدم ExoPlayer، قد تحتاج إلى ضبط الحد الأقصى لعدد القنوات التي يمكن أن يخرجها برنامج ترميز الصوت على عدد كبير، وذلك حسب تنسيق محتوى الصوت المتعدد القنوات. يضمن ذلك أن يخرج برنامج ترميز الصوت PCM متعدد القنوات ليتم تحويله إلى صوت مكاني على المنصة.

    Kotlin

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

    Java

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

    للاطّلاع على مثال عملي، راجِع MediaCodecAudioRenderer.java في ExoPlayer. لإيقاف ميزة "تجسيم الصوت" بنفسك، بغض النظر عن التخصيص الذي يوفّره مصنّع المعدات الأصلية، اطّلِع على إيقاف ميزة "الصوت المكاني".

  • AudioAttributes: يكون الصوت مؤهلاً للتجسيم إذا تم ضبط قيمة usage على USAGE_MEDIA أو USAGE_GAME.

  • AudioFormat: استخدِم قناع قناة يتضمّن على الأقل قنوات AudioFormat.CHANNEL_OUT_QUAD (الأمامية اليسرى والأمامية اليمنى والخلفية اليسرى والخلفية اليمنى) ليكون الصوت مؤهلاً للتجسيم الصوتي. في المثال أدناه، نستخدم AudioFormat.CHANNEL_OUT_5POINT1 لمقطع صوتي بنظام 5.1. بالنسبة إلى مقطع صوتي استيريو، استخدِم AudioFormat.CHANNEL_OUT_STEREO.

    إذا كنت تستخدم Media3، يمكنك استخدام Util.getAudioTrackChannelConfig(int channelCount) لتحويل عدد القنوات إلى قناع قنوات.

    بالإضافة إلى ذلك، اضبط الترميز على AudioFormat.ENCODING_PCM_16BIT إذا كنت قد ضبطت برنامج الترميز على إخراج PCM متعدد القنوات.

    Kotlin

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

    Java

    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)