مطالعه موردی - JAM با Chrome

چگونه راک صوتی را ساختیم

معرفی

JAM with Chrome یک پروژه موسیقی مبتنی بر وب است که توسط Google ایجاد شده است. JAM با Chrome به مردم از سرتاسر جهان اجازه می‌دهد باندی تشکیل دهند و در زمان واقعی در مرورگر جم کنند. ما در DinahMoe خوشحالیم که بخشی از این پروژه هستیم. نقش ما تولید موسیقی برای اپلیکیشن و طراحی و توسعه مولفه موسیقی بود. توسعه شامل سه حوزه اصلی بود: " ایستگاه کاری موسیقی " شامل پخش میانی، نمونه‌های نرم‌افزار، جلوه‌های صوتی، مسیریابی و میکس. یک موتور منطق موسیقی برای کنترل موسیقی به صورت تعاملی در زمان واقعی. و یک مؤلفه همگام سازی که اطمینان حاصل می کند که همه پخش کننده ها در یک جلسه موسیقی را دقیقاً در یک زمان می شنوند، پیش نیازی برای توانایی با هم بازی کردن.

برای دستیابی به بالاترین سطح ممکن از اصالت، دقت و کیفیت صدا، ما از Web Audio API استفاده کردیم. این مطالعه موردی برخی از چالش‌هایی که با آن‌ها مواجه شدیم و نحوه حل آنها را مورد بحث قرار می‌دهد. در حال حاضر تعدادی مقاله مقدماتی عالی در اینجا در HTML5Rocks وجود دارد تا شما را با Web Audio شروع کنید، بنابراین ما مستقیماً به انتهای عمیق این استخر خواهیم رفت.

نوشتن جلوه های صوتی سفارشی

Web Audio API تعدادی افکت مفید در مشخصات گنجانده شده است، اما ما به جلوه های دقیق تری برای ابزارهای خود در JAM با Chrome نیاز داشتیم. به عنوان مثال، یک گره تاخیر بومی در Web Audio وجود دارد، اما انواع مختلفی از تاخیر وجود دارد - تاخیر استریو، تاخیر پینگ پنگ، تاخیر slapback و لیست ادامه دارد. خوشبختانه، ایجاد همه اینها در وب صوتی با استفاده از گره های افکت بومی و کمی تخیل امکان پذیر است.

از آنجایی که می‌خواستیم بتوانیم از گره‌های بومی و جلوه‌های سفارشی خودمان به‌طور شفاف استفاده کنیم، تصمیم گرفتیم که قالبی بسازیم که بتواند به این هدف دست یابد. گره‌های بومی در 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 بومی، و همچنین تطبیق ورودی‌ها/خروجی‌های تعریف‌شده توسط کاربر هنگام اتصال و غیره توسعه داد. نگاهی به مشخصات بیندازید تا ببینید گره های بومی چه کاری می توانند انجام دهند.

اکنون که الگوی اصلی خود را برای ایجاد جلوه های سفارشی داشتیم، گام بعدی این بود که در واقع به گره سفارشی رفتار سفارشی بدهیم. بیایید نگاهی به گره تاخیر slapback داشته باشیم.

مثل شما می گویید که شما آن را به هم بزنید

تأخیر slapback، که گاهی اوقات اکو slapback نامیده می شود، یک افکت کلاسیک است که بر روی تعدادی از سازها، از آواز سبک دهه 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 با کروم، ما یک سیستم گذرگاه صوتی، مشابه آنچه در تخته‌های میکس فیزیکی یافت می‌شود، توسعه داده‌ایم. این به ما این امکان را می دهد که تمام سازهایی را که به افکت ریورب نیاز دارند به یک گذرگاه یا کانال مشترک متصل کنیم و سپس به جای افزودن یک ریورب به هر ساز جداگانه، ریورب را به آن اتوبوس اضافه کنیم. این یک بهینه سازی اصلی است و توصیه می شود به محض اینکه شروع به انجام برنامه های پیچیده تر کردید، کاری مشابه انجام دهید.

مسیریابی اتوبوس صوتی

خوشبختانه دستیابی به این امر در 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);

و voila، ما تاخیر، یکسان سازی و Reverb (که یک افکت نسبتاً گران است، از نظر عملکرد) را با نصف هزینه اعمال کرده ایم، گویی که افکت ها را برای هر ساز جداگانه اعمال کرده ایم. اگر بخواهیم مقداری ادویه اضافی به اتوبوس اضافه کنیم، می‌توانیم دو گره افزایش جدید اضافه کنیم - preGain و postGain - که به ما امکان می‌دهد صداها را در یک اتوبوس به دو روش مختلف خاموش یا محو کنیم. preGain قبل از اثرات قرار می گیرد و postGain در انتهای زنجیره قرار می گیرد. اگر PreGain را محو کنیم، افکت‌ها همچنان پس از اینکه گین به پایان رسید طنین انداز می‌شوند، اما اگر postGain را محو کنیم، همه صداها همزمان قطع می‌شوند.

از اینجا به کجا؟

این روش هایی که در اینجا توضیح دادم، می توانند و باید بیشتر توسعه یابند. چیزهایی مانند ورودی و خروجی گره های سفارشی و روش های اتصال، می توانند/باید با استفاده از وراثت مبتنی بر نمونه اولیه پیاده سازی شوند. اتوبوس‌ها باید بتوانند افکت‌ها را به‌صورت پویا با دریافت فهرستی از افکت‌ها ایجاد کنند.

برای جشن انتشار JAM با Chrome، تصمیم گرفتیم چارچوب افکت‌های خود را منبع باز کنیم. اگر این مقدمه کوتاه علاقه شما را غلغلک داده است، لطفاً نگاهی بیندازید و با خیال راحت مشارکت کنید. در اینجا بحثی در مورد استانداردسازی قالب برای موجودیت های صوتی وب سفارشی در جریان است. مشارکت کنید!