借助 Jetpack SceneCore 中的空间音频功能,您可以在 Android XR 应用中打造沉浸式音频体验。
空间音效可模拟用户在 3D 环境中感知声音的方式。它会营造出声音从各个方向(包括用户上方和下方)传来的感觉。系统通过在 3D 空间中的特定位置模拟一个或多个“虚拟扬声器”来实现此效果。
未针对 Android XR 设计或修改的现有应用会在 Android XR 中自动实现音频空间化。当用户在空间中移动时,所有应用音频都将从应用界面所渲染到的面板发出。例如,如果时钟应用中的计时器响铃,音频听起来会像是从应用面板位置传来的。Android XR 会自动改变声音,以实现位置逼真感。例如,应用面板与用户之间的感知距离会微妙地影响音量,从而增强真实感。
如需详细了解现有应用如何呈现空间音频,请参阅本页面上的向应用添加立体声和环绕声。
如果您要针对 XR 优化应用,Jetpack SceneCore 可提供用于高级空间音频自定义的工具。您可以精确定位 3D 环境中的声音,使用全方位音频实现逼真的声场,并利用内置的环绕声集成。
Android XR 中可用的空间音频类型
Android XR 支持位置音频、立体声音频、环绕声音频和全景声音频。
定位音频
位置音频可以定位为从 3D 空间中的特定点播放。 例如,您可以在虚拟环境的角落放置一只正在吠叫的狗的 3D 模型。您可以让多个实体从各自的位置发出声音。如需渲染位置音频,文件必须是单声道或立体声。
空间化立体声和环绕声
所有 Android 媒体格式均支持定位音频、立体声和环绕声。除了这些格式之外,Android XR 设备还可能支持 Dolby Atmos、Dolby Digital 和 Dolby Digital+ 音频格式。
立体声音频是指具有两个声道的音频格式,而环绕声音频是指具有两个以上声道的音频格式,例如 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:可最大限度地控制音频数据的加载方式。 允许直接写入音频缓冲区,或者写入您合成或解码的自有音频文件。
检查媒体格式支持
某些媒体格式受 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 自动空间化音频的移动应用。
对于立体声,音箱放置方式与环绕声类似,只是只有左声道和右声道分别位于面板的左侧和右侧。
如果您有多个面板,并且想要选择哪个面板发出音频,或者想要让立体声或环绕声相对于另一个 Entity 进行渲染,可以使用 PointSourceAttributes 定义中央声道的所在位置。剩余频道将按前文所述放置。在这些情况下,您还必须使用 MediaPlayer。
当用户在空间中移动时,立体声和环绕声虚拟扬声器会移动并调整,以确保扬声器始终处于最佳位置。
如果您已配置 MediaPlayer 或 ExoPlayer 以便在应用进入后台时继续播放立体声或环绕声,那么当应用进入后台时,虚拟扬声器定位将会改变。由于没有面板或空间中的其他点来锚定声音,因此空间音频会随用户移动(换句话说,它是“锁定头部”的)。
环绕声示例
以下示例使用 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 }
代码要点
- 与位置音频示例中一样,第一步是使用
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.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 表明,声场文件定义了四个声道。