空間音訊

空間音訊提供身歷其境的聽覺饗宴,讓使用者能位於動作中心,讓內容聽起來更逼真。音效的「空間化」功能可產生多喇叭效果,與環場音效設定類似,只是透過耳機而已。

例如,在電影中,汽車的聲音可能會在使用者後方開始播放,然後往前移動,然後跑到遠處。在視訊通訊中,您可以將語音分開並放置在使用者的附近,方便識別說話者。

如果內容使用支援的音訊格式,您可以從 Android 13 (API 級別 33) 開始,在應用程式中加入空間音訊。

功能查詢

使用 Spatializer 類別查詢裝置的空間化功能和行為。首先,請從 AudioManager 擷取 Spatializer 的例項:

Kotlin

val spatializer = audioManager.spatializer

Java

Spatializer spatializer = AudioManager.getSpatializer();

取得 Spatializer 後,請檢查下列四個條件,哪些條件必須維持 true,才能讓裝置輸出空間化音訊:

條件 查看
裝置是否支援空間化功能? getImmersiveAudioLevel() 不是 SPATIALIZER_IMMERSIVE_LEVEL_NONE
是否有空間化功能?
適用情況取決於與目前音訊輸出路徑的相容性。
isAvailable() 為「true
是否啟用空間化功能? isEnabled() 為「true
含有指定參數的音軌可以空間化嗎? canBeSpatialized() 為「true

例如,如果目前音軌無法使用空間化,或是音訊輸出裝置完全停用,就可能不符合這些條件。

頭部追蹤

使用支援的耳機時,平台可根據使用者的頭部位置調整音訊的空間化。如要檢查抬頭追蹤器是否適用於目前的音訊輸出轉送,請呼叫 isHeadTrackerAvailable()

相容內容

Spatializer.canBeSpatialized() 表示具有指定屬性的音訊能否透過目前的輸出裝置轉送方式進行空間化。這個方法使用 AudioAttributesAudioFormat,兩者詳情請見下文。

AudioAttributes

AudioAttributes 物件會描述音訊串流的使用方式 (例如遊戲音訊標準媒體),以及播放行為和內容類型

呼叫 canBeSpatialized() 時,請使用與 Player 設定相同的 AudioAttributes 例項。舉例來說,如果您使用 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 版則會導入聲道數量限制。如果您使用 Media3 程式庫的 ExoPlayer 模組 (套件名稱 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()。同樣地,您也可以使用 Player.getTrackSelectionParameters() 取得 ExoPlayer 目前的參數。舉例來說,如要選取中途播放的立體聲音軌,請按照下列步驟操作:

Kotlin

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

Java

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

請注意,在播放期間變更音軌選取參數,可能會導致播放中斷。如要進一步瞭解如何調整玩家的曲目選取參數,請參閱 ExoPlayer 文件的音軌選取部分。

預設空間化行為

Android 的預設空間化行為包含下列可由原始設備製造商 (OEM) 自訂的行為:

  • 只有多頻道內容需要空間化,而非立體聲內容。 如果您不使用 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);
    

    如需實際範例,請參閱 ExoPlayer 的 MediaCodecAudioRenderer.java。如要自行關閉空間化功能,無論原始設備製造商 (OEM) 自訂設定為何,請參閱「停用空間音訊」一文。

  • AudioAttributes:如果 usage 設為 USAGE_MEDIAUSAGE_GAME,音訊即符合空間化資格。

  • AudioFormat:使用至少包含 AudioFormat.CHANNEL_OUT_QUAD 聲道的聲道遮罩 (左前方、右前方、左後方和右後方),這樣音訊才能符合空間化資格。在下方範例中,我們針對 5.1 音軌使用 AudioFormat.CHANNEL_OUT_5POINT1。如果是立體聲音軌,請使用 AudioFormat.CHANNEL_OUT_STEREO

    如果您使用的是 Media3,可以使用 Util.getAudioTrackChannelConfig(int channelCount) 將頻道計數轉換為頻道遮罩。

    此外,如果您已將解碼器設為輸出多管道 PCM,請將編碼設為 AudioFormat.ENCODING_PCM_16BIT

    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)