กรณีศึกษา - JAM with Chrome

วิธีที่เราทำให้เพลงร็อก

เกริ่นนำ

JAM with Chrome เป็นโครงการเพลงบนเว็บที่ Google สร้างขึ้น JAM ใน Chrome ช่วยให้ผู้คนจากทั่วทุกมุมโลกตั้งวงดนตรีและร่วมวงกันได้แบบเรียลไทม์ภายในเบราว์เซอร์ พวกเราที่ DinahMoe รู้สึกยินดีอย่างยิ่งที่ได้เป็นส่วนหนึ่งของโครงการนี้ บทบาทของเราคือการผลิตเพลงสำหรับแอปพลิเคชัน แล้วออกแบบและพัฒนาองค์ประกอบเพลง การพัฒนานี้ประกอบด้วย 3 ส่วนหลัก ได้แก่ "เวิร์กสเตชันเพลง" ประกอบด้วยการเล่นเพลง MIDI, ซอฟต์แวร์ตัวอย่าง, เอฟเฟกต์เสียง, การกำหนดเส้นทางและการมิกซ์ ตลอดจนเครื่องมือตรรกะของดนตรีที่ควบคุมเพลงด้วยการโต้ตอบแบบเรียลไทม์ และคอมโพเนนต์การซิงค์ที่ดูแลให้ผู้เล่นทุกคนในเซสชันได้ยินเพลงในเวลาเดียวกันซึ่งเป็นข้อกำหนดเบื้องต้นสำหรับการเล่นร่วมกัน

เราเลือกใช้ Web Audio API เพื่อให้ได้ความถูกต้อง ความถูกต้อง และคุณภาพเสียงในระดับสูงสุด กรณีศึกษานี้จะพูดถึงความท้าทายบางส่วนที่เราถูกนำเสนอและวิธีที่เราแก้ปัญหาเหล่านั้น มีบทความแนะนำดีๆ จำนวนหนึ่งที่ HTML5Rocks เพื่อให้คุณได้เริ่มต้นใช้งาน Web Audio ดังนั้นเราจะข้ามไปที่ส่วนปลายสุดของสระว่ายน้ำ

การเขียนเอฟเฟกต์เสียงที่กำหนดเอง

Web Audio API มีเอฟเฟกต์ที่เป็นประโยชน์มากมายรวมอยู่ในข้อกำหนดดังกล่าว แต่เราต้องการเอฟเฟกต์ที่ซับซ้อนยิ่งขึ้นสำหรับเครื่องดนตรีใน JAM ที่มี Chrome ตัวอย่างเช่น จะมีโหนดการหน่วงเวลาดั้งเดิมใน Web Audio แต่การหน่วงเวลามีหลายประเภท ได้แก่ การหน่วงเวลาสเตอริโอ, การหน่วงเวลาเสียงปิงปอง, การเลื่อนเวลาสับเปลี่ยน และรายการจะดำเนินต่อไป โชคดีที่เครื่องมือทั้งหมดนี้สามารถสร้างใน Web Audio โดยใช้โหนดเอฟเฟกต์แบบดั้งเดิมและจินตนาการบางอย่าง

เนื่องจากเราต้องการใช้โหนดเนทีฟและเอฟเฟกต์ที่กำหนดเองของเราในแบบที่โปร่งใสที่สุดเท่าที่จะเป็นไปได้ เราจึงตัดสินใจว่าจะสร้างรูปแบบ Wrapper ที่จะบรรลุเป้าหมายนี้ โหนดดั้งเดิมใน Web Audio ใช้วิธีการเชื่อมต่อของตนเองเพื่อลิงก์โหนดเข้าด้วยกัน เราจึงต้องจำลองลักษณะการทำงานนี้ นี่คือลักษณะของแนวคิดเบื้องต้น:

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);
การกำหนดเส้นทางโหนดที่กำหนดเอง

ความแตกต่างเพียงอย่างเดียวระหว่างโหนดที่กำหนดเองและโหนดเนทีฟคือเราต้องเชื่อมต่อกับพร็อพเพอร์ตี้อินพุตโหนดที่กำหนดเอง แน่ใจว่ามีวิธีหลบเลี่ยงเรื่องนี้ได้ แต่วิธีนี้ใกล้เพียงพอสำหรับวัตถุประสงค์ของเราแล้ว และยังพัฒนารูปแบบนี้ต่อไปเพื่อจำลองวิธียกเลิกการเชื่อมต่อของ AudioNode แบบเนทีฟ รวมทั้งเพื่อรองรับอินพุต/เอาต์พุตที่ผู้ใช้กำหนดเมื่อเชื่อมต่อ ฯลฯ ลองดูที่ข้อกำหนดเพื่อดูว่าโหนดเนทีฟทำอะไรได้บ้าง

เมื่อเรามีรูปแบบพื้นฐานสำหรับการสร้างเอฟเฟกต์ที่กำหนดเองแล้ว ขั้นตอนต่อไปคือการกำหนดลักษณะการทำงานที่กำหนดเองให้กับโหนดที่กำหนดเอง มาดูโหนดการหน่วงเวลาของ Slackback กัน

ตอบโต้แบบที่เธอตั้งใจ

ท่วงทำนองของแถบสแลปแบ็ค (Slapback Echo) เป็นเอฟเฟกต์ที่นิยมใช้กับเครื่องดนตรีหลายชนิด ตั้งแต่เสียงร้องสไตล์ยุค 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);
    };
};
การกำหนดเส้นทางภายในของโหนด Slabback

บางคนอาจทราบอยู่แล้วว่าดีเลย์นี้อาจใช้กับการหน่วงเวลาที่นานขึ้นด้วย ดังนั้นจึงกลายเป็นการหน่วงเวลาแบบโมโนตามปกติเมื่อมีเสียงเตือน ด้านล่างนี้เป็นตัวอย่างการใช้การหน่วงเวลาเพื่อให้คุณได้ฟังเสียง

กำลังกำหนดเส้นทางเสียง

เมื่อทำงานกับเครื่องดนตรีและชิ้นส่วนดนตรีชนิดต่างๆ ในแอปพลิเคชันเสียงระดับมืออาชีพ คุณจำเป็นต้องมีระบบการกำหนดเส้นทางที่ยืดหยุ่นซึ่งจะช่วยให้คุณมิกซ์และปรับระดับเสียงได้อย่างมีประสิทธิภาพ เราได้พัฒนาระบบบัสเสียงใน JAM กับ Chrome ซึ่งคล้ายกับระบบที่ใช้ในแผ่นมิกซ์เซอร์วิส ซึ่งจะช่วยให้เราสามารถต่อเครื่องดนตรีทุกชนิดที่ต้องการเอฟเฟกต์เสียงก้องกับรถบัสหรือช่องสัญญาณทั่วไป แล้วเพิ่ม Reverb ลงในรถบัสนั้นแทนการเพิ่มเสียงก้องไปยังเครื่องดนตรีแต่ละชิ้นที่แยกกัน นี่เป็นการเพิ่มประสิทธิภาพที่สำคัญ และเราขอแนะนำว่าควรดำเนินการที่คล้ายกันทันทีที่เริ่มใช้แอปพลิเคชันที่ซับซ้อนมากขึ้น

การกำหนดเส้นทางของ AudioBus

แต่โชคดีที่การเพิ่มประสิทธิภาพของ 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 โหนด ได้แก่ ค่าเกนล่วงหน้าและการโพสต์กำไร ซึ่งช่วยให้เราปิดหรือเบาเสียงในรถประจำทางได้ 2 วิธี โดยแทรกล่วงหน้าไว้ก่อนเอฟเฟ็ก และใส่ PostGET ไว้ที่ท้ายห่วงโซ่ จากนั้น หากเราค่อยๆ เบาเสียงล่วงหน้า เอฟเฟกต์จะยังดังขึ้นหลังจากที่เสียงได้มาถึงด้านล่างแล้ว แต่หากเราค่อยๆ เบาลงในโพสต์เกน เสียงทั้งหมดจะปิดเสียงพร้อมกัน

จากที่นี่ไปที่ไหน

วิธีการที่ฉันอธิบายไว้ที่นี่สามารถและควรจะได้รับการพัฒนาเพิ่มเติม ควรนำสิ่งต่างๆ เช่น อินพุตและเอาต์พุตของโหนดที่กำหนดเอง และวิธีการเชื่อมต่อ ไปใช้โดยใช้การสืบทอดต้นแบบต้นแบบ รถบัสควรสร้างเอฟเฟกต์แบบไดนามิกได้โดยส่งรายการเอฟเฟกต์มาให้

เพื่อเป็นการเฉลิมฉลองการเปิดตัว JAM ด้วย Chrome เราได้ตัดสินใจที่จะทำให้เฟรมเวิร์กของเอฟเฟกต์เป็นโอเพนซอร์ส ถ้าการแนะนำสั้นๆ นี้ทำให้คุณน่าสนใจ ก็ติดตามได้เลย มีการพูดคุยกันที่นี่เกี่ยวกับการกำหนดรูปแบบให้กับรายการ Web Audio ที่กำหนดเอง มาร่วมกับเรา