2013-02-09 101 views
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); 
} 

回答

2

我认为有两个主要原因:

  1. 据了解,Timer对象在Flash Player是不准确的,这之间的延迟是火灾发生波动。
  2. Sound.play()方法也会在声音实际开始播放之前引入一些延迟,理论上这个延迟可能会波动。在Chrome中使用的PPAPI版本的Flash Player中,延迟尤为明显。

有几种解决方案。我建议这些之一:

  1. 使用前由整个节拍器周期(tick1-pause1-tick2-pause2),并使用Sound.play()方法的第二个参数,它只是循环的声音;
  2. 使用动态声音生成。

第二个选项更灵活,但更难实施。 Basicaly,您需要创建一个Sound对象的新实例,订阅它的SAMPLE_DATA事件并将其称为play()方法。在处理程序中,您将检查event.position/44.1,它将以毫秒为单位给出声音生成的当前位置。然后,如果您决定是时候播放tick1还是tick2声音,您可以拨打tickN.extract(event.data, ...),其中tickN是tick1或tick2 Sound对象,否则请写下静音。

你可以阅读更多关于动态声音产生here

此外,请注意,当您拨打Sound.play()时,它将返回一个SoundChannel对象,该对象具有position属性。这是一个正在播放的声音的ms(未生成)的位置,它是准确的。因此,使用这个属性,你可以想出第三种方法:创建一个Sound对象并设置一个SAMPLE_DATA处理器,就像在动态声音生成解决方案中那样,但是一直将静音(零)写入event.data对象。这是获得声音通道而不实际播放声音所必需的。然后,使用较高的帧速率(60 FPS)和一个具有最小可能延迟(1 ms)的TimerTimer每次触发时,检查soundChannel.position以确定是否是时间播放滴答声,如果是这样,就像在您的示例中那样播放它。这种方法很可能解决Timer不准确的问题,但它不能处理由tickSound.play()方法引起的延迟。

+0

谢谢。我决定使用第二种方法。研究如何抽样和取样声音有一段时间,但它似乎是最好的解决方案,因为我不想使用服务器端预生成声音。 – 2013-02-10 12:07:26

+0

不客气:)请注意,有2048个样本(大约50毫秒)和8192个样本(大约50毫秒)的上限和下限。190 ms),您可以在单个SAMPLE_DATA事件中传递,因此,在某些情况下,您需要使用滴答声并在连续几次事件中写入。 – skozin 2013-02-10 18:33:22

+0

原因是可能发生的情况是当前event.position比下一次tick更早,但(event.position + 2048)更晚。您将写入A =(tickTime * 44.1 - event.position)沉默样本,然后B =(2048 - A)从开始的滴答声音样本。然后,在下一个采样数据事件中,C =(tickSound.length * 44.1-B)从不是第B'个样本开始的滴答声样本,以及再次D =(2048-C)沉默样本沉默= 2零漂)。 – skozin 2013-02-10 18:34:00