0

在 Flash 中生成声波时遇到以下问题。这是生成器部分:

const SAMPLING_RATE:int = 44100;
const TWO_PI:Number = 2 * Math.PI;
const TWO_PI_OVER_SR:Number = TWO_PI / SAMPLING_RATE;
const SAMPLE_SIZE:int = 8192 / 4;


function generateSine(fq:Number):ByteArray
{
var bytes:ByteArray = new ByteArray();
var sample:Number;
var amp:Number = 1;


for (var i:int=0; i<SAMPLE_SIZE; i++)
{
    sample = amp* Math.sin(i  * TWO_PI_OVER_SR *   fq  );
    bytes.writeFloat(sample);
}


bytes.position = 0;
return bytes;
 }

这是播放部分:

function playbackSampleHandler(event:SampleDataEvent):void
{
var sample:Number;

for (var i:int = 0; i <  SAMPLE_SIZE && soundData.bytesAvailable; i++)
{
    sample = soundData.readFloat();
    event.data.writeFloat(sample);
    event.data.writeFloat(sample);
}
 }



 function onClickPlay(e:MouseEvent)
 {
soundData= new ByteArray();
    var sound:ByteArray= new ByteArray();
    sound=generateSine(440);
soundData.writeBytes(sound,0,sound.length);
soundData.position = 0;
morphedSound.addEventListener(SampleDataEvent.SAMPLE_DATA, playbackSampleHandler);
soundChannel = morphedSound.play();             
 }

快速解释:我基本上将正弦函数的值写入 ByteArray,然后播放处理程序读取该数组并播放声音。我只写了一个 8192/4 样本缓冲区。

问题:我的问题是,当你这样做时,声音结束时会出现恼人的咔嗒声,这是正弦波被削减到 0 以外的值的伪影。所以我的问题是如何避免这种情况?

奖励:我还想知道,当 Flash 中的缓冲区如此严格(即从 2048 到 8192)时,我如何才能生成恰好 100 ms 的正弦波?

链接:如果有帮助,我的代码基于本教程 http://www.bit-101.com/blog/?p=2669 和我自己的探索。

4

2 回答 2

1

您需要保留在样本数据处理结束时出现的正弦波相位。否则,您的正弦波不会平稳地继续,您会得到您所说的点击。

var totalSamples:int=0;
function generateSine(fq:Number):ByteArray
{
var bytes:ByteArray = new ByteArray();
var sample:Number;
var amp:Number = 1;


for (var i:int=0; i<SAMPLE_SIZE; i++)
{
    sample = amp* Math.sin((i+totalSamples)  * TWO_PI_OVER_SR *   fq  );
    // this uses stored phase ^^^
    bytes.writeFloat(sample);
}
totalSamples+=SAMPLE_SIZE; // this preserves phase between subsequent samples

bytes.position = 0;
return bytes;

}

更新:我注意到你连续两次调用生成器 - 这太糟糕了。如果您只需要一个函数的工作结果,则绝不能调用该函数两次。

函数 onClickPlay(e:MouseEvent) { soundData= generateSine(440); // 如果我们可以使用赋值,为什么还要使用 writeBytes?soundData.position = 0; morphedSound.addEventListener(SampleDataEvent.SAMPLE_DATA,playbackSampleHandler);soundChannel = morphedSound.play();
}

此外,要使声音以完整(半)波的声音结束并删除您需要的最后一个咔嗒声,请使用以下技术:

function generateSine(fq:Number,cleanFinish:Boolean=false):ByteArray
{
var bytes:ByteArray = new ByteArray();
var sample:Number;
var amp:Number = 1;


for (var i:int=0; i<SAMPLE_SIZE; i++)
{
    sample = amp* Math.sin((i+totalSamples)  * TWO_PI_OVER_SR *   fq  );
    // this uses stored phase ^^^
    bytes.writeFloat(sample);
    if (cleanFinish) 
      if ((Math.abs(sample)<(SAMPLING_RATE/fq/3))&&(i>SAMPLE_SIZE-SAMPLING_RATE/fq)) break; 
    // this triggers a drop if we are sampling the last wave already
}
totalSamples+=i; // this preserves phase between subsequent samples

bytes.position = 0;
return bytes;

}

使用设置为 true 的可选布尔参数调用此函数以接收正确终止的波。

于 2013-08-26T10:44:32.480 回答
0

“sample”的所有 SAMPLE_SIZE 值必须分成三部分:第一个和第三个部分必须各有 250 个值,中间的一个是其余的:SAMPLE_SIZE - 500。如下所示:生成一个包含 250 个值的数组,介于 0 和.996 步长为 .004 :我的意思是:.000, .004, .008, ... , .996,并用它们填充一个数组,例如 mod[i] ,其中 i 从零到 249。(mod 用于调制...)如下修改初始循环,因此将导致从 0 衰减到(+/-)amp(前 250 个值),然后直到最后 250 个值的其余值保持(+/-)amp,最后,最后 250 个值从 (+/-)amp 衰减到 0。讨厌的点击仍然存在……历史!我希望这有帮助。

function generateSine(fq:Number):ByteArray
{
   var bytes:ByteArray = new ByteArray();
   var sample:Number;
   var amp:Number = 1;


 for (var i:int=0; i< 250; i++)
   {
      sample = amp* Math.sin(i  * TWO_PI_OVER_SR *   fq  ) * mod[i];
      bytes.writeFloat(sample);
   }

   for (var i:int=250; i<SAMPLE_SIZE - 250; i++)
   {
      sample = amp* Math.sin(i  * TWO_PI_OVER_SR *   fq  );
      bytes.writeFloat(sample);
   }

   for (var i:int=SAMPLE_SIZE - 250; i<SAMPLE_SIZE; i++)
   {
      sample = amp* Math.sin(i  * TWO_PI_OVER_SR *   fq  ) * (.996 - mod[i]);
      bytes.writeFloat(sample);
   }


   bytes.position = 0;
   return bytes;
}
于 2017-04-06T12:42:01.837 回答