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