ขณะนี้เบราว์เซอร์จำนวนมากสามารถเข้าถึงอินพุตวิดีโอและเสียงจากผู้ใช้ได้แล้ว อย่างไรก็ตาม เบราว์เซอร์อาจเป็นประสบการณ์แบบอินไลน์แบบไดนามิกแบบสมบูรณ์ หรืออาจมีการมอบสิทธิ์ให้กับแอปอื่นในอุปกรณ์ของผู้ใช้ ทั้งนี้ขึ้นอยู่กับเบราว์เซอร์นั้นๆ
เริ่มต้นอย่างง่ายๆ และค่อยๆ เติบโต
วิธีที่ง่ายที่สุดคือขอไฟล์ที่บันทึกไว้ล่วงหน้าจากผู้ใช้ คุณสามารถทำได้โดยการสร้างองค์ประกอบอินพุตไฟล์อย่างง่ายและเพิ่มตัวกรอง accept
ที่บ่งชี้ว่าเรารับเฉพาะไฟล์เสียงเท่านั้น และแอตทริบิวต์ capture
ที่ระบุว่าเราต้องการรับจากไมโครโฟนโดยตรง
<input type="file" accept="audio/*" capture />
วิธีนี้ใช้งานได้ในทุกแพลตฟอร์ม ในเดสก์ท็อป ระบบจะแจ้งให้ผู้ใช้อัปโหลดไฟล์จากระบบไฟล์ (ไม่สนใจแอตทริบิวต์ capture
) ใน Safari บน iOS แอปจะเปิดแอปไมโครโฟนซึ่งให้คุณบันทึกเสียงแล้วส่งกลับไปยังหน้าเว็บ ส่วนใน 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 และใช้ตัวกรองกับ Web Audio
แม้ว่าการใช้วิธีการขององค์ประกอบอินพุตเพื่อเข้าถึงข้อมูลเสียงมีแพร่หลาย แต่วิธีนี้ก็เป็นตัวเลือกที่น่าสนใจน้อยที่สุด เราต้องการเข้าถึงไมโครโฟน และมอบประสบการณ์การใช้งานที่ดีในหน้าเว็บโดยตรง
เข้าถึงไมโครโฟนแบบอินเทอร์แอกทีฟ
เบราว์เซอร์ที่ทันสมัยสามารถต่อสายตรงไปยังไมโครโฟนช่วยให้เราสร้างประสบการณ์ที่ผสานรวมเข้ากับหน้าเว็บได้อย่างเต็มที่โดยที่ผู้ใช้จะไม่ต้องออกจากเบราว์เซอร์
รับสิทธิ์เข้าถึงไมโครโฟน
เราเข้าถึงไมโครโฟนได้โดยตรงโดยใช้ API ในข้อกำหนดของ WebRTC ที่ชื่อ getUserMedia()
getUserMedia()
จะแจ้งให้ผู้ใช้
เข้าถึงไมโครโฟนและกล้องที่เชื่อมต่ออยู่
หาก API เสร็จสมบูรณ์ จะส่งกลับ Stream
ที่จะมีข้อมูลจากกล้องหรือไมโครโฟน จากนั้นเราสามารถแนบกับองค์ประกอบ <audio>
, แนบไปยังสตรีม WebRTC, แนบกับ Web Audio AudioContext
หรือบันทึกโดยใช้ MediaRecorder
API
หากต้องการรับข้อมูลจากไมโครโฟน เราเพิ่งตั้งค่า audio: true
ในออบเจ็กต์ข้อจำกัดที่ส่งไปยัง getUserMedia()
API
<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');
});
จากนั้น คุณจะส่งเงิน deviceId
ที่ต้องการใช้เมื่อโทรหา getUserMedia
ได้
navigator.mediaDevices.getUserMedia({
audio: {
deviceId: devices[0].deviceId,
},
});
จริงๆ แล้ว การดำเนินการนี้ไม่มีประโยชน์ สิ่งที่เราทำได้มีเพียงการนำข้อมูลเสียงมาเล่น
เข้าถึงข้อมูลดิบจากไมโครโฟน
ในการเข้าถึงข้อมูลดิบจากไมโครโฟน เราจะต้องใช้สตรีมที่สร้างโดย getUserMedia()
แล้วใช้ Web Audio API เพื่อประมวลผลข้อมูล Web Audio API เป็น API แบบง่ายที่ใช้แหล่งที่มาของอินพุตและเชื่อมต่อแหล่งที่มาเหล่านั้นกับโหนดที่สามารถประมวลผลข้อมูลเสียง (ปรับค่าเกน ฯลฯ) และกับลำโพงในท้ายที่สุดเพื่อให้ผู้ใช้ได้ยินเสียง
หนึ่งในโหนดที่คุณเชื่อมต่อได้คือ AudioWorkletNode
โหนดนี้ช่วยให้คุณมีขีดความสามารถระดับต่ำสำหรับการประมวลผลเสียงที่กำหนดเอง การประมวลผลเสียงจริงจะเกิดขึ้นในเมธอดโค้ดเรียกกลับ process()
ใน AudioWorkletProcessor
เรียกใช้ฟังก์ชันนี้เพื่อป้อนอินพุตและพารามิเตอร์ รวมทั้งดึงข้อมูลเอาต์พุต
ดูข้อมูลเพิ่มเติมได้ที่ Enter Audio Worklet
<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
ในภายหลังได้ ซึ่งหลังจากนั้นสามารถนํามาใช้เพื่อบันทึกข้อมูลลงในเว็บเซิร์ฟเวอร์หรือลงพื้นที่เก็บข้อมูลในอุปกรณ์ของผู้ใช้ได้โดยตรง
ขอสิทธิ์ใช้ไมโครโฟนอย่างมีความรับผิดชอบ
หากผู้ใช้ยังไม่เคยให้สิทธิ์เว็บไซต์เข้าถึงไมโครโฟนไว้ เมื่อคุณเรียกใช้ getUserMedia
เบราว์เซอร์จะแจ้งให้ผู้ใช้ให้สิทธิ์ไมโครโฟนแก่เว็บไซต์
ผู้ใช้ไม่ชอบได้รับแจ้งเรื่องการเข้าถึงอุปกรณ์ที่มีประสิทธิภาพในเครื่องและจะบล็อกคำขออยู่บ่อยครั้ง หรืออาจจะเพิกเฉยต่อคำขอหากไม่เข้าใจบริบทของข้อความแจ้งที่สร้างขึ้น วิธีที่ดีที่สุดคือการขอสิทธิ์เข้าถึงไมโครโฟน ในครั้งแรกที่จำเป็นเท่านั้น เมื่อผู้ใช้ให้สิทธิ์การเข้าถึงแล้ว ระบบจะไม่ถามผู้ใช้อีก แต่หากปฏิเสธการเข้าถึง คุณจะขอสิทธิ์จากผู้ใช้ไม่ได้อีก
ใช้ API สิทธิ์เพื่อตรวจสอบว่าคุณมีสิทธิ์เข้าถึงอยู่แล้วหรือไม่
getUserMedia
API จะแจ้งให้คุณทราบว่าคุณเข้าถึงไมโครโฟนได้แล้วหรือยัง วิธีนี้ทำให้เกิดปัญหากับคุณ คุณต้องขอสิทธิ์เข้าถึงไมโครโฟนเพื่อจัดเตรียม UI ที่ดีเพื่อให้ผู้ใช้ให้สิทธิ์คุณเข้าถึงไมโครโฟนได้
ซึ่งแก้ไขได้ในบางเบราว์เซอร์โดยใช้ Permission API API navigator.permission
ช่วยให้คุณค้นหาสถานะความสามารถในการเข้าถึง 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 () {};
});