捕获声音输入

Flash Player 9 和更高版本,Adobe AIR 1.0 和更高版本

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

访问麦克风

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

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 属性指示是允许还是拒绝对麦克风的访问,如下例所示:

import flash.media.Microphone; 
 
var mic:Microphone = Microphone.getMicrophone(); 
mic.addEventListener(StatusEvent.STATUS, this.onMicStatus); 
 
function onMicStatus(event:StatusEvent):void 
{ 
    if (event.code == "Microphone.Unmuted") 
    { 
        trace("Microphone access was allowed."); 
    }  
    else if (event.code == "Microphone.Muted") 
    { 
         trace("Microphone access was denied."); 
    } 
}

如果允许访问,则 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) ,除非您确信用户使用耳机来播放声音,或者使用除扬声器以外的某种设备。

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

var mic:Microphone = Microphone.getMicrophone(); 
mic.setUseEchoSuppression(true); 
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 事件:

import flash.events.ActivityEvent; 
import flash.events.StatusEvent; 
import flash.media.Microphone; 
 
var deviceArray:Array = Microphone.names; 
trace("Available sound input devices:"); 
for (var i:int = 0; i < deviceArray.length; i++) 
{ 
    trace(" " + deviceArray[i]); 
} 
 
var mic:Microphone = Microphone.getMicrophone(); 
mic.gain = 60; 
mic.rate = 11; 
mic.setUseEchoSuppression(true); 
mic.setLoopBack(true); 
mic.setSilenceLevel(5, 1000); 
     
mic.addEventListener(ActivityEvent.ACTIVITY, this.onMicActivity); 
mic.addEventListener(StatusEvent.STATUS, this.onMicStatus); 
     
var micDetails:String = "Sound input device name: " + mic.name + '\n'; 
micDetails += "Gain: " + mic.gain + '\n'; 
micDetails += "Rate: " + mic.rate + " kHz" + '\n'; 
micDetails += "Muted: " + mic.muted + '\n'; 
micDetails += "Silence level: " + mic.silenceLevel + '\n'; 
micDetails += "Silence timeout: " + mic.silenceTimeout + '\n'; 
micDetails += "Echo suppression: " + mic.useEchoSuppression + '\n'; 
trace(micDetails); 
 
function onMicActivity(event:ActivityEvent):void 
{ 
    trace("activating=" + event.activating + ", activityLevel=" +  
        mic.activityLevel); 
} 
 
function onMicStatus(event:StatusEvent):void 
{ 
    trace("status: level=" + event.level + ", code=" + event.code); 
}

在运行上面的示例时,对着系统麦克风说话或发出噪音,并观察所生成的、显示在控制台或调试窗口中的 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 对象:

var mic:Microphone = Microphone.getMicrophone(); 
mic.setSilenceLevel(0, DELAY_LENGTH); 
mic.addEventListener(SampleDataEvent.SAMPLE_DATA, micSampleDataHandler); 
function micSampleDataHandler(event:SampleDataEvent):void { 
    while(event.data.bytesAvailable)     { 
        var sample:Number = event.data.readFloat(); 
        soundBytes.writeFloat(sample); 
    } 
}

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

const DELAY_LENGTH:int = 4000; 
var mic:Microphone = Microphone.getMicrophone(); 
mic.setSilenceLevel(0, DELAY_LENGTH); 
mic.gain = 100; 
mic.rate = 44; 
mic.addEventListener(SampleDataEvent.SAMPLE_DATA, micSampleDataHandler); 
 
var timer:Timer = new Timer(DELAY_LENGTH); 
timer.addEventListener(TimerEvent.TIMER, timerHandler); 
timer.start(); 
 
function micSampleDataHandler(event:SampleDataEvent):void 
{ 
    while(event.data.bytesAvailable) 
    { 
        var sample:Number = event.data.readFloat(); 
        soundBytes.writeFloat(sample); 
    } 
} 
var sound:Sound = new Sound(); 
var channel:SoundChannel; 
function timerHandler(event:TimerEvent):void 
{ 
    mic.removeEventListener(SampleDataEvent.SAMPLE_DATA, micSampleDataHandler); 
    timer.stop(); 
    soundBytes.position = 0; 
    sound.addEventListener(SampleDataEvent.SAMPLE_DATA, playbackSampleHandler); 
    channel.addEventListener( Event.SOUND_COMPLETE, playbackComplete ); 
    channel = sound.play(); 
} 
 
function playbackSampleHandler(event:SampleDataEvent):void 
{ 
    for (var i:int = 0; i < 8192 && soundBytes.bytesAvailable > 0; i++) 
    { 
        trace(sample); 
        var sample:Number = soundBytes.readFloat(); 
        event.data.writeFloat(sample); 
        event.data.writeFloat(sample); 
    } 
} 
 
function playbackComplete( event:Event ):void 
{ 
    trace( "Playback finished."); 
}

有关从声音样本数据播放声音的详细信息,请参阅 处理动态生成的音频