借助 Jetpack SceneCore 中的空间音频功能,您可以在 Android XR 应用中打造沉浸式音频体验。
空间音频可模拟用户在 3D 环境中感知声音的方式。它会营造出声音从各个方向(包括用户上方和下方)传来的感觉。系统通过在 3D 空间中的特定位置模拟一个或多个“虚拟扬声器”来实现此效果。
未针对 Android XR 设计或修改的现有应用会在 Android XR 中自动实现音频空间化。当用户在空间中移动时,所有应用音频都将从应用界面所渲染到的面板发出。例如,如果时钟应用中的计时器响铃,音频听起来会像是从应用面板位置传来的。Android XR 会自动改变声音,以实现位置逼真感。例如,应用面板与用户之间的感知距离会微妙地影响音量,从而增强真实感。
如需详细了解现有应用如何呈现空间音频,请参阅本页面上的向应用添加立体声和环绕声。
如果您要针对 XR 优化应用,Jetpack SceneCore 可提供用于高级空间音频自定义的工具。您可以在 3D 环境中精确定位声音,使用全方位音频打造逼真的声场,并利用内置的环绕声集成功能。
Android XR 中提供的空间音频类型
Android XR 支持位置音频、立体声音频、环绕声音频和 Ambisonic 音频。
定位音频
位置音频可以定位为从 3D 空间中的特定点播放。 例如,您可以在虚拟环境的角落放置一只正在吠叫的狗的 3D 模型。您可以让多个实体从各自的位置发出声音。如需渲染位置音频,文件必须是单声道或立体声。
空间化立体声和环绕声
所有 Android 媒体格式均支持定位音频、立体声和环绕声。
立体声音频是指具有两个声道的音频格式,而环绕声音频是指具有两个以上声道的音频格式,例如 5.1 环绕声或 7.1 环绕声配置。每个声道的声音数据都与一个扬声器相关联。例如,在以立体声播放音乐时,左侧音箱声道发出的乐器轨道可能与右侧音箱声道发出的不同。
环绕声通常用于电影和电视节目中,通过使用多个扬声器声道来增强真实感和沉浸感。例如,对话通常通过中置扬声器声道播放,而直升机飞行的声音可能会依次使用不同的声道,营造出直升机在 3D 空间中飞行的感觉。
Ambisonic 音频
全景声 (Ambisonic) 是一种音频格式,可为用户提供身临其境的音景。将环绕立体声用于背景环境声音或您想要复制环绕听众的完整球形声场的其他场景。Android XR 支持一阶、二阶和三阶环绕声中的 AmbiX 环绕声格式。我们建议使用 Opus (.ogg
) 和 PCM/Wave (.wav
) 文件类型。
将空间音频与 Jetpack SceneCore 搭配使用
使用 Jetpack SceneCore 实现空间音频涉及检查空间功能并选择用于加载空间音频的 API。
检查空间功能
在使用空间音频功能之前,请检查 Session
是否支持空间音频。在以下部分的所有代码段中,系统都会先检查功能,然后再尝试播放空间化音频。
加载空间音频
您可以使用以下任一 API 加载空间音频,以便在 Jetpack SceneCore 中使用。
SoundPool
:非常适合大小小于 1 MB 的短音效,它们会提前加载,并且可以重复使用。 这是加载位置音频的绝佳方式。ExoPlayer
:非常适合加载立体声和环绕声内容,例如音乐和视频。还允许进行后台媒体播放。MediaPlayer
:提供加载全方位立体声的最简单方式。AudioTrack
:可提供对音频数据加载方式的最大控制权。 允许直接写入音频缓冲区,或者写入您合成或解码的自有音频文件。
向应用添加位置音频
位置音源由 PointSourceParams
和关联的 Entity
定义。Entity
的位置和方向决定了 PointSourceParams
在 3D 空间中的渲染位置。
位置音频示例
以下示例将音效音频文件加载到声音池中,并在 Entity
的位置播放该文件。
// Check spatial capabilities before using spatial audio if (session.scene.spatialCapabilities .hasCapability(SpatialCapabilities.SPATIAL_CAPABILITY_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 自动空间化音频的移动应用。
对于立体声,音箱放置方式与环绕声类似,只是左声道和右声道分别位于面板的左侧和右侧。
如果您有多个面板,并且想要选择哪个面板发出音频,或者想要让立体声或环绕声相对于另一个 Entity
进行渲染,则可以使用 PointSourceAttributes
来定义中央声道的所在位置。剩余频道将按前文所述放置。在这些情况下,您还必须使用 MediaPlayer
。
当用户在空间中移动时,立体声和环绕声虚拟扬声器会移动并调整,以确保扬声器始终处于最佳位置。
如果您已配置 MediaPlayer
或 ExoPlayer
以便在应用进入后台时继续播放立体声或环绕声,则虚拟扬声器定位会在应用进入后台时发生变化。由于没有面板或其他空间点来锚定声音,因此空间音频会随用户移动(换句话说,它会“锁定头部”)。
环绕声示例
以下示例使用 MediaPlayer
加载 5.1 音频文件,并将该文件的中置声道设置为 Entity
。
// Check spatial capabilities before using spatial audio if (session.scene.spatialCapabilities.hasCapability(SpatialCapabilities.SPATIAL_CAPABILITY_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 }
代码要点
- 与位置音频示例中一样,第一步是使用
spatialCapabilities
检查空间音频功能是否可用。 - 将
contentType
设置为AudioAttributes.CONTENT_TYPE_MUSIC
,并将使用情况设置为AudioAttributes.USAGE_MEDIA
,可让系统将此音频文件视为环绕声。
向应用添加全景声场
播放环境立体声声场的最简单方法是使用 MediaPlayer
加载文件。由于全方位立体声适用于整个声场,因此您无需指定 Entity
来提供位置。您需要创建一个 SoundFieldAttributes
实例,并使用适当的环绕立体声阶数指定声道数。
Ambionics 示例
以下示例使用 MediaPlayer
播放全景声场。
// Check spatial capabilities before using spatial audio if (session.scene.spatialCapabilities.hasCapability(SpatialCapabilities.SPATIAL_CAPABILITY_SPATIAL_AUDIO)) { // The session has spatial audio capabilities val soundFieldAttributes = SoundFieldAttributes(SpatializerConstants.AMBISONICS_ORDER_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 发出信号,表明声场文件定义了四个声道。