空間音訊提供身歷其境的聽覺饗宴,讓影音內容變得更加逼真。音訊會經過「空間化」處理,產生宛如有多個喇叭的音效體驗 (類似環場音效設定),但透過耳機播放。
舉例來說,在電影中,車輛的聲音可能會從使用者身後開始,然後向前移動,最後消失在遠方。在視訊通話中,系統可以將聲音分離並放置在使用者周圍,方便辨識講者。
如果內容使用支援的音訊格式,您可以從 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()
可指出是否可使用目前的輸出裝置路由,將具有指定屬性的音訊轉換為空間化音訊。這個方法會使用 AudioAttributes
和 AudioFormat
,兩者皆會在下方進一步說明。
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 中的預設空間化行為包含下列行為,這些行為可由原始設備製造商自訂:
只有多聲道內容會經過空間化處理,而非立體聲內容。如果您未使用 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_MEDIA
或USAGE_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)