2013-02-09 146 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