Chrome 64 دارای یک ویژگی جدید مورد انتظار در Web Audio API - AudioWorklet است. در اینجا مفاهیم و کاربرد ایجاد یک پردازشگر صوتی سفارشی با کد جاوا اسکریپت را خواهید آموخت. به دموهای زنده نگاهی بیندازید. مقاله بعدی مجموعه، الگوی طراحی Worklet Audio ، ممکن است خواندنی جالب برای ساخت یک برنامه صوتی پیشرفته باشد.
پس زمینه: ScriptProcessorNode
پردازش صدا در Web Audio API در یک رشته مجزا از رشته اصلی UI اجرا می شود، بنابراین به راحتی اجرا می شود. برای فعال کردن پردازش صوتی سفارشی در جاوا اسکریپت، Web Audio API یک ScriptProcessorNode را پیشنهاد کرد که از کنترلکنندههای رویداد برای فراخوانی اسکریپت کاربر در رشته UI اصلی استفاده میکرد.
دو مشکل در این طراحی وجود دارد: مدیریت رویداد از نظر طراحی ناهمزمان است و اجرای کد در رشته اصلی اتفاق میافتد. اولی تأخیر را القا میکند و دومی رشته اصلی را که معمولاً مملو از وظایف مختلف مربوط به UI و DOM است، تحت فشار قرار میدهد و باعث میشود UI "جنگ" یا صدا "شکلی" ایجاد کند. به دلیل این نقص اساسی طراحی، ScriptProcessorNode
از مشخصات منسوخ شده و با AudioWorklet جایگزین شده است.
مفاهیم
Audio Worklet کد جاوا اسکریپت ارائه شده توسط کاربر را در رشته پردازش صدا نگه می دارد. این بدان معناست که برای پردازش صدا نیازی به پرش به موضوع اصلی نیست. این بدان معناست که کد اسکریپت ارائهشده توسط کاربر روی رشته رندر صوتی ( AudioWorkletGlobalScope
) همراه با سایر AudioNodes
داخلی اجرا میشود که تأخیر اضافی و رندر همزمان صفر را تضمین میکند.
ثبت نام و نمونه گیری
استفاده از Audio Worklet از دو بخش تشکیل شده است: AudioWorkletProcessor
و AudioWorkletNode
. این بیشتر از استفاده از ScriptProcessorNode است، اما برای دادن قابلیت سطح پایین برای پردازش صوتی سفارشی به توسعه دهندگان نیاز است. AudioWorkletProcessor
پردازنده صوتی واقعی نوشته شده در کد جاوا اسکریپت را نشان می دهد و در AudioWorkletGlobalScope
زندگی می کند. AudioWorkletNode
همتای AudioWorkletProcessor
است و از اتصال به و از دیگر AudioNodes
در رشته اصلی مراقبت می کند. این در محدوده اصلی جهانی قرار دارد و مانند یک AudioNode
معمولی عمل می کند.
در اینجا یک جفت قطعه کد وجود دارد که ثبت و نمونه سازی را نشان می دهد.
// 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()
شیء جدید Audio Worklet بارگیری و ثبت کرد. APIهای Worklet از جمله Audio Worklet فقط در یک زمینه امن در دسترس هستند، بنابراین صفحه ای که از آنها استفاده می کند باید از طریق HTTPS ارائه شود، اگرچه http://localhost
برای آزمایش محلی ایمن در نظر گرفته می شود.
می توانید AudioWorkletNode
برای تعریف یک گره سفارشی که توسط پردازنده در حال اجرا بر روی Worklet پشتیبانی می شود، زیر کلاس قرار دهید.
// This is the "processors.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);
متد registerProcessor()
در AudioWorkletGlobalScope
یک رشته برای نام پردازنده و تعریف کلاس می گیرد. پس از تکمیل ارزیابی کد اسکریپت در حوزه جهانی، وعده AudioWorklet.addModule()
حل می شود و به کاربران اطلاع می دهد که تعریف کلاس آماده استفاده در حوزه جهانی اصلی است.
پارامترهای صوتی سفارشی
یکی از موارد مفید در مورد AudioNodes، اتوماسیون پارامترهای زمانبندی شده با AudioParam
است. AudioWorkletNodes میتواند از این پارامترها برای دریافت پارامترهایی استفاده کند که میتوانند به صورت خودکار با نرخ صدا کنترل شوند.
پارامترهای صوتی تعریف شده توسط کاربر را می توان در تعریف کلاس AudioWorkletProcessor
با تنظیم مجموعه ای از AudioParamDescriptor
اعلان کرد. موتور 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()
process callback در AudioWorkletProcessor
اتفاق می افتد. باید توسط یک کاربر در تعریف کلاس پیاده سازی شود. موتور 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()
process را می توان برای کنترل طول عمر AudioWorkletNode
استفاده کرد تا توسعه دهندگان بتوانند ردپای حافظه را مدیریت کنند. برگرداندن متد false
from process()
باعث غیرفعال بودن پردازنده می شود و موتور WebAudio
دیگر متد را فراخوانی نمی کند. برای زنده نگه داشتن پردازنده، متد باید true
باشد. در غیر این صورت، جفت گره و پردازنده زبالههایی هستند که در نهایت توسط سیستم جمعآوری میشوند.
ارتباط دو طرفه با MessagePort
گاهی اوقات، یک AudioWorkletNode
سفارشی میخواهد کنترلهایی را که به AudioParam
نگاشت نمیشوند، نشان دهد، مانند یک ویژگی type
مبتنی بر رشته که برای کنترل یک فیلتر سفارشی استفاده میشود. برای این منظور و فراتر از آن، AudioWorkletNode
و AudioWorkletProcessor
مجهز به MessagePort
برای ارتباط دوطرفه هستند. هر نوع داده سفارشی از طریق این کانال قابل تبادل است.
MessagePort را می توان با ویژگی .port
در هر دو گره و پردازنده دسترسی داشت. متد port.postMessage()
گره پیامی را به handler 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!');
});
/* "processors.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);
MessagePort
از قابلیت انتقال پشتیبانی میکند که به شما امکان میدهد ذخیرهسازی داده یا یک ماژول WASM را بر روی مرز رشته انتقال دهید. این امکان بی شماری را در مورد نحوه استفاده از سیستم Audio Worklet باز می کند.
راه بروید: یک GainNode بسازید
در اینجا یک مثال کامل از GainNode است که در بالای AudioWorkletNode
و AudioWorkletProcessor
ساخته شده است.
فایل index.html
:
<!doctype html>
<html>
<script>
const context = new AudioContext();
// Loads module script with 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>
فایل gain-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);
این اصول اساسی سیستم Audio Worklet را پوشش می دهد. دموهای زنده در مخزن GitHub تیم Chrome WebAudio موجود است.
انتقال ویژگی: آزمایشی به پایدار
Audio Worklet به طور پیش فرض برای Chrome 66 یا جدیدتر فعال است. در کروم 64 و 65، این ویژگی پشت پرچم آزمایشی قرار داشت.