守望者--AIR技术交流

 找回密码
 立即注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

搜索
热搜: ANE FlasCC 炼金术
查看: 1921|回复: 3

[音频分析] AS3声音合成

[复制链接]
  • TA的每日心情
    擦汗
    2018-4-10 15:18
  • 签到天数: 447 天

    [LV.9]以坛为家II

    1742

    主题

    2094

    帖子

    13万

    积分

    超级版主

    Rank: 18Rank: 18Rank: 18Rank: 18Rank: 18

    威望
    562
    贡献
    29
    金币
    51690
    钢镚
    1422

    开源英雄守望者

    发表于 2015-1-11 18:02:24 | 显示全部楼层 |阅读模式
    AS3声音合成<1>声音基础,噪声  

    要想成为高手,就得像高手学习,多看高手的作品.下面是BIT-101大大写的Sound Synthesis一文.

    原为地址:http://www.bit-101.com/blog/?p=2660

    学习就应该不怕麻烦,既得之,则译之!下面是译文:


    我一直都想写一些关于声音合成的东西。但是因为没有找到很好这方面的素材而迟迟没有动作.所以现在只能找些素材东拼西凑写下这篇文章.

    首先,我们来看一下Sound对象的一些基本结构,如果通过代码来控制它并创建一些随机的声音。稍后,再学习如何创建真正的声音波形及声音的合成等等.

    开门见山

    Flash10具有声音合成的功能,实际这个功能在Flash9就已经有了,但是实现同样的效果对Flash9来说是一件头疼的事,而这些声音合成的API在Flash10中变得更加标准化了.

    要进行声音合成,首先要创建一个Sound对象并对它的SAMPLE_DATA事件(SampleDataEvent.SAMPLE_DATA)进行侦听.这个事件在声音中没有声音数据播放时触发.然后播放声音.

    1. var sound:Sound=new Sound();

    2. sound.addEventListener(SampleDataEvent.SAMPLE_DATA,onSampleData);

    3. sound.play();
    复制代码

    此时,因为没有加载任何如MP3,WAV等声音,也没有导入任何的声音数据流,也就是说没有任何声音播放,所以SAMPLE_DATA事件会立即触发.因此我们要添加一个侦听器函数:

    1. function onSampleData(se:SampleDataEvent):void

    2. {

    3. }
    复制代码

    接下来要给这个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个随机的样本值并写入二进制数据中.有一点你必须知道,实际我们做了两次写入二进制数据的动作,一次是给左声道,一次给右声道,下面是完整的代码:

    1. import flash.media.Sound;

    2. import flash.events.SampleDataEvent;



    3. var sound:Sound=new Sound();

    4. sound.addEventListener(SampleDataEvent.SAMPLE_DATA,onSampleData);

    5. sound.play();



    6. function onSampleData(event:SampleDataEvent):void

    7. {

    8.     for(var i:int = 0; i < 2048; i++)

    9.     {

    10.         var sample:Number = Math.random() * 2.0 - 1.0; // -1 to 1

    11.         event.data.writeFloat(sample); // left

    12.         event.data.writeFloat(sample); // right

    13.     }

    14. }
    复制代码

    测试上面的代码,你会听到一些兹兹的有点像收音机找不到频道的声音.注意,现在我们生成的噪音中,左右声道是同一个样本值,因为两个通道的二进制数据写入的是同一个值,所以我们生成的是一个单频道的声音.生成立体声音的代码如下:

    1. function onSampleData(event:SampleDataEvent):void

    2. {

    3.     for(var i:int = 0; i < 2048; i++)

    4.     {

    5.         var sampleA:Number = Math.random() * 2.0 - 1.0; // -1 to 1

    6.         var sampleB:Number = Math.random() * 2.0 - 1.0; // -1 to 1

    7.         event.data.writeFloat(sampleA); // left

    8.         event.data.writeFloat(sampleB); // right

    9.     }

    10. }
    复制代码

    现在我们为每个通道写入一个不同的随机值作为样本.测试代码,可以感觉到声音有点”空间”的感觉了(带上耳机效果更明显些).这个”空间”的感觉很细微,你可能感觉不到他跟单频道区别,所以为了可以在这两种效果之间快速切换,我们稍微修改一下代码如下:

    1. import flash.media.Sound;

    2. import flash.events.SampleDataEvent;

    3. import flash.events.MouseEvent;



    4. var sound:Sound = new Sound();

    5. sound.addEventListener(SampleDataEvent.SAMPLE_DATA, onSampleData);

    6. sound.play();



    7. var mono:Boolean = true;

    8. stage.addEventListener(MouseEvent.CLICK, onClick);

    9. function onClick(event:MouseEvent):void

    10. {

    11.     mono = !mono;

    12. }



    13. function onSampleData(event:SampleDataEvent):void

    14. {

    15.     for(var i:int = 0; i < 2048; i++)

    16.     {

    17.         var sampleA:Number = Math.random() * 2.0 - 1.0; // -1 to 1

    18.         var sampleB:Number = Math.random() * 2.0 - 1.0; // -1 to 1

    19.         event.data.writeFloat(sampleA); // left

    20.         if(mono)

    21.         {

    22.             event.data.writeFloat(sampleA); // left again

    23.         }

    24.         else

    25.         {

    26.             event.data.writeFloat(sampleB); // right

    27.         }

    28.     }

    29. }
    复制代码

    我添加一个布尔值变量,mono,当鼠标点击时它在true和false之间进行切换.如果mono为true,在左右声道中分别写入sampleA.如果mono为false,则在左声道写入sampleA,在右声道写入sampleB.测试代码并在舞台中点击鼠标.差别还是很细微的,不过现在你应该能够感觉到了.

    要想看到(更确切的说应该是听到)延迟的结果,在for循环中将2048改8192.现在点击鼠标时,你可以很明显的感觉到从鼠标点击到声音从单声道切换为立体声之间的延迟了.

    另外还要注意样本数.我说”通常”是使用2048到8192之间的值.实际上,如果你试着用大于8192的值,你会得到一个错误提示”某个参数无效”.所以样本数被强制限制小于8192.你可以选择一个小于2048的值,但那样声音会跳过样本数然后认为声音已经播放结束了.所以不会在触发SAMPLE_DATA事件,相反会触发COMPLETE事件.所以如果你想要声音持续播放的话,需要一直提供至少2048个样本数.

    下一部分,我们将学习创建一些简单的波形.


    本文来自:http://7301766.blog.163.com/blog/static/144633559201102542156651/



    守望者AIR技术交流社区(www.airmyth.com)
    回复

    使用道具 举报

  • TA的每日心情
    擦汗
    2018-4-10 15:18
  • 签到天数: 447 天

    [LV.9]以坛为家II

    1742

    主题

    2094

    帖子

    13万

    积分

    超级版主

    Rank: 18Rank: 18Rank: 18Rank: 18Rank: 18

    威望
    562
    贡献
    29
    金币
    51690
    钢镚
    1422

    开源英雄守望者

     楼主| 发表于 2015-1-11 18:05:05 | 显示全部楼层
    AS3声音合成<2>波形  

    本文将为你介绍如何使用AS3声音对象创建一个特定频率的波形。我会默认你已经看过本系列的第一节内容。

    声音基础

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

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


    1. import flash.media.Sound;

    2. import flash.events.SampleDataEvent;

    3. import flash.events.MouseEvent;



    4. var position:int = 0;

    5. var sound:Sound = new Sound();

    6. sound.addEventListener(SampleDataEvent.SAMPLE_DATA, onSampleData);

    7. sound.play();



    8. function onSampleData(event:SampleDataEvent):void

    9. {

    10.               for(var i:int = 0; i < 2048; i++)

    11.               {

    12.                             var phase:Number = position / 44100 * Math.PI * 2;

    13.                             position ++;

    14.                             var sample:Number = Math.sin(phase);

    15.                             event.data.writeFloat(sample); // left

    16.                             event.data.writeFloat(sample); // right

    17.               }

    18. }
    复制代码

    运行上面的代码,程序会在每一秒种创建一个完整的正弦波。当然,这是一个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的基础上计算出来:

    1. 440*2^(n/12)
    复制代码

    我们可以再添加一个变量n和一个timer对象,并在timer事件处理函数中对n逐次加1,然后用上的公式计算出不同的音调:

    1. import flash.media.Sound;

    2. import flash.events.SampleDataEvent;

    3. import flash.events.MouseEvent;

    4. import flash.utils.Timer;

    5. import flash.events.TimerEvent;



    6. var position:int = 0;

    7. var n:Number = 0;

    8. var sound:Sound = new Sound();

    9. sound.addEventListener(SampleDataEvent.SAMPLE_DATA, onSampleData);

    10. sound.play();



    11. function onSampleData(event:SampleDataEvent):void

    12. {

    13.               for(var i:int = 0; i < 2048; i++)

    14.               {

    15.                             var phase:Number = position / 44100 * Math.PI * 2;

    16.                             position ++;

    17.                             var sample:Number = Math.sin(phase * 440 * Math.pow(2, n / 12));

    18.                             event.data.writeFloat(sample); // left

    19.                             event.data.writeFloat(sample); // right

    20.               }

    21. }



    22. var timer:Timer = new Timer(500);

    23. timer.addEventListener(TimerEvent.TIMER, onTimer);

    24. timer.start();

    25. function onTimer(event:TimerEvent):void

    26. {

    27.               n++;

    28. }
    复制代码

    我们也可以借助于Math.random方法做一个简单的人工作曲器:

    1. function onTimer(event:TimerEvent):void

    2. {

    3.               n = Math.floor(Math.random() * 20 - 5);

    4.               timer.delay = 125 * (1 + Math.floor(Math.random() * 8));

    5. }
    复制代码

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

    仅仅凭此,你可以创建自己的音序器、迷你小钢琴或者其他乐器了。稍后,我会尝试介绍其他波形、合成波、封套和其他话题。

    本帖子中包含更多资源

    您需要 登录 才可以下载或查看,没有帐号?立即注册

    x
    守望者AIR技术交流社区(www.airmyth.com)
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    擦汗
    2018-4-10 15:18
  • 签到天数: 447 天

    [LV.9]以坛为家II

    1742

    主题

    2094

    帖子

    13万

    积分

    超级版主

    Rank: 18Rank: 18Rank: 18Rank: 18Rank: 18

    威望
    562
    贡献
    29
    金币
    51690
    钢镚
    1422

    开源英雄守望者

     楼主| 发表于 2015-1-11 18:08:15 | 显示全部楼层
    as3 捕获声音输入  
    捕获声音输入

    应用程序可通过 Microphone 类连接到用户系统上的麦克风或其它声音输入设备,并将输入音频广播到该系统的扬声器,或者将音频数据发送到远程服务器(如 Flash Media Server)。您可以从麦克风访问原始音频数据并进行记录或处理;还可以直接将音频发送到系统的扬声器或将压缩的音频数据发送到远程服务器。对于发送到 远程服务器的数据,可以使用 Speex 或 Nellymoser 编解码器。(从 Flash Player 10 和 Adobe AIR 1.5 开始支持 Speex 编解码器。)


    访问麦克风

    Microphone 类没有构造函数方法。相反,应使用静态 Microphone.getMicrophone() 方法来获取新的 Microphone 实例,如下所示:

    1. var mic:Microphone = Microphone.getMicrophone();
    复制代码

    不使用参数调用 Microphone.getMicrophone() 方法时,将返回在用户系统上发现的第一个声音输入设备。

    系统可能连接了多个声音输入设备。应用程序可以使用 Microphone.names 属性来获取所有可用声音输入设备名称的数组。然后,它可以使用 index 参数(与数组中的设备名称的索引值相匹配)来调用 Microphone.getMicrophone() 方法。

    系统可能没有连接麦克风或其它声音输入设备。可以使用 Microphone.names 属性或Microphone.getMicrophone() 方法来检查用户是否安装了声音输入设备。如果用户未安装声音输入设备,则 names 数组的长度为零,并且 getMicrophone() 方法返回值 null。

    当应用程序调用 Microphone.getMicrophone() 方法时,Flash Player 将显示“Flash Player 设置”对话框,它提示用户允许或拒绝 Flash Player 对系统上的摄像头和麦克风的访问。在用户单击此对话框中的“允许”或“拒绝”按钮后,将调度 StatusEvent。该 StatusEvent 实例的code 属性指示是允许还是拒绝对麦克风的访问,如下例所示:

    1. import flash.media.Microphone;

    2. var mic:Microphone = Microphone.getMicrophone();
    3. mic.addEventListener(StatusEvent.STATUS, this.onMicStatus);

    4. function onMicStatus(event:StatusEvent):void
    5. {
    6.     if (event.code == "Microphone.Unmuted")
    7.     {
    8.         trace("Microphone access was allowed.");
    9.     }  
    10.     else if (event.code == "Microphone.Muted")
    11.     {
    12.          trace("Microphone access was denied.");
    13.     }
    14. }
    复制代码

    如果允许访问,则 StatusEvent.code 属性将包含“Microphone.Unmuted”,如果拒绝访问,则该属性包含“Microphone.Muted”。

    当用户允许或拒绝对麦克风的访问时,Microphone.muted 属性将被分别设置为 true 或false。但是,在调度 StatusEvent 之前,Microphone 实例上未设置 muted 属性,因此在检查 Microphone.muted 属性之前,应用程序还应等待调度 StatusEvent.STATUS 事件。

    要使 Flash Player 显示设置对话框,应用程序窗口的大小必须足以显示它(至少是 215 x 138 像素)。否则,自动拒绝访问。

    在 AIR 应用程序沙箱中运行的内容不需要用户对麦克风的访问权限。因此,从来不调度对麦克风静音和取消静音的状态事件。在应用程序沙箱外的 AIR 中运行的内容要求用户权限,因此可以调度这些状态事件。


    将麦克风音频传送到本地扬声器

    可以使用参数值 true 调用 Microphone.setLoopback() 方法,以将来自麦克风的音频输入传送到本地系统扬声器。

    如果将来自本地麦克风的声音传送到本地扬声器,则会存在创建音频回馈循环的风险,这可能会导致非常大的振鸣声,并且可能会损坏声音硬件。使用参数值 true 调用Microphone.setUseEchoSuppression() 方法可降低发生音频回馈的风险,但不会完全消除该风险。Adobe 建议您始终在调用 Microphone.setLoopback(true) 之前调用Microphone.setUseEchoSuppression(true),除非您确信用户使用耳机来播放声音,或者使用除扬声器以外的某种设备。

    以下代码说明了如何将来自本地麦克风的音频传送到本地系统扬声器:

    1. var mic:Microphone = Microphone.getMicrophone();
    2. mic.setUseEchoSuppression(true);
    3. mic.setLoopBack(true);
    复制代码
    更改麦克风音频

    应用程序可以使用两种方法更改来自麦克风的音频数据。第一,它可以更改输入声音的增益,这会有效地将输入值乘以指定的数值以创建更大或更小的声音。Microphone.gain 属性接受介于 0 和 100 之间的数值(含 0 和 100)。值 50 相当于乘数 1,它指定正常音量。值 0 相当于乘数 0,它可有效地将输入音频静音。大于 50 的值指定的音量高于正常音量。

    应用程序也可以更改输入音频的采样率。较高的采样率可提高声音品质,但它们也会创建更密集的数据流(使用更多的资源进行传输和存储)。Microphone.rate 属性表示以千赫 (kHz) 为单位测量的音频采样率。默认采样率是 8 kHz。如果麦克风支持较高的采样率,您可以将 Microphone.rate 属性设置为高于 8 kHz 的值。例如,如果将 Microphone.rate 属性设置为值 11,则会将采样率设置为 11 kHz;如果将该属性设置为 22,则会将采样率设置为 22 kHz,依此类推。采样率取决于所选择的编解码器。如果使用的是 Nellymoser 编解码器,则可以指定 5、8、11、16、22 和 44 kHz 作为采样率。使用 Speex 编解码器(Flash Player 10 和 Adobe AIR 1.5 中开始提供)时,只能使用 16 kHz。



    检测麦克风活动

    为节省带宽和处理资源,Flash Player 将尝试检测何时麦克风不传输声音。当麦克风的活动级别处于静音级别阈值以下一段时间后,Flash Player 将停止传输音频输入,并改为调度一个简单的 ActivityEvent。如果使用 Speex 编解码器(Flash Player 10 或更高版本和 Adobe AIR 1.5 或更高版本中提供),请将静音级别设置为 0,以确保应用程序持续传输音频数据。Speex 语音活动检测将自动减少带宽。

    注: 当应用程序监视麦克风时,Microphone 对象只调度 Activity 事件。因此,如果不调用setLoopBack( true )、为示例数据事件添加侦听器、将麦克风附加到 NetStream 对象,则不调度任何活动事件。

    Microphone 类的以下三个属性用于监视和控制活动检测:

    • activityLevel 只读属性指示麦克风检测的音量,范围从 0 到 100。
    • silenceLevel 属性指定激活麦克风并调度 ActivityEvent.ACTIVITY 事件所需的音量。silenceLevel 属性也使用从 0 到 100 的范围,默认值为 10。
    • silenceTimeout 属性描述活动级别处于静音级别以下多长时间(以毫秒为单位)后,才会调度 ActivityEvent.ACTIVITY 事件以指示麦克风现在处于静音状态。silenceTimeout 默认值是 2000。

    Microphone.silenceLevel 属性和 Microphone.silenceTimeout 属性都是只读的,但可以使用 Microphone.setSilenceLevel() 方法来更改它们的值。

    在某些情况下,在检测到新活动时激活麦克风的过程可能会导致短暂的延迟。通过将麦克风始终保持活动状态,可以消除此类激活延迟。应用程序可以调用Microphone.setSilenceLevel() 方法并将 silenceLevel 参数设置为零,以通知 Flash Player 将麦克风保持活动状态并持续收集音频数据,即使未检测到任何声音也是如此。反之,如果将 silenceLevel 参数设置为 100,则可以完全禁止激活麦克风。

    以下示例显示了有关麦克风的信息,并报告 Microphone 对象调度的 activity 事件和 status 事件:

    1. import flash.events.ActivityEvent;
    2. import flash.events.StatusEvent;
    3. import flash.media.Microphone;

    4. var deviceArray:Array = Microphone.names;
    5. trace("Available sound input devices:");
    6. for (var i:int = 0; i < deviceArray.length; i++)
    7. {
    8.     trace(" " + deviceArray[i]);
    9. }

    10. var mic:Microphone = Microphone.getMicrophone();
    11. mic.gain = 60;
    12. mic.rate = 11;
    13. mic.setUseEchoSuppression(true);
    14. mic.setLoopBack(true);
    15. mic.setSilenceLevel(5, 1000);
    16.      
    17. mic.addEventListener(ActivityEvent.ACTIVITY, this.onMicActivity);
    18. mic.addEventListener(StatusEvent.STATUS, this.onMicStatus);
    19.      
    20. var micDetails:String = "Sound input device name: " + mic.name + '\n';
    21. micDetails += "Gain: " + mic.gain + '\n';
    22. micDetails += "Rate: " + mic.rate + " kHz" + '\n';
    23. micDetails += "Muted: " + mic.muted + '\n';
    24. micDetails += "Silence level: " + mic.silenceLevel + '\n';
    25. micDetails += "Silence timeout: " + mic.silenceTimeout + '\n';
    26. micDetails += "Echo suppression: " + mic.useEchoSuppression + '\n';
    27. trace(micDetails);

    28. function onMicActivity(event:ActivityEvent):void
    29. {
    30.     trace("activating=" + event.activating + ", activityLevel=" +  
    31.         mic.activityLevel);
    32. }

    33. function onMicStatus(event:StatusEvent):void
    34. {
    35.     trace("status: level=" + event.level + ", code=" + event.code);
    36. }
    复制代码

    在运行上面的示例时,对着系统麦克风说话或发出噪音,并观察所生成的、显示在控制台或调试窗口中的 trace 语句。


    向媒体服务器发送音频以及从中接收音频

    将 ActionScript 与 Flash Media Server 等流媒体服务器配合使用时,可以使用额外的音频功能。

    具体而言,应用程序可以将 Microphone 对象附加到 NetStream 对象,并将数据直接从用户的麦克风传输到服务器。音频数据也可以从服务器流式传输到应用程序,并作为 MovieClip 的一部分或通过使用 Video 对象进行播放。

    从 Flash Player 10 和 Adobe AIR 1.5 开始提供 Speex 编解码器。若要设置对于发送到媒体服务器的压缩音频所使用的编解码器,请设置 Microphone 对象的 codec 属性。此属性可以包含两个值,可使用 SoundCodec 类对这两个值进行枚举。将 codec 属性设置为SoundCodec.SPEEX 将选择使用 Speex 编解码器来压缩音频。将该属性设置为SoundCodec.NELLYMOSER(默认值)将选择使用 Nellymoser 编解码器来压缩音频。

    有关详细信息,请参阅 www.adobe.com/go/learn_fms_docs_cn 上提供的在线 Flash Media Server 文档。



    捕获麦克风声音数据

    在 Flash Player 10.1 和 AIR 2 或更高版本中,可以以浮点值的字节数组形式从麦克风数据捕获数据。每个值表示一个单声道音频数据样本。

    要获得麦克风数据,可为 Microphone 对象的 sampleData 事件设置事件侦听器。Microphone 对象会在用声音样本填充麦克风缓冲时定期调度 sampleData 事件。SampleDataEvent 对象包括一个 data 属性,它是声音样本的一个字节数组。样本均表示为浮点值,每个浮点值表示一个单声道声音样本。

    以下代码将麦克风声音数据捕获到名为 soundBytes 的 ByteArray 对象:

    1. var mic:Microphone = Microphone.getMicrophone();
    2. mic.setSilenceLevel(0, DELAY_LENGTH);
    3. mic.addEventListener(SampleDataEvent.SAMPLE_DATA, micSampleDataHandler);
    4. function micSampleDataHandler(event:SampleDataEvent):void {
    5.     while(event.data.bytesAvailable)     {
    6.         var sample:Number = event.data.readFloat();
    7.         soundBytes.writeFloat(sample);
    8.     }
    9. }
    复制代码

    您可以在播放音频时为 Sound 对象重复使用该示例字节。如果要重复使用样本字节,您应将 Microphone 对象的 rate 属性设置为 44,它表示 Sound 对象所使用的采样率。(您还可以将以低采样率捕获的麦克风样本转化为 Sound 对象所需的 44 kHz 采样率。)另外,请记住,Microphone 对象捕获单声道样本,而 Sound 对象使用立体声;因此,应将 Microphone 对象捕获的每个字节写入 Sound 对象两次。以下示例捕获 4 秒钟长的麦克风数据并使用 Sound 对象播放它:

    1. const DELAY_LENGTH:int = 4000;
    2. var mic:Microphone = Microphone.getMicrophone();
    3. mic.setSilenceLevel(0, DELAY_LENGTH);
    4. mic.gain = 100;
    5. mic.rate = 44;
    6. mic.addEventListener(SampleDataEvent.SAMPLE_DATA, micSampleDataHandler);

    7. var timer:Timer = new Timer(DELAY_LENGTH);
    8. timer.addEventListener(TimerEvent.TIMER, timerHandler);
    9. timer.start();

    10. function micSampleDataHandler(event:SampleDataEvent):void
    11. {
    12.     while(event.data.bytesAvailable)
    13.     {
    14.         var sample:Number = event.data.readFloat();
    15.         soundBytes.writeFloat(sample);
    16.     }
    17. }
    18. var sound:Sound = new Sound();
    19. var channel:SoundChannel;
    20. function timerHandler(event:TimerEvent):void
    21. {
    22.     mic.removeEventListener(SampleDataEvent.SAMPLE_DATA, micSampleDataHandler);
    23.     timer.stop();
    24.     soundBytes.position = 0;
    25.     sound.addEventListener(SampleDataEvent.SAMPLE_DATA, playbackSampleHandler);
    26.     channel.addEventListener( Event.SOUND_COMPLETE, playbackComplete );
    27.     channel = sound.play();
    28. }

    29. function playbackSampleHandler(event:SampleDataEvent):void
    30. {
    31.     for (var i:int = 0; i < 8192 && soundBytes.bytesAvailable > 0; i++)
    32.     {
    33.         trace(sample);
    34.         var sample:Number = soundBytes.readFloat();
    35.         event.data.writeFloat(sample);
    36.         event.data.writeFloat(sample);
    37.     }
    38. }

    39. function playbackComplete( event:Event ):void
    40. {
    41.     trace( "Playback finished.");
    42. }
    复制代码











    守望者AIR技术交流社区(www.airmyth.com)
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    擦汗
    2018-4-10 15:18
  • 签到天数: 447 天

    [LV.9]以坛为家II

    1742

    主题

    2094

    帖子

    13万

    积分

    超级版主

    Rank: 18Rank: 18Rank: 18Rank: 18Rank: 18

    威望
    562
    贡献
    29
    金币
    51690
    钢镚
    1422

    开源英雄守望者

     楼主| 发表于 2015-1-11 18:16:29 | 显示全部楼层
    处理动态生成的音频  
    注: 从 Flash Player 10 和 Adobe AIR 1.5 开始可以动态生成音频。

    可以动态生成音频数据,而不是加载或流式传输现有声音。在为 Sound 对象的 sampleData 事件分配事件侦听器时,可以生成音频数据。(sampleData 事件在 flash.events 包的 SampleDataEvent 类中定义。)在这种情况下,Sound 对象不从文件中加载声音数据。相反,该对象将用作声音数据的套接字,声音数据通过使用您分配给此事件的函数流入该对象。

    在您将 sampleData 事件侦听器添加到 Sound 对象后,该对象将定期请求数据以添加到声音缓冲区。此缓冲区包含 Sound 对象要播放的数据。在调用 Sound 对象的 play() 方法时,它会在请求新的声音数据时调度 sampleData 事件。(只有在 Sound 对象尚未从文件加载 mp3 数据时,此操作才生效。)

    SampleDataEvent 对象包含 data 属性。在事件侦听器中,将 ByteArray 对象写入此 data 对象。写入此对象的字节数组将添加到 Sound 对象播放的缓冲声音数据中。缓冲区中的字节数组是由从 -1 到 1 的浮点值组成的流。各浮点值均代表声音样本的一个声道(左声道或右声道)的幅度。声音按每秒 44,100 个样本进行采样。每个样本均包含左声道和右声道,在字节数组中以浮点数据的形式交错排列。

    在处理函数中,使用 ByteArray.writeFloat() 方法写入 sampleData 事件的 data 属性。例如

    1. var mySound:Sound = new Sound();
    2. mySound.addEventListener(SampleDataEvent.SAMPLE_DATA, sineWaveGenerator);
    3. mySound.play();
    4. function sineWaveGenerator(event:SampleDataEvent):void
    5. {
    6.     for (var i:int = 0; i < 8192; i++)
    7.     {
    8.         var n:Number = Math.sin((i + event.position) / Math.PI / 4);
    9.         event.data.writeFloat(n);
    10.         event.data.writeFloat(n);
    11.     }
    12. }
    复制代码

    当您调用 Sound.play() 时,该应用程序将开始调用事件处理函数,并请求声音样本数据。在播放声音时,应用程序将继续发送事件,直至您停止提供数据或调用 SoundChannel.stop()。

    事件的滞后时间因平台而异,在以后的 Flash Player 和 AIR 版本中也可能改变。请不要依赖某个特定的滞后时间;而应计算出相应的滞后时间。若要计算滞后时间,请使用以下公式:

    1. (SampleDataEvent.position / 44.1) - SoundChannelObject.position
    复制代码

    向 SampleDataEvent 对象的 data 属性提供 2048 到 8192 个样本(对于每次事件侦听器调用)。为了获得最佳性能,请尽可能多地提供样本(最多可达 8192 个样本)。提供的样本越少,在播放过程中就越有可能出现单击和弹出事件。此行为对于不同的平台会有所不同,并且会在各种情况下发生。例如,在调整浏览器的 大小时。在仅提供了 2048 个样本时,工作在某一个平台上的代码可能在运行于其它不同平台时将不能很好地工作。若要尽可能缩短滞后时间,请考虑允许用户选择数据量。

    如果提供的样本少于 2048 个(每次 sampleData 事件侦听器调用),则应用程序将在播放完剩余的样本后停止。然后,SoundChannel 对象调度 SoundComplete 事件。


    对源自 mp3 数据的声音数据进行修改

    使用 Sound.extract() 方法提取 Sound 对象中的数据。可以使用(和修改)该数据,将其写入另一个 Sound 对象的动态流以进行播放。例如,以下代码使用加载的 MP3 文件的字节,并通过过滤函数 upOctave() 进行传递:

    1. var mySound:Sound = new Sound();
    2. var sourceSnd:Sound = new Sound();
    3. var urlReq:URLRequest = new URLRequest("test.mp3");
    4. sourceSnd.load(urlReq);
    5. sourceSnd.addEventListener(Event.COMPLETE, loaded);
    6. function loaded(event:Event):void
    7. {
    8.     mySound.addEventListener(SampleDataEvent.SAMPLE_DATA, processSound);
    9.     mySound.play();
    10. }
    11. function processSound(event:SampleDataEvent):void
    12. {
    13.         var bytes:ByteArray = new ByteArray();
    14.         sourceSnd.extract(bytes, 8192);
    15.         event.data.writeBytes(upOctave(bytes));
    16. }
    17. function upOctave(bytes:ByteArray):ByteArray
    18. {
    19.     var returnBytes:ByteArray = new ByteArray();
    20.     bytes.position = 0;
    21.     while(bytes.bytesAvailable > 0)
    22.     {
    23.         returnBytes.writeFloat(bytes.readFloat());
    24.         returnBytes.writeFloat(bytes.readFloat());
    25.         if (bytes.bytesAvailable > 0)
    26.         {
    27.             bytes.position += 8;
    28.         }
    29.     }
    30.     return returnBytes;
    31. }
    复制代码
    有关生成声音的限制

    将 sampleData 事件侦听器与 Sound 对象一起使用时,启用的其它 Sound 方法仅包括 Sound.extract() 和 Sound.play()。调用任何其它方法或属性将导致异常。仍启用 SoundChannel 对象的所有方法和属性。








    守望者AIR技术交流社区(www.airmyth.com)
    回复 支持 反对

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    
    关闭

    站长推荐上一条 /4 下一条

    QQ|手机版|Archiver|网站地图|小黑屋|守望者 ( 京ICP备14061876号

    GMT+8, 2019-8-18 12:35 , Processed in 0.051166 second(s), 32 queries .

    守望者AIR

    守望者AIR技术交流社区

    本站成立于 2014年12月31日

    快速回复 返回顶部 返回列表