Web Audio API के साथ शुरुआत करना

HTML5 <audio> एलिमेंट से पहले, वेब पर आवाज़ चलाने के लिए Flash या किसी अन्य प्लगिन की ज़रूरत होती थी. वेब पर ऑडियो के लिए अब प्लगिन की ज़रूरत नहीं होती. हालांकि, ऑडियो टैग की वजह से, बेहतर गेम और इंटरैक्टिव ऐप्लिकेशन लागू करने में काफ़ी समस्याएं आती हैं.

Web Audio API, JavaScript का एक हाई-लेवल एपीआई है. इसका इस्तेमाल वेब ऐप्लिकेशन में ऑडियो को प्रोसेस करने और उसे सिंथेसाइज़ करने के लिए किया जाता है. इस एपीआई का लक्ष्य, आधुनिक गेम ऑडियो इंजन में मौजूद क्षमताओं को शामिल करना है. साथ ही, आधुनिक डेस्कटॉप ऑडियो प्रोडक्शन ऐप्लिकेशन में मौजूद मिक्सिंग, प्रोसेसिंग, और फ़िल्टर करने से जुड़े कुछ टास्क को भी शामिल करना है. इस एपीआई का इस्तेमाल करने के बारे में यहां बताया गया है.

AudioContext का इस्तेमाल शुरू करना

AudioContext का इस्तेमाल, सभी आवाज़ों को मैनेज करने और चलाने के लिए किया जाता है. Web Audio API का इस्तेमाल करके आवाज़ बनाने के लिए, एक या उससे ज़्यादा साउंड सोर्स बनाएं और उन्हें AudioContext इंस्टेंस से मिले साउंड डेस्टिनेशन से कनेक्ट करें. यह कनेक्शन सीधे तौर पर होना ज़रूरी नहीं है. यह कई इंटरमीडिएट AudioNodes से होकर गुज़र सकता है. ये ऑडियो सिग्नल के लिए प्रोसेसिंग मॉड्यूल के तौर पर काम करते हैं. इस राउटिंग के बारे में ज़्यादा जानकारी, Web Audio स्पेसिफ़िकेशन में दी गई है.

AudioContext का एक इंस्टेंस, कई साउंड इनपुट और जटिल ऑडियो ग्राफ़ के साथ काम कर सकता है. इसलिए, हमें बनाए जाने वाले हर ऑडियो ऐप्लिकेशन के लिए, इनमें से सिर्फ़ एक की ज़रूरत होगी.

नीचे दिए गए स्निपेट से AudioContext बनाया जाता है:

var context;
window.addEventListener('load', init, false);
function init() {
    try {
    context = new AudioContext();
    }
    catch(e) {
    alert('Web Audio API is not supported in this browser');
    }
}

WebKit पर आधारित पुराने ब्राउज़र के लिए, webkit प्रीफ़िक्स का इस्तेमाल करें. जैसे, webkitAudioContext.

Web Audio API के कई दिलचस्प फ़ंक्शन, AudioContext के तरीके हैं. जैसे, AudioNode बनाना और ऑडियो फ़ाइल के डेटा को डिकोड करना.

आवाज़ें लोड हो रही हैं

Web Audio API, कम से लेकर सामान्य अवधि के साउंड के लिए AudioBuffer का इस्तेमाल करता है. साउंड फ़ाइलें फ़ेच करने के लिए, XMLHttpRequest का इस्तेमाल किया जाता है.

एपीआई, ऑडियो फ़ाइल के डेटा को कई फ़ॉर्मैट में लोड करने की सुविधा देता है. जैसे, WAV, MP3, AAC, OGG, और अन्य. अलग-अलग ऑडियो फ़ॉर्मैट के लिए, ब्राउज़र पर काम करने की सुविधा अलग-अलग होती है.

नीचे दिए गए स्निपेट में, साउंड सैंपल लोड करने का तरीका बताया गया है:

var dogBarkingBuffer = null;
var context = new AudioContext();

function loadDogSound(url) {
    var request = new XMLHttpRequest();
    request.open('GET', url, true);
    request.responseType = 'arraybuffer';

    // Decode asynchronously
    request.onload = function() {
    context.decodeAudioData(request.response, function(buffer) {
        dogBarkingBuffer = buffer;
    }, onError);
    }
    request.send();
}

ऑडियो फ़ाइल का डेटा बाइनरी (टेक्स्ट नहीं) होता है. इसलिए, हमने अनुरोध के responseType को 'arraybuffer' पर सेट किया है. ArrayBuffers के बारे में ज़्यादा जानने के लिए, XHR2 के बारे में यह लेख पढ़ें.

ऑडियो फ़ाइल का डेटा (डिकोड नहीं किया गया) मिलने के बाद, इसे बाद में डिकोड करने के लिए सेव किया जा सकता है. इसके अलावा, इसे AudioContext decodeAudioData() तरीके का इस्तेमाल करके तुरंत डिकोड किया जा सकता है. यह तरीका, request.response में सेव किए गए ऑडियो फ़ाइल के डेटा का ArrayBuffer लेता है. साथ ही, इसे एसिंक्रोनस तरीके से डिकोड करता है. इससे मुख्य JavaScript एक्ज़ीक्यूशन थ्रेड ब्लॉक नहीं होती.

decodeAudioData() के पूरा होने पर, यह एक कॉलबैक फ़ंक्शन को कॉल करता है. यह फ़ंक्शन, डिकोड किए गए पीसीएम ऑडियो डेटा को AudioBuffer के तौर पर उपलब्ध कराता है.

आवाज़ें चलाना

एक या उससे ज़्यादा AudioBuffers लोड होने के बाद, हम आवाज़ें चलाने के लिए तैयार हैं. मान लें कि हमने अभी-अभी AudioBuffer में कुत्ते के भौंकने की आवाज़ लोड की है और लोडिंग पूरी हो गई है. इसके बाद, हम इस बफ़र को इस कोड की मदद से चला सकते हैं.

var context = new AudioContext();

function playSound(buffer) {
    var source = context.createBufferSource(); // creates a sound source
    source.buffer = buffer;                    // tell the source which sound to play
    source.connect(context.destination);       // connect the source to the context's destination (the speakers)
    source.noteOn(0);                          // play the source now
}

जब भी कोई व्यक्ति कोई बटन दबाता है या माउस से किसी चीज़ पर क्लिक करता है, तब इस playSound() फ़ंक्शन को कॉल किया जा सकता है.

noteOn(time) फ़ंक्शन की मदद से, गेम और समय के हिसाब से ज़रूरी अन्य ऐप्लिकेशन के लिए, सटीक साउंड प्लेबैक को शेड्यूल करना आसान हो जाता है. हालांकि, इस शेड्यूलिंग सुविधा को सही तरीके से काम करने के लिए, पक्का करें कि आपके साउंड बफ़र पहले से लोड हों.

Web Audio API को ऐब्स्ट्रैक्ट करना

हालांकि, एक ऐसा सामान्य लोडिंग सिस्टम बनाना बेहतर होगा जो इस खास आवाज़ को लोड करने के लिए हार्ड-कोड न किया गया हो. ऑडियो ऐप्लिकेशन या गेम में, कम से लेकर सामान्य अवधि तक के कई साउंड इस्तेमाल किए जाते हैं. इन साउंड को मैनेज करने के कई तरीके हैं. यहां BufferLoader का इस्तेमाल करके, ऐसा करने का एक तरीका बताया गया है. यह वेब स्टैंडर्ड का हिस्सा नहीं है.

BufferLoader क्लास का इस्तेमाल करने का तरीका यहां बताया गया है. आइए, दो AudioBuffers बनाएं. इसके बाद, जैसे ही वे लोड हो जाएं, उन्हें एक साथ चलाएं.

window.onload = init;
var context;
var bufferLoader;

function init() {
    context = new AudioContext();

    bufferLoader = new BufferLoader(
    context,
    [
        '../sounds/hyper-reality/br-jam-loop.wav',
        '../sounds/hyper-reality/laughter.wav',
    ],
    finishedLoading
    );

    bufferLoader.load();
}

function finishedLoading(bufferList) {
    // Create two sources and play them both together.
    var source1 = context.createBufferSource();
    var source2 = context.createBufferSource();
    source1.buffer = bufferList[0];
    source2.buffer = bufferList[1];

    source1.connect(context.destination);
    source2.connect(context.destination);
    source1.noteOn(0);
    source2.noteOn(0);
}

समय के साथ तालमेल बिठाना: लय के साथ आवाज़ें चलाना

Web Audio API की मदद से डेवलपर, ऑडियो चलाने का सटीक समय तय कर सकते हैं. इसे दिखाने के लिए, आइए एक आसान रिदम ट्रैक सेट अप करें. ड्रमकिट का सबसे लोकप्रिय पैटर्न यह है:

ड्रम का एक सामान्य रॉक पैटर्न

इसमें हर आठवीं नोट पर हाई हैट और हर चौथाई नोट पर किक और स्नेयर को बारी-बारी से 4/4 टाइम में बजाया जाता है.

मान लें कि हमने kick, snare, और hihat बफ़र लोड कर लिए हैं. इन्हें लोड करने का कोड यह है:

for (var bar = 0; bar < 2; bar++) {
    var time = startTime + bar * 8 * eighthNoteTime;
    // Play the bass (kick) drum on beats 1, 5
    playSound(kick, time);
    playSound(kick, time + 4 * eighthNoteTime);

    // Play the snare drum on beats 3, 7
    playSound(snare, time + 2 * eighthNoteTime);
    playSound(snare, time + 6 * eighthNoteTime);

    // Play the hi-hat every eighth note.
    for (var i = 0; i < 8; ++i) {
    playSound(hihat, time + i * eighthNoteTime);
    }
}

यहां, हम शीट संगीत में दिखने वाले अनलिमिटेड लूप के बजाय सिर्फ़ एक बार दोहराते हैं. playSound फ़ंक्शन एक ऐसा तरीका है जो तय किए गए समय पर बफ़र चलाता है. यह इस तरह काम करता है:

function playSound(buffer, time) {
    var source = context.createBufferSource();
    source.buffer = buffer;
    source.connect(context.destination);
    source.noteOn(time);
}

किसी आवाज़ का वॉल्यूम बदलना

आवाज़ में बदलाव करने के लिए, सबसे बुनियादी कार्रवाइयों में से एक है आवाज़ कम या ज़्यादा करना. Web Audio API का इस्तेमाल करके, हम अपने सोर्स को AudioGainNode के ज़रिए डेस्टिनेशन तक रूट कर सकते हैं, ताकि वॉल्यूम में बदलाव किया जा सके:

इस फ़्लोचार्ट में दिखाया गया है कि ऑडियो, सोर्स से GainNode और फिर डेस्टिनेशन तक कैसे पहुंचता है.

इस कनेक्शन को इस तरह सेट अप किया जा सकता है:

// Create a gain node.
var gainNode = context.createGainNode();
// Connect the source to the gain node.
source.connect(gainNode);
// Connect the gain node to the destination.
gainNode.connect(context.destination);

ग्राफ़ सेट अप करने के बाद, प्रोग्राम के हिसाब से वॉल्यूम में बदलाव किया जा सकता है. इसके लिए, gainNode.gain.value में इस तरह बदलाव करें:

// Reduce the volume.
gainNode.gain.value = 0.5;

दो आवाज़ों के बीच क्रॉस-फ़ेडिंग

अब मान लें कि हमारे पास थोड़ा मुश्किल उदाहरण है, जिसमें हमें एक साथ कई आवाज़ें बजानी हैं, लेकिन उनके बीच क्रॉस फ़ेड करना है. यह डीजे जैसे ऐप्लिकेशन में एक सामान्य मामला है, जहां हमारे पास दो टर्नटेबल होते हैं और हम एक साउंड सोर्स से दूसरे साउंड सोर्स पर पैन करना चाहते हैं.

इसे इस ऑडियो ग्राफ़ की मदद से किया जा सकता है:

ऑडियो ग्राफ़ में दो सोर्स, अलग-अलग गेन नोड से कनेक्ट किए गए हैं. इसके बाद, इन्हें एक ही डेस्टिनेशन में रूट किया जाता है.

इसे सेट अप करने के लिए, हम सिर्फ़ दो AudioGainNodes बनाते हैं और इस फ़ंक्शन का इस्तेमाल करके, हर सोर्स को नोड से कनेक्ट करते हैं:

function createSource(buffer) {
    var source = context.createBufferSource();
    // Create a gain node.
    var gainNode = context.createGainNode();
    source.buffer = buffer;
    // Turn on looping.
    source.loop = true;
    // Connect source to gain.
    source.connect(gainNode);
    // Connect gain to destination.
    gainNode.connect(context.destination);

    return {
    source: source,
    gainNode: gainNode
    };
}

इक्वल पावर क्रॉसफ़ेडिंग

नैव लीनियर क्रॉसफ़ेड अप्रोच में, सैंपल के बीच पैन करते समय आवाज़ कम हो जाती है.

लीनियर क्रॉसफ़ेड को दो सीधी लाइनों के तौर पर दिखाया गया है. इन्हें समय के साथ ऐम्प्लिट्यूड लेवल के तौर पर मैप किया गया है.
लीनियर क्रॉसफ़ेड

इस समस्या को हल करने के लिए, हम इक्वल पावर कर्व का इस्तेमाल करते हैं. इसमें, संबंधित गेन कर्व नॉन-लीनियर होते हैं और ज़्यादा ऐम्प्लिट्यूड पर इंटरसेक्ट करते हैं. इससे ऑडियो के अलग-अलग हिस्सों के बीच वॉल्यूम में होने वाली गिरावट कम हो जाती है. इससे, अलग-अलग हिस्सों के बीच क्रॉसफ़ेड ज़्यादा एक जैसा होता है. हालांकि, इन हिस्सों के लेवल में थोड़ा अंतर हो सकता है.

इक्वल पावर क्रॉसफ़ेड को दो घुमावदार लाइनों के तौर पर दिखाया गया है. इन्हें समय के साथ ऐम्प्लिट्यूड लेवल के तौर पर मैप किया गया है.
बराबर पावर वाला क्रॉसफ़ेड

प्लेलिस्ट में क्रॉसफ़ेडिंग की सुविधा

क्रॉसफ़ेडर का इस्तेमाल, म्यूज़िक प्लेयर ऐप्लिकेशन में भी किया जाता है. जब कोई गाना बदलता है, तो हम चाहते हैं कि मौजूदा ट्रैक धीरे-धीरे बंद हो जाए और नया ट्रैक धीरे-धीरे शुरू हो जाए, ताकि ट्रांज़िशन में कोई रुकावट न आए. इसके लिए, क्रॉसफ़ेड को आने वाले समय के लिए शेड्यूल करें. हालांकि, इस तरह की शेड्यूलिंग के लिए setTimeout का इस्तेमाल किया जा सकता है, लेकिन यह सटीक नहीं है. Web Audio API की मदद से, हम AudioParam इंटरफ़ेस का इस्तेमाल करके, पैरामीटर की आने वाली वैल्यू को शेड्यूल कर सकते हैं. जैसे, AudioGainNode की गेन वैल्यू.

इसलिए, किसी प्लेलिस्ट के लिए, हम ट्रैक के बीच ट्रांज़िशन कर सकते हैं. इसके लिए, हमें मौजूदा ट्रैक की आवाज़ को धीरे-धीरे कम करना होगा और अगले ट्रैक की आवाज़ को धीरे-धीरे बढ़ाना होगा. ऐसा मौजूदा ट्रैक के खत्म होने से कुछ समय पहले करना होगा:

function playHelper(bufferNow, bufferLater) {
    var playNow = createSource(bufferNow);
    var source = playNow.source;
    var gainNode = playNow.gainNode;
    var duration = bufferNow.duration;
    var currTime = context.currentTime;
    // Fade the playNow track in.
    gainNode.gain.linearRampToValueAtTime(0, currTime);
    gainNode.gain.linearRampToValueAtTime(1, currTime + ctx.FADE_TIME);
    // Play the playNow track.
    source.noteOn(0);
    // At the end of the track, fade it out.
    gainNode.gain.linearRampToValueAtTime(1, currTime + duration-ctx.FADE_TIME);
    gainNode.gain.linearRampToValueAtTime(0, currTime + duration);
    // Schedule a recursive track change with the tracks swapped.
    var recurse = arguments.callee;
    ctx.timer = setTimeout(function() {
    recurse(bufferLater, bufferNow);
    }, (duration - ctx.FADE_TIME) - 1000);
}

Web Audio API, पैरामीटर की वैल्यू को धीरे-धीरे बदलने के लिए, RampToValue तरीकों का एक आसान सेट उपलब्ध कराता है. जैसे, linearRampToValueAtTime और exponentialRampToValueAtTime.

ट्रांज़िशन टाइमिंग फ़ंक्शन को, ऊपर दिए गए उदाहरण की तरह, पहले से मौजूद लीनियर और एक्सपोनेंशियल फ़ंक्शन में से चुना जा सकता है. हालांकि, setValueCurveAtTime फ़ंक्शन का इस्तेमाल करके, वैल्यू के ऐरे के ज़रिए अपनी वैल्यू कर्व भी तय की जा सकती है.

किसी आवाज़ पर सामान्य फ़िल्टर इफ़ेक्ट लागू करना

इस फ़्लोचार्ट में दिखाया गया है कि ऑडियो, सोर्स से BiquadFilterNode और फिर डेस्टिनेशन तक कैसे पहुंचता है.

Web Audio API की मदद से, एक ऑडियो नोड से दूसरे ऑडियो नोड में आवाज़ भेजी जा सकती है. इससे, प्रोसेसर की एक जटिल चेन बनाई जा सकती है. इसका इस्तेमाल, साउंडफ़ॉर्म में जटिल इफ़ेक्ट जोड़ने के लिए किया जाता है.

इसके लिए, अपने साउंड सोर्स और डेस्टिनेशन के बीच BiquadFilterNodes को रखा जा सकता है. इस तरह का ऑडियो नोड, कई तरह के लो-ऑर्डर फ़िल्टर कर सकता है. इनका इस्तेमाल ग्राफ़िक इक्वलाइज़र बनाने के लिए किया जा सकता है. साथ ही, इनका इस्तेमाल ज़्यादा जटिल इफ़ेक्ट बनाने के लिए भी किया जा सकता है. इनका मुख्य काम, किसी आवाज़ के फ़्रीक्वेंसी स्पेक्ट्रम के किन हिस्सों पर ज़ोर देना है और किन हिस्सों को कम करना है, यह तय करना है.

इन तरह के फ़िल्टर इस्तेमाल किए जा सकते हैं:

  • लो पास फ़िल्टर
  • हाई पास फ़िल्टर
  • बैंड पास फ़िल्टर
  • लो शेल्फ़ फ़िल्टर
  • हाई शेल्फ़ फ़िल्टर
  • पीकिंग फ़िल्टर
  • नॉच फ़िल्टर
  • सभी पास फ़िल्टर

साथ ही, सभी फ़िल्टर में पैरामीटर शामिल होते हैं. इनकी मदद से, गैन की कुछ मात्रा, फ़िल्टर लागू करने की फ़्रीक्वेंसी, और क्वालिटी फ़ैक्टर तय किया जा सकता है. लो-पास फ़िल्टर, कम फ़्रीक्वेंसी वाली आवाज़ों को बनाए रखता है, लेकिन ज़्यादा फ़्रीक्वेंसी वाली आवाज़ों को हटा देता है. ब्रेक-ऑफ़ पॉइंट, फ़्रीक्वेंसी वैल्यू से तय होता है. साथ ही, Q फ़ैक्टर की कोई यूनिट नहीं होती है. यह ग्राफ़ का आकार तय करता है. गैन का असर सिर्फ़ कुछ फ़िल्टर पर पड़ता है. जैसे, लो-शेल्फ़ और पीकिंग फ़िल्टर. इसका असर इस लो-पास फ़िल्टर पर नहीं पड़ता.

आइए, साउंड सैंपल से सिर्फ़ बेस निकालने के लिए, एक सामान्य लो-पास फ़िल्टर सेट अप करें:

// Create the filter
var filter = context.createBiquadFilter();
// Create the audio graph.
source.connect(filter);
filter.connect(context.destination);
// Create and specify parameters for the low-pass filter.
filter.type = 0; // Low-pass filter. See BiquadFilterNode docs
filter.frequency.value = 440; // Set cutoff to 440 HZ
// Playback the sound.
source.noteOn(0);

आम तौर पर, फ़्रीक्वेंसी कंट्रोल को लॉगरिदमिक स्केल पर काम करने के लिए बदला जाना चाहिए, क्योंकि इंसानों के सुनने की क्षमता भी इसी सिद्धांत पर काम करती है. जैसे, A4 440 हर्ट्ज़ है और A5 880 हर्ट्ज़ है. ज़्यादा जानकारी के लिए, ऊपर दिए गए सोर्स कोड के लिंक में FilterSample.changeFrequency फ़ंक्शन देखें.

आखिर में, ध्यान दें कि सैंपल कोड की मदद से, फ़िल्टर को कनेक्ट और डिसकनेक्ट किया जा सकता है. इससे AudioContext ग्राफ़ में डाइनैमिक तरीके से बदलाव होता है. node.disconnect(outputNumber) को कॉल करके, हम ग्राफ़ से AudioNodes को डिसकनेक्ट कर सकते हैं. उदाहरण के लिए, ग्राफ़ को फ़िल्टर से डायरेक्ट कनेक्शन पर फिर से रूट करने के लिए, यह तरीका अपनाएं:

// Disconnect the source and filter.
source.disconnect(0);
filter.disconnect(0);
// Connect the source directly.
source.connect(context.destination);

ज़्यादा सुनने की सुविधा

हमने एपीआई की बुनियादी बातों के बारे में बताया है. इसमें ऑडियो सैंपल लोड करने और चलाने के बारे में भी बताया गया है. हमने गेन नोड और फ़िल्टर वाले ऑडियो ग्राफ़ बनाए हैं. साथ ही, कुछ सामान्य साउंड इफ़ेक्ट चालू करने के लिए, शेड्यूल किए गए साउंड और ऑडियो पैरामीटर में बदलाव किए हैं. अब आप कुछ बेहतरीन वेब ऑडियो ऐप्लिकेशन बनाने के लिए तैयार हैं!

अगर आपको प्रेरणा चाहिए, तो कई डेवलपर ने Web Audio API का इस्तेमाल करके पहले ही शानदार काम किया है. मेरे कुछ पसंदीदा गेम ये हैं:

  • AudioJedit, ब्राउज़र में काम करने वाला एक ऐसा टूल है जो साउंड स्प्लाइसिंग के लिए SoundCloud के परमानेंट लिंक का इस्तेमाल करता है.
  • ToneCraft, एक साउंड सीक्वेंसर है. इसमें 3D ब्लॉक को स्टैक करके आवाज़ें बनाई जाती हैं.
  • Plink, एक साथ मिलकर संगीत बनाने वाला गेम है. इसमें Web Audio और WebSockets का इस्तेमाल किया जाता है.