标签:
声音从本质上讲就是空气压力的变化。这对初学者来说是一个非常难理解的术语。空气由各种各样的分子组成,但是这些分子在空气中的分布是不均匀的。有些区域的分子排列比较紧密,因此空气压力也比较大;而有些区域的分子排列的间距就比较大。例如吉他琴弦的震动时,它会以特定的速度前后运动。当琴弦像某一方向移动时,它会将该方向上存在的分子挤到一起,进而使这一部分的空气密度变大。当琴弦像反方向回弹时,会生成一小部分的真空,当然这不是真正意义上的真空,只是这部分区域中包含的分子很少。然后琴弦再次回弹,创建另一个高密度空气区域,依次类推。
这些高密度和低密度空气开始通过空气传播,最终碰到你的耳朵。然后高密度空气会将你的耳膜“挤压”进去,然后稀薄的空气让耳膜再次恢复原形,结果就是你的耳膜开始以与吉他琴弦大致相同的频率进行震动。然后这个震动会带动你的骨头震动,然后以相同的频率刺激你的神经,像你的大脑传递一个信号,这就是“C大调”。
当你使用麦克风或者耳机录音时,需要通过一种鼓膜或者其他可以震动的东西震动创建电信号,以同样的方法或者其他方法记录声音。在声音回放时,计算机会重新产生这些电信号,并使扬声器以相同的频率震动。这个震动跟吉他琴弦产生的震动是一样的,以同样的方法通过挤压空气并传播到你的耳朵,然后你会听到的是相同的声音。
但是,在讨论到声音合成时,我们就要从头开始了。Flash(或者说你的声卡)通过耳机或者扬声器处理并产生正确的电信号,然后震动空气。但是你要用数学的方法计算出这个震动的幅度和频率。
在本教程的第一部分,我们创建了一些随机值来使扬声器或者耳机毫无规律的震动,这样产生的结果是一个类似无线电的噪声。要生成真实的音调还有很多工作要做,还有很多东西要学习。
在模拟声音的世界里,比如老唱片或八轨磁带(那是和我的年龄差不多久远的年代了),声音是通过唱针撞击唱片盘道或者磁带上磁场变化来编码的。数字声音则是通过一定的时间间隔对声压采样实现的。
举一个最简单的声音形式,正弦波,下面是一个平滑的模拟声音曲线:
下面是对这个曲线进行50次采样数字声音形式:
可以看到,采样后的版本并不像模拟声音曲线那么精确。但是在高质量的数字声音中,采样的样本数非常多,足以让大部分人无法感觉到它与模拟声音的区别。在Flash中进行声音合成时,每秒钟采集44100个样本。记住这个数字,稍后的计算中会用到它。
现在我们需要对声音采样一些样本,然后创建上面你所看到的正弦波。正弦波的峰值是1.0,谷值是-1.0,以及中值是0.0。为了方便理解,首先我在一秒的时间内只创建一个正弦波。然后使用一个名为position的变量来记录位置。其初始值为0,每创建一个样本对其加1。因此在第一秒的声音中,这个变量的取值范围是0到44100。
如果用这个position变量除以44100,我们可以得到一个0.0到1.0值,然再乘以2PI,这样就得到了一个0到2PI的值,这正是用Math.sin函数创建正弦波所需要的。代码如下:
import flash.media.Sound;
import flash.events.SampleDataEvent;
import flash.events.MouseEvent;
var position:int = 0;
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 phase:Number = position / 44100 * Math.PI * 2;
position ++;
var sample:Number = Math.sin(phase);
event.data.writeFloat(sample); // left
event.data.writeFloat(sample); // right
}
}
运行上面的代码,程序会在每一秒种创建一个完整的正弦波。当然,这是一个1HZ的声波(注:HZ是频率单位,频率即采样次数,而不是样本数,正文中提到的44100是样本数),这个频率太低是人耳听不到的。要生成指定频率的声音,只需用频率乘以你要听到的频道就可以了。人耳可以听到的频率大致在25到25000HZ之间。标准音节里的中央A是440HZ。我们来试一下,稍微改动一下下面的代码:
var sample:Number = Math.sin(phase * 440);
此时你会听到中央A调。你可以在网上轻松的找到各个音调对应的频率:
A 440
B flat 466
B 494
C 523
C sharp 554
D 587
D sharp 622
E 659
F 698
F sharp 740
G 784
A flat 831
A 880
或者,你也可以了解每个音调之间的计算关系,上面的音调都可以通过下面的的公式在440HZ的基础上计算出来:
440*2^(n/12)
我们可以再添加一个变量n和一个timer对象,并在timer事件处理函数中对n逐次加1,然后用上的公式计算出不同的音调:
import flash.media.Sound;
import flash.events.SampleDataEvent;
import flash.events.MouseEvent;
import flash.utils.Timer;
import flash.events.TimerEvent;
var position:int = 0;
var n:Number = 0;
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 phase:Number = position / 44100 * Math.PI * 2;
position ++;
var sample:Number = Math.sin(phase * 440 * Math.pow(2, n / 12));
event.data.writeFloat(sample); // left
event.data.writeFloat(sample); // right
}
}
var timer:Timer = new Timer(500);
timer.addEventListener(TimerEvent.TIMER, onTimer);
timer.start();
function onTimer(event:TimerEvent):void
{
n++;
}
我们也可以借助于Math.random方法做一个简单的人工作曲器:
function onTimer(event:TimerEvent):void
{
n = Math.floor(Math.random() * 20 - 5);
timer.delay = 125 * (1 + Math.floor(Math.random() * 8));
}
这几句代码会为我们生成不同时长不同音调的声音。
标签:
原文地址:http://www.cnblogs.com/pmx-pmx/p/5075368.html