【iOS ARKit】播放3D音频

3D音频

       在前面系列中,我们了解如何定位追踪用户(实际是定位用户的移动设备)的位置与方向,然后通过摄像机的投影矩阵将虚拟物体投影到用户移动设备屏幕。如果用户移动了,则通过VIO 和 IMU更新用户的位置与方向信息,更新投影矩阵,这样就可以把虚拟物体固定在空间的某点上(这个点就是锚点),从而达到以假乱真的视觉体验。

      3D音效处理的目的是让用户进一步相信AR 应用虚拟生成的数字世界是真实的,营造沉浸的AR体验。事实上,3D音效在电影、电视、电子游戏中被广泛应用,但在AR 场景中,3D声音的处理有其特别之处,类似于电影采用的技术并不能很好地解决 AR中3D音效的问题。

     在电影院中,观众的位置是固定的,因此可以通过在影院的四周都加装上音响设备,通过设计不同位置音响设备上声音的大小和延迟,就能给观众营造逼真的3D 声音效果。经过大量的研究与努力,人们根据人耳的结构与声音的传播特性开发出了很多技术,可以只用两个音响或者耳机就能模拟出3D音效,这种技术叫双耳声(Binaural Sound),它的技术原理如图11-2所示。

在图11-2中,从声源发出来的声音会直接传播到左耳和右耳,但因为左耳离声源近,所以声音会先到达左耳再到达右耳,由于在传播过程中的衰减,左耳听到的声音要比右耳大,这是直接的声音信号,大脑会接收到两只耳朵传过来的信号。同时,从声源发出的声音也会被周围的物体反射,这些反射与直接信号相比有一定的延迟并且音量更小,这些是间接的声音信号。大脑会采集到直接信号与所有的间接信号并比较从左耳与右耳采集的信号,经过分析计算,从而达到定位声源的效果。在了解大脑的工作模式后,就可以通过算法控制两个音响或者耳机的音量与延迟来达到模拟3D声源的效果,让大脑产生出虚拟的3D声场效果。

3D声场原理

      3D声场,也称为三维音频、虚拟3D音频、双耳音频等,它是根据人耳对声音信号的感知特性,使用信号处理的方法对到达两耳的声音信号进行模拟,以重建复杂的空间声场。

      通俗地说就是把耳朵以外的世界看作一个系统,对任意一个声音源,在耳膜处接收到信号后,三维声场重建就是把两个耳朵接收到的声音尽可能准确地模拟出来,让人产生听到三维音频的感觉。

      如前所述,当人耳在接收到声源发出的声音时,人的耳廓、耳道、头盖骨、肩部等对声波的折射、绕射和衍射及鼓膜接收到的信息会被大脑所接收,大脑通过经验对声音的方位进行判断。与大脑工作原理类似,在计算机中通过信号处理的数学方法,构建头部相关传输函数(HeadRelated Transfer Functions, HRTF),根据多组的滤波器计算人耳接收到的声源的“位置信息”。

    目前3D声场重建技术口冬比校成戴们不时加送之上术都假设用户是静止的(或者说与用户位置无关),而在 AR应用中,情况却有很大不同,AR应用的用户是随时移动的,这意味着用户周围的3D声音也需要调整,这一特殊情况导致目前的3D声场重建技术在AR应用时失效。

RealityKit 中的 3D 音效

      ARKit 通过世界跟踪功能定位声源位置,然后根据用户与声源的相对位置和方向自动混音,将3D音频技术带人 AR中。在 AR场景中放置一个声源,当用户接近或远离时,声音音量大小会自动增加或减弱,当用户围绕声源旋转时,声音也会呈现沉浸式的3D效果。在 RealityKit 中,使用3D音效的典型代码如代码如下所示,稍后我们将对代码进行详细解析。

//
//  Audio3DView.swift
//  ARKitDeamo
//
//  Created by zhaoquan du on 2024/3/22.
//import SwiftUI
import ARKit
import RealityKit
import Combinestruct Audio3DView: View {var body: some View {Audio3DViewContainer().navigationTitle("3D音频").edgesIgnoringSafeArea(.all)}
}struct Audio3DViewContainer:UIViewRepresentable {func makeUIView(context: Context) -> some ARView {let arView = ARView(frame: .zero)let config = ARWorldTrackingConfiguration()config.planeDetection = .horizontal//createPlane(arView: arView)arView.session.run(config)arView.createAudioPlane()return arView}func updateUIView(_ uiView: UIViewType, context: Context) {}static var audioEvent : Cancellable!func createPlane(arView:ARView){let planAnchor = AnchorEntity(plane: .horizontal)let boxMesh = MeshResource.generateBox(size: 0.2)let boxMaterial = SimpleMaterial(color: .red, isMetallic: true)let boxEntity = ModelEntity(mesh: boxMesh, materials: [boxMaterial])guard  let audio = try? AudioFileResource.load(named: "fox.mp3",in: .main, inputMode: .spatial,loadingStrategy: .preload,shouldLoop: false) else {return}let audioControler = boxEntity.prepareAudio(audio)audioControler.play()boxEntity.generateCollisionShapes(recursive: false)planAnchor.addChild(boxEntity)arView.scene.addAnchor(planAnchor)arView.installGestures(for: boxEntity)Audio3DViewContainer.audioEvent = arView.scene.subscribe(to: AudioEvents.PlaybackCompleted.self) { event inprint("音频播放完毕")}}
}
var audioEvent : Cancellable!
extension ARView{func createAudioPlane(){do{let planeAnchor = AnchorEntity(plane:.horizontal)let boxMesh = MeshResource.generateBox(size: 0.2)let boxMaterial = SimpleMaterial(color:.red,isMetallic: true)let boxEntity = ModelEntity(mesh:boxMesh,materials:[boxMaterial])let audio = try AudioFileResource.load(named:"fox.mp3",in:.main,inputMode: .spatial,loadingStrategy: .preload,shouldLoop: false)boxEntity.playAudio(audio)let audioController = boxEntity.prepareAudio(audio)audioController.play()boxEntity.generateCollisionShapes(recursive: false)planeAnchor.addChild(boxEntity)self.scene.addAnchor(planeAnchor)self.installGestures(.all,for:boxEntity)audioEvent = self.scene.subscribe(to: AudioEvents.PlaybackCompleted.self){ event inprint("音频播放完毕")}}catch{print("Error Loading audio file")}}
}#Preview {Audio3DView()
}

     编译运行 AR 应用,使用耳机(注意耳机上的左右耳塞勿戴反,一般会标有I.和 R字样)或者双通道音响体验3D音效,在检测到的平面放置虛拟立方体对象后,移动手机或者旋转手机朝向,体验在 AR场景中声源定位的效果。

从代码清单 11-3中我们可以看出,在 RealityKit 中使用3D音频分为3步:

(1) 加载音频。

(2)设置音频播放参数。

(3)将音频放置到 AR 场景中并播放。

下面我们针对这3个步骤进行详细学习。在 RealityKit 中使用音频,必须将音频加载为 AudioResource(或者其子类 AudioFileResource)类型对象才能正确播放,通常使用 AudioFileResource 类将音频从文件系统或者 URL 加载到内存中,该类有4个方法,可以同步/异步从文件/URL 中加载音频,如表11-2所示。

表 11-2 AudioFileResource 类加载音频方法

方法

描述

load (named: String, in: Bundle?,inputMode: AudioResource. InputMode,loadingStrategy: AudioFileResource. LoadingStrategy,shouldLoop: Bool)—>

AudioFileResource

同步从程序 Bundle 中加载音频

loadAsync(named: String,in: Bundle?, inputMode: AudioResource. InputMode,loadingStrategy: AudioFileResource. LoadingStrategy,shouldLoop: Bool)—>

LoadRequest < AudioFileResource >

异步从程序 Bundle 中加载音频

load ( contentsOf: URL, withName: String?, inputMode: AudioResource.

InputMode,loadingStrategy: AudioFileResource. LoadingStrategy,shouldLoop:

Bool) -> AudioFileResource

同步从 URL 中加载音频

load Async(contentsOf: URL, withName: String?, inputMode: AudioResource.

InputMode,loadingStrategy: AudioFileResource. LoadingStrategy,shouldLoop:

Bool) -> LoadRequest < AudioFileResource >

异步从 URL 中加载音频

       在 RealityKit 中加载音频与加载模型一样,每一种同步加载方法都有对应的异步加载方法。加载方法中的参数因加载方法不同而不同,基本的参数及其意义如表11-3所示。

表11-3 加载音频方法中各参数的意义

描述

方法

named

从 Bundle 中加载时文件路径与名称

contentsOf

从 URL 中加载时的 URL 地址

withName

从 URL 中加载时的音频名称

in

从程序 Bundle 中加载时音频所在 Bundle 名称

shouldLoop

布尔值,是否循环播放

loadingStrategy

AudioFileResource. LoadingStrategy 枚举类型,指定加载音频时的策略,共有两个枚举值:preload

(预加载音频,在使用之前将音频加载到内存中)、stream(流媒体编码,边加载边播放)。通常在使用时,preload 适合短小、内存占用少,播放频度高的音频,而 stream适合较长、播放频度低的音频

inputMode

AudioResource. InputMode 枚举类型,指定3D音频类型,共有3个枚举值:nonSpatial(不使用3D音效)、spatial(使用空间音效)、ambient(环境音效,声音不会随距离发生音量变化,但声音可以反映方向变化,如用户围绕声源转动时,音效会发生变化)

      在加载音频完成后,可以通过实体对象的 prepareAudio(_:)方法获取一个 AudioPlaybackController 类型的音频控制器,利用该控制器可以使用其 play()、pause()、stop()方法控制音频的播放,可以通过isPlaying 属性获取音频的播放状态,还可以设置音频的播放增益(gain)、速度(speed)、混音(reverb SendL.evel),及衰減(fade()方法)和音频播放完后的回调(completionHandler),可以满足音频使用的各类个性化需求。

     通常在 AR中使用3D,音频,需要将音频绑定到实体对象(Entity)上,当实体对象放置在场景中时,实体对象所在的空间位置即为声源位置,RealityKit 会根据用户设备所在空间位置与声源位置进行3D音效模拟,营造沉浸式的声场效果。

     利用 AudioPlaybackController 类可以很方便地控制音频的播放,而且可以重复地进行暂停、播放等操作,但如果只需要一次性地播放,也可以不使用该类,而直接使用boxEntity. playAudio(audio),这种方法更简洁,当音频播放完后即结束,特别适合3D物体音效模拟,如子弹击中柽物时的音频播放。

     在使用 AudioPlaybackController 类控制音频播放时,可以通过其 completionHandler 属性设置音频播放完后的回调函数。除此之外,也可以通过订阅 AudioEvents 事件进行后续处理,目前,音频只有一个AudioEvents. PlaybackCompleted 事件,即音频播放完毕事件。

     在订阅 AudioEvents事件时有两点需要注意,一是保存事件订阅的引用,不然无法捕获事件,具体可参阅第2章相关内容:三是只有当 shouldLoop 设置为 false(即不循环播放)时,才会触发 AudioEvents.PlaybackCompleted 事件。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/770426.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

钡铼技术R40路由器助力构建无人值守的智能化污水处理厂

钡铼技术R40路由器作为智能化污水处理厂的关键网络设备&#xff0c;发挥着至关重要的作用&#xff0c;助力构建无人值守的智能化污水处理系统。在现代社会&#xff0c;污水处理是城市环境保护和可持续发展的重要组成部分&#xff0c;而智能化污水处理厂借助先进的技术和设备&am…

后端常问面经之Java基础

基本数据类型 Java中有8种基本数据类型&#xff1a; 6种数字类型&#xff1a; 4种整数型&#xff1a;byte、short、int、long 2种浮点型&#xff1a;float、double 1种字符类型&#xff1a;char 1种布尔类型&#xff1a;boolean 数据类型的默认值以及所占空间如下&#x…

由浅到深认识Java语言(25):正则表达式

该文章Github地址&#xff1a;https://github.com/AntonyCheng/java-notes 在此介绍一下作者开源的SpringBoot项目初始化模板&#xff08;Github仓库地址&#xff1a;https://github.com/AntonyCheng/spring-boot-init-template & CSDN文章地址&#xff1a;https://blog.c…

2024 Python3.10 系统入门+进阶(三):Python变量类型和运算符

目录 一、Python变量的定义和使用二、Python整数类型&#xff08;int&#xff09;详解三、Python小数/浮点数&#xff08;float&#xff09;类型详解四、Python复数类型(complex)详解---了解五、Python字符串详解(包含长字符串和原始字符串)5.1 处理字符串中的引号5.2 字符串的…

后端常见面经之JVM

JVM组成 有垃圾回收的是哪些地方&#xff1f; 垃圾回收主要是针对堆内存中的对象进行的&#xff0c;包括以下几个方面&#xff1a; 堆内存&#xff1a;垃圾回收主要针对堆内存中不再被引用的对象进行回收&#xff0c;包括新生代和老年代中的对象。 永久代/元空间&#xff1a…

Selenium 自动化 —— 切换浏览器窗口

更多内容请关注我的 Selenium 自动化 专栏&#xff1a; 入门和 Hello World 实例使用WebDriverManager自动下载驱动Selenium IDE录制、回放、导出Java源码浏览器窗口操作 平时我们在使用浏览器时&#xff0c;通常会打开多个窗口&#xff0c;然后再多个窗口中来回切换&#xf…

众邦科技CRMEB商城商业版任意文件写入getshell 0day

代码审计 接口&#xff1a;/adminapi/system/crud 处理的代码如下 public function save(SystemCrudDataService $service, $id 0){$data $this->request->postMore([[pid, 0],//上级菜单id[menuName, ],//菜单名[tableName, ],//表名[modelName, ],//模块名称[table…

手机网页视频无水印采集工具|抖音视频关键词批量下载软件

轻松获取手机网页视频无水印的神器&#xff01;让您随心所欲畅享精彩视频内容&#xff01; 随着网络视频的盛行&#xff0c;如何方便快捷地获取您感兴趣的视频内容成为一大需求。现推出一款手机网页视频无水印采集工具&#xff0c;功能强大&#xff0c;操作简便&#xff0c;助您…

[项目前置]如何用webbench进行压力测试

测试软件 采用webbench进行服务器性能测试。 Webbench是知名的网站压力测试工具&#xff0c;它是由Lionbridge公司开发。 webbench的标准测试可以向我们展示服务器的两项内容&#xff1a; 每秒钟相应请求数 和 每秒钟传输数据量 webbench测试原理是&#xff0c;创建指定数…

03-CSS盒模型(padding、margin、opactiy、cursor、display、css3前缀)

一、CSS盒模型 CSS 盒模型规定了处理元素内容、内边距、边框 和 外边距 的方式。最内部分是元素内容&#xff0c;直接包围内容的是内边距。内边距呈现了元素的背景。内边距的边缘是边框。边框以外是外边距。 1.元素的尺寸: height 设置元素的高度。属性值&#xff1a;auto&am…

开源流程图表库(01):Mermaid.js生成流程图、时序图、甘特图等

一、Mermaid.js的特点 Mermaid.js是一个用于生成流程图、时序图、甘特图等各种图表的开源库。它使用简洁的文本语法来描述图表结构&#xff0c;并将其转换为可视化的图形。 Mermaid.js的主要特点包括&#xff1a; 简洁易用&#xff1a;Mermaid.js使用简单的文本语法来描述图表…

FPGA电平标准

1.LVTTL&#xff1a;&#xff08;3.3v&#xff09; 2.LVCOMS&#xff1a;&#xff08;1.8v&#xff09; 3.LVDS&#xff08;1.8v&#xff09;&#xff1a;LVDS_25&#xff08;2.5v&#xff09; 4&#xff1a;如果是ddr3与fpga相连接fpga的vcco推荐&#xff08;1.5v&#xff09;…

Qt实现简易的多线程TCP服务器(支持多个客户端连接)附源码

目录 一.UI界面的设计 二.服务器的启动 三.实现自定义的TcpServer类 1.在widget中声明自定义TcpServer类的成员变量 2.在TcpServer的构造函数中对于我们声明的m_widget进行初始化&#xff0c;m_widget我们用于后续的显示消息等&#xff0c;说白了就是主界面的更新显示等 …

卡尔曼滤波器_3.3

目标 了解卡尔曼滤波在目标跟踪中的应用知道卡尔曼滤波的原理&#xff1a;预测阶段和更新阶段 卡尔曼滤波器&#xff08;Kalman Filter&#xff09;是一种利用线性系统理论和概率统计原理&#xff0c;对含有噪声的动态系统进行状态估计的最优滤波器。它由匈牙利裔美国电气工程…

web前端之3D标签动画、指定范围的随机数、动态设置css变量、文档片段对象、反向动画

MENU 效果图htmlJavaScriptstyle 效果图 html <div class"container"></div>JavaScript // 祝词 var words [健康码常绿,股票飙红,生意兴隆,财源广进,心想事成,永远十八,身体健康,大富大贵,大吉大利,万事如意,美梦成真,吉祥如意,鸿运当头,五福临门,吉…

正式发布:VitePress 1.0 现代化静态站点生成器!

大家好&#xff0c;我是奇兵&#xff0c;今天介绍一下现代化静态站点生成器!&#xff0c;希望能帮到大家。 3 月 21 日&#xff0c; 由 Vue 团队出品的现代化静态站点生成器 VitePress 正式发布 1.0 版本&#xff01;它专为构建快速、以内容为中心的网站而生&#xff0c;能够轻…

Apache Spark

一、Apache Spark 1、Spark简介 Apache Spark是用于大规模数据 (large-scala data) 处理的统一 (unified) 分析引擎。 Spark官网 Spark最早源于一篇论文Resilient Distributed Datasets: A Fault-Tolerant Abstraction for In-Memory Cluster Computing,该论文是由加州大学柏…

C# 将 Word 转文本存储到数据库并进行管理

目录 前言 1. 创建数据库表格 2. 安装必需的 NuGet 包 3. 转换 Word 文档为文本 4. 将文本存储到数据库 5. 完整示例 前言 C# 是一种通用的编程语言&#xff0c;可以用于开发各种类型的应用程序&#xff0c;包括处理文本和数据库管理。在这篇文章中&#xff0c;我将向您…

问卷调查数据分析指南!掌握方法,精准把握用户需求!

“我们可以用自定义报表、交叉报表、自定义过滤器等放方式进行问卷调查数据分析。“ 问卷调查的过程中&#xff0c;问卷设计、问卷分发、问卷收集可能都不是让我们最头疼的。经过几天的奋战&#xff0c;终于拿到了数据&#xff0c;但是问题也随之而来。收集上来的问卷信息&…

linux操作系统——线程控制+线程封装

1.理解用户级线程 我们前面用到的所有跟线程有关的接口全部都不是系统直接提供的接口&#xff0c;而是原生线程库pthread提供的接口。我们前面谈到了由于用户只认线程&#xff0c;而linux操作系统是通过用轻量级进程模拟线程&#xff0c;并不是真正的线程&#xff0c;所以linu…