केस स्टडी - वेब ऑडियो के साथ HTML5 गेम की एक कहानी

फ़ील्डरनर

फ़ील्डरनर का स्क्रीनशॉट
फ़ील्डरनर का स्क्रीनशॉट

फ़ील्डरनर्स, टावर-डिफ़ेंस स्टाइल का एक अवॉर्ड विजेता गेम है. इसे पहली बार साल 2008 में iPhone के लिए रिलीज़ किया गया था. तब से इसे कई दूसरे प्लैटफ़ॉर्म पर पोर्ट किया जा चुका है. अक्टूबर 2011 में, Chrome ब्राउज़र हाल ही के प्लैटफ़ॉर्म में से एक था. फ़ील्डरनर को HTML5 प्लैटफ़ॉर्म पर पोर्ट करने में आई चुनौतियों में से एक थी, साउंड चलाने का तरीका.

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

कुछ चुनौतियां दिखाई गईं

फ़ील्डरनर को HTML5 पर पोर्ट करने के दौरान, हमें ऑडियो टैग के साथ ऑडियो प्लेबैक में समस्याओं का सामना करना पड़ा. शुरुआत में हमने इसके बजाय Web Audio API पर ध्यान देने का फ़ैसला किया. WebAudio का इस्तेमाल करने से, हमें इस समस्या को सुलझाने में मदद मिली. जैसे, फ़ील्डरनर की ज़रूरत के हिसाब से एक साथ कई इफ़ेक्ट देना. फिर भी, फ़ील्डरनर के HTML5 के लिए एक ऑडियो सिस्टम विकसित करते समय हमें कुछ गंभीर समस्याओं का सामना करना पड़ता है, जिनके बारे में अन्य डेवलपर को पता होना चाहिए.

AudioBufferSourceNodes की प्रकृति

AudioBufferSourceNodes WebAudio के साथ साउंड चलाने का मुख्य तरीका है. यह समझना बहुत ज़रूरी है कि वे सिर्फ़ एक बार इस्तेमाल किए जा सकने वाले ऑब्जेक्ट हैं. आप एक AudioBufferSourceNode बनाएं, उसे बफ़र असाइन करें, और उसे ग्राफ़ से कनेक्ट करें. साथ ही, उसे NoteOn या NoteGrainOn के साथ चलाएं. इसके बाद, प्लेबैक बंद करने के लिए NoteOff को कॉल किया जा सकता है. हालांकि, NoteOn या NoteGrainOn को कॉल करके सोर्स को फिर से नहीं चलाया जा सकता. इसके लिए, आपको एक और AudioBufferSourceNode बनाना होगा. हालांकि, एक ही AudioBuffer ऑब्जेक्ट का फिर से इस्तेमाल किया जा सकता है. हालांकि, आपके पास ऐसे एक से ज़्यादा ऐसे AudioBufferSourceNodes हो सकते हैं जो एक ही AudioBuffer इंस्टेंस पर ले जाते हों!. आपको 'गिव मी अ बीट' में फ़ील्डरनर्स का प्लेबैक स्निपेट मिल सकता है.

ऐसा कॉन्टेंट जो कैश मेमोरी में सेव नहीं किया गया है

रिलीज़ होने पर,Fieldrunners HTML5 सर्वर ने म्यूज़िक फ़ाइलों के लिए बड़ी संख्या में अनुरोध दिखाए. यह नतीजा, Chrome 15 के उस पेज से शुरू हुआ जिसमें हमने फ़ाइल को कई हिस्सों में डाउनलोड किया और फिर उसे कैश मेमोरी में सेव नहीं किया. इस वजह से, हमने अपनी अन्य ऑडियो फ़ाइलों की तरह ही म्यूज़िक फ़ाइलें लोड करने का फ़ैसला किया. ऐसा करना सबसे अच्छा नहीं माना जाता है, लेकिन अन्य ब्राउज़र के कुछ वर्शन अब भी ऐसा करते हैं.

फ़ोकस से बाहर होने पर, आवाज़ बंद करने की सुविधा

पहले, यह पता लगाना मुश्किल था कि आपका गेम का टैब कब फ़ोकस में नहीं था. फ़ील्डरनर ने Chrome 13 से पहले पोर्ट करना शुरू कर दिया था. इस वर्शन में पेज विज़िबिलिटी एपीआई ने टैब को धुंधला करने की सुविधा का पता लगाने के लिए, हमारे मुश्किल कोड की ज़रूरत को बदल दिया. अगर पूरे गेम में रुकावट न आए, तो गेम की आवाज़ को म्यूट या रोकने के लिए, हर गेम को एक छोटा स्निपेट लिखने के लिए visibility API का इस्तेमाल करना चाहिए. फ़ील्डरनर ने requestAnimationFrame एपीआई का इस्तेमाल किया है. इसलिए, गेम को रोकने की प्रोसेस बिना किसी रुकावट के ठीक से लागू की गई है, लेकिन आवाज़ को रोकने की नहीं.

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

अजीब बात यह है कि इस लेख के बारे में सुझाव मिलने के दौरान, हमें सूचना मिली थी कि आवाज़ को रोकने के लिए हम जिस तकनीक का इस्तेमाल कर रहे थे वह सही नहीं थी. ऐसा इसलिए, क्योंकि हम वेब ऑडियो में एक गड़बड़ी का इस्तेमाल कर रहे थे, ताकि आवाज़ों को रोका जा सके. आने वाले समय में इसे ठीक किया जाएगा. इसलिए, प्लेबैक को रोकने के लिए नोड या सबग्राफ़ को डिसकनेक्ट करके, आवाज़ को रोका नहीं जा सकता.

एक आसान वेब ऑडियो नोड आर्किटेक्चर

फ़ील्डरनर का ऑडियो मॉडल बेहद आसान है. इस मॉडल में ये सुविधाएं सेट काम कर सकती हैं:

  • साउंड इफ़ेक्ट की आवाज़ कंट्रोल करें.
  • बैकग्राउंड संगीत ट्रैक की आवाज़ कंट्रोल करें.
  • सभी ऑडियो म्यूट करें.
  • गेम रुकने पर, आवाज़ से सूचना देने वाली सुविधा बंद करें.
  • गेम फिर से शुरू होने पर उन सूचनाओं को फिर से चालू कर दें.
  • गेम के टैब पर फ़ोकस बंद होने पर, सभी ऑडियो बंद कर दें.
  • ज़रूरत के हिसाब से आवाज़ सुनाई देने के बाद, वीडियो चलाना फिर से शुरू करें.

Web Audio के साथ ऊपर बताई गई सुविधाएं पाने के लिए, इसने दिए गए तीन संभावित नोड का इस्तेमाल किया है: DestinationNode, GainNode, AudioBufferSourceNode. AudioBufferSourceNodes साउंड चलाता है. GainNodes, AudioBufferSourceNodes को एक साथ कनेक्ट करता है. डेस्टिनेशन नोड को वेब ऑडियो कॉन्टेक्स्ट से बनाया गया है. इसे डेस्टिनेशन कहते हैं. यह प्लेयर पर साउंड चलाता है. वेब ऑडियो में कई तरह के नोड होते हैं, लेकिन इन्हीं के साथ ही हम किसी गेम में आवाज़ के लिए एक बहुत ही आसान ग्राफ़ बना सकते हैं.

नोड ग्राफ़ चार्ट

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

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

function AudioManager() {
  // map for loaded sounds
  this.sounds = {};

  // create our permanent nodes
  this.nodes = {
    destination: this.audioContext.destination,
    masterGain: this.audioContext.createGain(),

    backgroundMusicGain: this.audioContext.createGain(),

    coreEffectsGain: this.audioContext.createGain(),
    effectsGain: this.audioContext.createGain(),
    pausedEffectsGain: this.audioContext.createGain()
  };

  // and setup the graph
  this.nodes.masterGain.connect( this.nodes.destination );

  this.nodes.backgroundMusicGain.connect( this.nodes.masterGain );

  this.nodes.coreEffectsGain.connect( this.nodes.masterGain );
  this.nodes.effectsGain.connect( this.nodes.coreEffectsGain );
  this.nodes.pausedEffectsGain.connect( this.nodes.coreEffectsGain );
}

ज़्यादातर गेम में, साउंड इफ़ेक्ट और संगीत को अलग से कंट्रोल किया जा सकता है. ऊपर दिए गए हमारे ग्राफ़ की मदद से, इसे आसानी से पूरा किया जा सकता है. हर गेन नोड में एक "gain" एट्रिब्यूट होता है. इसे 0 और 1 के बीच की किसी भी दशमलव वैल्यू पर सेट किया जा सकता है. इसका इस्तेमाल वॉल्यूम को खास तौर पर कंट्रोल करने के लिए किया जा सकता है. हम संगीत और साउंड इफ़ेक्ट वाले चैनलों की आवाज़ को अलग-अलग कंट्रोल करना चाहते हैं. इसलिए, हर चैनल के लिए हमारे पास एक गेन नोड है, जहां से हम उनकी आवाज़ को कंट्रोल कर सकते हैं.

function setArbitraryVolume() {
  var musicGainNode = this.nodes.backgroundMusicGain;

  // set music volume to 50%
  musicGainNode.gain.value = 0.5;
}

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

function arbitraryCrossfade( track1, track2 ) {
  track1.gain.linearRampToValueAtTime( 0, 1 );
  track2.gain.linearRampToValueAtTime( 1, 1 );
}

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

आवाज़ रोकी जा रही है

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

AudioManager.prototype.pauseEffects = function() {
  this.nodes.effectsGain.disconnect();
}

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

AudioManager.prototype.resumeEffects = function() {
  this.nodes.effectsGain.connect( this.nodes.coreEffectsGain );
}

फ़ील्डरनर को शिपिंग करने के बाद, हमें पता चला कि सिर्फ़ किसी नोड या सबग्राफ़ को डिसकनेक्ट करने से AudioBufferSourceNodes का प्लेबैक नहीं रुकेगा. हमने असल में WebAudio में एक बग का फ़ायदा लिया है जो फ़िलहाल उन नोड का प्लेबैक बंद कर देता है जो ग्राफ़ में डेस्टिनेशन नोड से कनेक्ट नहीं हैं. इसलिए, यह पक्का करने के लिए कि हम आने वाले समय में इस समस्या को ठीक करने के लिए तैयार हैं, हमें यहां दिए गए कोड जैसे कुछ कोड की ज़रूरत है:

AudioManager.prototype.pauseEffects = function() {
  this.nodes.effectsGain.disconnect();

  var now = Date.now();
  for ( var name in this.sounds ) {
    var sound = this.sounds[ name ];

    if ( !sound.ignorePause && ( now - sound.source.noteOnAt < sound.buffer.duration * 1000 ) ) {
      sound.pausedAt = now - sound.source.noteOnAt;
      sound.source.noteOff();
    }
  }
}

AudioManager.prototype.resumeEffects = function() {
  this.nodes.effectsGain.connect( this.nodes.coreEffectsGain );

  var now = Date.now();
  for ( var name in this.sounds ) {
    if ( sound.pausedAt ) {
      this.play( sound.name );
      delete sound.pausedAt;
    }
  }
};

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

फ़ोकस खोना

इस सुविधा के लिए हमारा मास्टर नोड काम करता है. जब कोई ब्राउज़र इस्तेमाल करने वाला व्यक्ति किसी दूसरे टैब पर स्विच करता है, तो गेम नहीं दिखता. नज़रों से दूर, दिमाग से बाहर होने की वजह से, इसलिए तो यह आवाज़ भी सुननी चाहिए. ऐसी कुछ तरकीबें हैं जिनका इस्तेमाल करके यह पता लगाया जा सकता है कि किसी गेम का पेज किसको दिखे. हालांकि, visibility API की मदद से यह बहुत आसान हो गया है.

फ़ील्डरनर सिर्फ़ ऐक्टिव टैब के तौर पर काम करेंगे, क्योंकि इसके अपडेट लूप को कॉल करने के लिए, requestAnimationFrame का इस्तेमाल किया जाता है. हालांकि, उपयोगकर्ता के दूसरे टैब में होने पर भी, वेब ऑडियो कॉन्टेक्स्ट में लूप वाले इफ़ेक्ट और बैकग्राउंड ट्रैक चलते रहेंगे. हालांकि, visibility API के बारे में बताने वाले बहुत छोटे स्निपेट की मदद से इसे रोका जा सकता है.

function AudioManager() {
  // map and node setup
  // ...

  // disable all sound when on other tabs
  var self = this;
  window.addEventListener( 'webkitvisibilitychange', function( e ) {
    if ( document.webkitHidden ) {
      self.nodes.masterGain.disconnect();

      // As noted in Pausing Sounds disconnecting isn't enough.
      // For Fieldrunners calling our new pauseEffects method would be
      // enough to accomplish that, though we may still need some logic
      // to not resume if already paused.
      self.pauseEffects();
    } else {
      self.nodes.masterGain.connect( this.nodes.destination );
      self.resumeEffects();
    }
  });
}

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

पेज विज़िबिलिटी एपीआई यह जानना आसान बनाता है कि आपका टैब कब फ़ोकस में नहीं है. अगर आपके पास आवाज़ को रोकने के लिए एक असरदार कोड पहले से मौजूद है, तो गेम टैब के छिपे होने पर, आवाज़ को रोकने के लिए सेट की गई आवाज़ को कुछ ही लाइन में लिखना ज़रूरी है.

गिव मी अ बीट

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

फ़ील्डरनर किसी गेम की अलग-अलग घटनाओं, जैसे कि किसी किरदार के मरने के लिए, उसकी अलग-अलग कॉपी को बजाने की जगह, एक बार में एक ही साउंड बजाएं. अगर घंटी बजने के बाद आवाज़ की ज़रूरत होगी, तो वह रीस्टार्ट हो सकता है, लेकिन पहले से चल रहे समय पर नहीं. फ़ील्डरनर के ऑडियो डिज़ाइन के लिए यह फ़ैसला है, क्योंकि इसमें ऐसी आवाज़ें हैं जिन्हें तेज़ी से चलाने के लिए कहा जाता है. अगर फिर से शुरू करने या एक से ज़्यादा बार चलाने की अनुमति दी जाए, तो यह बंद हो सकता है. AudioBufferSourceNodes को एक शॉट के तौर पर इस्तेमाल किया जाना चाहिए. कोई नोड बनाएं, बफ़र अटैच करें, ज़रूरत के हिसाब से लूप बूलियन वैल्यू सेट करें, ग्राफ़ पर उस नोड से कनेक्ट करें जो डेस्टिनेशन पर ले जाएगा, NoteOn या NoteGrainOn पर ले जाएगा. वैकल्पिक रूप से, Noteoff को कॉल करें.

फ़ील्डरनर के लिए यह कुछ ऐसा दिखता है:

AudioManager.prototype.play = function( options ) {
  var now = Date.now(),
    // pull from a map of loaded audio buffers
    sound = this.sounds[ options.name ],
    channel,
    source,
    resumeSource;

  if ( !sound ) {
    return;
  }

  if ( sound.source ) {
    var source = sound.source;
    if ( !options.loop && now - source.noteOnAt > sound.buffer.duration * 1000 ) {
      // discard the previous source node
      source.stop( 0 );
      source.disconnect();
    } else {
      return;
    }
  }

  source = this.audioContext.createBufferSource();
  sound.source = source;
  // track when the source is started to know if it should still be playing
  source.noteOnAt = now;

  // help with pausing
  sound.ignorePause = !!options.ignorePause;

  if ( options.ignorePause ) {
    channel = this.nodes.pausedEffectsGain;
  } else {
    channel = this.nodes.effectsGain;
  }

  source.buffer = sound.buffer;
  source.connect( channel );
  source.loop = options.loop || false;

  // Fieldrunners' current code doesn't consider sound.pausedAt.
  // This is an added section to assist the new pausing code.
  if ( sound.pausedAt ) {
    source.start( ( sound.buffer.duration * 1000 - sound.pausedAt ) / 1000 );
    source.noteOnAt = now + sound.buffer.duration * 1000 - sound.pausedAt;

    // if you needed to precisely stop sounds, you'd want to store this
    resumeSource = this.audioContext.createBufferSource();
    resumeSource.buffer = sound.buffer;
    resumeSource.connect( channel );
    resumeSource.start(
      0,
      sound.pausedAt,
      sound.buffer.duration - sound.pausedAt / 1000
    );
  } else {
    // start play immediately with a value of 0 or less
    source.start( 0 );
  }
}

बहुत ज़्यादा स्ट्रीमिंग

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

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

AudioManager.prototype.load = function( options ) {
  var xhr,
      // pull from a map of name, object pairs
      sound = this.sounds[ options.name ];

  if ( sound ) {
    // this is a great spot to add success methods to a list or use promises
    // for handling the load event or call success if already loaded
    if ( sound.buffer && options.success ) {
      options.success( options.name );
    } else if ( options.success ) {
      sound.success.push( options.success );
    }

    // one buffer is enough so shortcut here
    return;
  }

  sound = {
    name: options.name,
    buffer: null,
    source: null,
    success: ( options.success ? [ options.success ] : [] )
  };
  this.sounds[ options.name ] = sound;

  xhr = new XMLHttpRequest();
  xhr.open( 'GET', options.path, true );
  xhr.responseType = 'arraybuffer';
  xhr.onload = function( e ) {
    sound.buffer = self._context.createBuffer( xhr.response, false );

    // call all waiting handlers
    sound.success.forEach( function( success ) {
      success( sound.name );
    });
    delete sound.success;
  };
  xhr.onerror = function( e ) {

    // failures are uncommon but you want to do deal with them

  };
  xhr.send();
}

खास जानकारी

Chrome और HTML5 में फ़ील्डरनर का इस्तेमाल सबसे ज़्यादा हुआ. इसके अपने काम के अलावा, इसकी मदद से जावास्क्रिप्ट में हज़ारों C++ लाइनें दिखाई गई हैं. कुछ दिलचस्प दुविधाएं और HTML5 उत्तेजित करने के खास फ़ैसले भी हैं. अगर आपने किसी एक को दोहराया है, तो AudioBufferSourceNodes सिर्फ़ एक बार इस्तेमाल किए जाने वाले ऑब्जेक्ट हैं. उन्हें बनाएं, ऑडियो बफ़र अटैच करें, इसे वेब ऑडियो ग्राफ़ से कनेक्ट करें, और NoteOn या NoteGrainOn के साथ चलाएं. क्या आपको वह साउंड फिर से चलाना है? इसके बाद, एक और AudioBufferSourceNode बनाएं.