0

基本的にAS3を使って音を出しています。{0,50,100,0,20,500,200,100}それぞれがミリ秒を表すような配列を関数に渡すことができる必要があります。それは「オフ、オン、オフ、オン、オフ」のようなもので、遅延やしゃっくりなしでミリ秒まで正確に再生するトーンが必要です。

これを達成するためにタイマーを使用して関数を作成しようとしました...しかし、実際には必要なほど正確ではありません。わずかな遅延があり、本当に短いものを必要なだけ短く再生していないことが目立ちます.

トーンを再生してから、SoundTransform を使用してボリュームのオンとオフを切り替えることを考えていました。サウンドの開始と停止ではなく、実際にボリュームを操作しているだけなので、高速化に役立つ可能性があります。時間。

しかし、速度が低下しているのは音量ではなく、タイマーがそれほど信頼できないだけかもしれません。これが私のコードです。関数は、私が持っている別の関数で停止するまでループします。これをより正確にする方法について何か提案はありますか?

すべてのタイマーで配列を処理する私の関数

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);
            }  

        }


    }
}
4

3 に答える 3

2

タイマーは正確ではないことで知られています (これは Flash Player のアーキテクチャによるものです:こちらをお読みください)。

正確な時間ベースの計算が必要な場合はいつでも、getTimer()メソッドを使用して 2 つの時点の間の経過時間を計算します。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;
   }

}
于 2012-10-27T22:45:33.050 に答える
0

ジェネレーターを調整する必要があります。オン/オフの切り替えを表す一連のミリ秒がありますよね? サンプリングするたびに、サウンドが現在どの状態であるか、オンかオフかを知る必要があります。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;
    }
    ...
}

このようなサウンドを同じ配列から再度生成したくない場合は、一度生成されたサウンドを再生するメソッドを使用できます。次のしきい値のメソッドは、次の値のインライン計算に置き換える必要があります。これらはミリ秒単位ではなくサンプル単位で測定されることに注意してください。したがって、それらもサンプルに変換する必要があります。

于 2012-10-29T07:06:02.703 に答える
0

生のサンプル データを自分で作成してトーンを生成しているため、別のスレッドで実行されている別のものに依存するのではなく、トーンを作成するコードで音量を調整するのが最も正確です。クロックと関連する任意の遅延を使用して、オンとオフを切り替えます。

シンプルなトーンしかないので、トーンが「クロスゼロ」するのを待ってから開始および停止することで、オンとオフを切り替えることができます。通常はこれで問題なく動作します。または、名目上 1 または 0 の定数を掛けて、オンとオフを変更する必要がある場合は、値を 1 と 0 の間でランプします。傾斜する簡単な方法は、線形補間を使用することです。

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

于 2012-10-28T15:27:18.233 に答える