在 XR 應用程式中加入空間音訊

適用 XR 裝置
這份指南可協助您為這類 XR 裝置打造體驗。
XR 頭戴式裝置
有線 XR 眼鏡

Jetpack SceneCore 的空間音訊功能可讓您在 Android XR 應用程式中,打造身歷其境的音訊體驗。

空間音訊會模擬使用者在 3D 環境中感知聲音的方式,營造出聲音從四面八方傳來的感覺,包括使用者上方和下方。系統會在 3D 空間的特定位置模擬一或多個「虛擬揚聲器」,藉此達到效果。

如果現有應用程式並非專為 Android XR 設計或修改,Android XR 會自動將音訊空間化。使用者在空間中移動時,所有應用程式音訊都會從應用程式 UI 顯示的面板發出。舉例來說,如果時鐘應用程式的計時器響起,音訊會從應用程式面板的位置發出。Android XR 會自動調整音效,呈現逼真的位置感。舉例來說,應用程式面板與使用者之間的距離會影響音量,讓使用者更有身歷其境的感覺。

如要進一步瞭解現有應用程式如何算繪空間音訊,請參閱本頁面的「在應用程式中加入立體聲和環場音效」。

如果您要針對 XR 最佳化應用程式,Jetpack SceneCore 提供進階空間音訊自訂工具。您可以在 3D 環境中精確定位聲音、使用全向性音訊呈現逼真的音場,以及善用內建的環繞音效整合功能。

Android XR 支援的空間音效類型

Android XR 支援位置、立體聲、環場音效和 Ambisonic 音訊。

位置音訊

位置音訊可設定為從 3D 空間中的特定點播放。 舉例來說,您可以在虛擬環境的角落放置一隻狗的 3D 模型,並讓牠發出吠叫聲。您可以讓多個實體從各自的位置發出聲音。如要算繪位置音訊,檔案必須是單聲道或立體聲。

空間化立體聲和環場音效

所有 Android 媒體格式都支援位置、立體聲和環場音效。除了上述格式,Android XR 裝置也可能支援 Dolby AtmosDolby DigitalDolby Digital+ 音訊格式。

立體聲音訊是指具有兩個聲道的音訊格式,環場音效是指具有兩個以上聲道的音訊格式,例如 5.1 環場音效7.1 環場音效設定。每個聲道的聲音資料都會與一個音箱相關聯。舉例來說,播放立體聲音樂時,左揚聲器聲道發出的樂器音軌可能與右揚聲器不同。

電影和電視節目通常會使用環場音效,透過多個音箱聲道來提升真實感和沉浸感。舉例來說,對話通常會從中央揚聲器聲道播放,而直升機飛行的聲音可能會依序使用不同聲道,營造直升機在 3D 空間中飛行的感覺。

全方位音訊

全景聲 (或全景聲) 就像音訊的 Skybox,可為使用者提供沉浸式音景。使用 Ambisonics 技術播放背景環境音效,或在其他情境中複製環繞聽者的全球形音場。Android XR 支援AmbiX 環繞聲音訊格式,包括第一、第二和第三階環繞聲。建議使用 Opus (.ogg) 和 PCM/Wave (.wav) 檔案類型。

使用 Jetpack SceneCore 空間音訊

使用 Jetpack SceneCore 實作空間音訊時,需要檢查空間功能,並選擇用於載入空間音訊的 API。

檢查空間功能

使用空間音訊功能前,請確認 Session 支援空間音訊。在下列各節的所有程式碼片段中,系統都會先檢查功能,再嘗試播放空間化音訊。

載入空間音訊

您可以使用下列任一 API,載入 Jetpack SceneCore 中使用的空間音訊。

  • SoundPool:適合用於大小不到 1 MB 的短音效,會預先載入,且可重複使用。這是載入位置音訊的絕佳方式。
  • ExoPlayer:適合載入立體聲和環繞音效內容,例如音樂和影片。還能讓媒體在背景播放。
  • MediaPlayer:提供載入 Ambisonic 音訊的最簡單方式。
  • AudioTrack:可充分控管音訊資料的載入方式。可直接寫入音訊緩衝區,或寫入您合成或解碼的音訊檔案。

檢查是否支援媒體格式

Android 平台支援部分媒體格式。不過,特定 Android XR 裝置可能支援其他格式,例如 Dolby Atmos。如要查詢媒體格式支援情形,請使用 ExoPlayer 的 AudioCapabilities

val audioCapabilities = AudioCapabilities.getCapabilities(context, androidx.media3.common.AudioAttributes.DEFAULT, null)
if (audioCapabilities.supportsEncoding(C.ENCODING_AC3)) {
    // Device supports playback of the Dolby Digital media format.
}
if (audioCapabilities.supportsEncoding(C.ENCODING_E_AC3)) {
    // Device supports playback of the Dolby Digital Plus media format.
}
if (audioCapabilities.supportsEncoding(C.ENCODING_E_AC3_JOC)) {
    // Device supports playback of the Dolby Digital Plus with Dolby Atmos media format.
}

檢查這些功能可能涉及封鎖呼叫,不應在主執行緒上呼叫

在應用程式中加入位置音訊

位置音效來源是由 PointSourceParams 和相關聯的 Entity 定義。Entity 的位置和方向會決定 PointSourceParams 在 3D 空間中的算繪位置。

位置音訊範例

以下範例會將音效音訊檔案載入音效集區,並在 Entity 的位置播放。

// Check spatial capabilities before using spatial audio
if (session.scene.spatialCapabilities.contains(SpatialCapability.SPATIAL_AUDIO)
) { // The session has spatial audio capabilities
    val maxVolume = 1F
    val lowPriority = 0
    val infiniteLoop = -1
    val normalSpeed = 1F

    val soundPool = SoundPool.Builder()
        .setAudioAttributes(
            AudioAttributes.Builder()
                .setContentType(CONTENT_TYPE_SONIFICATION)
                .setUsage(USAGE_ASSISTANCE_SONIFICATION)
                .build()
        )
        .build()

    val pointSource = PointSourceParams(entity)

    val soundEffect = appContext.assets.openFd("sounds/tiger_16db.mp3")
    val pointSoundId = soundPool.load(soundEffect, lowPriority)

    soundPool.setOnLoadCompleteListener { soundPool, sampleId, status ->
        // wait for the sound file to be loaded into the soundPool
        if (status == 0) {
            SpatialSoundPool.play(
                session = session,
                soundPool = soundPool,
                soundID = pointSoundId,
                params = pointSource,
                volume = maxVolume,
                priority = lowPriority,
                loop = infiniteLoop,
                rate = normalSpeed
            )
        }
    }
} else {
    // The session does not have spatial audio capabilities
}

程式碼相關重點

  • 首先,請使用 spatialCapabilities 檢查目前是否提供空間音訊功能。
  • 將 contentType 設為 CONTENT_TYPE_SONIFICATION,並將 usage 設為 USAGE_ASSISTANCE_SONIFICATION,系統就會將這個音訊檔案視為音效。
  • 上述範例會將音訊檔案載入集區,然後立即使用,方便您將程式碼放在一起。理想情況下,您應該在載入應用程式時,非同步載入所有音效,這樣需要時,音訊檔案就能在集區中派上用場。

在應用程式中加入立體聲和環繞音效

建議使用 Exoplayer,在應用程式中加入立體聲和環場音效。如要進一步瞭解如何搭配 Exoplayer 使用空間音訊,請參閱空間音訊指南

立體聲和環場音效揚聲器位置

透過環繞喇叭定位,虛擬環繞喇叭會根據中央喇叭的位置和方向,以標準 ITU 設定環繞使用者。

根據預設,中央聲道喇叭會放在應用程式的 mainPanelEntity。包括 Android XR 自動空間化的行動應用程式。

如要使用立體聲,揚聲器擺放位置與環繞音效類似,但只有左右聲道分別位於面板的左右兩側。

如果你有多個面板,並想選擇要發出音訊的面板,或是想讓立體聲或環繞音訊相對於另一個  進行算繪,可以使用 PointSourceAttributes 定義中央聲道的所在位置。Entity其餘管道會如先前所述放置。在這些情況下,您也必須使用 MediaPlayer

使用者在空間中移動時,立體聲和環繞音效虛擬音箱會隨之移動和調整,確保音箱始終處於最佳位置。

如果已設定 MediaPlayerExoPlayer 在背景繼續播放立體聲或環繞音效,應用程式進入背景時,虛擬揚聲器位置會改變。由於沒有面板或其他空間點可錨定聲音,空間音訊會隨著使用者移動 (也就是「頭部鎖定」)。

環場音效範例

以下範例會使用 MediaPlayer 載入 5.1 音訊檔案,並將檔案的中央聲道設為 Entity

// Check spatial capabilities before using spatial audio
if (session.scene.spatialCapabilities.contains(SpatialCapability.SPATIAL_AUDIO)) {
    // The session has spatial audio capabilities

    val pointSourceAttributes = PointSourceParams(session.scene.mainPanelEntity)

    val mediaPlayer = MediaPlayer()

    val fivePointOneAudio = appContext.assets.openFd("sounds/aac_51.ogg")
    mediaPlayer.reset()
    mediaPlayer.setDataSource(fivePointOneAudio)

    val audioAttributes =
        AudioAttributes.Builder()
            .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
            .setUsage(AudioAttributes.USAGE_MEDIA)
            .build()

    SpatialMediaPlayer.setPointSourceParams(
        session,
        mediaPlayer,
        pointSourceAttributes
    )

    mediaPlayer.setAudioAttributes(audioAttributes)
    mediaPlayer.prepare()
    mediaPlayer.start()
} else {
    // The session does not have spatial audio capabilities
}

程式碼相關重點

在應用程式中新增 Ambisonic 音場

如要播放全景聲音場,最簡單的方法就是使用 MediaPlayer 載入檔案。由於全景聲適用於整個音場,因此您不需要指定 Entity 來提供位置。而是建立 SoundFieldAttributes 的執行個體,並指定適當的環繞音場階數和聲道數。

Ambionics 示例

以下範例使用 MediaPlayer 播放 Ambisonics 音場。

// Check spatial capabilities before using spatial audio
if (session.scene.spatialCapabilities.contains(SpatialCapability.SPATIAL_AUDIO)) {
    // The session has spatial audio capabilities

    val soundFieldAttributes =
        SoundFieldAttributes(SpatializerConstants.AmbisonicsOrder.FIRST_ORDER)

    val mediaPlayer = MediaPlayer()

    val soundFieldAudio = appContext.assets.openFd("sounds/foa_basketball_16bit.wav")

    mediaPlayer.reset()
    mediaPlayer.setDataSource(soundFieldAudio)

    val audioAttributes =
        AudioAttributes.Builder()
            .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
            .setUsage(AudioAttributes.USAGE_MEDIA)
            .build()

    SpatialMediaPlayer.setSoundFieldAttributes(
        session,
        mediaPlayer,
        soundFieldAttributes
    )

    mediaPlayer.setAudioAttributes(audioAttributes)
    mediaPlayer.prepare()
    mediaPlayer.start()
} else {
    // The session does not have spatial audio capabilities
}

程式碼相關重點

  • 與先前的程式碼片段相同,第一步是使用 hasCapability() 檢查空間音訊功能是否可用。
  • contentType 和使用方式僅供參考。
  • AMBISONICS_ORDER_FIRST_ORDER 會向 SceneCore 發出信號,表示音場檔案定義了四個聲道。