অডিও ওয়ার্কলেট লিখুন

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

পটভূমি: ScriptProcessorNode

ওয়েব অডিও API-তে অডিও প্রক্রিয়াকরণ প্রধান UI থ্রেড থেকে একটি পৃথক থ্রেডে চলে, তাই এটি মসৃণভাবে চলে। জাভাস্ক্রিপ্টে কাস্টম অডিও প্রসেসিং সক্ষম করার জন্য, ওয়েব অডিও এপিআই একটি স্ক্রিপ্টপ্রসেসর নোড প্রস্তাব করেছে যা ইভেন্ট হ্যান্ডলার ব্যবহার করে প্রধান UI থ্রেডে ব্যবহারকারীর স্ক্রিপ্ট চালু করতে।

এই ডিজাইনে দুটি সমস্যা রয়েছে: ইভেন্ট হ্যান্ডলিং ডিজাইন অনুসারে অ্যাসিঙ্ক্রোনাস, এবং কোড এক্সিকিউশন প্রধান থ্রেডে ঘটে। পূর্ববর্তীটি লেটেন্সি প্ররোচিত করে এবং পরবর্তীটি প্রধান থ্রেডকে চাপ দেয় যা সাধারণত বিভিন্ন UI এবং DOM-সম্পর্কিত কাজগুলির সাথে ভিড় করে যার ফলে হয় UI "জ্যাঙ্ক" বা অডিও "গ্লচ" হয়। এই মৌলিক ডিজাইনের ত্রুটির কারণে, ScriptProcessorNode স্পেসিফিকেশন থেকে বাতিল করা হয়েছে এবং AudioWorklet দিয়ে প্রতিস্থাপিত হয়েছে।

ধারণা

অডিও ওয়ার্কলেট ব্যবহারকারীর সরবরাহ করা জাভাস্ক্রিপ্ট কোডকে অডিও প্রসেসিং থ্রেডের মধ্যে সুন্দরভাবে রাখে — অর্থাৎ, অডিও প্রসেস করতে মূল থ্রেডে যেতে হবে না। এর অর্থ হল ব্যবহারকারীর সরবরাহকৃত স্ক্রিপ্ট কোডটি অডিও রেন্ডারিং থ্রেডে (AudioWorkletGlobalScope) অন্যান্য অন্তর্নির্মিত অডিওনোডের সাথে চলতে পারে, যা শূন্য অতিরিক্ত লেটেন্সি এবং সিঙ্ক্রোনাস রেন্ডারিং নিশ্চিত করে।

প্রধান গ্লোবাল স্কোপ এবং অডিও ওয়ার্কলেট স্কোপ ডায়াগ্রাম
আকার 1

রেজিস্ট্রেশন এবং ইনস্ট্যান্টেশন

অডিও ওয়ার্কলেট ব্যবহার করে দুটি অংশ থাকে: অডিও ওয়ার্কলেট প্রসেসর এবং অডিও ওয়ার্কলেট নোড। এটি ScriptProcessorNode ব্যবহার করার চেয়ে বেশি জড়িত, তবে কাস্টম অডিও প্রক্রিয়াকরণের জন্য ডেভেলপারদের নিম্ন-স্তরের ক্ষমতা দেওয়ার জন্য এটি প্রয়োজন। AudioWorkletProcessor জাভাস্ক্রিপ্ট কোডে লিখিত প্রকৃত অডিও প্রসেসরের প্রতিনিধিত্ব করে এবং এটি AudioWorkletGlobalScope-এ থাকে। AudioWorkletNode হল AudioWorkletProcessor-এর কাউন্টারপার্ট এবং মূল থ্রেডের অন্যান্য অডিও নোডের সাথে সংযোগের যত্ন নেয়। এটি প্রধান বৈশ্বিক সুযোগে উন্মোচিত হয় এবং একটি নিয়মিত অডিওনোডের মতো কাজ করে।

এখানে এক জোড়া কোড স্নিপেট রয়েছে যা রেজিস্ট্রেশন এবং ইনস্ট্যান্টেশন প্রদর্শন করে।

// The code in the main global scope.
class MyWorkletNode extends AudioWorkletNode {
  constructor(context) {
    super(context, 'my-worklet-processor');
  }
}

let context = new AudioContext();

context.audioWorklet.addModule('processors.js').then(() => {
  let node = new MyWorkletNode(context);
});

একটি AudioWorkletNode তৈরি করার জন্য কমপক্ষে দুটি জিনিস প্রয়োজন: একটি AudioContext অবজেক্ট এবং একটি স্ট্রিং হিসাবে প্রসেসরের নাম৷ একটি প্রসেসর সংজ্ঞা নতুন অডিও ওয়ার্কলেট অবজেক্টের addModule() কল দ্বারা লোড এবং নিবন্ধিত হতে পারে। অডিও ওয়ার্কলেট সহ ওয়ার্কলেট এপিআই শুধুমাত্র একটি সুরক্ষিত প্রেক্ষাপটে উপলব্ধ, এইভাবে সেগুলি ব্যবহার করা একটি পৃষ্ঠা অবশ্যই HTTPS-এর মাধ্যমে পরিবেশন করা উচিত, যদিও http://localhost স্থানীয় পরীক্ষার জন্য নিরাপদ বলে বিবেচিত হয়৷

এটাও লক্ষণীয় যে আপনি ওয়ার্কলেটে চলমান প্রসেসর দ্বারা সমর্থিত একটি কাস্টম নোড সংজ্ঞায়িত করতে AudioWorkletNode সাবক্লাস করতে পারেন।

// This is "processor.js" file, evaluated in AudioWorkletGlobalScope upon
// audioWorklet.addModule() call in the main global scope.
class MyWorkletProcessor extends AudioWorkletProcessor {
  constructor() {
    super();
  }

  process(inputs, outputs, parameters) {
    // audio processing code here.
  }
}

registerProcessor('my-worklet-processor', MyWorkletProcessor);

AudioWorkletGlobalScope-এ registerProcessor() পদ্ধতিটি নিবন্ধিত হওয়ার জন্য প্রসেসরের নাম এবং শ্রেণির সংজ্ঞার জন্য একটি স্ট্রিং নেয়। গ্লোবাল স্কোপে স্ক্রিপ্ট কোড মূল্যায়ন শেষ হওয়ার পরে, AudioWorklet.addModule() থেকে প্রতিশ্রুতিটি সমাধান করা হবে এবং ব্যবহারকারীদের জানিয়ে দেওয়া হবে যে ক্লাসের সংজ্ঞাটি প্রধান বিশ্বব্যাপী পরিসরে ব্যবহারের জন্য প্রস্তুত।

কাস্টম অডিও প্যারাম

AudioNodes সম্পর্কে দরকারী জিনিসগুলির মধ্যে একটি হল AudioParams এর সাথে নির্ধারিত পরামিতি অটোমেশন। AudioWorkletNodes এগুলিকে এক্সপোজড প্যারামিটার পেতে ব্যবহার করতে পারে যা অডিও হারে স্বয়ংক্রিয়ভাবে নিয়ন্ত্রণ করা যায়।

অডিও ওয়ার্কলেট নোড এবং প্রসেসর ডায়াগ্রাম
চিত্র 2

AudioParamDescriptors এর একটি সেট আপ করে ব্যবহারকারী-সংজ্ঞায়িত অডিওপ্যারামগুলি একটি AudioWorkletProcessor শ্রেণীর সংজ্ঞায় ঘোষণা করা যেতে পারে। অন্তর্নিহিত WebAudio ইঞ্জিন একটি AudioWorkletNode নির্মাণের পরে এই তথ্যটি তুলে নেবে এবং তারপর সেই অনুযায়ী নোডের সাথে AudioParam অবজেক্ট তৈরি ও লিঙ্ক করবে।

/* A separate script file, like "my-worklet-processor.js" */
class MyWorkletProcessor extends AudioWorkletProcessor {

  // Static getter to define AudioParam objects in this custom processor.
  static get parameterDescriptors() {
    return [{
      name: 'myParam',
      defaultValue: 0.707
    }];
  }

  constructor() { super(); }

  process(inputs, outputs, parameters) {
    // |myParamValues| is a Float32Array of either 1 or 128 audio samples
    // calculated by WebAudio engine from regular AudioParam operations.
    // (automation methods, setter) Without any AudioParam change, this array
    // would be a single value of 0.707.
    const myParamValues = parameters.myParam;

    if (myParamValues.length === 1) {
      // |myParam| has been a constant value for the current render quantum,
      // which can be accessed by |myParamValues[0]|.
    } else {
      // |myParam| has been changed and |myParamValues| has 128 values.
    }
  }
}

AudioWorkletProcessor.process() পদ্ধতি

প্রকৃত অডিও প্রক্রিয়াকরণ অডিও ওয়ার্কলেটপ্রসেসরের process() কলব্যাক পদ্ধতিতে ঘটে এবং এটি অবশ্যই শ্রেণি সংজ্ঞায় ব্যবহারকারীর দ্বারা প্রয়োগ করা উচিত। WebAudio ইঞ্জিন ইনপুট এবং প্যারামিটার ফিড করতে এবং আউটপুট আনতে আইসোক্রোনাস পদ্ধতিতে এই ফাংশনটি চালু করবে।

/* AudioWorkletProcessor.process() method */
process(inputs, outputs, parameters) {
  // The processor may have multiple inputs and outputs. Get the first input and
  // output.
  const input = inputs[0];
  const output = outputs[0];

  // Each input or output may have multiple channels. Get the first channel.
  const inputChannel0 = input[0];
  const outputChannel0 = output[0];

  // Get the parameter value array.
  const myParamValues = parameters.myParam;

  // if |myParam| has been a constant value during this render quantum, the
  // length of the array would be 1.
  if (myParamValues.length === 1) {
    // Simple gain (multiplication) processing over a render quantum
    // (128 samples). This processor only supports the mono channel.
    for (let i = 0; i < inputChannel0.length; ++i) {
      outputChannel0[i] = inputChannel0[i] * myParamValues[0];
    }
  } else {
    for (let i = 0; i < inputChannel0.length; ++i) {
      outputChannel0[i] = inputChannel0[i] * myParamValues[i];
    }
  }

  // To keep this processor alive.
  return true;
}

উপরন্তু, process() পদ্ধতির রিটার্ন মান AudioWorkletNode-এর জীবনকাল নিয়ন্ত্রণ করতে ব্যবহার করা যেতে পারে যাতে বিকাশকারীরা মেমরির পদচিহ্ন পরিচালনা করতে পারে। process() পদ্ধতি থেকে false প্রত্যাবর্তন প্রসেসরটিকে নিষ্ক্রিয় হিসাবে চিহ্নিত করবে এবং WebAudio ইঞ্জিন আর পদ্ধতিটি চালু করবে না। প্রসেসরকে জীবিত রাখতে, পদ্ধতিটি true হতে হবে। অন্যথায়, নোড/প্রসেসর জোড়া শেষ পর্যন্ত সিস্টেম দ্বারা সংগ্রহ করা আবর্জনা হবে।

মেসেজপোর্টের সাথে দ্বি-নির্দেশিক যোগাযোগ

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

চিত্র 2
চিত্র 2

নোড এবং প্রসেসর উভয়েই .port অ্যাট্রিবিউটের মাধ্যমে মেসেজপোর্ট অ্যাক্সেস করা যেতে পারে। নোডের port.postMessage() পদ্ধতি সংশ্লিষ্ট প্রসেসরের port.onmessage হ্যান্ডলারে একটি বার্তা পাঠায় এবং এর বিপরীতে।

/* The code in the main global scope. */
context.audioWorklet.addModule('processors.js').then(() => {
  let node = new AudioWorkletNode(context, 'port-processor');
  node.port.onmessage = (event) => {
    // Handling data from the processor.
    console.log(event.data);
  };

  node.port.postMessage('Hello!');
});
/* "processor.js" file. */
class PortProcessor extends AudioWorkletProcessor {
  constructor() {
    super();
    this.port.onmessage = (event) => {
      // Handling data from the node.
      console.log(event.data);
    };

    this.port.postMessage('Hi!');
  }

  process(inputs, outputs, parameters) {
    // Do nothing, producing silent output.
    return true;
  }
}

registerProcessor('port-processor', PortProcessor);

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

ওয়াকথ্রু: একটি গেইননোড তৈরি করা

সবকিছু একসাথে রেখে, এখানে AudioWorkletNode এবং AudioWorkletProcessor এর উপরে নির্মিত GainNode-এর একটি সম্পূর্ণ উদাহরণ।

Index.html

<!doctype html>
<html>
<script>
  const context = new AudioContext();

  // Loads module script via AudioWorklet.
  context.audioWorklet.addModule('gain-processor.js').then(() => {
    let oscillator = new OscillatorNode(context);

    // After the resolution of module loading, an AudioWorkletNode can be
    // constructed.
    let gainWorkletNode = new AudioWorkletNode(context, 'gain-processor');

    // AudioWorkletNode can be interoperable with other native AudioNodes.
    oscillator.connect(gainWorkletNode).connect(context.destination);
    oscillator.start();
  });
</script>
</html>

get-processor.js

class GainProcessor extends AudioWorkletProcessor {

  // Custom AudioParams can be defined with this static getter.
  static get parameterDescriptors() {
    return [{ name: 'gain', defaultValue: 1 }];
  }

  constructor() {
    // The super constructor call is required.
    super();
  }

  process(inputs, outputs, parameters) {
    const input = inputs[0];
    const output = outputs[0];
    const gain = parameters.gain;
    for (let channel = 0; channel < input.length; ++channel) {
      const inputChannel = input[channel];
      const outputChannel = output[channel];
      if (gain.length === 1) {
        for (let i = 0; i < inputChannel.length; ++i)
          outputChannel[i] = inputChannel[i] * gain[0];
      } else {
        for (let i = 0; i < inputChannel.length; ++i)
          outputChannel[i] = inputChannel[i] * gain[i];
      }
    }

    return true;
  }
}

registerProcessor('gain-processor', GainProcessor);

এটি অডিও ওয়ার্কলেট সিস্টেমের মৌলিক কভার করে। লাইভ ডেমোগুলি Chrome WebAudio টিমের GitHub সংগ্রহস্থলে উপলব্ধ।

বৈশিষ্ট্য পরিবর্তন: স্থিতিশীল থেকে পরীক্ষামূলক

Chrome 66 বা তার পরবর্তী সংস্করণের জন্য অডিও ওয়ার্কলেট ডিফল্টরূপে সক্রিয় থাকে৷ Chrome 64 এবং 65-এ, বৈশিষ্ট্যটি পরীক্ষামূলক পতাকার পিছনে ছিল।