空間音訊

空間音訊提供身歷其境的聽覺饗宴,讓影音內容變得更加逼真。「空間化」音效功能可產生宛如有多個喇叭的音效體驗 (類似環場音效設定),但透過耳機播放。

舉例來說,在電影中,車輛的聲音可能會從使用者後方開始,往前移動,然後消失在遠方。在視訊通話中,聲音可以分開並放置在使用者周圍,方便辨識說話者。

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

查詢功能

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

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() 指出是否可使用目前的輸出裝置路徑,將具有指定屬性的音訊空間化。這個方法會採用 AudioAttributesAudioFormat,兩者都會在下文詳細說明。

AudioAttributes

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

呼叫 canBeSpatialized() 時,請使用與 Player 相同的 AudioAttributes 執行個體。舉例來說,如果您使用 Jetpack Media3 程式庫,且尚未自訂 AudioAttributes,請使用 AudioAttributes.DEFAULT

停用空間音訊

如要指出內容已空間化,請呼叫 setIsContentSpatialized(true),以免音訊經過雙重處理。或者,您也可以呼叫 setSpatializationBehavior(AudioAttributes.SPATIALIZATION_BEHAVIOR_NEVER),調整空間化行為以完全停用空間化。

AudioFormat

AudioFormat 物件會說明音軌的格式和聲道設定詳細資料。

例項化 AudioFormat 以傳遞至 canBeSpatialized() 時,請將 encoding 設為與解碼器預期的輸出格式相同。此外,您也應設定管道遮罩,與內容的管道設定相符。如需使用特定值的指引,請參閱「預設空間化行為」一節。

監聽 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聲道 (左前、右前、左後和右後) 的聲道遮罩,音訊才能進行空間化處理。在以下範例中,我們使用 AudioFormat.CHANNEL_OUT_5POINT1 代表 5.1 音軌。如果是立體聲音軌,請使用 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)