擷取聲音輸入

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 屬性,取得所有可用聲音輸入裝置的名稱陣列。然後,可以呼叫 Microphone.getMicrophone() 方法並搭配 index 參數,該參數符合陣列中裝置名稱的索引值。

系統可能沒有連接麥克風或其它聲音輸入裝置。您可以使用 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 屬性,因此您的應用程式也應該等到傳送 StatusEvent.STATUS 事件之後,再檢查 Microphone.muted 屬性。

為了讓 Flash Player 顯示設定對話方塊,應用程式視窗必須夠大才能顯示它 (至少 215 * 138 像素)。否則,會自動拒絕存取。

在 AIR 應用程式安全執行程序中執行的內容,不需要使用者權限,即可存取麥克風。因此,永遠不會傳送麥克風的靜音和取消靜音的狀態事件。在應用程式安全執行程序之外執行的 AIR 內容,確實需要使用者的權限,因此可能會傳送這些狀態事件。

將麥克風音效遞送至本機喇叭

藉由呼叫 Microphone.setLoopback() 方法並搭配值為 true 的參數,可以將來自麥克風的音效輸入遞送至本機系統喇叭。

當來自本機麥克風的聲音遞送至本機喇叭時,建立音效回應迴圈時將具有風險,因為這可能會造成大聲且長而尖銳的聲音,而且有可能損毀聲音硬體。呼叫 Microphone.setUseEchoSuppression() 方法並搭配值為 true 的參數會減少 (但是不會完全消除) 發生音訊回授的風險。除非您確定使用者使用耳機或喇叭以外的播放裝置來播放聲音,否則 Adobe 建議在呼叫 Microphone.setLoopback(true) 之前,一定要呼叫 Microphone.setUseEchoSuppression(true)

下列程式碼將說明如何將音效從本機麥克風遞送至本機系統喇叭:

var mic:Microphone = Microphone.getMicrophone(); 
mic.setUseEchoSuppression(true); 
mic.setLoopBack(true);

改變麥克風音效

您的應用程式可以透過兩種方式,修改來自麥克風的音效資料。首先,它可以變更輸入聲音的增量,這將可有效地將輸入值乘以指定的量,以建立較大聲或較小聲的聲音。 Microphone.gain 屬性可以接受介於 0 與 100 (包含兩者) 之間的數值。值為 50 就像乘以 1 倍一樣,會指定正常的音量。值為零就像乘以零一樣,可有效地將輸入音效化為無聲。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 物件傳送的活動事件與狀態事件:

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 轉碼器來壓縮音效。

如需詳細資訊,請參閱 Flash Media Server 線上說明文件,網址為 www.adobe.com/go/learn_fms_docs_tw

捕捉麥克風聲音資料

在 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."); 
}

如需有關從聲音取樣資料播放聲音的詳細資訊,請參閱 使用動態產生的音效