מקרה לדוגמה – JAM עם Chrome

איך שיפרנו את האודיו

Oskar Eriksson
Oskar Eriksson

מבוא

JAM with Chrome הוא פרויקט מוזיקלי מבוסס-אינטרנט שנוצר על ידי Google. JAM with Chrome מאפשר לאנשים מכל העולם ליצור להקה ולנגן ביחד בזמן אמת בתוך הדפדפן. אנחנו ב-DinahMoe נהנינו מאוד לקחת חלק בפרויקט הזה. התפקיד שלנו היה לייצר מוזיקה לאפליקציה, ולעצב ולפתח את רכיב המוזיקה. הפיתוח כלל שלושה תחומים עיקריים: 'תחנת עבודה למוזיקה', כולל הפעלת MIDI, סמפלים של תוכנות, אפקטים של אודיו, ניתוב וערבוב. מנוע לוגיקה של מוזיקה לשליטה במוזיקה באופן אינטראקטיבי בזמן אמת. רכיב סנכרון שמבטיח שכל הנגנים בסשן ישמעו את המוזיקה באותו זמן בדיוק, תנאי הכרחי לנגינה משותפת.

כדי להשיג את רמת האותנטיות, הדיוק ואיכות האודיו הגבוהות ביותר, בחרנו להשתמש ב-Web Audio API. בניתוח המקרה הזה נסביר על חלק מהאתגרים שניצבנו בפניהם ועל הדרכים שבהן פתרנו אותם. כבר יש כמה מאמרים מעולים בנושא Web Audio ב-HTML5Rocks שיעזרו לכם להתחיל, אז נתחיל ישר מהחלק העמוק של המאגר.

כתיבת אפקטים קוליים מותאמים אישית

לממשק Web Audio API יש כמה אפקטים שימושיים שכלולים במפרט, אבל היינו צריכים אפקטים מורכבים יותר לכלים שלנו ב-JAM with Chrome. לדוגמה, יש צומת עיכוב מקורי ב-Web Audio, אבל יש הרבה סוגים של עיכובים – עיכוב סטריאו, עיכוב פינג-פונג, עיכוב סלאבקבק ועוד. למרבה המזל, אפשר ליצור את כל האפקטים האלה ב-Web Audio באמצעות צמתים מקומיים של אפקטים וקצת דמיון.

מכיוון שרצינו להשתמש בצורה שקופה ככל האפשר בצמתים המקוריים ובאפקטים המותאמים אישית שלנו, החלטנו ליצור פורמט מעטפת שיעזור לנו לעשות זאת. הצמתים המקומיים ב-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 מקומיים, וגם להתאים את הקלט/הפלט שהוגדרו על ידי המשתמש במהלך החיבור וכו'. כדאי לעיין במפרט כדי לראות מה הצמתים המקוריים יכולים לעשות.

עכשיו, אחרי שסיפקנו את התבנית הבסיסית ליצירת אפקטים מותאמים אישית, השלב הבא הוא לתת לצומת המותאם אישית התנהגות מותאמת אישית. נבחן צומת של עיכוב סלאפבק.

תשובה חזקה

עיכוב סלאבק (slapback delay), שנקרא לפעמים הד סלאבק, הוא אפקט קלאסי שמשמש במספר כלים, החל מקול של שנות ה-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);
    };
};
ניתוב פנימי של צומת ה-slapback

חלק מכם כבר הבינו שאפשר להשתמש בעיכוב הזה גם עם זמני עיכוב גדולים יותר, וכך הוא הופך לעיכוב מונו רגיל עם משוב. הנה דוגמה לשימוש בהשהיה הזו כדי שתוכלו לשמוע איך היא נשמעת.

ניתוב אודיו

כשעובדים עם כלים שונים וחלקים מוזיקליים באפליקציות אודיו מקצועיות, חובה להשתמש במערכת ניתוב גמישה שמאפשרת לכם לערבב ולבצע מודולציה של הצלילים בדרכים יעילות. ב-JAM with Chrome פיתחנו מערכת של אודיו באס, בדומה לזו שקיימת בלוחות פיזיים של מיקסרים. כך אנחנו יכולים לחבר את כל הכלים שצריכים אפקט הדהוד לבורד או לערוץ משותף, ואז להוסיף את ההדהוד לבורד הזה במקום להוסיף הדהוד לכל כלי בנפרד. זו אופטימיזציה משמעותית, ומומלץ מאוד לבצע פעולה דומה ברגע שמתחילים לעבוד על אפליקציות מורכבות יותר.

ניתוב של 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);

וואלה, הוספנו עיכוב, איזון ותהודה (שהוא אפקט יקר למדי מבחינת הביצועים) במחצית העלות של הוספת האפקטים לכל מכשיר בנפרד. אם נרצה להוסיף קצת פלפל לבוס, נוכל להוסיף שני צמתים חדשים של הגברה – preGain ו-postGain – שיאפשרו לנו להשבית או להעביר את הצלילים בבוס בשתי דרכים שונות. ה-preGain מופיע לפני האפקטים, וה-postGain מופיע בסוף השרשרת. אם לאחר מכן נבצע דעיכה של preGain, האפקטים עדיין ימשיכו להדהד אחרי שהשיפור יגיע לשפל, אבל אם נבצע דעיכה של postGain, כל הצליל יושתק באותו זמן.

מה עושים עכשיו?

אפשר, ורצוי, לפתח את השיטות שתיארתי כאן. דברים כמו הקלט והפלט של הצמתים המותאמים אישית ושיטות החיבור, אפשר או צריך להטמיע באמצעות ירושה מבוססת-אב טיפוס. האוטובוסים אמורים ליצור אפקטים באופן דינמי על ידי העברת רשימת אפקטים.

כדי לחגוג את ההשקה של JAM ב-Chrome, החלטנו להפוך את מסגרת האפקטים שלנו לקוד פתוח. אם ההקדמה הקצרה הזו עוררה את סקרנותכם, מומלץ לעיין בספרייה ולתרום לתוכן שלה. כאן מתנהל דיון בנושא סטנדרטיזציה של פורמט לישות בהתאמה אישית של Web Audio. רוצה להצטרף?