3

我正在使用 Flex + AS3 编写简单的节拍器组件。例如,我希望它在每 500 毫秒后播放“tick1”声音,并且每 4 次播放另一个声音“tick2”。但实际上声音之间的延迟并不相等 - 有时更小,有时更大。我在最新的 Chrome 上测试它。

这是我的代码:

//Somewhere here button bound to the 'toggle' function

import flash.utils.Timer;
import flash.events.TimerEvent;
import flash.media.SoundTransform;
import flash.media.SoundChannel;

private var bpm:Number = 120; //2 bit per second, delay=500ms
private var period:Number = 4;
private var timer:Timer = new Timer(bpm, period);

[Embed(source='sounds/1.mp3')]
private var tickSound1Class:Class;
private var tickSound1:Sound;

[Embed(source='sounds/2.mp3')]
private var tickSound2Class:Class;
private var tickSound2:Sound;

private var trans:SoundTransform = new SoundTransform(1);

private function init():void {
    ....

    tickSound1 = new tickSound1Class() as Sound;
    tickSound2 = new tickSound2Class() as Sound;

    update();


    timer.addEventListener(TimerEvent.TIMER, onTimerEvent);

    ....
}

private function update():void {
    timer.delay = 1000 * 60/bpm;
    timer.repeatCount = 0;
}

private function toggle():void {
    if (timer.running) {
        timer.reset();
        startStopButton.label = "Start";
    } else {
        update();
        timer.start();
        startStopButton.label = "Stop";
    }
}

private function onTimerEvent(event:TimerEvent):void {
    var t:Timer = event.currentTarget as Timer;

    if (t.currentCount % period == 0)
        tickSound1.play(0, 0, trans);
    else
        tickSound2.play(0, 0, trans);
}
4

1 回答 1

2

我认为主要有两个原因:

  1. 众所周知,TimerFlash Player 中的对象不准确,其触发之间的延迟会波动。
  2. Sound.play()方法还在声音真正开始播放之前引入了一些延迟,理论上这个延迟是可以波动的。在 Chrome 中使用的 PPAPI 版本的 Flash Player 中,延迟尤其明显。

有几种解决方案。我会建议其中之一:

  1. 使用整个节拍器循环(tick1-pause1-tick2-pause2)的预先组合的声音,并使用Sound.play()方法的第二个参数循环它;
  2. 使用动态声音生成。

第二种选择更灵活,但更难实施。基本上,您需要创建一个新的Sound对象实例,订阅它的SAMPLE_DATA事件并调用它的play()方法。在处理程序中,您将检查event.position / 44.1,这将为您提供声音生成的当前位置(以 ms 为单位)。然后,如果您决定是时候播放 tick1 或 tick2 声音,您将调用tickN.extract(event.data, ...),其中tickN是 tick1 或 tick2Sound对象,否则写入静音。

您可以在此处阅读有关动态声音生成的更多信息。

另外,请注意,当您调用Sound.play()它时,它会返回一个SoundChannel具有该position属性的对象。是否是当前正在播放(未生成)的声音的毫秒位置,它是准确的。因此,使用此属性,您可以提出第三种方法:创建一个Sound对象并设置一个SAMPLE_DATA处理程序,就像在动态声音生成解决方案中一样,但始终将静音(零)写入event.data处理程序内的对象。这是在不实际播放声音的情况下获得声音通道所必需的。然后,使用高帧率 (60 FPS) 和Timer尽可能小的延迟 (1 ms)。每次Timer发生火灾时,检查soundChannel.position确定是否是时候播放滴答声了,如果是的话,就像你在示例中所做的那样播放它。这种方法很可能解决了Timer不准确的问题,但无法处理tickSound.play()方法造成的延迟。

于 2013-02-09T21:54:51.677 回答