标签:
我一直都想写一些关于声音合成的东西。但是因为没有找到很好这方面的素材而迟迟没有动作.所以现在只能找些素材东拼西凑写下这篇文章.
首先,我们来看一下Sound对象的一些基本结构,如果通过代码来控制它并创建一些随机的声音。稍后,再学习如何创建真正的声音波形及声音的合成等等.
Flash10具有声音合成的功能,实际这个功能在Flash9就已经有了,但是实现同样的效果对Flash9来说是一件头疼的事,而这些声音合成的API在Flash10中变得更加标准化了.
要进行声音合成,首先要创建一个Sound对象并对它的SAMPLE_DATA事件(SampleDataEvent.SAMPLE_DATA)进行侦听.这个事件在声音中没有声音数据播放时触发.然后播放声音.
var sound:Sound=new Sound();
sound.addEventListener(SampleDataEvent.SAMPLE_DATA,onSampleData);
sound.play();
此时,因为没有加载任何如MP3,WAV等声音,也没有导入任何的声音数据流,也就是说没有任何声音播放,所以SAMPLE_DATA事件会立即触发.因此我们要添加一个侦听器函数:
function onSampleData(se:SampleDataEvent):void
{
}
接下来要给这个Sound对象添加一些声音数据来播放.那么如何添加呢?侦听器方法的形参SampleDataEvent有个二进制的data属性,可以通过对这个属性赋值二进制数据来给声音对象添加播放的声音数据.对二进制变量赋值要使用ByteArray.float方法.通常情况下赋值范围在-1.0到1.0之间.此时对二进制变量设置的每个float数值就是常说的样本.样本数通常为2048和8192.
OK.这个范围已经够宽了.那哪个值最好的呢?如果你选择比较低的值比如2048,声音播放时,数据流会很快超出这些值,然后再次出发SAMPLE_DATA事件,要求再次添加声音数据.如果选择比较大的值比如8192的话,声音需要花费4倍于2048的时间来超出这些值,因此事件侦听器被触发的频率也随之降低了4倍.
所以样本数越大运行性能越好.但是如果是动态的生成声音数据,样本数越大意味着延迟越大.延迟指的是UI或者程序变更声音与真正听到声音变更这之间的声音差.举个例子,比如你需要在用户按下按钮时将一个400HZ的音调变到800HZ.但是当用户按下按钮时声音在400HZ的音调上取了8000个样本,此时声音会继续播放这8000个样本直至播放完毕,然后调用SAMPLE_DATA事件侦听器要求跟多的数据.因此用户可能会感觉到从他们按下按钮到听到声音变调,中间有一点延迟.如果选择比较小的样本数-2048-可以缩短延迟而使其不易察觉.
现在我们来制造一些噪音.我们在-1.0到1.0之间选区2048个随机的样本值并写入二进制数据中.有一点你必须知道,实际我们做了两次写入二进制数据的动作,一次是给左声道,一次给右声道,下面是完整的代码:
import flash.media.Sound;
import flash.events.SampleDataEvent;
var sound:Sound=new Sound();
sound.addEventListener(SampleDataEvent.SAMPLE_DATA,onSampleData);
sound.play();
function onSampleData(event:SampleDataEvent):void
{
for(var i:int = 0; i < 2048; i++)
{
var sample:Number = Math.random() * 2.0 - 1.0; // -1 to 1
event.data.writeFloat(sample); // left
event.data.writeFloat(sample); // right
}
}
测试上面的代码,你会听到一些兹兹的有点像收音机找不到频道的声音.注意,现在我们生成的噪音中,左右声道是同一个样本值,因为两个通道的二进制数据写入的是同一个值,所以我们生成的是一个单频道的声音.生成立体声音的代码如下:
function onSampleData(event:SampleDataEvent):void
{
for(var i:int = 0; i < 2048; i++)
{
var sampleA:Number = Math.random() * 2.0 - 1.0; // -1 to 1
var sampleB:Number = Math.random() * 2.0 - 1.0; // -1 to 1
event.data.writeFloat(sampleA); // left
event.data.writeFloat(sampleB); // right
}
}
现在我们为每个通道写入一个不同的随机值作为样本.测试代码,可以感觉到声音有点”空间”的感觉了(带上耳机效果更明显些).这个”空间”的感觉很细微,你可能感觉不到他跟单频道区别,所以为了可以在这两种效果之间快速切换,我们稍微修改一下代码如下:
import flash.media.Sound;
import flash.events.SampleDataEvent;
import flash.events.MouseEvent;
var sound:Sound = new Sound();
sound.addEventListener(SampleDataEvent.SAMPLE_DATA, onSampleData);
sound.play();
var mono:Boolean = true;
stage.addEventListener(MouseEvent.CLICK, onClick);
function onClick(event:MouseEvent):void
{
mono = !mono;
}
function onSampleData(event:SampleDataEvent):void
{
for(var i:int = 0; i < 2048; i++)
{
var sampleA:Number = Math.random() * 2.0 - 1.0; // -1 to 1
var sampleB:Number = Math.random() * 2.0 - 1.0; // -1 to 1
event.data.writeFloat(sampleA); // left
if(mono)
{
event.data.writeFloat(sampleA); // left again
}
else
{
event.data.writeFloat(sampleB); // right
}
}
}
我添加一个布尔值变量,mono,当鼠标点击时它在true和false之间进行切换.如果mono为true,在左右声道中分别写入sampleA.如果mono为false,则在左声道写入sampleA,在右声道写入sampleB.测试代码并在舞台中点击鼠标.差别还是很细微的,不过现在你应该能够感觉到了.
要想看到(更确切的说应该是听到)延迟的结果,在for循环中将2048改8192.现在点击鼠标时,你可以很明显的感觉到从鼠标点击到声音从单声道切换为立体声之间的延迟了.
另外还要注意样本数.我说”通常”是使用2048到8192之间的值.实际上,如果你试着用大于8192的值,你会得到一个错误提示”某个参数无效”.所以样本数被强制限制小于8192.你可以选择一个小于2048的值,但那样声音会跳过样本数然后认为声音已经播放结束了.所以不会在触发SAMPLE_DATA事件,相反会触发COMPLETE事件.所以如果你想要声音持续播放的话,需要一直提供至少2048个样本数.
标签:
原文地址:http://www.cnblogs.com/pmx-pmx/p/5075356.html