دراسة حالة - JAM مع Chrome

كيف أبدعنا في المحتوى الصوتي

مقدمة

JAM with Chrome هو مشروع موسيقي على الويب أنشأته Google. تتيح ميزة JAM with Chrome للمستخدمين من جميع أنحاء العالم تشكيل فرقة وعزف الموسيقى في الوقت الفعلي داخل المتصفّح. لقد كان من دواعي سروري في DinahMoe أن أكون جزءًا من هذا المشروع. كان دورنا هو إنتاج الموسيقى للتطبيق وتصميم مكوّن الموسيقى وتطويره. يتألف التطوير من ثلاث مجالات رئيسية: "محطة عمل موسيقية" تتضمّن تشغيل ملفات MIDI وأدوات تحليل البرامج والتأثيرات الصوتية وعمليات التوجيه والاختلاط، ومحرك منطق موسيقي للتحكّم في الموسيقى بشكل تفاعلي في الوقت الفعلي، ومكوّن مزامنة يضمن سماع جميع اللاعبين في الجلسة للموسيقى في الوقت نفسه بالضبط، وهو شرط أساسي للتمكن من اللعب معًا.

لتحقيق أعلى مستوى ممكن من الأصالة والدقة وجودة الصوت، اخترنا استخدام Web Audio API. ستتناول دراسة الحالة هذه بعض التحديات التي واجهناها وكيفية حلّها. تتوفّر حاليًا في HTML5Rocks عدة مقالات تمهيدية رائعة لمساعدتك في بدء استخدام Web Audio، لذا سننتقل مباشرةً إلى الجزء المتقدّم من الموضوع.

كتابة تأثيرات صوتية مخصّصة

تتضمّن واجهة برمجة التطبيقات 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 الأصلية، بالإضافة إلى استيعاب المدخلات/المخارجها التي يحدّدها المستخدم عند الاتصال وما إلى ذلك. اطّلِع على المواصفات لمعرفة الإجراءات التي يمكن أن تتّخذها العقد الأصلية.

بعد أن حصلنا على النمط الأساسي لإنشاء تأثيرات مخصّصة، كانت الخطوة التالية هي منح العقدة المخصّصة بعض السلوك المخصّص. لنلقِ نظرة على عقدة تأخير الارتداد.

ردّ قاطع

إنّ تأثير "التأخير الارتدادي"، الذي يُطلق عليه أحيانًا "صدى الارتداد"، هو تأثير كلاسيكي يُستخدَم في عدد من الآلات الموسيقية، بدءًا من الأصوات بأسلوب الخمسينيات وحتى القيثارات الكهربائية. يأخذ هذا التأثير الصوت الوافد ويشغّل نسخة من الصوت بتأخير طفيف يتراوح بين 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، طوّرنا نظامًا لمسار الصوت، مشابهًا للأنظمة المتوفّرة في لوحات المزج. يتيح لنا ذلك ربط جميع الآلات الموسيقية التي تحتاج إلى تأثير صدى بمسار أو قناة مشتركة، ثم إضافة الصدى إلى هذا المسار بدلاً من إضافة صدى إلى كل آلة موسيقية منفصلة. هذا تحسين كبير، ويُنصح بإجراء إجراء مشابه فور بدء استخدام تطبيقات أكثر تعقيدًا.

توجيه 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 with Chrome، قرّرنا إتاحة إطار عمل التأثيرات المفتوح المصدر. إذا أعجبك هذا الملخّص الموجز، يُرجى الاطّلاع عليه والمساهمة فيه. هناك مناقشة جارية هنا بشأن توحيد تنسيق عناصر Web Audio المخصّصة. المشاركة