許多瀏覽器現在都能存取使用者的影片和音訊輸入內容。不過,視瀏覽器而定,這可能會是完整的動態內嵌體驗,也可能會委派給使用者裝置上的其他應用程式。
從簡單開始,逐步增加
最簡單的方法就是請使用者提供預錄檔案。方法是建立簡單的檔案輸入元素,並加入 accept
篩選器,指出我們只接受音訊檔案,以及 capture
屬性,指出我們要直接從麥克風取得檔案。
<input type="file" accept="audio/*" capture />
這個方法適用於所有平台。在電腦上,系統會提示使用者從檔案系統上傳檔案 (忽略 capture
屬性)。在 iOS 版 Safari 中,系統會開啟麥克風應用程式,讓您錄下音訊並傳回網頁;在 Android 上,系統會讓使用者選擇要使用哪個應用程式錄下音訊,然後傳回網頁。
使用者完成錄製並返回網站後,您必須以某種方式取得檔案資料。您可以將 onchange
事件附加至輸入元素,然後讀取事件物件的 files
屬性,即可快速存取。
<input type="file" accept="audio/*" capture id="recorder" />
<audio id="player" controls></audio>
<script>
const recorder = document.getElementById('recorder');
const player = document.getElementById('player');
recorder.addEventListener('change', function (e) {
const file = e.target.files[0];
const url = URL.createObjectURL(file);
// Do something with the audio file.
player.src = url;
});
</script>
</audio>
取得檔案存取權後,您可以自由使用檔案。舉例來說,您可以執行下列操作:
- 直接附加至
<audio>
元素,以便播放 - 下載到使用者的裝置
- 將其附加至
XMLHttpRequest
上傳至伺服器 - 將其傳送至 Web Audio API,並套用濾鏡
雖然使用輸入元素方法存取音訊資料的做法很普遍,但這是最不受歡迎的做法。我們真的很想存取麥克風,並直接在網頁中提供良好的使用體驗。
以互動方式存取麥克風
現代瀏覽器可以直接連線至麥克風,讓我們打造與網頁完全整合的體驗,使用者不必離開瀏覽器。
取得麥克風存取權
我們可以使用 WebRTC 規格中的 API (稱為 getUserMedia()
),直接存取麥克風。getUserMedia()
會提示使用者存取已連結的麥克風和攝影機。
如果成功,API 會傳回 Stream
,其中包含相機或麥克風的資料,然後我們可以將其附加至 <audio>
元素、WebRTC 串流、Web Audio AudioContext
,或使用 MediaRecorder
API 儲存。
如要從麥克風取得資料,我們只需在傳遞至 getUserMedia()
API 的限制條件物件中設定 audio: true
。
<audio id="player" controls></audio>
<script>
const player = document.getElementById('player');
const handleSuccess = function (stream) {
if (window.URL) {
player.srcObject = stream;
} else {
player.src = stream;
}
};
navigator.mediaDevices
.getUserMedia({audio: true, video: false})
.then(handleSuccess);
</script>
如果您想選擇特定麥克風,可以先列舉可用的麥克風。
navigator.mediaDevices.enumerateDevices().then((devices) => {
devices = devices.filter((d) => d.kind === 'audioinput');
});
然後,您可以在呼叫 getUserMedia
時傳遞要使用的 deviceId
。
navigator.mediaDevices.getUserMedia({
audio: {
deviceId: devices[0].deviceId,
},
});
這項資訊本身並沒有太大用處。我們只能擷取音訊資料並播放。
存取麥克風的原始資料
如要存取麥克風的原始資料,我們必須使用 getUserMedia()
建立的串流,然後使用 Web Audio API 處理資料。Web Audio API 是一種簡單的 API,可接收輸入來源,並將這些來源連結至可處理音訊資料 (調整增益等) 的節點,最後連結至揚聲器,讓使用者能夠聽到音訊。
您可以連結的其中一個節點是 AudioWorkletNode
。這個節點可提供自訂音訊處理功能的低階功能。實際的音訊處理作業會在 AudioWorkletProcessor
的 process()
回呼方法中執行。呼叫這個函式可提供輸入內容和參數,並擷取輸出內容。
詳情請參閱「輸入音訊工作區」一文。
<script>
const handleSuccess = async function(stream) {
const context = new AudioContext();
const source = context.createMediaStreamSource(stream);
await context.audioWorklet.addModule("processor.js");
const worklet = new AudioWorkletNode(context, "worklet-processor");
source.connect(worklet);
worklet.connect(context.destination);
};
navigator.mediaDevices.getUserMedia({ audio: true, video: false })
.then(handleSuccess);
</script>
// processor.js
class WorkletProcessor extends AudioWorkletProcessor {
process(inputs, outputs, parameters) {
// Do something with the data, e.g. convert it to WAV
console.log(inputs);
return true;
}
}
registerProcessor("worklet-processor", WorkletProcessor);
緩衝區中儲存的資料是來自麥克風的原始資料,您可以透過多種方式處理這些資料:
- 直接上傳至伺服器
- 儲存在本機
- 將其轉換為專用檔案格式 (例如 WAV),然後儲存至伺服器或本機
儲存麥克風資料
如要儲存麥克風資料,最簡單的方法就是使用 MediaRecorder
API。
MediaRecorder
API 會採用 getUserMedia
建立的串流,然後逐步將串流中的資料儲存到您偏好的目的地。
<a id="download">Download</a>
<button id="stop">Stop</button>
<script>
const downloadLink = document.getElementById('download');
const stopButton = document.getElementById('stop');
const handleSuccess = function(stream) {
const options = {mimeType: 'audio/webm'};
const recordedChunks = [];
const mediaRecorder = new MediaRecorder(stream, options);
mediaRecorder.addEventListener('dataavailable', function(e) {
if (e.data.size > 0) recordedChunks.push(e.data);
});
mediaRecorder.addEventListener('stop', function() {
downloadLink.href = URL.createObjectURL(new Blob(recordedChunks));
downloadLink.download = 'acetest.wav';
});
stopButton.addEventListener('click', function() {
mediaRecorder.stop();
});
mediaRecorder.start();
};
navigator.mediaDevices.getUserMedia({ audio: true, video: false })
.then(handleSuccess);
</script>
在本例中,我們將資料直接儲存到陣列,之後再轉換為 Blob
,然後用於將資料儲存到 Web 伺服器,或直接儲存到使用者裝置的儲存空間。
以負責任的態度要求使用麥克風
如果使用者先前未授予網站存取麥克風的權限,您一呼叫 getUserMedia
,瀏覽器就會提示使用者授予網站麥克風權限。
使用者不喜歡收到要求存取機器上強大裝置的提示,因此經常會封鎖這類要求,如果他們不瞭解提示的建立背景,也會忽略這類提示。最佳做法是僅在首次需要時要求存取麥克風。使用者授予存取權後,系統就不會再要求他們授權,但如果他們拒絕授權,您就無法再次要求他們授權。
使用權限 API 檢查是否已具備存取權
getUserMedia
API 無法提供您是否已取得麥克風存取權的資訊。這會導致一個問題:為了提供良好的 UI,讓使用者授予麥克風存取權,您必須要求麥克風存取權。
您可以使用 Permission API 在部分瀏覽器中解決這個問題。navigator.permission
API 可讓您查詢存取特定 API 的狀態,而不必再次提示。
如要查詢是否有權存取使用者的麥克風,您可以將 {name: 'microphone'}
傳入查詢方法,並傳回下列任一項目:
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 () {};
});