ก่อนที่จะมีองค์ประกอบ <audio> ของ HTML5 คุณต้องใช้ Flash หรือปลั๊กอินอื่นๆ เพื่อให้เว็บมีเสียง แม้ว่าเสียงบนเว็บจะไม่ต้องใช้ปลั๊กอินอีกต่อไป แต่แท็กเสียงก็มีข้อจำกัดที่สำคัญสำหรับการใช้งานเกมที่ซับซ้อนและแอปพลิเคชันแบบอินเทอร์แอกทีฟ
Web Audio API เป็น JavaScript API ระดับสูงสำหรับการประมวลผลและสังเคราะห์เสียงในเว็บแอปพลิเคชัน เป้าหมายของ API นี้คือการรวมความสามารถที่พบในเครื่องมือเสียงของเกมสมัยใหม่ รวมถึงงานมิกซ์ ประมวลผล และกรองบางอย่างที่พบในแอปพลิเคชันการผลิตเสียงบนเดสก์ท็อปสมัยใหม่ ต่อไปนี้เป็นข้อมูลเบื้องต้นเกี่ยวกับการใช้ API ที่มีประสิทธิภาพนี้
เริ่มต้นใช้งาน AudioContext
AudioContext มีไว้สำหรับจัดการและเล่นเสียงทั้งหมด หากต้องการสร้างเสียงโดยใช้ Web Audio API ให้สร้างแหล่งที่มาของเสียงอย่างน้อย 1 แหล่งและเชื่อมต่อกับปลายทางเสียงที่อินสแตนซ์ AudioContext มีให้ การเชื่อมต่อนี้ไม่จำเป็นต้องเป็นแบบโดยตรง และสามารถผ่าน AudioNodes ระดับกลางจำนวนเท่าใดก็ได้ซึ่งทำหน้าที่เป็นโมดูลประมวลผลสำหรับสัญญาณเสียง การกำหนดเส้นทางนี้อธิบายไว้อย่างละเอียดในข้อกำหนดของ Web Audio
AudioContext อินสแตนซ์เดียวสามารถรองรับอินพุตเสียงหลายรายการและกราฟเสียงที่ซับซ้อนได้ ดังนั้นเราจึงต้องการเพียงอินสแตนซ์เดียวสำหรับแอปพลิเคชันเสียงแต่ละแอปที่เราสร้าง
ข้อมูลโค้ดต่อไปนี้จะสร้าง AudioContext
var context;
window.addEventListener('load', init, false);
function init() {
try {
context = new AudioContext();
}
catch(e) {
alert('Web Audio API is not supported in this browser');
}
}
สำหรับเบราว์เซอร์ที่ใช้ WebKit รุ่นเก่า ให้ใช้คำนำหน้า webkit เช่นเดียวกับ webkitAudioContext
ฟังก์ชันการทำงานของ Web Audio API ที่น่าสนใจหลายอย่าง เช่น การสร้าง AudioNode และการถอดรหัสข้อมูลไฟล์เสียง เป็นเมธอดของ AudioContext
กำลังโหลดเสียง
Web Audio API ใช้ AudioBuffer สำหรับเสียงที่มีความยาวสั้นถึงปานกลาง วิธีพื้นฐานคือการใช้ XMLHttpRequest เพื่อดึงไฟล์เสียง
API รองรับการโหลดข้อมูลไฟล์เสียงในหลายรูปแบบ เช่น WAV, MP3, AAC, OGG และอื่นๆ การรองรับรูปแบบเสียงต่างๆ ของเบราว์เซอร์จะแตกต่างกันไป
ข้อมูลโค้ดต่อไปนี้แสดงการโหลดตัวอย่างเสียง
var dogBarkingBuffer = null;
var context = new AudioContext();
function loadDogSound(url) {
var request = new XMLHttpRequest();
request.open('GET', url, true);
request.responseType = 'arraybuffer';
// Decode asynchronously
request.onload = function() {
context.decodeAudioData(request.response, function(buffer) {
dogBarkingBuffer = buffer;
}, onError);
}
request.send();
}
ข้อมูลไฟล์เสียงเป็นแบบไบนารี (ไม่ใช่ข้อความ) ดังนั้นเราจึงตั้งค่า responseType ของคำขอเป็น 'arraybuffer' ดูข้อมูลเพิ่มเติมเกี่ยวกับ ArrayBuffers ได้ที่บทความเกี่ยวกับ XHR2
เมื่อได้รับข้อมูลไฟล์เสียง (ที่ยังไม่ได้ถอดรหัส) แล้ว คุณสามารถเก็บข้อมูลไว้เพื่อถอดรหัสในภายหลัง หรือจะถอดรหัสทันทีโดยใช้เมธอด decodeAudioData() ของ AudioContext ก็ได้ วิธีนี้จะใช้ ArrayBuffer ของข้อมูลไฟล์เสียงที่จัดเก็บไว้ใน request.response และถอดรหัสแบบอะซิงโครนัส (ไม่บล็อกชุดข้อความการดำเนินการ JavaScript หลัก)
เมื่อ decodeAudioData() ทำงานเสร็จแล้ว ระบบจะเรียกใช้ฟังก์ชัน Callback ซึ่งให้ข้อมูลเสียง PCM ที่ถอดรหัสแล้วเป็น AudioBuffer
กำลังเล่นเสียง
เมื่อโหลด AudioBuffers อย่างน้อย 1 รายการแล้ว เราก็พร้อมที่จะเล่นเสียง สมมติว่าเราเพิ่งโหลด AudioBuffer ที่มีเสียงสุนัขเห่าและโหลดเสร็จแล้ว จากนั้นเราจะเล่นบัฟเฟอร์นี้ได้ด้วยโค้ดต่อไปนี้
var context = new AudioContext();
function playSound(buffer) {
var source = context.createBufferSource(); // creates a sound source
source.buffer = buffer; // tell the source which sound to play
source.connect(context.destination); // connect the source to the context's destination (the speakers)
source.noteOn(0); // play the source now
}
ระบบอาจเรียกใช้ฟังก์ชัน playSound() นี้ทุกครั้งที่มีคนกดแป้นหรือคลิกบางอย่างด้วยเมาส์
ฟังก์ชัน noteOn(time) ช่วยให้กำหนดเวลาการเล่นเสียงที่แม่นยำสำหรับเกมและแอปพลิเคชันอื่นๆ ที่มีความสำคัญต่อเวลาได้ง่าย อย่างไรก็ตาม เพื่อให้การกำหนดเวลานี้ทำงานได้อย่างถูกต้อง โปรดตรวจสอบว่าได้โหลดบัฟเฟอร์เสียงไว้ล่วงหน้าแล้ว
การแยก Web Audio API
แน่นอนว่าการสร้างระบบการโหลดทั่วไปที่ไม่ได้ฮาร์ดโค้ดเพื่อโหลดเสียงที่เฉพาะเจาะจงนี้จะดีกว่า มีหลายวิธีในการจัดการกับเสียงสั้นถึงปานกลางจำนวนมากที่แอปพลิเคชันเสียงหรือเกมจะใช้ วิธีหนึ่งคือการใช้ BufferLoader (ไม่ได้เป็นส่วนหนึ่งของมาตรฐานเว็บ)
ต่อไปนี้เป็นตัวอย่างวิธีใช้คลาส BufferLoader
มาสร้าง AudioBuffers 2 รายการกัน และทันทีที่โหลดเสร็จแล้ว ให้เล่นทั้ง 2 รายการพร้อมกัน
window.onload = init;
var context;
var bufferLoader;
function init() {
context = new AudioContext();
bufferLoader = new BufferLoader(
context,
[
'../sounds/hyper-reality/br-jam-loop.wav',
'../sounds/hyper-reality/laughter.wav',
],
finishedLoading
);
bufferLoader.load();
}
function finishedLoading(bufferList) {
// Create two sources and play them both together.
var source1 = context.createBufferSource();
var source2 = context.createBufferSource();
source1.buffer = bufferList[0];
source2.buffer = bufferList[1];
source1.connect(context.destination);
source2.connect(context.destination);
source1.noteOn(0);
source2.noteOn(0);
}
การจัดการกับเวลา: การเล่นเสียงตามจังหวะ
Web Audio API ช่วยให้นักพัฒนาแอปกำหนดเวลาการเล่นได้อย่างแม่นยำ เพื่อสาธิตเรื่องนี้ เรามาสร้างแทร็กจังหวะง่ายๆ กัน รูปแบบกลองชุดที่น่าจะ เป็นที่รู้จักมากที่สุดคือรูปแบบต่อไปนี้
ซึ่งจะเล่นไฮแฮททุกๆ โน้ตเบ็ดหนึ่งชั้น และเล่นกระเดื่องกับสแนร์สลับกันทุกๆ โน้ตตัวดำในจังหวะ 4/4
สมมติว่าเราได้โหลดบัฟเฟอร์ kick, snare และ hihat แล้ว โค้ดที่ใช้ทำสิ่งนี้จะง่ายมาก
for (var bar = 0; bar < 2; bar++) {
var time = startTime + bar * 8 * eighthNoteTime;
// Play the bass (kick) drum on beats 1, 5
playSound(kick, time);
playSound(kick, time + 4 * eighthNoteTime);
// Play the snare drum on beats 3, 7
playSound(snare, time + 2 * eighthNoteTime);
playSound(snare, time + 6 * eighthNoteTime);
// Play the hi-hat every eighth note.
for (var i = 0; i < 8; ++i) {
playSound(hihat, time + i * eighthNoteTime);
}
}
ในที่นี้ เราจะเล่นซ้ำเพียงครั้งเดียวแทนที่จะเล่นวนซ้ำแบบไม่จำกัดตามที่เห็นในโน้ตเพลง ฟังก์ชัน playSound เป็นเมธอดที่เล่นบัฟเฟอร์ตามเวลาที่ระบุ ดังนี้
function playSound(buffer, time) {
var source = context.createBufferSource();
source.buffer = buffer;
source.connect(context.destination);
source.noteOn(time);
}
การเปลี่ยนระดับเสียง
การดำเนินการขั้นพื้นฐานที่สุดอย่างหนึ่งที่คุณอาจต้องการทำกับเสียงคือการเปลี่ยนระดับเสียง เมื่อใช้ Web Audio API เราสามารถกำหนดเส้นทางแหล่งที่มาไปยังปลายทางผ่าน AudioGainNode เพื่อปรับระดับเสียงได้ ดังนี้

การตั้งค่าการเชื่อมต่อนี้ทำได้ดังนี้
// Create a gain node.
var gainNode = context.createGainNode();
// Connect the source to the gain node.
source.connect(gainNode);
// Connect the gain node to the destination.
gainNode.connect(context.destination);
หลังจากตั้งค่ากราฟแล้ว คุณสามารถเปลี่ยนระดับเสียงแบบเป็นโปรแกรมได้โดยการจัดการ gainNode.gain.value ดังนี้
// Reduce the volume.
gainNode.gain.value = 0.5;
การเฟดเสียงข้ามระหว่าง 2 เสียง
ทีนี้ สมมติว่าเรามีสถานการณ์ที่ซับซ้อนขึ้นเล็กน้อย โดยเรากำลังเล่นเสียงหลายเสียงแต่ต้องการทำครอสเฟดระหว่างเสียงเหล่านั้น กรณีนี้เป็นกรณีที่พบบ่อยในแอปพลิเคชันที่คล้ายกับดีเจ ซึ่งเรามีเครื่องเล่นแผ่นเสียง 2 เครื่องและต้องการแพนจากแหล่งเสียงหนึ่งไปยังอีกแหล่งเสียงหนึ่งได้
ซึ่งทำได้โดยใช้กราฟเสียงต่อไปนี้
ในการตั้งค่านี้ เราเพียงสร้าง AudioGainNodes 2 รายการ และเชื่อมต่อแต่ละแหล่งที่มาผ่านโหนด โดยใช้ฟังก์ชันต่อไปนี้
function createSource(buffer) {
var source = context.createBufferSource();
// Create a gain node.
var gainNode = context.createGainNode();
source.buffer = buffer;
// Turn on looping.
source.loop = true;
// Connect source to gain.
source.connect(gainNode);
// Connect gain to destination.
gainNode.connect(context.destination);
return {
source: source,
gainNode: gainNode
};
}
การทำครอสเฟดแบบ Equal Power
วิธีการครอสเฟดเชิงเส้นแบบง่ายจะแสดงการลดระดับเสียงเมื่อคุณแพนระหว่างตัวอย่าง
เพื่อแก้ไขปัญหานี้ เราใช้เส้นโค้งกำลังเท่ากัน ซึ่งเส้นโค้งอัตราขยายที่สอดคล้องกันจะไม่เป็นเชิงเส้น และตัดกันที่แอมพลิจูดที่สูงขึ้น ซึ่งจะช่วยลดการลดลงของระดับเสียงระหว่างขอบเขตเสียง ส่งผลให้การทำครอสเฟดระหว่างขอบเขตที่มีระดับเสียงต่างกันเล็กน้อยมีความสม่ำเสมอมากขึ้น
การเฟดไขว้เพลย์ลิสต์
แอปพลิเคชัน Crossfader ที่พบได้ทั่วไปอีกแอปหนึ่งคือแอปพลิเคชันโปรแกรมเล่นเพลง
เมื่อเพลงเปลี่ยน เราต้องการเฟดแทร็กปัจจุบันออกและเฟดแทร็กใหม่เข้า เพื่อหลีกเลี่ยงการเปลี่ยนเพลงที่กระทันหัน โดยกำหนดเวลาการครอสเฟดในอนาคต แม้ว่าเราจะใช้ setTimeout เพื่อกำหนดเวลานี้ได้ แต่ก็ไม่แม่นยำ เมื่อใช้ Web Audio API เราสามารถใช้อินเทอร์เฟซ AudioParam เพื่อกำหนดเวลาค่าในอนาคตสำหรับพารามิเตอร์ต่างๆ เช่น ค่าเกนของ AudioGainNode
ดังนั้น เมื่อมีเพลย์ลิสต์ เราสามารถเปลี่ยนระหว่างแทร็กได้โดยกำหนดเวลาลดเกนในแทร็กที่กำลังเล่นอยู่ และเพิ่มเกนในแทร็กถัดไป โดยทั้ง 2 อย่างจะเกิดขึ้นก่อนที่แทร็กปัจจุบันจะเล่นจบเล็กน้อย
function playHelper(bufferNow, bufferLater) {
var playNow = createSource(bufferNow);
var source = playNow.source;
var gainNode = playNow.gainNode;
var duration = bufferNow.duration;
var currTime = context.currentTime;
// Fade the playNow track in.
gainNode.gain.linearRampToValueAtTime(0, currTime);
gainNode.gain.linearRampToValueAtTime(1, currTime + ctx.FADE_TIME);
// Play the playNow track.
source.noteOn(0);
// At the end of the track, fade it out.
gainNode.gain.linearRampToValueAtTime(1, currTime + duration-ctx.FADE_TIME);
gainNode.gain.linearRampToValueAtTime(0, currTime + duration);
// Schedule a recursive track change with the tracks swapped.
var recurse = arguments.callee;
ctx.timer = setTimeout(function() {
recurse(bufferLater, bufferNow);
}, (duration - ctx.FADE_TIME) - 1000);
}
Web Audio API มีชุดเมธอด RampToValue ที่สะดวกในการเปลี่ยนค่าของพารามิเตอร์ทีละน้อย เช่น linearRampToValueAtTime และ exponentialRampToValueAtTime
แม้ว่าคุณจะเลือกฟังก์ชันเวลาการเปลี่ยนภาพจากฟังก์ชันเชิงเส้นและแบบเอกซ์โพเนนเชียลในตัวได้ (ดังที่แสดงด้านบน) แต่คุณยังระบุเส้นโค้งค่าของคุณเองผ่านอาร์เรย์ของค่าได้โดยใช้ฟังก์ชัน setValueCurveAtTime
การใช้เอฟเฟกต์ฟิลเตอร์ง่ายๆ กับเสียง

Web Audio API ช่วยให้คุณส่งเสียงจากโหนดเสียงหนึ่งไปยังอีกโหนดหนึ่งได้ ซึ่งจะสร้างเชนโปรเซสเซอร์ที่อาจซับซ้อนเพื่อเพิ่มเอฟเฟกต์ที่ซับซ้อนให้กับรูปแบบเสียงของคุณ
วิธีหนึ่งในการดำเนินการนี้คือการวาง BiquadFilterNode ไว้ระหว่างแหล่งที่มาของเสียงและปลายทาง โหนดเสียงประเภทนี้สามารถใช้ตัวกรองลำดับต่ำได้หลากหลาย ซึ่งสามารถใช้เพื่อสร้างอีควอไลเซอร์กราฟิกและเอฟเฟกต์ที่ซับซ้อนยิ่งขึ้น โดยส่วนใหญ่จะเกี่ยวข้องกับการเลือกส่วนของสเปกตรัมความถี่ของเสียงที่จะเน้นและส่วนที่จะลดทอน
ประเภทตัวกรองที่รองรับมีดังนี้
- ตัวกรองความถี่ต่ำผ่าน
- ตัวกรองความถี่สูงผ่าน
- ตัวกรองแถบความถี่
- ตัวกรอง Low Shelf
- ตัวกรอง High Shelf
- ฟิลเตอร์ Peaking
- ตัวกรองน็อตช์
- ตัวกรอง All Pass
และตัวกรองทั้งหมดจะมีพารามิเตอร์เพื่อระบุปริมาณอัตราขยาย ความถี่ในการใช้ตัวกรอง และปัจจัยด้านคุณภาพ ตัวกรองความถี่ต่ำจะเก็บช่วงความถี่ต่ำไว้ แต่จะทิ้งความถี่สูง จุดตัดจะกำหนดโดยค่าความถี่ และปัจจัย Q จะไม่มีหน่วย และกำหนดรูปร่างของกราฟ อัตราขยายจะมีผลกับตัวกรองบางตัวเท่านั้น เช่น ตัวกรอง Low-shelf และ Peaking และไม่มีผลกับตัวกรอง Low-pass นี้
มาตั้งค่าตัวกรองความถี่ต่ำผ่านแบบง่ายเพื่อแยกเฉพาะเสียงเบสจากตัวอย่างเสียงกัน
// Create the filter
var filter = context.createBiquadFilter();
// Create the audio graph.
source.connect(filter);
filter.connect(context.destination);
// Create and specify parameters for the low-pass filter.
filter.type = 0; // Low-pass filter. See BiquadFilterNode docs
filter.frequency.value = 440; // Set cutoff to 440 HZ
// Playback the sound.
source.noteOn(0);
โดยทั่วไปแล้ว การควบคุมความถี่จะต้องได้รับการปรับแต่งให้ทำงานในสเกลลอการิทึม เนื่องจากการได้ยินของมนุษย์ทำงานตามหลักการเดียวกัน (นั่นคือ A4 คือ 440hz และ A5 คือ 880hz) ดูรายละเอียดเพิ่มเติมได้ที่ฟังก์ชัน FilterSample.changeFrequency ในลิงก์ซอร์สโค้ดด้านบน
สุดท้าย โปรดทราบว่าโค้ดตัวอย่างช่วยให้คุณเชื่อมต่อและยกเลิกการเชื่อมต่อตัวกรองได้ ซึ่งจะเปลี่ยนกราฟ AudioContext แบบไดนามิก เราสามารถยกเลิกการเชื่อมต่อ AudioNode จากกราฟได้โดยเรียกใช้ node.disconnect(outputNumber)
เช่น หากต้องการเปลี่ยนเส้นทางกราฟจากการผ่านตัวกรองเป็นการเชื่อมต่อโดยตรง เราสามารถทำดังนี้
// Disconnect the source and filter.
source.disconnect(0);
filter.disconnect(0);
// Connect the source directly.
source.connect(context.destination);
ฟังเพิ่มเติม
เราได้พูดถึงข้อมูลเบื้องต้นเกี่ยวกับ API แล้ว ซึ่งรวมถึงการโหลดและการเล่นตัวอย่างเสียง เราได้สร้างกราฟเสียงที่มีโหนดเกนและตัวกรอง รวมถึงเสียงที่กำหนดเวลาไว้และการปรับแต่งพารามิเตอร์เสียงเพื่อเปิดใช้เอฟเฟกต์เสียงทั่วไปบางอย่าง ถึงตอนนี้ คุณก็พร้อมที่จะสร้างแอปพลิเคชันเสียงบนเว็บที่ยอดเยี่ยมแล้ว
หากคุณกำลังมองหาแรงบันดาลใจ นักพัฒนาซอฟต์แวร์หลายรายได้สร้างผลงานที่ยอดเยี่ยมโดยใช้ Web Audio API แล้ว ตัวอย่างรายการโปรดของฉันมีดังนี้
- AudioJedit เครื่องมือต่อเสียงในเบราว์เซอร์ที่ใช้ลิงก์ถาวรของ SoundCloud
- ToneCraft ซีเควนเซอร์เสียงที่สร้างเสียงโดยการวางบล็อก 3 มิติซ้อนกัน
- Plink เกมสร้างเพลงที่ทำงานร่วมกันโดยใช้ Web Audio และ Web Sockets