- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我在 iOS 游戏应用程序中使用 AVAudioEngine 处理音频。我遇到的一个问题是 AVAudioPlayerNode.play() 需要很长时间才能执行,这在游戏等实时应用程序中可能是个问题。
play() 只是激活播放器节点——您不必在每次播放声音时都调用它。因此,不必经常调用它,但必须偶尔调用它,例如在最初激活播放器时,或在它被停用后(在某些情况下会发生)。即使只是偶尔调用,执行时间长也会成为一个问题,尤其是当您需要同时对多个玩家调用 play() 时。
play() 的执行时间似乎与 AVAudioSession.ioBufferDuration 的值成正比,您可以使用 AVAudioSession.setPreferredIOBufferDuration() 请求更改该值。这是我用来测试的一些代码:
import AVFoundation
import UIKit
class ViewController: UIViewController {
private let engine = AVAudioEngine()
private let player = AVAudioPlayerNode()
private let ioBufferSize = 1024.0 // Or 256.0
override func viewDidLoad() {
super.viewDidLoad()
let audioSession = AVAudioSession.sharedInstance()
try! audioSession.setPreferredIOBufferDuration(ioBufferSize / 44100.0)
try! audioSession.setActive(true)
engine.attach(player)
engine.connect(player, to: engine.mainMixerNode, format: nil)
try! engine.start()
print("IO buffer duration: \(audioSession.ioBufferDuration)")
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if player.isPlaying {
player.stop()
} else {
let startTime = CACurrentMediaTime()
player.play()
let endTime = CACurrentMediaTime()
print("\(endTime - startTime)")
}
}
}
以下是我使用缓冲区大小 1024(我相信这是默认值)获得的 play() 的一些示例计时:
0.0218
0.0147
0.0211
0.0160
0.0184
0.0194
0.0129
0.0160
以下是使用 256 缓冲区大小的一些示例计时:
0.0014
0.0029
0.0033
0.0023
0.0030
0.0039
0.0031
0.0032
正如您在上面看到的,对于 1024 的缓冲区大小,执行时间往往在 15-20 毫秒范围内(大约 60 FPS 的完整帧)。缓冲区大小为 256,约为 3 毫秒 - 还不错,但当您每帧只有约 17 毫秒的时间可以使用时,它仍然很昂贵。
这是在运行 iOS 12.4.2 的 iPad Mini 2 上进行的。这显然是一台旧设备,但我在模拟器上看到的结果似乎成正比,因此它似乎与缓冲区大小和函数本身的行为有关,而不是与所使用的硬件有关。我不知道引擎盖下发生了什么,但 play() 似乎可能会阻塞直到下一个音频周期开始或类似的事情。
请求较小的缓冲区大小似乎是部分解决方案,但存在一些潜在的缺点。根据文档 here ,较小的缓冲区大小可能意味着从文件流式传输时更多的磁盘访问,并且无论如何,请求可能根本不会被兑现。另外,here ,有人报告与低缓冲区大小相关的播放问题。考虑到所有这些因素,我不愿意将此作为解决方案。
这让我的 play() 执行时间在 15-20 毫秒范围内,这通常意味着在 60 FPS 时会丢失帧。如果我安排一次只调用 play() 一次,而且很少调用,也许它不会引人注意,但这并不理想。
我已经在其他地方搜索过相关信息并询问过这个问题,但似乎在实践中遇到这种行为的人并不多,或者这对他们来说不是问题。
AVAudioEngine 旨在用于实时应用程序,因此如果我是正确的,AVAudioPlayerNode.play() 会阻塞与缓冲区大小成比例的大量时间,这似乎是一个设计问题。我意识到这可能不是很多人正在处理的问题,但我在这里发帖询问是否有人遇到过 AVAudioEngine 的这个特定问题,如果是的话,是否有人可以提供任何见解、建议或解决方法。
最佳答案
我对此进行了相当彻底的调查。这是我的发现。
现在已经在各种设备和 iOS 版本(包括撰写本文时的最新版本 13.2)上测试了该行为,并让其他人也对其进行了测试,我目前的结论是,执行时间较长AVAudioPlayerNode.play()
是固有的,没有明显的解决方法。正如我在原来的帖子中所指出的,可以通过请求较短的缓冲区持续时间来减少执行时间,但正如前面所讨论的,这似乎不是一个可行的解决方案。
我从可靠的消息来源听说,在后台线程上调用 play()
(例如使用 Grand Central Dispatch)应该是安全的,实际上这将是解决问题的一种方法。然而,尽管在不同线程上调用 play()
(或其他与 AVAudioEngine
相关的函数)在技术上可能是安全的,但我对这是否是一个好的做法持怀疑态度。想法(下面有进一步解释)。
据我所知,文档没有说明这一点,但是 AVAudioEngine
会在各种情况下抛出 NSException
,如果没有特殊处理,将会导致在 Swift 中终止应用程序。
导致抛出 NSException
的原因之一是,如果您在引擎未运行时调用 AVAudioPlayerNode.play()
。显然,如果您只需要担心自己的代码,则可以采取措施确保不会发生这种情况。
但是,iOS 本身有时会自行停止引擎,例如当发生音频中断时。如果您随后在重新启动引擎之前调用 play()
,则会抛出 NSException
。如果对 play()
的所有调用都在主线程上,则很容易避免此错误,但多线程使问题变得复杂,并且似乎可能会带来意外调用 play() 的风险
发动机停止后。尽管可能有办法解决这个问题,但多线程似乎会带来不良的复杂性和脆弱性,因此我选择不追求它。
我目前的策略如下。由于前面讨论的原因,我没有使用多线程。相反,我正在尽一切努力减少对 play()
的调用次数,无论是整体调用还是每帧调用。除其他外,这包括仅支持立体声音频(出于各种原因,支持单声道和立体声可能会导致更多地调用 play()
,这是不希望的)。
最后,我还研究了 AVAudioEngine
的替代方案。 OpenAL 在 iOS 上仍然受支持,但已被弃用。使用低级 API(例如音频队列服务或音频单元)的自定义实现是可能的,但并不简单。我还研究了一些开源解决方案,但我研究的选项本身在底层使用了 AVAudioEngine
,因此遇到了相同的问题,和/或有其他缺点或自身的限制。当然,也有可用的商业选项,这可能为一些开发人员提供解决方案。
关于iOS - AVAudioPlayerNode.play() 执行速度很慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58157605/
所以我的应用程序的评论显示在我的游戏控制台中,但是由于某种原因,当我在 google Play 商店上访问我的应用程序链接并以普通用户的身份查看它时,我没有收到新评论的通知,我的显示了 13 条评论,
一旦我在 2.3 play 项目上运行 'sbt compile',我就不能再使用 'sbt compile' 来编译任何 Play 2.2.x 项目。这是我运行 sbt 命令时的错误。 [info]
我有点困惑,想得到一个解释。 我正在使用 Java Play 2 和 Akka Actor 。我使用 play run 启动系统。 不过,我刚刚看到一个视频,使用了命令: play akka star
当我玩游戏时,我遇到两个选择play.api.mvc和 play.mvc包裹 有什么不同? 最佳答案 从戏! 2 文档: The API available in the play.api packa
从 2.3 迁移到 2.4 后,我收到此错误。我应该使用的正确导入语句是什么? error: value routesImport is not a member of object play.Pla
所以我在 google play 上有一个应用程序已经将近 6 个月了,最近两个月我更新了我的应用程序屏幕截图,从那时起,每次我更新我的应用程序时,我都开始收到应用程序拒绝。 上次我提出上诉并被接受,
我有以下代码: Snapshots.OpenSnapshotResult result; result = Games.Snapshots.open(googleApiClient, "save",
在过去 72 小时内,我为 Google Play 开发者计划支付的 Google 电子钱包付款显示为“您的购买正在处理中”。我知道这可能需要长达 48 小时,但这是他们处理时间最坏情况之后的一整天。
我在 Play 商店发布了应用程序,我不知道为什么应用程序显示预注册,我想为我的用户提供直接下载选项。伸出援手将不胜感激。 最佳答案 instructions for pre-registration
我有一个 PHP 后端,它与 Google Play 服务集成以验证从 APP 进行的购买。购买信息返回收据和签名,我需要验证购买是否正确。 我收到: { ...rest of the data
我在 Google Play Developer Console 上创建了我的 Android 应用程序的草稿。我已经填写了所有需要的信息。必需的步骤之一是“内容评级”。我已填写表格以自动分配 Goo
我已经设置好了 com.typesafe.play play_2.13 2.7.4 在项目 pom.xml 中。但是,当我尝试遵循this tutorial时,语句 pla
我在 Play 商店上发布了一个应用程序,并收到了一些评论。在Google Play开发者控制台中,我在一些评论中看不到应用程序的版本。这是我在“应用程序”标题下找到的内容。 版本代码 — 版本名称
假设 A 是所有者。我希望我们团队的 B、C 和 D 用户能够上传我们应用程序的新版本。这可能吗?来自 this我不太清楚用户有什么样的权限。如果有人对这部分有任何经验,欢迎。 最佳答案 您需要 go
我正在尝试将应用重新提交到 Google Play,但我似乎可以找到一种技术上的方法来实现此目的。 我对“您的应用主要针对 COPPA 定义的 13 岁以下 child 吗?”的回答是肯定的,然后在不
我想分享一下我在分析 Google Play 控制台的新功能时遇到的情况,并尝试找到解决方案。 正如你们许多人可能已经知道的那样,Google 已在 Google Play 控制台上发布了更新并引入了
我有两个用 playframework 编写的应用程序。我想加入另一个。我有一个数据库,我想在它们之间共享我的登录类。应用程序对类、方法、变量使用不同的名称。 我怎样才能实现它?我应该创建 jar 版
对于我的硕士论文,我需要自动将来自 Google Play 的不同 Android 应用程序的信息写入一个文本文件。所以我使用 perl 脚本语言来实现这种自动化。我的 perl 脚本可以在 Goog
我想测试子项目是如何工作的,尤其是 routes在主项目 ( this was not visible before ) 中考虑了子项目的数量。 我在这里阅读了文档: https://github.c
我正在使用 Play 框架 2.1.2,我有一个 handlere 方法返回一个 Promise,如 Play 的 2.1.2 documentation 中所述 但是 Play 抛出编译错误说: C
我是一名优秀的程序员,十分优秀!