錄製用戶的音頻

錄製用戶的音頻


許多瀏覽器如今都能訪問用戶的視頻和音頻輸入。 不過,根據瀏覽器的不一樣,這一功能可能體現爲一種全動態的內置體驗,也可能經過受權給用戶設備上的其餘應用來實現。web

從簡單作起,按部就班

最簡易的作法是直接要求用戶提供預先錄製的文件。 其實現步驟是:建立一個簡單的文件輸入元素,而後添加一個表示咱們只能接受音頻文件的 accept 過濾器,在理想的狀況下,咱們能夠直接從麥克風獲取這些文件。數組

<input type="file" accept="audio/*" capture="microphone">複製代碼

此方法在全部平臺上都有效。在桌面平臺上,它會提示用戶經過文件系統上傳文件(忽略 capture="microphone")。 在 iOS 上的 Safari 中,它會打開麥克風應用以便您錄製音頻,而後將其傳回網頁;在 Android 上,它容許用戶選擇使用哪個應用來錄製音頻,錄製完畢後將其傳回網頁。瀏覽器

用戶完成錄製並返回網站後,您須要以某種方式掌握文件數據。 爲 input 元素附加一個 onchange 事件,而後讀取事件對象的 files 屬性,即可快速得到文件訪問權。bash

<input type="file" accept="audio/*" capture="microphone" id="recorder"><audio id="player" controls></audio><script>  var recorder = document.getElementById('recorder');  var player = document.getElementById('player')' recorder.addEventListener('change', function(e) { var file = e.target.files[0]; // Do something with the audio file. player.src = URL.createObjectURL(file); });</script>複製代碼

得到對文件的訪問權後,即可隨意對其執行任何操做。例如,能夠執行如下操做:服務器

  • 將其直接附加到一個 <audio> 元素,這樣便能播放文件
  • 將其下載至用戶的設備
  • 經過將其附加到一個 XMLHttpRequest,上傳至服務器
  • 經過 Web Audio API 傳遞文件並對其應用過濾器

儘管使用 input 元素方法得到對音頻數據訪問權的狀況廣泛存在,倒是最沒有吸引力的方案。 由於咱們真正須要的是得到對麥克風的訪問權,直接在頁面內提供良好的體驗。網絡

以交互方式訪問麥克風

現代瀏覽器可直連麥克風,咱們能夠藉此打造與網頁徹底集成的體驗,讓用戶永遠都不須要離開瀏覽器。ide

得到對麥克風的訪問權

咱們能夠利用 WebRTC 規範中名爲 getUserMedia() 的 API 直接訪問麥克風。getUserMedia() 將提示用戶授予對其相連麥克風和攝像頭的訪問權。網站

若是受權成功,該 API 將返回一個 Stream,其中包含來自攝像頭或麥克風的數據,而後咱們能夠將數據附加到一個 <audio> 元素、將其附加到一個網絡音頻 AudioContext 或使用 MediaRecorder API 對其進行保存。ui

要從麥克風獲取數據,咱們只需在傳遞給 getUserMedia() API 的約束對象中設置 audio: truethis

<audio id="player" controls></audio><script>  var player = document.getElementById('player');  var handleSuccess = function(stream) {    if (window.URL) {      player.src = window.URL.createObjectURL(stream);    } else {      player.src = stream;    }  };  navigator.mediaDevices.getUserMedia({ audio: true, video: false })      .then(handleSuccess)</script>複製代碼

這段代碼自己的用處並不大。咱們所能作的就是獲取音頻數據並進行播放。

從麥克風獲取原始數據

要從麥克風獲取原始數據,咱們須要獲取 getUserMedia() 建立的卡片信息流,而後利用 Web Audio API 處理數據。 Web Audio API 是一個簡單的 API,用於獲取輸入源並將這些輸入源鏈接到能夠處理音頻數據(調節增益等)的節點,最終目的是鏈接到揚聲器以便用戶可以聽到聲音。

能夠鏈接的其中一個節點是 ScriptProcessorNode。每次音頻緩衝區已滿,須要您進行處理時,該節點都會發出一個 onaudioprocess 事件。此時,您能夠將數據保存到本身的緩衝區內,留供之後使用。

<script>  var handleSuccess = function(stream) {    var context = new AudioContext();    var input = context.createMediaStreamSource(stream)    var processor = context.createScriptProcessor(1024,1,1);    source.connect(processor);    processor.connect(context.destination);    processor.onaudioprocess = function(e){      // Do something with the data, i.e Convert this to WAV      console.log(e.inputBuffer);    };  };  navigator.mediaDevices.getUserMedia({ audio: true, video: false })      .then(handleSuccess)</script>複製代碼

保留在緩衝區內的數據是來自麥克風的原始數據,在這些數據的處理上有如下這幾種選擇:

  • 將其直接上傳至服務器
  • 將其存儲在本地
  • 將其轉換爲專用文件格式(例如 WAV),而後保存至服務器或本地

保存來自麥克風的數據

要想保存來自麥克風的數據,最簡便的方法是使用 MediaRecorder API。

MediaRecorder API 將獲取 getUserMedia 建立的卡片信息流,而後漸進式地將卡片信息流中的數據保存到首選目的地。

<a id="download">Download<button id="stop">Stop<script>  let shouldStop = false;  let stopped = false;  const downloadLink = document.getElementById('download');  const stopButton = document.getElementById('stop');  stopButton.addEventListener('click', function() {    shouldStop = true;  })  var handleSuccess = function(stream) {    const options = {mimeType: 'video/webm;codecs=vp9'};    const recordedChunks = [];    const mediaRecorder = new MediaRecorder(stream, options);    mediaRecorder.addEventListener('dataavailable', function(e) {      if (e.data.size > 0) {        recordedChunks.push(e.data);      }      if(shouldStop === true && stopped === false) {        mediaRecorder.stop();        stopped = true;      }    });    mediaRecorder.addEventListener('stop', function() {      downloadLink.href = URL.createObjectURL(new Blob(recordedChunks));      downloadLink.download = 'acetest.wav';    });    mediaRecorder.start();  };  navigator.mediaDevices.getUserMedia({ audio: true, video: false })      .then(handleSuccess);</script>複製代碼

在咱們這種狀況下,咱們要將數據直接保存到一個數組中,而後在稍後轉換成 Blob 後再將其保存到網絡服務器,或直接保存在用戶設備的存儲內。

以負責任的方式請求麥克風使用權限

若是用戶以前未授予網站對麥克風的訪問權,則調用 getUserMedia 時瀏覽器會當即提示用戶授予網站對麥克風的訪問權。

用戶討厭在其機器上收到索要功能強大設備訪問權的提示,他們經常會屏蔽權限請求,而若是他們不瞭解提示的產生環境,也會將其忽略。最好的作法是在首次須要權限時只請求訪問麥克風。 一旦用戶授予了訪問權,就不會再次收到提示,但若是他們拒絕受權,您就沒法再次得到訪問權以向用戶請求權限。

Warning: 在頁面加載時請求得到對麥克風的訪問權將致使大多數用戶拒絕您訪問麥克風。

利用 Permission API 確認是否已得到訪問權

getUserMedia API 並不能讓您瞭解本身是否已得到對麥克風的訪問權。 這就帶來了一個問題:爲了提供友善的 UI,讓用戶願意授予對麥克風的訪問權,您就必須請求得到對麥克風的訪問權。

在某些瀏覽器中,能夠利用 Permission API 來解決這個問題。navigator.permission API 讓您沒必要再次提示用戶即可查詢到訪問特定 API 能力的狀態。

要想查詢是否有權訪問用戶的麥克風,能夠將 {name: 'microphone'} 傳入 query 方法,後者將返回:

  • granted — 用戶以前已授予對麥克風的訪問權;
  • prompt — 用戶還沒有授予訪問權,調用 getUserMedia 時將會收到提示;
  • denied — 系統或用戶已顯式屏蔽對麥克風的訪問權,您將沒法得到對其的訪問權。

如今您就能夠進行快速檢查,以確認是否須要改動用戶界面來適應用戶須要執行的操做。

navigator.permissions.query({name:'microphone'}).then(function(result) {  if (result.state == 'granted') {  } else if (result.state == 'prompt') {  } else if (result.state == 'denied') {  }  result.onchange = function() {  };});複製代碼
相關文章
相關標籤/搜索