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

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

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

إذا كان المحتوى يستخدم تنسيقًا صوتيًا متوافقًا، يمكنك إضافة ميزة "الصوت المكاني" إلى تطبيقك بدءًا من 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)