【问题标题】:How to play a seamless loop from an AudioSprite in AS3, without SAMPLE_DATA如何在没有 SAMPLE_DATA 的情况下从 AS3 中的 AudioSprite 播放无缝循环
【发布时间】:2015-04-08 20:49:47
【问题描述】:

我创建了一批用这个工具组装的声音:

AudioSprite

https://github.com/tonistiigi/audiosprite

输出通常用于 JS 库,例如 Howler、Zynga Jukebox 或 SoundJS - 但我想看看是否可以在 AS3 中实现。

我开始创建一个声音播放器,它可以根据该工具生成的 JSON 和 MP3 文件加载、解析和播放声音。

到目前为止一切顺利! ... 除了循环。

现在,大问题是 - 有没有一种方法可以无缝播放声音循环,因为所有音乐和声音共存于同一个 MP3 文件中,并且它具有 start &结束 范围播放并停止它?

Example of how the sounds are placed in the file:

mygame_sounds.mp3 = [BUZZ + LASER + BOING ... + TRACKLOOP]

我正在寻找一种涉及使用SAMPLE_DATA 事件的解决方案(因为它会占用大量 CPU 资源)。如果没有办法,请解释原因。

到目前为止,我在使用给定 AudioSprite 持续时间后触发的 flash.utils.Timer 对象取得了一定的成功,但并不一致。

要停止/处理 非循环 声音,我依赖于 Master Timer(以非常短的间隔运行),这似乎“削减”了样本适当地。但是我已经尝试过使用这个主定时器来一遍又一遍地播放循环声音——同样的延迟问题。

是否有任何方法可以预测/测量声音完成一次传递时预期的延迟时间?

【问题讨论】:

  • 对不起,误导性的 [howler.js] 标签,但我没有足够的代表来创建“audiosprite”。如果可以,请这样做。您也可以在此问题中替换此处的标记。
  • 是否愿意解释原因,Mr. / Ms. Downvote?
  • 我真的不知道为什么要投反对票,所以我给了 +1 以提高它的水平...无论如何,您到底遇到了什么样的延迟问题?你不能在你的应用中预处理声音文件(创建不同的声音对象来循环声音)吗?
  • 理论上,当然,一个单独的 WAV/MP3 可以用于需要无缝重复的声音,但这更多的是一个可重用性问题(能够重用相同的 AudioSprite 合并的 MP3 声音之间HTML5 和 Flash,给定一个 JSON 文件来描述 id/start/end/loop 属性)。

标签: actionscript-3 flash audio mp3 howler.js


【解决方案1】:

在 SoundJS 中,我们找不到允许在 AS3 中平滑循环音频精灵的方法,因此使用了计时器。我们发现 Web Audio 是唯一允许平滑循环的 api,因此如果可能使用任何其他插件,建议不要使用音频精灵来处理需要平滑循环的声音。

希望对您有所帮助。

【讨论】:

  • 感谢 OJay。实际上,当需要平滑循环时,我一直在使用 Sound 的 .extract().loadPCMFromByteArray() 方法来创建单独的 Sound 对象。我不确定它是否适用于所有声音长度,但到目前为止,我认为我有一些有希望的东西(稍后会发布答案)。
  • @bigp 是的,考虑将音频精灵拆分成几个元素并在本地循环这些元素,这不会产生巨大的开销,因为您可以将所有生成的 MP3 打包到一个 SWF 中以使用 XML 进行下载/JSON 元数据来解释什么文件是什么,如果你说使用不同语言的可互换字符配音集。
【解决方案2】:

您无法从较大的音频文件中检索到轨道的平滑循环的原因是您无法更快地检查每个 SWF 帧一次的声音位置,其长度取决于stage.frameRate 和您的总处理时间应用程序,通常是多种多样的。因此,如果您的循环声音持续说 5.123 秒(我不在乎有多少样本,只是它的长度不会构成完整的帧数,无论 stage.frameRate),您的声音将尝试播放 5.125 秒( 40 fps 时 205 帧,IMO 最适合这种特殊声音),5.133 秒(30 fps 时 154 帧)或一些奇怪的帧数,如果 SWF 会遇到延迟。由于 AS3/Flash 引擎优化,无法通过任何方式完全控制多余的毫秒。因此,请考虑不使用音频精灵并转换为音频包(SWF 中的多个音频文件,或 MP3 中的一个声音)。

【讨论】:

  • 关于帧速率的优点。我过去做过 SWF Sound-pack 的事情,虽然它对于 Sound 循环来说既好又可靠,但我只是好奇它是否可以被 AudioSprite 格式交换,只使用一个 Sound 对象来加载和播放所有内容。我最终能够获得平滑的循环(裁剪适量的样本以提取/加载PCM)。话虽如此,我认为可能值得研究一个混合,其中每个“精灵”实例化一个新的声音,从主声音文件中提取。然后,任何声音都可以循环播放,即使它们没有在 JSON 文件中标记。
  • 在 AS3 端分割你的音频精灵?当然,这应该可以实现无缝循环。
【解决方案3】:

虽然我仍在研究完美的解决方案,但这是我能想到的最好的解决方案:

  1. 加载 JSON 文件/ByteArray。
  2. 解析 JSON 文件以获取每个精灵的 ID、开始和结束时间。
  3. 将 MP3 文件/ByteArray(需要 loadCompressedDataFromByteArray())加载到主 Sound 对象中。
  4. 加载后,检查是否有任何精灵被标记为“循环”。
  5. 为上述循环创建单独的 Sound 对象,并通过 loadPCMFromByteArray() 使用一些“ma​​gic-numbers”从 master Sound 中提取部分(详情如下)。
  6. 要播放一次性声音,请调用主声音的play(sprite.start * 1000)(取决于格式,通常 JSON 的 start 值以秒为单位,需要以毫秒为单位)。
  7. 要播放无缝循环声音,请调用单个 Sound 对象(在步骤 #5 中创建)的play(0, 9999) 方法。

我不会详细介绍如何停止声音(SoundChannel.stop(),bam!),但我会解释上面提到的“幻数”。看到这个sn-p:

    var goldenOffset:UInt = (64 << 5);
    var goldenDuration:UInt = (64 << 2);
    var sampleRate:UInt = 44100;
    
    for (id in loops) {
        var sprite:AudioSpriteItem = _mapSprites.get(id);
        var loop:Sound = _mapLoops.get(id);
        var sampleBytes = new ByteArray();
        var samplesTotal:UInt = cast(sprite.duration * sampleRate + goldenDuration);
        var samplesStart:UInt = cast(sprite.start * sampleRate + goldenOffset);
        
        _sound.extract(sampleBytes, samplesTotal, samplesStart);
        
        sampleBytes.position = 0;
        loop.loadPCMFromByteArray(sampleBytes, samplesTotal, "float", true);
    }

老实说,这些神奇的 goldenOffsetgoldenDuration 值是通过反复试验发现的。我可以在没有它们的情况下关闭到一个无缝循环,只需用sampleRate 计算startduration(默认情况下假设为44100),但每个结局都有一点小问题它。

经过多次调整,这两个“64 左位移”值使循环听起来更流畅。

如果您想尝试/阅读代码,我在 github 上发布了 Haxe 项目(编译后的 SWC 也可以在 /bin 文件夹中找到)。

FLAudioSprite

Github 页面:https://github.com/bigp/FLAudioSprite

SWF 演示(下载):bit.ly/FLAudioSpriteSWFDemo

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-27
    • 1970-01-01
    • 1970-01-01
    • 2015-06-01
    相关资源
    最近更新 更多