কেস স্টাডি - ওয়েব অডিও সহ একটি HTML5 গেমের গল্প

ফিল্ডরানাররা

ফিল্ডরানার্স স্ক্রিনশট
ফিল্ডরানার্স স্ক্রিনশট

ফিল্ডরানার্স হল একটি পুরষ্কার-বিজয়ী টাওয়ার-প্রতিরক্ষা শৈলীর গেম যা মূলত 2008 সালে আইফোনের জন্য প্রকাশিত হয়েছিল। তারপর থেকে এটি অন্যান্য অনেক প্ল্যাটফর্মে পোর্ট করা হয়েছে। সবচেয়ে সাম্প্রতিক প্ল্যাটফর্মগুলির মধ্যে একটি ছিল অক্টোবর 2011-এ Chrome ব্রাউজার । ফিল্ডরানারদের একটি HTML5 প্ল্যাটফর্মে পোর্ট করার চ্যালেঞ্জগুলির মধ্যে একটি হল কীভাবে শব্দ বাজাবেন।

ফিল্ডরানাররা সাউন্ড ইফেক্টের জটিল ব্যবহার করে না, তবে এটি তার সাউন্ড এফেক্টের সাথে কীভাবে ইন্টারঅ্যাক্ট করতে পারে তার কিছু প্রত্যাশা নিয়ে আসে। গেমটিতে 88টি সাউন্ড ইফেক্ট রয়েছে যা এক সময়ে অনেক সংখ্যক খেলার আশা করা যায়। এই শব্দগুলির বেশিরভাগই খুব ছোট এবং গ্রাফিকাল উপস্থাপনার সাথে কোনও সংযোগ বিচ্ছিন্ন না করার জন্য যতটা সম্ভব সময়মত একটি ফ্যাশনে চালানো দরকার।

কিছু চ্যালেঞ্জ দেখা দিয়েছে

ফিল্ডরানার্সকে HTML5 এ পোর্ট করার সময় আমরা অডিও ট্যাগ সহ অডিও প্লেব্যাকের সমস্যাগুলির সম্মুখীন হয়েছিলাম এবং এর পরিবর্তে ওয়েব অডিও API- তে ফোকাস করার সিদ্ধান্ত নিয়েছিলাম। WebAudio ব্যবহার করা আমাদের ফিল্ডরানারদের প্রয়োজন এমন উচ্চ সংখ্যক সমসাময়িক প্রভাবগুলি দেওয়ার মতো সমস্যাগুলি সমাধান করতে সহায়তা করেছে। তবুও, Fieldrunners HTML5-এর জন্য একটি অডিও সিস্টেম তৈরি করার সময় আমরা কয়েকটি সূক্ষ্ম সমস্যায় পড়েছি যা অন্য বিকাশকারীরা সচেতন হতে চাইতে পারেন।

AudioBufferSourceNodes এর প্রকৃতি

AudioBufferSourceNodes হল WebAudio-এর সাথে শব্দ বাজানোর আপনার প্রাথমিক পদ্ধতি। এটা বোঝা খুবই গুরুত্বপূর্ণ যে এগুলি একবার ব্যবহারযোগ্য বস্তু। আপনি একটি AudioBufferSourceNode তৈরি করুন, এটিকে একটি বাফার বরাদ্দ করুন, এটিকে গ্রাফের সাথে সংযুক্ত করুন এবং নোটঅন বা নোটগ্রেইনঅন দিয়ে এটি চালান। এর পরে আপনি প্লেব্যাক বন্ধ করতে নোটঅফ কল করতে পারেন, কিন্তু আপনি নোটঅন বা নোটগ্রেইনঅন কল করে আবার উত্সটি চালাতে পারবেন না - আপনাকে অন্য একটি অডিওবাফারসোর্সনোড তৈরি করতে হবে৷ আপনি করতে পারেন - এবং এটি মূল - একই অন্তর্নিহিত অডিওবাফার অবজেক্টটি পুনরায় ব্যবহার করতে পারেন, তবে (আসলে, আপনার একাধিক সক্রিয় অডিওবাফার সোর্সনোডও থাকতে পারে যা একই অডিওবাফার উদাহরণকে নির্দেশ করে!) আপনি গিভ মি এ বিট-এ ফিল্ডরানার্স থেকে একটি প্লেব্যাক স্নিপেট খুঁজে পেতে পারেন।

নন-ক্যাশিং কন্টেন্ট

রিলিজের সময় Fieldrunners HTML5 সার্ভার মিউজিক ফাইলের জন্য বিপুল সংখ্যক অনুরোধ দেখিয়েছে। এই ফলাফলটি Chrome 15 থেকে ফাইলটিকে খণ্ডে ডাউনলোড করার এবং তারপরে এটি ক্যাশে না করার প্রক্রিয়া থেকে উদ্ভূত হয়েছে। সেই সময়ে প্রতিক্রিয়া হিসাবে আমরা আমাদের বাকি অডিও ফাইলগুলির মতো সঙ্গীত ফাইলগুলি লোড করার সিদ্ধান্ত নিয়েছি। এটি করা সাবঅপ্টিমাল কিন্তু অন্যান্য ব্রাউজারগুলির কিছু সংস্করণ এখনও এটি করে।

ফোকাসের বাইরে থাকলে সাইলেন্স করা

আপনার গেমের ট্যাব কখন ফোকাসের বাইরে থাকে তা সনাক্ত করা আগে কঠিন ছিল। ফিল্ডরানাররা Chrome 13 এর আগে পোর্ট করা শুরু করেছিল যেখানে পৃষ্ঠা দৃশ্যমানতা API ট্যাব অস্পষ্টতা সনাক্ত করতে আমাদের জটিল কোডের প্রয়োজনীয়তা প্রতিস্থাপন করেছে। প্রতিটি গেমের দৃশ্যমানতা API ব্যবহার করা উচিত একটি ছোট স্নিপেট লেখার জন্য তাদের শব্দ নিঃশব্দ বা থামানোর জন্য যদি পুরো গেমটি বিরতি না দেয়। যেহেতু ফিল্ডরানাররা রিকোয়েস্ট অ্যানিমেশনফ্রেম এপিআই ব্যবহার করেছে, তাই গেম পজিং অস্পষ্টভাবে পরিচালনা করা হয়েছিল, কিন্তু সাউন্ড পজিং নয়।

বিরাম শব্দ

অদ্ভুতভাবে এই নিবন্ধটির প্রতিক্রিয়া পাওয়ার সময় আমাদের জানানো হয়েছিল যে শব্দগুলিকে থামানোর জন্য আমরা যে কৌশলটি ব্যবহার করছিলাম তা উপযুক্ত ছিল না - আমরা শব্দের প্লেব্যাক থামাতে ওয়েব অডিওর বর্তমান বাস্তবায়নে একটি বাগ ব্যবহার করছিলাম। যেহেতু এটি ভবিষ্যতে স্থির করা হবে, আপনি প্লেব্যাক বন্ধ করার জন্য একটি নোড বা সাবগ্রাফ সংযোগ বিচ্ছিন্ন করে শব্দকে বিরতি দিতে পারবেন না।

একটি সাধারণ ওয়েব অডিও নোড আর্কিটেকচার

Fieldrunners একটি খুব সাধারণ অডিও মডেল আছে. এই মডেল নিম্নলিখিত বৈশিষ্ট্য সেট সমর্থন করতে পারেন:

  • সাউন্ড এফেক্টের ভলিউম নিয়ন্ত্রণ করুন।
  • ব্যাকগ্রাউন্ড মিউজিক ট্র্যাকের ভলিউম নিয়ন্ত্রণ করুন।
  • সমস্ত অডিও নিঃশব্দ করুন।
  • খেলা বন্ধ করা হলে শব্দ বাজানো বন্ধ করুন।
  • গেমটি আবার শুরু হলে সেই একই শব্দগুলি আবার চালু করুন।
  • গেমের ট্যাব ফোকাস হারালে সমস্ত অডিও বন্ধ করুন।
  • প্রয়োজন অনুসারে একটি শব্দ বাজানোর পরে প্লেব্যাক পুনরায় চালু করুন।

ওয়েব অডিওর সাথে উপরের বৈশিষ্ট্যগুলি অর্জন করতে, এটি প্রদত্ত সম্ভাব্য নোডগুলির মধ্যে 3টি ব্যবহার করেছে: গন্তব্য নোড, গেইননোড, অডিওবাফারসোর্স নোড৷ AudioBufferSourceNodes শব্দগুলি চালায়। GainNodes একসাথে AudioBufferSourceNodes সংযোগ করে। ডেস্টিনেশন নোড, ওয়েব অডিও প্রসঙ্গ দ্বারা তৈরি, গন্তব্য বলা হয়, প্লেয়ারের জন্য শব্দ বাজায়। ওয়েব অডিওতে আরও অনেক ধরণের নোড রয়েছে তবে শুধুমাত্র এইগুলি দিয়ে আমরা একটি গেমের শব্দগুলির জন্য একটি খুব সাধারণ গ্রাফ তৈরি করতে পারি।

নোড গ্রাফ চার্ট

একটি ওয়েব অডিও নোড গ্রাফ লিফ নোড থেকে গন্তব্য নোডে নিয়ে যায়। ফিল্ডরানাররা 6টি স্থায়ী লাভ নোড ব্যবহার করে, কিন্তু ভলিউমের উপর সহজে নিয়ন্ত্রণ করার জন্য এবং বৃহত্তর সংখ্যক অস্থায়ী নোড সংযোগ করার জন্য 3টি যথেষ্ট যা প্লেব্যাক বাফার করবে। প্রথমে একটি মাস্টার গেইন নোড প্রতিটি চাইল্ড নোডকে গন্তব্যের সাথে সংযুক্ত করে। মাস্টার গেইন নোডের সাথে অবিলম্বে সংযুক্ত দুটি লাভ নোড, একটি মিউজিক চ্যানেলের জন্য এবং অন্যটি সমস্ত সাউন্ড এফেক্ট লিঙ্ক করার জন্য।

ফিল্ডরানারদের একটি বৈশিষ্ট্য হিসাবে একটি বাগ ভুল ব্যবহারের কারণে 3টি অতিরিক্ত লাভ নোড ছিল। আমরা সেই নোডগুলিকে গ্রাফ থেকে বাজানো শব্দগুলির গ্রুপগুলিকে ক্লিপ করার জন্য ব্যবহার করেছি যা তাদের অগ্রগতি বন্ধ করে দেয়। আমরা শব্দ থামানোর জন্য এটি করেছি। যেহেতু এটি সঠিক নয়, আমরা এখন উপরে বর্ণিত হিসাবে শুধুমাত্র 3টি মোট লাভ নোড ব্যবহার করব। নিম্নলিখিত স্নিপেটগুলির মধ্যে অনেকগুলি আমাদের ভুল নোডগুলিকে অন্তর্ভুক্ত করবে, আমরা কী করেছি তা দেখায় এবং কীভাবে আমরা স্বল্প মেয়াদে এটি ঠিক করব। কিন্তু দীর্ঘ মেয়াদে আপনি আমাদের coreEffectsGain নোডের পরে আমাদের নোডগুলি ব্যবহার না করতে চাইবেন।

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 );
}

বেশিরভাগ গেমই সাউন্ড এফেক্ট এবং মিউজিকের আলাদা নিয়ন্ত্রণের অনুমতি দেয়। এটি আমাদের উপরের গ্রাফ দিয়ে সহজেই সম্পন্ন করা যেতে পারে। প্রতিটি লাভ নোডের একটি "লাভ" বৈশিষ্ট্য রয়েছে যা 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 );
}

Fieldrunners শিপিং করার পরে, আমরা আবিষ্কার করেছি যে শুধুমাত্র একটি নোড বা সাবগ্রাফ সংযোগ বিচ্ছিন্ন করলে 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;
    }
  }
};

যদি আমরা এটি আগে জানতাম যে আমরা একটি বাগ অপব্যবহার করছি, আমাদের অডিও কোডের গঠন খুব ভিন্ন হবে। যেমন, এটি এই নিবন্ধের বেশ কয়েকটি বিভাগকে প্রভাবিত করেছে। এটির সরাসরি প্রভাব এখানে রয়েছে কিন্তু আমাদের কোড স্নিপেটে ফোকাস হারানো এবং বীট দাও। এটি আসলে কীভাবে কাজ করে তা জানার জন্য ফিল্ডরানার্স নোড গ্রাফ (যেহেতু আমরা প্লেব্যাকের সংক্ষিপ্তকরণের জন্য নোড তৈরি করেছি) এবং অতিরিক্ত কোড যা ওয়েব অডিও নিজে থেকে করে না এমন পজ স্টেট রেকর্ড করবে এবং প্রদান করবে উভয়ের পরিবর্তন প্রয়োজন।

ফোকাস হারানো

আমাদের মাস্টার নোড এই বৈশিষ্ট্যটির জন্য কার্যকর হয়। যখন একজন ব্রাউজার ব্যবহারকারী অন্য ট্যাবে স্যুইচ করে, গেমটি আর দৃশ্যমান হয় না। দৃষ্টির বাইরে, মনের বাইরে এবং তাই শব্দটি চলে যাওয়া উচিত। একটি গেমের পৃষ্ঠার জন্য নির্দিষ্ট দৃশ্যমান অবস্থা নির্ধারণ করার জন্য এমন কৌশল রয়েছে যা দৃশ্যমানতা API-এর মাধ্যমে অনেক সহজ হয়ে উঠেছে।

ফিল্ডরানাররা রিকোয়েস্ট অ্যানিমেশনফ্রেম ব্যবহার করার জন্য এটির আপডেট লুপ কল করার জন্য শুধুমাত্র সক্রিয় ট্যাব হিসাবে খেলবে। কিন্তু ওয়েব অডিও প্রসঙ্গ লুপড ইফেক্ট এবং ব্যাকগ্রাউন্ড ট্র্যাক চালানো চালিয়ে যাবে যখন একজন ব্যবহারকারী অন্য ট্যাবে থাকবে। কিন্তু আমরা এটি একটি খুব ছোট দৃশ্যমানতা 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();
    }
  });
}

এই নিবন্ধটি লেখার আগে, আমরা ভেবেছিলাম যে মাস্টারটিকে মিউট করার পরিবর্তে সমস্ত শব্দকে বিরতি দেওয়ার জন্য সংযোগ বিচ্ছিন্ন করা যথেষ্ট হবে৷ সেই সময়ে নোডটি সংযোগ বিচ্ছিন্ন করে, আমরা এটি এবং এর বাচ্চাদের প্রক্রিয়াকরণ এবং খেলা থেকে বিরত করেছিলাম। যখন এটি পুনরায় সংযোগ করা হয় তখন সমস্ত শব্দ এবং সঙ্গীত বাজানো শুরু হবে যেখানে তারা ছেড়েছিল ঠিক যেমন গেম প্লে যেখানে ছেড়েছিল সেখানেই চলবে৷ কিন্তু এটি অপ্রত্যাশিত আচরণ। প্লেব্যাক বন্ধ করার জন্য শুধু সংযোগ বিচ্ছিন্ন করাই যথেষ্ট নয়।

যখন আপনার ট্যাব আর ফোকাসে থাকে না তখন পৃষ্ঠা দৃশ্যমানতা API এটিকে জানা খুব সহজ করে তোলে৷ যদি আপনার কাছে ইতিমধ্যেই সাউন্ড পজ করার কার্যকরী কোড থাকে, গেম ট্যাব লুকানো থাকলে সাউন্ড পজিং-এ লিখতে মাত্র কয়েক লাইন লাগে।

গিভ মি এ বিট

আমরা এখন কিছু জিনিস সেট আপ আছে. আমরা নোড একটি গ্রাফ আছে. প্লেয়ার যখন গেমটি বিরতি দেয় তখন আমরা শব্দগুলিকে বিরতি দিতে পারি এবং গেম মেনুগুলির মতো উপাদানগুলির জন্য নতুন শব্দগুলি চালাতে পারি। ব্যবহারকারী যখন একটি নতুন ট্যাবে স্যুইচ করে তখন আমরা সমস্ত শব্দ এবং সঙ্গীত বিরতি দিতে পারি। এখন আমরা আসলে একটি শব্দ বাজানো প্রয়োজন.

একটি চরিত্র মারা যাওয়ার মতো একটি গেম সত্তার একাধিক উদাহরণের জন্য শব্দের একাধিক কপি বাজানোর পরিবর্তে, ফিল্ডরানাররা তার সময়কালের জন্য শুধুমাত্র একবার একটি শব্দ বাজায়৷ বাজানো শেষ হওয়ার পরে যদি শব্দের প্রয়োজন হয় তবে এটি পুনরায় চালু হতে পারে তবে ইতিমধ্যে বাজানোর সময় নয়। এটি ফিল্ডরানার্সের অডিও ডিজাইনের জন্য একটি সিদ্ধান্ত কারণ এটিতে এমন শব্দ রয়েছে যা দ্রুত বাজানোর জন্য অনুরোধ করা হয়েছে যা অন্যথায় আবার চালু করার অনুমতি দিলে বা একাধিক দৃষ্টান্ত চালানোর অনুমতি দিলে একটি আনন্দদায়ক ক্যাকোফোনি তৈরি হয়। AudioBufferSourceNodes এক-শট হিসাবে ব্যবহার করা হবে বলে আশা করা হচ্ছে। একটি নোড তৈরি করুন, একটি বাফার সংযুক্ত করুন, প্রয়োজনে লুপ বুলিয়ান মান সেট করুন, গ্রাফের একটি নোডের সাথে সংযোগ করুন যা গন্তব্যে নিয়ে যাবে, নোটঅন বা নোটগ্রেইনঅনকে কল করুন এবং বিকল্পভাবে নোটঅফ কল করুন।

ফিল্ডরানারদের জন্য এটি এমন কিছু দেখায়:

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 );
  }
}

খুব বেশি স্ট্রিমিং

ফিল্ডরানার্স মূলত একটি অডিও ট্যাগ সহ বাজানো ব্যাকগ্রাউন্ড মিউজিক দিয়ে চালু করা হয়েছিল। রিলিজের সময়, আমরা আবিষ্কার করেছি যে গেমের বাকি বিষয়বস্তুর অনুরোধের তুলনায় মিউজিক ফাইলগুলিকে অসম পরিমাণে অনুরোধ করা হচ্ছে। কিছু গবেষণার পরে আমরা আবিষ্কার করেছি যে সেই সময়ে ক্রোম ব্রাউজার মিউজিক ফাইলগুলির স্ট্রিম করা অংশগুলিকে ক্যাশ করছিল না। এর ফলে ব্রাউজার এটি শেষ হওয়ার সাথে সাথে প্রতি কয়েক মিনিটে প্লে ট্র্যাকের অনুরোধ করে। আরও সাম্প্রতিক পরীক্ষায়, ক্রোম ক্যাশে স্ট্রিম করা ট্র্যাকগুলি যদিও অন্যান্য ব্রাউজারগুলি এখনও এটি করছে না। মিউজিক প্লেব্যাকের মতো কার্যকারিতার জন্য অডিও ট্যাগ সহ বড় অডিও ফাইলগুলি স্ট্রিম করা সর্বোত্তম কিন্তু কিছু ব্রাউজার সংস্করণের জন্য আপনি আপনার সঙ্গীত লোড করতে চাইতে পারেন যেভাবে আপনি সাউন্ড ইফেক্ট লোড করেন।

যেহেতু সমস্ত সাউন্ড ইফেক্ট ওয়েব অডিওর মাধ্যমে বাজছিল তাই আমরা ব্যাকগ্রাউন্ড মিউজিক বাজানোকে ওয়েব অডিওতেও সরিয়ে নিয়েছি। এর মানে হল যে আমরা XMLHttpRequests এবং arraybuffer রেসপন্স টাইপ দিয়ে সমস্ত ইফেক্ট লোড করেছিলাম সেইভাবে আমরা ট্র্যাকগুলি লোড করব।

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();
}

সারাংশ

Fieldrunners ছিল Chrome এবং HTML5-এ আনার জন্য একটি বিস্ফোরণ৷ জাভাস্ক্রিপ্টে হাজার হাজার C++ লাইন আনার নিজস্ব কাজের পাহাড়ের বাইরে, কিছু আকর্ষণীয় দ্বিধা এবং HTML5 এর জন্য নির্দিষ্ট সিদ্ধান্ত জাগিয়ে তোলে। অন্যগুলোর মধ্যে কোনোটি না হলে একটিকে পুনরাবৃত্তি করতে, AudioBufferSourceNodes এক সময় ব্যবহার করা বস্তু। সেগুলি তৈরি করুন, একটি অডিও বাফার সংযুক্ত করুন, এটিকে ওয়েব অডিও গ্রাফের সাথে সংযুক্ত করুন এবং নোটঅন বা নোটগ্রেইনঅনের সাথে খেলুন৷ আবার সেই শব্দ বাজাতে হবে? তারপর আরেকটি AudioBufferSourceNode তৈরি করুন।