Audio Worklet اکنون به صورت پیش فرض در دسترس است

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 داخلی اجرا می‌شود که تأخیر اضافی و رندر همزمان صفر را تضمین می‌کند.

نمودار دامنه جهانی اصلی و نمودار محدوده Worklet صوتی
شکل 1

ثبت نام و نمونه گیری

استفاده از 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 می‌تواند از این پارامترها برای دریافت پارامترهایی استفاده کند که می‌توانند به صورت خودکار با نرخ صدا کنترل شوند.

نمودار گره کارگاه صوتی و پردازنده
شکل 2

پارامترهای صوتی تعریف شده توسط کاربر را می توان در تعریف کلاس 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() را می توان برای کنترل طول عمر AudioWorkletNode استفاده کرد تا توسعه دهندگان بتوانند ردپای حافظه را مدیریت کنند. برگرداندن متد false from process() باعث غیرفعال بودن پردازنده می شود و موتور WebAudio دیگر متد را فراخوانی نمی کند. برای زنده نگه داشتن پردازنده، متد باید true باشد. در غیر این صورت، جفت گره و پردازنده زباله‌هایی هستند که در نهایت توسط سیستم جمع‌آوری می‌شوند.

ارتباط دو طرفه با MessagePort

گاهی اوقات، یک AudioWorkletNode سفارشی می‌خواهد کنترل‌هایی را که به AudioParam نگاشت نمی‌شوند، نشان دهد، مانند یک ویژگی type مبتنی بر رشته که برای کنترل یک فیلتر سفارشی استفاده می‌شود. برای این منظور و فراتر از آن، AudioWorkletNode و AudioWorkletProcessor مجهز به MessagePort برای ارتباط دوطرفه هستند. هر نوع داده سفارشی از طریق این کانال قابل تبادل است.

شکل 2
شکل 2

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، این ویژگی پشت پرچم آزمایشی قرار داشت.