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

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

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

AudioContext के साथ शुरुआत करना

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

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 के साथ किया गया है.

वेब ऑडियो एपीआई की कई दिलचस्प सुविधाएं, AudioContext के तरीके हैं. जैसे, ऑडियो नोड बनाना और ऑडियो फ़ाइल का डेटा डिकोड करना.

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

वेब ऑडियो एपीआई, कम से मध्यम अवधि के ऑडियो के लिए 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() पूरा होने पर, यह एक कॉलबैक फ़ंक्शन को कॉल करता है. यह फ़ंक्शन, डिकोड किए गए PCM ऑडियो डेटा को 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 क्लास का इस्तेमाल करने का उदाहरण दिया गया है. आइए, दो 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);
}

किसी साउंड का वॉल्यूम कम या ज़्यादा करना

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

गेन नोड वाला ऑडियो ग्राफ़
गेन नोड वाला ऑडियो ग्राफ़

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

// 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 की मदद से, linearRampToValueAtTime और exponentialRampToValueAtTime जैसे किसी पैरामीटर की वैल्यू को धीरे-धीरे बदलने के लिए, RampToValue तरीकों का एक आसान सेट दिया गया है.

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

किसी साउंड पर आसान फ़िल्टर इफ़ेक्ट लागू करना

BiquadFilterNode वाला ऑडियो ग्राफ़
BiquadFilterNode वाला ऑडियो ग्राफ़

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

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

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

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

साथ ही, सभी फ़िल्टर में ऐसे पैरामीटर शामिल होते हैं जिनकी मदद से कुछ फ़ायदे, फ़िल्टर लागू करने की फ़्रीक्वेंसी, और क्वालिटी के फ़ैक्टर शामिल किए जाते हैं. लो-पास फ़िल्टर, कम फ़्रीक्वेंसी रेंज को बनाए रखता है, लेकिन ज़्यादा फ़्रीक्वेंसी को हटा देता है. ब्रेक-ऑफ़ पॉइंट, फ़्रीक्वेंसी वैल्यू से तय होता है. साथ ही, 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 440hz है और A5 880hz है. ज़्यादा जानकारी के लिए, ऊपर दिए गए सोर्स कोड लिंक में FilterSample.changeFrequency फ़ंक्शन देखें.

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

// 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, एक ऐसा गेम है जिसमें वेब ऑडियो और वेब स्लॉट का इस्तेमाल करके, मिलकर संगीत बनाया जा सकता है.