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

איך יצרנו את הסאונד לאודיו

Oskar Eriksson
Oskar Eriksson

מבוא

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

כדי להשיג את הרמה הגבוהה ביותר של אותנטיות, דיוק ואיכות אודיו, בחרנו להשתמש ב-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);
ניתוב הצומת המותאם אישית

ההבדל היחיד בין צומת מותאם אישית לבין צומת מקורי הוא שאנחנו צריכים להתחבר למאפיין הקלט של הצמתים המותאמים אישית. אני בטוח שיש דרכים לעקוף את הבעיה, אבל זה היה מספיק קרוב למטרות שלנו. ניתן להמשיך לפתח את הדפוס הזה כדי לדמות שיטות ניתוק של 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);
    };
};
ניתוב פנימי של צומת ה-Slapback

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

אודיו בניתוב

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

לאן מכאן?

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

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