码迷,mamicode.com
首页 > 其他好文 > 详细

拉登大叔的声音合成2

时间:2015-12-25 13:19:18      阅读:126      评论:0      收藏:0      [点我收藏+]

标签:

声音基础

声音从本质上讲就是空气压力的变化。这对初学者来说是一个非常难理解的术语。空气由各种各样的分子组成,但是这些分子在空气中的分布是不均匀的。有些区域的分子排列比较紧密,因此空气压力也比较大;而有些区域的分子排列的间距就比较大。例如吉他琴弦的震动时,它会以特定的速度前后运动。当琴弦像某一方向移动时,它会将该方向上存在的分子挤到一起,进而使这一部分的空气密度变大。当琴弦像反方向回弹时,会生成一小部分的真空,当然这不是真正意义上的真空,只是这部分区域中包含的分子很少。然后琴弦再次回弹,创建另一个高密度空气区域,依次类推。

这些高密度和低密度空气开始通过空气传播,最终碰到你的耳朵。然后高密度空气会将你的耳膜“挤压”进去,然后稀薄的空气让耳膜再次恢复原形,结果就是你的耳膜开始以与吉他琴弦大致相同的频率进行震动。然后这个震动会带动你的骨头震动,然后以相同的频率刺激你的神经,像你的大脑传递一个信号,这就是“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));

}

这几句代码会为我们生成不同时长不同音调的声音。

拉登大叔的声音合成2

标签:

原文地址:http://www.cnblogs.com/pmx-pmx/p/5075368.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!