4

我定期弹奏一些音符。每一个都会延迟随机数毫秒,从而产生不规则的不规则效果。我如何解决它?

注意:我可以接受一些延迟,只要它是一致的。

“实现您自己的小型 SoundManager2 替换,针对时间敏感的播放进行优化”类型的答案是可以的,如果您知道如何做到这一点:) 但我现在试图避免在 Flash 中重写我的整个应用程序。

有关具有零可听延迟的应用程序示例,请参阅基于闪存的ToneMatrix

测试用例(在此处实时查看以 zip 格式获取):

<head>
<title></title>
<script type="text/javascript" 
src="http://www.schillmania.com/projects/soundmanager2/script/soundmanager2.js">
</script>
<script type="text/javascript">
soundManager.url = '.'
soundManager.flashVersion = 9
soundManager.useHighPerformance = true
soundManager.useFastPolling = true
soundManager.autoLoad = true

function recur(func, delay) {
    window.setTimeout(function() { recur(func, delay); func(); }, delay)
}

soundManager.onload = function() {
    var sound = soundManager.createSound("test", "test.mp3")
    recur(function() { sound.play() }, 300)
}
</script>
</head>
<body>
</body>
</html>
4

4 回答 4

10

我知道这不是您想听到的答案,但是无论您是否编写了自己的 Flash 库来播放声音,都无法阻止这种情况。

对于每个说“它对我来说很好!”的人。尝试在海报的演示播放时调整浏览器窗口的大小或移动浏览器窗口。您将听到的不仅仅是细微的延迟。这在 Firefox 和 IE 中最为明显,但即使是 Chrome 也会体验到它。

更糟糕的是,如果您在浏览器窗口的关闭框上单击并按住鼠标,声音会完全停止,直到您松开鼠标(您可以在关闭框之外释放它而不是实际关闭窗口,仅供参考)。

这里发生了什么?

事实证明,当您开始调整浏览器窗口大小或在浏览器窗口中移动时,浏览器会尝试将更改窗口属性的行为与跟上窗口中正在进行的 javascript 的行为进行多任务处理。它会在需要时缩短窗口。

当您在浏览器窗口中的关闭框上按住鼠标时,时间会完全停止。当您重新调整窗口大小或移动窗口时,这就是以较小的增量发生的情况:时间在 JavaScript 世界中以小的、零星的块(或大块,取决于您的机器有多慢)静止不动。

现在,您可能会说“当然,调整浏览器大小或按住关闭按钮会使浏览器暂停,但通常不会发生这种情况”。不幸的是,你会错的。

实际上,它一直在发生。我已经进行了测试,结果发现即使浏览器窗口完全静止,不触摸鼠标,也不触摸键盘,计算机上的后台进程仍然会导致“打嗝”,这意味着在短时间内(也许小到几毫秒)时间在浏览器中“静止不动”,完全随机的时间间隔在您的控制之外。

“静止不动”是什么意思?假设您有一个 setInterval() 调用(这也适用于 setTimeout)每 33 毫秒(大约每秒 30 帧)运行一次。现在,您会期望在每 33 个“真实世界”毫秒后调用您的函数。大多数时候,这是真的。

但是当“打嗝”开始发生时,您的 setInterval 调用可能会在 43 毫秒内发生。在这 10 毫秒内发生了什么?没有什么。时间暂停。浏览器上没有任何更新。如果您有声音播放,它将继续播放,但不会开始播放新的声音调用,因为根本没有执行任何 javascript。如果您有 5 个 setInterval() 函数正在运行,它们将在某个时间点全部暂停 10 毫秒。

告诉“时间静止”的唯一方法是在 setInterval 函数回调中轮询实际时间。您将能够看到浏览器大部分时间都在尝试跟上,但是当您开始调整窗口大小或做一些有压力的事情时,间隔会比平时长,但您的所有代码将保持同步(我正在使用这种技术制作游戏,所以你会看到你所有的游戏更新都是同步发生的,但只是有点卡顿)。

通常,我应该指出,这些口吃是完全不引人注意的,除非您编写一个函数来记录 setInterval 时间期间的实际时间(正如我在自己的测试中所做的那样),您甚至不会知道它。但是,如果您尝试使用重复的 play() 调用来创建某种类型的重复声音(例如 Asteriods 背景中的哔哔声),就会出现问题。

我的建议?如果你有一个你知道会循环播放的声音,给它一个较长的持续时间,也许 10 秒,你就不太可能注意到打嗝了(现在,屏幕上的图形可能仍然打嗝,但你搞砸了)。

如果您正在编写游戏并让主角用机关枪开火,请不要对 playSound('singleShot') 进行 10 次快速连续调用,对 playSound('machineGunFire10Rounds') 进行一次调用或类似的操作.

你必须做一些诡计才能绕过它,但在大多数情况下你会没事的。

似乎 Flash 小程序运行在一个进程中,该进程对常规浏览器/javascript 环境中发生的整个“时间冻结”事件的影响较小,但我仍然可以让它发生,即使在您指向 ToneMatrix 示例的链接上,通过调整或移动浏览器窗口。

但 Flash 似乎仍然比 javascript 好得多。当我不理会浏览器时,我愿意打赌 Flash 在任何时间都不会冻结,并且间隔总是按时运行。

tl;博士:

  • 你被你希望达到的目标搞砸了
  • 尝试使用一些解决方法来处理它
  • 用纯闪存重写您的项目(无 javascript)
  • 等待浏览器变得更好(Firefox 4 正在获得一个名为JaegerMonkey的新 javascript 引擎,这将很有趣)
  • 我怎么知道这一切?我已经使用 javascript、setInterval 和 soundManager/html5 音频调用进行了大量测试和记录
于 2010-05-27T20:11:13.123 回答
1

在我对你的问题的评论中,我提到我在播放你的样本时没有听到不规则的声音。这意味着我要么是“节奏失聪”,要么是您的设置中可能存在干扰良好实时性能的东西。您没有提及您的环境的任何细节,但您的计算机上运行的其他进程可能正在占用 CPU 周期,或者可能无法很好地处理声音延迟的旧版 Flash。我自己使用的是最新版本的 Flash (10.something),而您的参数需要 Flash 9。但也许我应该假设如果您足够聪明,可以使用 SoundManager2 和 StackOverflow,您就会消除这些问题。

所以这里有一些故障排除的可能性和我想到的评论:

1) SoundManager 站点有许多演示,包括JS-DOM "绘画" + Sound, V2。您是否在那里听到不规则的延迟和延迟?如果没有,也许您可​​以将他们在那里所做的事情与您正在做的事情进行比较。如果你是,那么也许看看你的机器环境。当我运行该演示时,它的响应速度非常快。(编辑:但是,仔细观察它,您可以看到笔刷印记的大小在笔划期间如何变化。由于它随鼠标事件之间的时间间隔而变化(假设您保持恒定的鼠标速度),您可以直观地看到查看鼠标事件模式中的任何异常情况。我可以看到印章大小的偶尔变化,这确实表明鼠标事件没有定期出现。这将我们带到了 Javascript 事件。

2) Javascript setTimeout()setInterval()在时间方面不是很可靠。大多数情况下,它们会在您请求的间隔的某个范围内返回,但可能会有很大的变化,通常是延迟,这使得它们不可靠。我发现在 Flash 中使用 ActionScript 时也是如此。您可能想要打印出您的 sound.play() 调用的时间,以查看异常是否是由于setTimeout/setInterval(). 如果是这种情况,您可以尝试缩短间隔,然后轮询系统时间以更接近您想要的 300 毫秒间隔。您可以使用轮询系统时间new Date().getTime(),这似乎具有毫秒精度。轮询当然是一种可怕的 hack,当它们可以用于其他用途时,它会占用周期,我一般不推荐它,但你可以尝试它是否有帮助。 编辑:这是John Resig 撰写的关于在 js 中处理输入和计时器事件的文章

3) 当 flash 播放声音时,通常会涉及延迟,只是为了让播放器“建立一个蒸汽头”并确保在下一个缓冲区请求被填充之前缓冲区中有足够的东西可以播放。在这种延迟和不间断播放的可靠性之间需要权衡。这可能是您无能为力的限制,除了“实施[ing]您自己的小型 SoundManager2 替代品”,我知道您不想这样做。

希望这可以帮助。我最近写了一个 AS3 声音实验,让我了解了一些基础知识,并将关注这个空间,看看人们提出了哪些其他建议。

于 2010-03-25T20:20:38.083 回答
0

正如另一个答案中所解释的,您无法避免这种情况。但...

做了一些实验来缓解这个问题,最后我求助于:

看看演示原型的源代码,并(如果可能)试一试 lowLag。它对我很有效。

祝你好运!

于 2013-05-31T12:14:49.733 回答
0

您正在使用 javascript 间隔,不能保证在准确的时间触发。我确信内部 Flash 时序要可靠得多。

但这可能会有所帮助,在您触发播放声音后再次触发。

window.setTimeout(function() { func(); recur(func, delay); }, delay);
于 2010-03-23T17:46:10.687 回答