难以创建功能以非常快速(以毫秒为单位)上下调整音调的音量



基本上我正在使用AS3生成音调。我需要能够传递我的功能一个数组,看起来像{0,50,100,0,20,500,200,100}每个代表毫秒。就像"关闭,打开,关闭,开,关闭",我需要音调才能完全播放到没有延迟或打ic的毫秒内。

我尝试使用计时器来实现这一目标...但实际上并不像我需要的那样精确。有轻微的延误,而且明显没有像需要的那样短的延迟。

我以为我只是在播放我的语气,然后使用音频打开和关闭音量,这可以帮助使它更快,因为我没有启动和停止声音,我只是在操纵实时体积。

,但也许不是缩短它的卷,也许只是计时器并不是那么可靠。这是我的代码,函数只需循环,直到我将其停止使用另一个功能。关于如何使它更加精确的任何建议?

我用所有计时器处理数组的功能

private function soundPattern(patternArr:Array):void
        {
            //setup vars
            var pTotal:Number = patternArr.length;
            var pCount:Number = 0;
            if(pTotal >=1)
            {
                //setup listenrs
                patTimer = new Timer(patternArr[pCount],1);
                patTimer.addEventListener(TimerEvent.TIMER_COMPLETE, comp);
                function comp(e:TimerEvent=null):void
                {
                    pCount++;
                    if(pCount != pTotal)
                    {
                        patTimer.removeEventListener(TimerEvent.TIMER_COMPLETE, comp);
                        toneGen.soundTrans.volume=1;
                        toneGen.toneChannel.soundTransform = toneGen.soundTrans;
                        patTimer = new Timer(patternArr[pCount],1);
                        patTimer.addEventListener(TimerEvent.TIMER_COMPLETE, compTwo);
                        if(patternArr[pCount]>0)
                        {
                            patTimer.reset();
                            patTimer.start();
                        }
                        else
                        {
                            compTwo();
                        }
                    }
                    else if(repeat)
                    {
                        trace("1resetting...");
                        patTimer.removeEventListener(TimerEvent.TIMER_COMPLETE, comp);
                        pCount = 0;
                        patTimer = new Timer(patternArr[pCount],1);
                        patTimer.addEventListener(TimerEvent.TIMER_COMPLETE, compTwo);
                        toneGen.soundTrans.volume=0;
                        toneGen.toneChannel.soundTransform = toneGen.soundTrans;
                        if(patternArr[pCount]>0)
                        {
                            patTimer.reset();
                            patTimer.start();
                        }
                        else
                        {
                            compTwo();
                        }
                    }
                }
                //in-between
                function compTwo(e:TimerEvent=null):void
                {
                    pCount++;
                    if(pCount != pTotal)
                    {
                        patTimer.removeEventListener(TimerEvent.TIMER_COMPLETE, compTwo);
                        patTimer = new Timer(patternArr[pCount],1);
                        patTimer.addEventListener(TimerEvent.TIMER_COMPLETE, comp);
                        toneGen.soundTrans.volume=0;
                        toneGen.toneChannel.soundTransform = toneGen.soundTrans;
                        if(patternArr[pCount]>0)
                        {
                            patTimer.reset();
                            patTimer.start();
                        }
                        else
                        {
                            comp();
                        }
                    }
                    else if(repeat)
                    {
                        trace("2resetting...");
                        patTimer.removeEventListener(TimerEvent.TIMER_COMPLETE, compTwo);
                        pCount = 0;
                        patTimer = new Timer(patternArr[pCount],1);
                        patTimer.addEventListener(TimerEvent.TIMER_COMPLETE, comp);
                        toneGen.soundTrans.volume=0;
                        toneGen.toneChannel.soundTransform = toneGen.soundTrans;
                        if(patternArr[pCount]>0)
                        {
                            patTimer.reset();
                            patTimer.start();
                        }
                        else
                        {
                            comp();
                        }
                    }
                }

                //get the tone started, but remember the first is a pause
                toneGen.startTone();
                //start things
                if(patternArr[pCount]>0)
                {
                    patTimer.reset();
                    patTimer.start();
                }
                else
                {
                    comp();
                }
            }
        }

这是我正在使用的tonegen类

package
{
    import flash.events.SampleDataEvent;
    import flash.media.Sound;
    import flash.media.SoundChannel;
    import flash.media.SoundTransform;
    public class ToneGenerator {
        [Bindable]
        public var amp_multiplier_right:Number = 0.5;
        [Bindable]
        public var amp_multiplier_left:Number = 0.5;
        [Bindable]
        public var freq_right:Number = 580;
        [Bindable]
        public var freq_left:Number = 580;
        public static const SAMPLING_RATE:int = 44100;
        public static const TWO_PI:Number = 2*Math.PI;
        public static const TWO_PI_OVER_SR:Number = TWO_PI/SAMPLING_RATE;
        public var tone:Sound;
        public var toneChannel:SoundChannel;
        public var soundTrans:SoundTransform;
        public function ToneGenerator() {
        }
        public function stopTone():void {
            if(tone)
            {
                toneChannel.stop();
                tone.removeEventListener(SampleDataEvent.SAMPLE_DATA, generateSineTone);
            }
        }
        public function startTone():void {
            tone = new Sound();
            tone.addEventListener(SampleDataEvent.SAMPLE_DATA, generateSineTone);
            soundTrans = new SoundTransform(0);
            toneChannel = tone.play();
            toneChannel.soundTransform = soundTrans;
        }
        public function generateSineTone(e:SampleDataEvent):void {
            var sample:Number;
            for(var i:int=0;i<8192;i++) {
                sample = Math.sin((i+e.position) * TWO_PI_OVER_SR * freq_left);
                e.data.writeFloat(sample * amp_multiplier_left);
                sample = Math.sin((i+e.position) * TWO_PI_OVER_SR * freq_right);
                e.data.writeFloat(sample * amp_multiplier_right);
            }  
        }

    }
}

众所周知,计时器不准确(这是由于闪光灯的架构:阅读此内容)。

只要您需要准确的基于时间的计算,请使用getTimer()方法来计算两个时刻之间经过的时间。您还需要一种方法来尽可能频繁地使用tick()方法(这将是您的准确性),在这种情况下,您可以使用Timer甚至Event.ENTER_FRAME

var ticker:Sprite = new Sprite();
sprite.addEventListener(Event.ENTER_FRAME, tick);
const delay:uint = 300;
var timeReference:uint;
var lastTimeReference:uint = getTimer();
function tick(evt:Event):void {
   timeReference = getTimer();
   if(timeReference - lastTimeReference >= delay)
   {
      trace("delay reached");
      lastTimeReference = timeReference;
   }
}

由于您是通过自己创建原始示例数据来生成音调的,因此最准确的事情是调整代码中创建音调的卷,而不是依靠其他内容它在单独的线程中运行,带有单独的时钟和相关的任意延迟以打开和关闭。

由于您只有简单的音调,因此您可以在开始和停止之前等待"交叉零"的音调来打开和关闭它们。这通常会正常工作。另外,您可以乘以名义上的一个或零的常数,然后在需要更改时将值延伸到一个和零之间。一种简单的升值方法是使用线性插值:

http://blog.bjornroche.com/2010/10/linear-inear-interpolation-for-audio-in-c.html

您必须调整发电机。您有一系列毫秒,代表开/关开关,对吗?每次采样时,都必须知道现在,开启或关闭声音的状态。鉴于您的样品为44100 Hz,您可以在声音停止时进行精确调整,何时应启动,通过将0.0而不是正弦波发送到通道中,而您的说明会停止声音。实际上,逻辑声音结构将不断播放,但是音调会间歇性。

以下是如何完成的草图:

public class CustomTone {
    private var tone:Sound;
    private var delays:Vector.<Number>;
    private var frequency:Number;
    private var onThreshold:Number;
    private var offThreshold:Number;
    private var finishThreshold:Number;
    private var isOn:Boolean=true;
    private var sequenced:Number; // how many samples were sequenced total. 
    // any check is done within this.
    public function CustomTone(freq:Number,toneArray:Array) {
        tone=new Sound();
        delays=Vector.<Number>(toneArray);
        frequency=freq;
        tone.addEventListener(SampleDataEvent.SAMPLE_DATA,generateTone); 
        tone.addEventListener(Event.COMPLETE,finishedGenerating);
        sequenced=0;
        // fill other values according to array [TODO]
    }
    // other function to taste. Should have there a play and stop functions, as well as to play presampled sound
    }
    private function generateTone(e:SampleDataEvent):void {
        var ep:Number=e.position;
        var howMany:int=Math.min(finishThreshold-sequenced,8192);
        for (var i:int=0;i<howMany;i++) {
            if (isOn) { 
                sample = Math.sin((i+ep) * TWO_PI_OVER_SR * frequency);
                e.data.writeFloat(sample * amp_multiplier_left);
                sample = Math.sin((i+ep) * TWO_PI_OVER_SR * frequency);
                e.data.writeFloat(sample * amp_multiplier_right);
            } else {
                e.data.writeFloat(0.0);
                e.data.writeFloat(0.0);
            }
            if ((i+ep)>offThreshold) {
                isOn=false;
                offThreshold=getNextOffThreshold();
            }
            if (i+ep>onThreshold) {
                isOn=true;
                onThreshold=getNextOnThreshold();
            }
        }
        sequenced+=howMany;
    }
    ...
}

注意,您可能希望不会从同一数组中再次生成这样的声音,因此您可以使用一种方法来重播曾经生成的声音。下一个阈值的方法应被下一个值的内联计算取代,请注意这些方法是在样本中测量的,而不是以毫秒为单位,因此您也应该将它们转换为样品。

最新更新