Chrome 64 כולל תכונה חדשה שכולם מצפים לה ב-Web Audio API – AudioWorklet. כאן תלמדו מושגים ואת אופן השימוש כדי ליצור מעבד אודיו מותאם אישית עם קוד JavaScript. כדאי לעיין הדגמות בשידור חי. במאמר הבא בסדרה, תבנית עיצוב Worklet של אודיו, יכול להיות מאמר מעניין שיעזור לכם לפתח אפליקציית אודיו מתקדמת.
רקע: ScriptProcessorNode
עיבוד האודיו ב-Web Audio API פועל בשרשור נפרד שרשור בממשק המשתמש, כך שהוא פועל בצורה חלקה. כדי להפעיל עיבוד אודיו בהתאמה אישית ב: JavaScript, ה-Web Audio API הציע ScriptProcessorNode שהשתמש בו גורמים מטפלים באירועים כדי להפעיל סקריפט של משתמש בשרשור הראשי של ממשק המשתמש.
יש שתי בעיות בעיצוב הזה: הטיפול באירועים הוא אסינכרוני
בפועל, והרצת הקוד מתבצעת בשרשור הראשי. הקודם
קובע את זמן האחזור, והשני לוחץ על ה-thread הראשי
עמוסים בדרך כלל במשימות שונות הקשורות לממשק המשתמש ול-DOM, שגורמות לכל ממשק משתמש
ל-'jank' או אודיו ל"תקלה". בגלל הפגם הבסיסי הזה בעיצוב,
ההוראה ScriptProcessorNode
הוצאה משימוש מהמפרט
הוחלף ב-AudioWorklet.
מושגים
worklet של האודיו שומר את קוד ה-JavaScript שסופק על ידי המשתמש
שרשור לעיבוד אודיו. כלומר לא צריך לדלג
לעיבוד אודיו. המשמעות היא שקוד הסקריפט שסופק על ידי המשתמש יכול לרוץ
בשרשור של רינדור אודיו (AudioWorkletGlobalScope
) יחד עם
AudioNodes
מובנה, שמבטיח אפס זמן אחזור נוסף וסינכרוני
ברינדור.
רישום ויצירה
השימוש ב-Worklet של אודיו מורכב משני חלקים: AudioWorkletProcessor
ו
AudioWorkletNode
. התהליך הזה כרוך יותר בשימוש ב-ScriptProcessorNode,
אבל הוא צריך כדי לתת למפתחים את היכולת ברמה הנמוכה ליצור אודיו מותאם אישית
בעיבוד. AudioWorkletProcessor
מייצג את מעבד האודיו בפועל
כתוב בקוד JavaScript, והוא נמצא ב-AudioWorkletGlobalScope
.
AudioWorkletNode
היא המקבילה ל-AudioWorkletProcessor
, והיא לוקחת
בחיבור אל AudioNodes
אחר ב-thread הראשי וממנו. הוא
נחשפת בהיקף הגלובלי הראשי ומתפקדת כמו 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 באובייקט 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()
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
מ-process()
סימני method
שהמעבד לא פעיל, והמנוע WebAudio
לא מפעיל יותר
. כדי שהמעבד יישאר פעיל, השיטה חייבת להחזיר true
.
אחרת, הצמד של הצומת והמעבד הוא אשפה שנאספת על ידי המערכת
בסופו של דבר.
תקשורת דו-כיוונית עם MessagePort
לפעמים, AudioWorkletNode
בהתאמה אישית רוצה לחשוף פקדים שלא
למפות ל-AudioParam
, למשל למאפיין type
שמבוסס על מחרוזות
שמשמש לשליטה במסנן מותאם אישית. למטרה הזו ואחריה,
AudioWorkletNode
ו-AudioWorkletProcessor
כוללים
MessagePort
לתקשורת דו-כיוונית. כל סוג של נתונים בהתאמה אישית
ניתן להחליף ביניהם דרך הערוץ הזה.
אפשר לגשת ל-MessagePort באמצעות המאפיין .port
גם בצומת וגם
במעבד. רכיב ה-method 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 דרך גבולות ה-thread. תיפתח
יש אינספור אפשרויות לאופן השימוש במערכת Audio Worklet.
הדרכה מפורטת: בניית ChangeNode
הנה דוגמה מלאה לשימוש ב- מקבלים GetNode, שמבוסס על
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.
מעבר תכונה: מניסיוני ליציב
worklet של האודיו מופעל כברירת מחדל ב-Chrome 66 ואילך. בגרסאות 64 ו-65 של Chrome, התכונה עמדה מאחורי הדגל של הניסוי.