ปัจจุบันเบราว์เซอร์จำนวนมากสามารถเข้าถึงอินพุตวิดีโอและเสียงจากผู้ใช้ได้แล้ว อย่างไรก็ตาม ประสบการณ์การใช้งานอาจเป็นแบบไดนามิกและแสดงในบรรทัดเต็มรูปแบบ หรืออาจมอบสิทธิ์ให้แอปอื่นในอุปกรณ์ของผู้ใช้ ทั้งนี้ขึ้นอยู่กับเบราว์เซอร์
เริ่มต้นแบบง่ายและค่อยเป็นค่อยไป
สิ่งที่ง่ายที่สุดคือขอให้ผู้ใช้ส่งไฟล์ที่บันทึกไว้ล่วงหน้า โดยสร้างองค์ประกอบอินพุตไฟล์ธรรมดา แล้วเพิ่มตัวกรอง 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 และใช้ฟิลเตอร์กับเสียง
แม้ว่าการใช้เมธอดองค์ประกอบอินพุตในการเข้าถึงข้อมูลเสียงจะพบได้ทั่วไป แต่ตัวเลือกนี้กลับไม่น่าดึงดูดใจที่สุด เราต้องการเข้าถึงไมโครโฟนและมอบประสบการณ์การใช้งานที่ยอดเยี่ยมในหน้าเว็บโดยตรง
เข้าถึงไมโครโฟนแบบอินเทอร์แอกทีฟ
เบราว์เซอร์สมัยใหม่สามารถเชื่อมต่อกับไมโครโฟนโดยตรง ซึ่งช่วยให้เราสร้างประสบการณ์การใช้งานที่ผสานรวมกับหน้าเว็บอย่างสมบูรณ์และผู้ใช้ไม่ต้องออกจากเบราว์เซอร์
รับสิทธิ์เข้าถึงไมโครโฟน
เราเข้าถึงไมโครโฟนโดยตรงได้โดยใช้ API ในข้อกำหนด WebRTC ที่ชื่อ getUserMedia()
getUserMedia()
จะแจ้งให้ผู้ใช้อนุญาตให้เข้าถึงไมโครโฟนและกล้องที่เชื่อมต่อ
หากดำเนินการสำเร็จ API จะแสดงผล Stream
ที่มีข้อมูลจากกล้องหรือไมโครโฟน จากนั้นเราจะแนบ Stream
นั้นกับองค์ประกอบ <audio>
, แนบกับสตรีม WebRTC, แนบกับ 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 ง่ายๆ ที่รับแหล่งที่มาของอินพุตและเชื่อมต่อแหล่งที่มาเหล่านั้นกับโหนดที่ประมวลผลข้อมูลเสียงได้ (ปรับ Gain ฯลฯ) และเชื่อมต่อกับลำโพงเพื่อให้ผู้ใช้ได้ยินเสียง
โหนดที่คุณเชื่อมต่อได้คือ AudioWorkletNode
นอตนี้จะให้ความสามารถระดับล่างสำหรับการประมวลผลเสียงที่กำหนดเอง การประมวลผลเสียงจริงเกิดขึ้นในเมธอด Callback ของ 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
เบราว์เซอร์จะแจ้งให้ผู้ใช้ให้สิทธิ์เข้าถึงไมโครโฟนแก่เว็บไซต์ของคุณ
ผู้ใช้ไม่ชอบที่ระบบแจ้งให้เข้าถึงอุปกรณ์ที่มีประสิทธิภาพบนเครื่องของตน และมักจะบล็อกคำขอ หรือจะเพิกเฉยหากไม่เข้าใจบริบทของการสร้างข้อความแจ้ง แนวทางปฏิบัติแนะนำคือการขอสิทธิ์เข้าถึงไมโครโฟนเฉพาะเมื่อจำเป็นเท่านั้น เมื่อผู้ใช้ให้สิทธิ์เข้าถึงแล้ว ระบบจะไม่ขอสิทธิ์อีก แต่หากผู้ใช้ปฏิเสธสิทธิ์เข้าถึง คุณจะขอสิทธิ์จากผู้ใช้ไม่ได้อีก
ใช้ Permissions 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 () {};
});