Chrome 64, Web Audio API'de merakla beklenen yeni bir özellikle birlikte geliyor: AudioWorklet. Burada, JavaScript koduyla özel bir ses işlemcisi oluşturmak için gereken kavramları ve kullanımları öğreneceksiniz. Canlı demolara göz atın. Bu serinin bir sonraki makalesi olan İşitsel Worklet Tasarım Deseni, ileri düzey bir işitsel uygulama oluşturmak için ilginizi çekebilir.
Arka plan: ScriptProcessorNode
Web Audio API'deki ses işleme, ana kullanıcı arayüzü iş parçacığından ayrı bir iş parçacığında çalıştığından sorunsuz bir şekilde çalışır. JavaScript'te özel ses işlemeyi etkinleştirmek için Web Audio API, ana kullanıcı arayüzü iş parçacığında kullanıcı komut dosyasını çağırmak üzere etkinlik işleyicileri kullanan bir ScriptProcessorNode önerdi.
Bu tasarımda iki sorun vardır: Etkinlik işleme, tasarım gereği asenkrondur ve kod yürütme ana iş parçacığında gerçekleşir. İlki gecikmeye neden olur, ikincisi ise genellikle çeşitli kullanıcı arayüzü ve DOM ile ilgili görevlerle dolu olan ana iş parçacığına baskı uygular. Bu da kullanıcı arayüzünün "takılmasına" veya sesin "takılmasına" neden olur. Bu temel tasarım hatası nedeniyle ScriptProcessorNode
'ün desteği sonlandırıldı ve AudioWorklet ile değiştirildi.
Kavramlar
Ses işleyicisi, kullanıcı tarafından sağlanan JavaScript kodunun tamamını ses işleme iş parçacığında tutar. Bu sayede, sesleri işlemek için ana mesaj dizisine atlamasına gerek kalmaz. Bu, kullanıcı tarafından sağlanan komut dosyası kodunun, diğer yerleşik AudioNodes
ile birlikte ses oluşturma iş parçacığında (AudioWorkletGlobalScope
) çalışacağı anlamına gelir. Bu da sıfır ek gecikmeye ve eşzamanlı oluşturmaya olanak tanır.
Kayıt ve örnek oluşturma
Ses iş parçacığı kullanma işlemi iki bölümden oluşur: AudioWorkletProcessor
ve
AudioWorkletNode
. Bu, ScriptProcessorNode'u kullanmaktan daha karmaşıktır ancak geliştiricilere özel ses işleme için düşük düzeyde bir özellik sunmak için gereklidir. AudioWorkletProcessor
, JavaScript kodunda yazılmış gerçek ses işleyiciyi temsil eder ve AudioWorkletGlobalScope
içinde bulunur.
AudioWorkletNode
, AudioWorkletProcessor
'un karşılığıdır ve ana mesaj dizisindeki diğer AudioNodes
ile olan bağlantıyı yönetir. Ana genel kapsamda gösterilir ve normal bir AudioNode
gibi çalışır.
Kayıt ve örneklendirmeyi gösteren bir çift kod snippet'i aşağıda verilmiştir.
// 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
oluşturmak için bir AudioContext nesnesi ve işlemci adını dize olarak eklemeniz gerekir. İşlemci tanımı, yeni Audio Worklet nesnesinin addModule()
çağrısı tarafından yüklenebilir ve kaydedilebilir.
Ses Workleti dahil Worklet API'leri yalnızca güvenli bir bağlamda kullanılabilir. Bu nedenle, bunları kullanan bir sayfanın HTTPS üzerinden sunulması gerekir. http://localhost
, yerel test için güvenli kabul edilir.
AudioWorkletNode
sınıfını alt sınıflandırarak iş parçasında çalışan işlemci tarafından desteklenen özel bir düğüm tanımlayabilirsiniz.
// 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);
AudioWorkletGlobalScope
içindeki registerProcessor()
yöntemi, kaydedilecek işleyicinin adı ve sınıf tanımı için bir dize alır.
Genel kapsamda komut dosyası kodu değerlendirmesi tamamlandıktan sonra, AudioWorklet.addModule()
tarafından verilen söz çözülerek kullanıcılara sınıf tanımının ana genel kapsamda kullanılmaya hazır olduğu bildirilir.
Özel ses parametreleri
AudioNodes'un kullanışlı özelliklerinden biri, AudioParam
ile planlanabilir parametre otomasyonudur. AudioWorkletNodes, ses hızında otomatik olarak kontrol edilebilen parametreleri almak için bunları kullanabilir.
Kullanıcı tanımlı ses parametreleri, bir AudioParamDescriptor
sınıf tanımında AudioParamDescriptor
grubu ayarlayarak tanımlanabilir.AudioWorkletProcessor
Temel WebAudio motoru, bir AudioWorkletNode oluşturulurken bu bilgileri alır ve ardından AudioParam
nesnelerini oluşturup düğüme uygun şekilde bağlar.
/* 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()
yöntemi
Gerçek ses işleme, AudioWorkletProcessor
içindeki process()
geri çağırma yönteminde gerçekleşir. Sınıf tanımında bir kullanıcı tarafından uygulanmalıdır. WebAudio motoru, girişler ve parametreleri beslemek ve çıktıları almak için bu işlevi eşzamanlı olarak çağırır.
/* 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;
}
Ayrıca, geliştiricilerin bellek ayak izini yönetebilmesi için process()
yönteminin döndürdüğü değer, AudioWorkletNode
nesnesinin ömrünü kontrol etmek amacıyla kullanılabilir. process()
yönteminden false
döndürülmesi, işleyiciyi etkin olmayan olarak işaretler ve WebAudio
motoru artık yöntemi çağırmaz. İşlemcinin etkin kalması için yöntemin true
döndürmesi gerekir.
Aksi takdirde, düğüm ve işlemci çifti zaman içinde sistem tarafından çöp toplanır.
MessagePort ile iki yönlü iletişim
Bazen özel bir AudioWorkletNode
, özel bir filtreyi kontrol etmek için kullanılan dize tabanlı bir type
özelliği gibi AudioParam
ile eşleşmeyen kontroller göstermek ister. Bu amaç ve daha fazlası için AudioWorkletNode
ve AudioWorkletProcessor
, iki yönlü iletişim için MessagePort
ile donatılmıştır. Bu kanal üzerinden her türlü özel veri alışverişi yapılabilir.
MessagePort'a hem düğümde hem de işlemcide .port
özelliğiyle erişilebilir. Nodun port.postMessage()
yöntemi, ilişkili işleyicinin port.onmessage
işleyicisine bir mesaj gönderir ve bunun tersi de geçerlidir.
/* 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
, veri depolama alanını veya WASM modülünü iş parçacığı sınırı üzerinden aktarmanıza olanak tanıyan aktarılabilir özelliği destekler. Bu, Audio Worklet sisteminin nasıl kullanılabileceği konusunda sayısız olasılık sunar.
Rehber: GainNode oluşturma
AudioWorkletNode
ve AudioWorkletProcessor
üzerine inşa edilmiş GainNode'un eksiksiz bir örneğini aşağıda bulabilirsiniz.
index.html
dosyası:
<!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
dosyası:
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);
Bu, ses iş parçacığı sisteminin temellerini kapsar. Canlı demolar Chrome WebAudio ekibinin GitHub deposunda mevcuttur.
Özellik geçişi: Deneysel'den kararlı sürüme
Ses iş parçacığı, Chrome 66 veya sonraki sürümlerde varsayılan olarak etkindir. Chrome 64 ve 65'te bu özellik deneysel işaretin arkasındaydı.