ওয়েব অডিও API দিয়ে শুরু করা

HTML5 <audio> উপাদানের আগে, ওয়েবের নীরবতা ভাঙতে ফ্ল্যাশ বা অন্য প্লাগইন প্রয়োজন ছিল। ওয়েবে অডিওর জন্য আর প্লাগইনের প্রয়োজন নেই, অডিও ট্যাগ অত্যাধুনিক গেম এবং ইন্টারেক্টিভ অ্যাপ্লিকেশন বাস্তবায়নের জন্য উল্লেখযোগ্য সীমাবদ্ধতা নিয়ে আসে।

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

Audio Context দিয়ে শুরু করা

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

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

পুরানো ওয়েবকিট-ভিত্তিক ব্রাউজারগুলির জন্য, webkitAudioContext এর মতো webkit উপসর্গ ব্যবহার করুন।

অনেক আকর্ষণীয় ওয়েব অডিও API কার্যকারিতা যেমন AudioNodes তৈরি করা এবং অডিও ফাইল ডেটা ডিকোড করা হল AudioContext এর পদ্ধতি।

লোড হচ্ছে শব্দ

ওয়েব অডিও API স্বল্প থেকে মাঝারি দৈর্ঘ্যের শব্দের জন্য একটি AudioBuffer ব্যবহার করে। মৌলিক পদ্ধতি হল শব্দ ফাইল আনার জন্য XMLHttpRequest ব্যবহার করা।

API একাধিক ফর্ম্যাটে অডিও ফাইল ডেটা লোড করা সমর্থন করে, যেমন 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 সম্পর্কে এই নিবন্ধটি দেখুন।

একবার (আনডিকোডেড) অডিও ফাইল ডেটা প্রাপ্ত হয়ে গেলে, এটি পরবর্তী ডিকোডিংয়ের জন্য রাখা যেতে পারে, অথবা অডিও কনটেক্সট decodeAudioData() পদ্ধতি ব্যবহার করে এটি সরাসরি ডিকোড করা যেতে পারে। এই পদ্ধতিটি request.response এ সংরক্ষিত অডিও ফাইল ডেটার ArrayBuffer নেয় এবং এটিকে অ্যাসিঙ্ক্রোনাসভাবে ডিকোড করে (প্রধান জাভাস্ক্রিপ্ট এক্সিকিউশন থ্রেড ব্লক না করে)।

যখন decodeAudioData() সমাপ্ত হয়, এটি একটি কলব্যাক ফাংশনকে কল করে যা AudioBuffer হিসাবে ডিকোড করা PCM অডিও ডেটা প্রদান করে।

বাজানো শব্দ

একটি সাধারণ অডিও গ্রাফ
একটি সাধারণ অডিও গ্রাফ

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

ওয়েব অডিও 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);
}

সময়ের সাথে মোকাবিলা করা: তালের সাথে শব্দ বাজানো

ওয়েব অডিও 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 ব্যবহার করতে পারি, এটি সুনির্দিষ্ট নয় । ওয়েব অডিও API এর সাথে, আমরা AudioGainNode এর লাভ মানের মতো পরামিতিগুলির জন্য ভবিষ্যতের মান নির্ধারণ করতে AudioParam ইন্টারফেস ব্যবহার করতে পারি।

এইভাবে, একটি প্লেলিস্ট দেওয়া হলে, বর্তমান ট্র্যাকটি বাজানো শেষ হওয়ার কিছুটা আগে আমরা বর্তমানে প্লে করা ট্র্যাকে একটি লাভ হ্রাস এবং পরবর্তীটিতে একটি লাভ বৃদ্ধির সময় নির্ধারণ করে ট্র্যাকগুলির মধ্যে স্থানান্তর করতে পারি:

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

ওয়েব অডিও API একটি প্যারামিটারের মান ধীরে ধীরে পরিবর্তন করতে RampToValue পদ্ধতির একটি সুবিধাজনক সেট প্রদান করে, যেমন linearRampToValueAtTime এবং exponentialRampToValueAtTime

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

একটি শব্দে একটি সাধারণ ফিল্টার প্রভাব প্রয়োগ করা

BiquadFilterNode সহ একটি অডিও গ্রাফ
BiquadFilterNode সহ একটি অডিও গ্রাফ

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

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

সমর্থিত ধরনের ফিল্টার অন্তর্ভুক্ত:

  • কম পাস ফিল্টার
  • উচ্চ পাস ফিল্টার
  • ব্যান্ড পাস ফিল্টার
  • কম তাক ফিল্টার
  • উচ্চ তাক ফিল্টার
  • পিকিং ফিল্টার
  • খাঁজ ফিল্টার
  • সমস্ত পাস ফিল্টার

এবং সমস্ত ফিল্টারে কিছু পরিমাণ লাভ নির্দিষ্ট করার পরামিতি, ফিল্টারটি প্রয়োগ করার ফ্রিকোয়েন্সি এবং একটি গুণমান ফ্যাক্টর অন্তর্ভুক্ত থাকে। লো-পাস ফিল্টার নিম্ন ফ্রিকোয়েন্সি পরিসীমা রাখে, কিন্তু উচ্চ ফ্রিকোয়েন্সি বাতিল করে। ব্রেক-অফ পয়েন্ট ফ্রিকোয়েন্সি মান দ্বারা নির্ধারিত হয়, এবং 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) কল করে গ্রাফ থেকে AudioNodes সংযোগ বিচ্ছিন্ন করতে পারি। উদাহরণস্বরূপ, গ্রাফটিকে ফিল্টার থেকে সরাসরি সংযোগে যাওয়ার জন্য পুনরায় রুট করতে, আমরা নিম্নলিখিতগুলি করতে পারি:

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

আরও শোনা

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

আপনি যদি অনুপ্রেরণা খুঁজছেন, অনেক বিকাশকারী ইতিমধ্যেই ওয়েব অডিও API ব্যবহার করে দুর্দান্ত কাজ তৈরি করেছেন। আমার প্রিয় কিছু অন্তর্ভুক্ত:

  • AudioJedit , একটি ইন-ব্রাউজার সাউন্ড স্প্লিসিং টুল যা সাউন্ডক্লাউড পারমালিঙ্ক ব্যবহার করে।
  • টোনক্রাফ্ট , একটি সাউন্ড সিকোয়েন্সার যেখানে 3D ব্লক স্ট্যাক করে শব্দ তৈরি করা হয়।
  • Plink , ওয়েব অডিও এবং ওয়েব সকেট ব্যবহার করে একটি সহযোগী সঙ্গীত তৈরির খেলা।