วิธีที่เราทำให้เสียงเจ๋ง
บทนำ
JAM with Chrome เป็นโปรเจ็กต์ดนตรีบนเว็บที่ Google สร้างขึ้น JAM with Chrome ช่วยให้ผู้คนจากทั่วโลกสามารถรวมกันเป็นวงดนตรีและเล่นดนตรีร่วมกันแบบเรียลไทม์ในเบราว์เซอร์ เราที่ DinahMoe รู้สึกยินดีอย่างยิ่งที่ได้เป็นส่วนหนึ่งของโปรเจ็กต์นี้ หน้าที่ของเราคือผลิตเพลงสำหรับแอปพลิเคชัน รวมถึงออกแบบและพัฒนาคอมโพเนนต์เพลง การพัฒนานี้ประกอบด้วย 3 หัวข้อหลัก ได้แก่ "เวิร์กสเตชันสำหรับทำเพลง" ซึ่งรวมถึงการเล่น MIDI, โปรแกรมซาวด์แซมเปิล, เอฟเฟกต์เสียง, การกำหนดเส้นทาง และการมิกซ์เพลง เครื่องมือทางตรรกะสำหรับควบคุมเพลงแบบอินเทอร์แอกทีฟแบบเรียลไทม์ และคอมโพเนนต์การซิงค์ที่ช่วยให้ผู้เล่นทุกคนในเซสชันได้ยินเพลงพร้อมกัน ซึ่งเป็นข้อกําหนดเบื้องต้นในการเล่นร่วมกัน
เราเลือกใช้ Web Audio API เพื่อให้ได้ระดับความน่าเชื่อถือ ความแม่นยำ และคุณภาพเสียงสูงสุด กรณีศึกษานี้จะกล่าวถึงความท้าทายบางอย่างที่เราพบและวิธีที่เราแก้ปัญหา บทความแนะนำที่ยอดเยี่ยมจำนวนหนึ่งเกี่ยวกับ Web Audio นั้นมีอยู่แล้วที่ HTML5Rocks เราจึงจะข้ามไปดูเรื่องขั้นสูงเลย
การเขียนเอฟเฟกต์เสียงที่กำหนดเอง
Web Audio API มีเอฟเฟกต์ที่มีประโยชน์หลายรายการรวมอยู่ในข้อกำหนดเฉพาะ แต่เราต้องการเอฟเฟกต์ที่ซับซ้อนมากขึ้นสำหรับเครื่องดนตรีใน JAM with Chrome ตัวอย่างเช่น มีโหนดการหน่วงเวลาแบบเนทีฟใน Web Audio แต่การหน่วงเวลามีหลายประเภท เช่น การหน่วงเวลาสเตอริโอ การหน่วงเวลาแบบปิงปอง การหน่วงเวลาแบบสแลบแบ็ก และอื่นๆ อีกมากมาย แต่โชคดีที่สามารถสร้างเอฟเฟกต์เหล่านี้ทั้งหมดใน Web Audio ได้โดยใช้โหนดเอฟเฟกต์แบบเนทีฟและจินตนาการ
เนื่องจากเราต้องการใช้โหนดเนทีฟและเอฟเฟกต์ที่กำหนดเองของเราในลักษณะที่โปร่งใสมากที่สุด เราจึงตัดสินใจว่าจำเป็นต้องสร้างรูปแบบ Wrapper ที่ทำได้ นอตแบบเนทีฟใน Web Audio ใช้เมธอด connect เพื่อลิงก์นอตเข้าด้วยกัน เราจึงต้องจำลองลักษณะการทำงานนี้ แนวคิดพื้นฐานมีดังนี้
var MyCustomNode = function(){
this.input = audioContext.createGain();
var output = audioContext.createGain();
this.connect = function(target){
output.connect(target);
};
};
รูปแบบนี้ทำให้เราอยู่ใกล้กับโหนดเนทีฟมาก มาดูวิธีใช้กัน
//create a couple of native nodes and our custom node
var gain = audioContext.createGain(),
customNode = new MyCustomNode(),
anotherGain = audioContext.createGain();
//connect our custom node to the native nodes and send to the output
gain.connect(customNode.input);
customNode.connect(anotherGain);
anotherGain.connect(audioContext.destination);
ความแตกต่างเพียงอย่างเดียวระหว่างโหนดที่กําหนดเองกับโหนดที่มาพร้อมระบบคือเราต้องเชื่อมต่อกับพร็อพเพอร์ตี้อินพุตของโหนดที่กําหนดเอง เราทราบดีว่าอาจมีวิธีหลีกเลี่ยงปัญหานี้ แต่วิธีนี้ก็เพียงพอสำหรับวัตถุประสงค์ของเรา รูปแบบนี้สามารถพัฒนาเพิ่มเติมเพื่อจำลองวิธีการยกเลิกการเชื่อมต่อของ AudioNodes เดิม รวมถึงรองรับอินพุต/เอาต์พุตที่ผู้ใช้กำหนดเมื่อเชื่อมต่อ และอื่นๆ โปรดดูข้อกําหนดเพื่อดูว่าโหนดแบบเนทีฟทําสิ่งใดได้บ้าง
ตอนนี้เรามีรูปแบบพื้นฐานในการสร้างเอฟเฟกต์ที่กำหนดเองแล้ว ขั้นตอนถัดไปคือการทำให้โหนดที่กำหนดเองมีลักษณะการทำงานที่กำหนดเอง มาดูโหนดดีเลย์แบบตบกลับกัน
ตอบกลับอย่างจริงจัง
ดีเลย์แบบสแลบแบ็ก (บางครั้งเรียกว่าเสียงสะท้อนแบบสแลบแบ็ก) เป็นเอฟเฟกต์คลาสสิกที่ใช้กับเครื่องดนตรีหลายชนิด ตั้งแต่เสียงร้องสไตล์ยุค 50 ไปจนถึงกีตาร์แนวเซอร์ฟ เอฟเฟกต์นี้จะรับเสียงที่เข้ามาและเล่นสำเนาของเสียงนั้นโดยมีความล่าช้าเล็กน้อยประมาณ 75-250 มิลลิวินาที ซึ่งให้ความรู้สึกเหมือนเสียงถูกตบกลับ จึงมีชื่อเช่นนั้น เราสามารถสร้างเอฟเฟกต์ได้ดังนี้
var SlapbackDelayNode = function(){
//create the nodes we'll use
this.input = audioContext.createGain();
var output = audioContext.createGain(),
delay = audioContext.createDelay(),
feedback = audioContext.createGain(),
wetLevel = audioContext.createGain();
//set some decent values
delay.delayTime.value = 0.15; //150 ms delay
feedback.gain.value = 0.25;
wetLevel.gain.value = 0.25;
//set up the routing
this.input.connect(delay);
this.input.connect(output);
delay.connect(feedback);
delay.connect(wetLevel);
feedback.connect(delay);
wetLevel.connect(output);
this.connect = function(target){
output.connect(target);
};
};
ดังที่บางท่านอาจทราบแล้ว ดีเลย์นี้สามารถใช้กับเวลาหน่วงที่นานขึ้นได้ด้วย ซึ่งจะกลายเป็นดีเลย์โมโนแบบปกติที่มีเสียงสะท้อน ต่อไปนี้เป็นตัวอย่างการใช้การเลื่อนเวลานี้เพื่อให้คุณได้ยินเสียง
การกำหนดเส้นทางเสียง
เมื่อทำงานกับเครื่องดนตรีและส่วนต่างๆ ของเพลงในแอปพลิเคชันเสียงระดับมืออาชีพ คุณต้องมีระบบการกำหนดเส้นทางที่ยืดหยุ่นซึ่งช่วยให้คุณผสมและปรับแต่งเสียงได้อย่างมีประสิทธิภาพ ใน JAM with Chrome เราได้พัฒนาระบบบัสเสียงที่คล้ายกับระบบที่มีอยู่ในบอร์ดมิกซ์จริง วิธีนี้ช่วยให้เราเชื่อมต่อเครื่องดนตรีทั้งหมดที่ต้องการเอฟเฟกต์เสียงสะท้อนกับบัสหรือแชแนลทั่วไป แล้วเพิ่มเสียงสะท้อนลงในบัสนั้นแทนที่จะเพิ่มเสียงสะท้อนลงในเครื่องดนตรีแต่ละเครื่องแยกกัน นี่เป็นการเพิ่มประสิทธิภาพที่สำคัญและเราขอแนะนำให้ทำสิ่งต่อไปนี้ทันทีที่คุณเริ่มทำแอปพลิเคชันที่ซับซ้อนมากขึ้น
โชคดีที่การดำเนินการนี้ทำได้ง่ายมากใน Web Audio โดยพื้นฐานแล้ว เราสามารถใช้โครงกระดูกที่เรากำหนดไว้สำหรับเอฟเฟกต์และใช้โครงกระดูกนั้นในลักษณะเดียวกัน
var AudioBus = function(){
this.input = audioContext.createGain();
var output = audioContext.createGain();
//create effect nodes (Convolver and Equalizer are other custom effects from the library presented at the end of the article)
var delay = new SlapbackDelayNode(),
convolver = new tuna.Convolver(),
equalizer = new tuna.Equalizer();
//route 'em
//equalizer -> delay -> convolver
this.input.connect(equalizer);
equalizer.connect(delay.input);
delay.connect(convolver);
convolver.connect(output);
this.connect = function(target){
output.connect(target);
};
};
การใช้งานจะมีลักษณะดังนี้
//create some native oscillators and our custom audio bus
var bus = new AudioBus(),
instrument1 = audioContext.createOscillator(),
instrument2 = audioContext.createOscillator(),
instrument3 = audioContext.createOscillator();
//connect our instruments to the same bus
instrument1.connect(bus.input);
instrument2.connect(bus.input);
instrument3.connect(bus.input);
bus.connect(audioContext.destination);
และเราก็ได้ใส่เอฟเฟกต์ดีเลย์ อีควอไลเซอร์ และรีเวิร์บ (ซึ่งเป็นเอฟเฟกต์ที่ค่อนข้างแพงในแง่ประสิทธิภาพ) ในราคาเพียงครึ่งเดียวเมื่อเทียบกับการใช้เอฟเฟกต์กับเครื่องดนตรีแต่ละชิ้นแยกกัน หากต้องการเพิ่มลูกเล่นให้กับบัส เราอาจเพิ่มโหนดการเพิ่มใหม่ 2 โหนด ได้แก่ preGain และ postGain ซึ่งจะช่วยให้เราปิดหรือเลือนเสียงในบัสได้ 2 วิธี โดยใส่ preGain ไว้ก่อนเอฟเฟกต์ และใส่ postGain ไว้ที่ท้ายเชน หากเราค่อยๆ ลดระดับของ preGain เสียงเอฟเฟกต์จะยังคงดังอยู่หลังจากที่ Gain ลดลงจนสุด แต่หากเราค่อยๆ ลดระดับของ postGain เสียงทั้งหมดจะปิดลงพร้อมกัน
ขั้นตอนถัดไป
วิธีการเหล่านี้ที่เราอธิบายไว้ที่นี่สามารถและควรได้รับการพัฒนาเพิ่มเติม สิ่งต่างๆ เช่น อินพุตและเอาต์พุตของโหนดที่กำหนดเอง และเมธอดเชื่อมต่อ ควร/อาจใช้การสืบทอดตามต้นแบบ โดยบัสควรสร้างเอฟเฟกต์แบบไดนามิกได้ด้วยการรับรายการเอฟเฟกต์
เพื่อเป็นการฉลองการเปิดตัว JAM กับ Chrome เราจึงตัดสินใจที่จะทําให้เฟรมเวิร์กของเอฟเฟกต์เป็นแบบโอเพนซอร์ส หากสนใจ โปรดดูและมีส่วนร่วม ขณะนี้มีการประชุมกันที่นี่เกี่ยวกับการกำหนดรูปแบบมาตรฐานสำหรับเอนทิตีเสียงบนเว็บที่กำหนดเอง มาร่วมกับเรา