অবস্থানগত অডিও এবং WebGL মিশ্রিত করা

Ilmari Heikkinen

ভূমিকা

এই নিবন্ধে আমি আপনার WebGL দৃশ্যে 3D সাউন্ড যোগ করতে ওয়েব অডিও API- তে অবস্থানগত অডিও বৈশিষ্ট্যটি কীভাবে ব্যবহার করবেন সে সম্পর্কে কথা বলতে যাচ্ছি। অডিওটিকে আরও বিশ্বাসযোগ্য করে তুলতে, আমি আপনাকে ওয়েব অডিও API এর সাথে সম্ভাব্য পরিবেশগত প্রভাবগুলির সাথে পরিচয় করিয়ে দেব। ওয়েব অডিও API-এর আরও পুঙ্খানুপুঙ্খ পরিচিতি পেতে, Boris Smus-এর Web Audio API-এর সাথে শুরু করা নিবন্ধটি দেখুন।

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

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

// Detect if the audio context is supported.
window.AudioContext = (
  window.AudioContext ||
  window.webkitAudioContext ||
  null
);

if (!AudioContext) {
  throw new Error("AudioContext not supported!");
} 

// Create a new audio context.
var ctx = new AudioContext();

// Create a AudioGainNode to control the main volume.
var mainVolume = ctx.createGain();
// Connect the main volume node to the context destination.
mainVolume.connect(ctx.destination);

// Create an object with a sound source and a volume control.
var sound = {};
sound.source = ctx.createBufferSource();
sound.volume = ctx.createGain();

// Connect the sound source to the volume control.
sound.source.connect(sound.volume);
// Hook up the sound volume control to the main volume.
sound.volume.connect(mainVolume);

// Make the sound source loop.
sound.source.loop = true;

// Load a sound file using an ArrayBuffer XMLHttpRequest.
var request = new XMLHttpRequest();
request.open("GET", soundFileName, true);
request.responseType = "arraybuffer";
request.onload = function(e) {

  // Create a buffer from the response ArrayBuffer.
  ctx.decodeAudioData(this.response, function onSuccess(buffer) {
    sound.buffer = buffer;

    // Make the sound source use the buffer and start playing it.
    sound.source.buffer = sound.buffer;
    sound.source.start(ctx.currentTime);
  }, function onFailure() {
    alert("Decoding the audio buffer failed");
  });
};
request.send();

অবস্থান

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

শুরু করতে, একটি অডিও উৎস তৈরি করুন এবং এটি একটি AudioPannerNode-এ সংযুক্ত করুন। তারপর AudioPannerNode এর অবস্থান সেট করুন। এখন আপনি একটি চলমান 3D শব্দ আছে. অডিও প্রসঙ্গ শ্রোতার অবস্থান ডিফল্টরূপে (0,0,0) থাকে, তাই যখন এইভাবে ব্যবহার করা হয়, তখন AudioPannerNode অবস্থান ক্যামেরা অবস্থানের সাথে আপেক্ষিক। যখনই আপনি ক্যামেরা সরান, আপনাকে AudioPannerNode অবস্থান আপডেট করতে হবে। AudioPannerNode পজিশনকে বিশ্বের সাপেক্ষ করতে, আপনাকে আপনার ক্যামেরার অবস্থানে অডিও প্রসঙ্গ শ্রোতার অবস্থান পরিবর্তন করতে হবে।

অবস্থান ট্র্যাকিং সেট আপ করার জন্য, আমাদের একটি AudioPannerNode তৈরি করতে হবে এবং এটিকে প্রধান ভলিউম পর্যন্ত তারের করতে হবে।

...
sound.panner = ctx.createPanner();
// Instead of hooking up the volume to the main volume, hook it up to the panner.
sound.volume.connect(sound.panner);
// And hook up the panner to the main volume.
sound.panner.connect(mainVolume);
...

প্রতিটি ফ্রেমে, AudioPannerNodes-এর অবস্থান আপডেট করুন। আমি নিচের উদাহরণে Three.js ব্যবহার করতে যাচ্ছি।

...
// In the frame handler function, get the object's position.
object.position.set(newX, newY, newZ);
object.updateMatrixWorld();
var p = new THREE.Vector3();
p.setFromMatrixPosition(object.matrixWorld);

// And copy the position over to the sound of the object.
sound.panner.setPosition(p.x, p.y, p.z);
...

শ্রোতার অবস্থান ট্র্যাক করতে, ক্যামেরা অবস্থানের সাথে মেলে অডিও প্রসঙ্গের শ্রোতার অবস্থান সেট করুন৷

...
// Get the camera position.
camera.position.set(newX, newY, newZ);
camera.updateMatrixWorld();
var p = new THREE.Vector3();
p.setFromMatrixPosition(camera.matrixWorld);

// And copy the position over to the listener.
ctx.listener.setPosition(p.x, p.y, p.z);
...

বেগ

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

শ্রোতা এবং AudioPannerNode-এর জন্য বেগ পেতে সবচেয়ে সহজ উপায় হল তাদের প্রতি-ফ্রেমের অবস্থানের উপর নজর রাখা। শ্রোতার বেগ হল ক্যামেরার বর্তমান অবস্থান বিয়োগ পূর্বের ফ্রেমে ক্যামেরার অবস্থান। একইভাবে, AudioPannerNode এর বেগ হল এর বর্তমান অবস্থান বিয়োগ পূর্ববর্তী অবস্থান।

বেগ ট্র্যাক করা বস্তুর পূর্ববর্তী অবস্থান পেয়ে, বর্তমান অবস্থান থেকে বিয়োগ করে এবং শেষ ফ্রেমের পর থেকে অতিবাহিত সময় দ্বারা ফলাফল ভাগ করে করা যেতে পারে। Three.js-এ এটি কীভাবে করবেন তা এখানে:

...
var dt = secondsSinceLastFrame;

var p = new THREE.Vector3();
p.setFromMatrixPosition(object.matrixWorld);
var px = p.x, py = p.y, pz = p.z;

object.position.set(newX, newY, newZ);
object.updateMatrixWorld();

var q = new THREE.Vector3();
q.setFromMatrixPosition(object.matrixWorld);
var dx = q.x-px, dy = q.y-py, dz = q.z-pz;

sound.panner.setPosition(q.x, q.y, q.z);
sound.panner.setVelocity(dx/dt, dy/dt, dz/dt);
...

ওরিয়েন্টেশন

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

AudioPannerNode-এর জন্য ওরিয়েন্টেশন ভেক্টর পেতে, আপনাকে শব্দ-নির্গত 3D অবজেক্টের মডেল ম্যাট্রিক্সের ঘূর্ণন অংশ নিতে হবে এবং এটির সাথে একটি vec3(0,0,1) গুন করতে হবে যেখানে এটি নির্দেশ করে শেষ হয়। কনটেক্সট লিসেনার ওরিয়েন্টেশনের জন্য, আপনাকে ক্যামেরার ওরিয়েন্টেশন ভেক্টর পেতে হবে। শ্রোতা অভিযোজনেরও একটি আপ ভেক্টর প্রয়োজন, কারণ এটি শ্রোতার মাথার রোল কোণ জানতে হবে। শ্রোতা অভিযোজন গণনা করতে, ক্যামেরার ভিউ ম্যাট্রিক্সের ঘূর্ণন অংশটি পান এবং অভিযোজনের জন্য একটি vec3(0,0,1) এবং আপ-ভেক্টরের জন্য একটি vec3(0,-1,0) গুণ করুন৷

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

Three.js-এ ওরিয়েন্টেশন ট্র্যাক করা একটু জটিল কারণ এতে কিছু ভেক্টর ম্যাথ জড়িত এবং 4x4 ওয়ার্ল্ড ম্যাট্রিক্সের অনুবাদ অংশকে শূন্য করা। এখনও, কোড অনেক লাইন না.

...
var vec = new THREE.Vector3(0,0,1);
var m = object.matrixWorld;

// Save the translation column and zero it.
var mx = m.elements[12], my = m.elements[13], mz = m.elements[14];
m.elements[12] = m.elements[13] = m.elements[14] = 0;

// Multiply the 0,0,1 vector by the world matrix and normalize the result.
vec.applyProjection(m);
vec.normalize();

sound.panner.setOrientation(vec.x, vec.y, vec.z);

// Restore the translation column.
m.elements[12] = mx;
m.elements[13] = my;
m.elements[14] = mz;
...

ক্যামেরা ওরিয়েন্টেশন ট্র্যাকিংয়ের জন্যও আপ ভেক্টর প্রয়োজন, তাই আপনাকে রূপান্তর ম্যাট্রিক্সের সাথে একটি আপ ভেক্টর গুণ করতে হবে।

...
// The camera's world matrix is named "matrix".
var m = camera.matrix;

var mx = m.elements[12], my = m.elements[13], mz = m.elements[14];
m.elements[12] = m.elements[13] = m.elements[14] = 0;

// Multiply the orientation vector by the world matrix of the camera.
var vec = new THREE.Vector3(0,0,1);
vec.applyProjection(m);
vec.normalize();

// Multiply the up vector by the world matrix.
var up = new THREE.Vector3(0,-1,0);
up.applyProjection(m);
up.normalize();

// Set the orientation and the up-vector for the listener.
ctx.listener.setOrientation(vec.x, vec.y, vec.z, up.x, up.y, up.z);

m.elements[12] = mx;
m.elements[13] = my;
m.elements[14] = mz;
...

আপনার শব্দের জন্য শব্দ শঙ্কু সেট করতে, আপনি প্যানার নোডের উপযুক্ত বৈশিষ্ট্য সেট করুন। শঙ্কু কোণগুলি ডিগ্রীতে থাকে এবং 0 থেকে 360 পর্যন্ত চলে।

...
sound.panner.coneInnerAngle = innerAngleInDegrees;
sound.panner.coneOuterAngle = outerAngleInDegrees;
sound.panner.coneOuterGain = outerGainFactor;
...

সব একসাথে

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

পরিবেশ সংক্রান্ত প্রতিক্রিয়া

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

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

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

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

...
var ctx = new webkitAudioContext();
var mainVolume = ctx.createGain();

// Create a convolver to apply environmental effects to the audio.
var convolver = ctx.createConvolver();

// Create a mixer that receives sound from the panners.
var mixer = ctx.createGain();

sounds.forEach(function(sound){
  sound.panner.connect(mixer);
});

// Create volume controllers for the plain audio and the convolver.
var plainGain = ctx.createGain();
var convolverGain = ctx.createGain();

// Send audio from the mixer to plainGain and the convolver node.
mixer.connect(plainGain);
mixer.connect(convolver);

// Hook up the convolver to its volume control.
convolver.connect(convolverGain);

// Send audio from the volume controls to the main volume control.
plainGain.connect(mainVolume);
convolverGain.connect(mainVolume);

// Finally, connect the main volume to the audio context's destination.
volume.connect(ctx.destination);
...

ConvolverNode কাজ করার জন্য, আপনাকে একটি বাফারে একটি আবেগ প্রতিক্রিয়া নমুনা লোড করতে হবে এবং ConvolverNode ব্যবহার করতে হবে। নমুনা লোড করা সাধারণ শব্দের নমুনার মতো একইভাবে ঘটে। নীচে এটি করার একটি উপায়ের একটি উদাহরণ:

...
loadBuffer(ctx, "impulseResponseExample.wav", function(buffer){
  convolver.buffer = buffer;
  convolverGain.gain.value = 0.7;
  plainGain.gain.value = 0.3;
})
...
function loadBuffer(ctx, filename, callback) {
  var request = new XMLHttpRequest();
  request.open("GET", soundFileName, true);
  request.responseType = "arraybuffer";
  request.onload = function() {
    // Create a buffer and keep the channels unchanged.
    ctx.decodeAudioData(request.response, callback, function() {
      alert("Decoding the audio buffer failed");
    });
  };
  request.send();
}

সারসংক্ষেপ

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

অডিও অভিজ্ঞতাকে আরও আকর্ষক করতে, আপনি পরিবেশের সাধারণ শব্দ সেট আপ করতে ওয়েব অডিও API-তে ConvolverNode ব্যবহার করতে পারেন। ক্যাথেড্রাল থেকে বন্ধ কক্ষ পর্যন্ত, আপনি ওয়েব অডিও API ব্যবহার করে বিভিন্ন প্রভাব এবং পরিবেশ অনুকরণ করতে পারেন।

তথ্যসূত্র