Worklet audio jest teraz domyślnie dostępny

Chrome 64 ma długo oczekiwaną nową funkcję w interfejsie Web Audio API: AudioWorklet Poznaj pojęcia i sposoby korzystania z tych narzędzi, aby tworzyć za pomocą niestandardowego procesora audio z kodem JavaScript. Zwróć uwagę na: prezentacji na żywo. Następny artykuł z serii Audio Worklet Design Pattern (wzorzec projektowania elementów audio) może być interesującą lekcją na temat tworzenia zaawansowanej aplikacji audio.

Tło: ScriptProcessorNode

Przetwarzanie dźwięku w Web Audio API działa w osobnym wątku niż główny UI, więc wszystko działa płynnie. Aby włączyć niestandardowe przetwarzanie dźwięku w: JavaScript, interfejs Web Audio API zaproponował skrypt ScriptProcessorNode, moduły obsługi zdarzeń wywołujące skrypt użytkownika w głównym wątku UI.

Na tym etapie występują 2 problemy: asynchroniczna obsługa zdarzeń. a wykonanie kodu odbywa się w wątku głównym. Poprzedni wywołuje opóźnienie, które z kolei wpływa na główny wątek, który często zatłoczony różnymi zadaniami związanymi z UI i DOM, do „zacinania” lub dźwięku, żeby coś się zacięło. Z powodu tego zasadniczego błędu projektowego Interfejs ScriptProcessorNode został wycofany ze specyfikacji i zastąpiono komponentem AudioWorklet.

Pojęcia

Audio Worklet przechowuje kod JavaScript dostarczony przez użytkownika w wątku przetwarzania dźwięku. Oznacza to, że nie musi przechodzić do elementu głównego w wątku na potrzeby przetwarzania dźwięku. Oznacza to, że kod skryptu dostarczonego przez użytkownika musi zostać uruchomiony w wątku renderowania dźwięku (AudioWorkletGlobalScope) wraz z innymi wbudowanej funkcji AudioNodes, która zapewnia zerowe opóźnienia i synchronizację synchroniczną jak renderowanie.

Diagram głównego zakresu globalnego i zakresu Worklet audio
Fig.1

Rejestracja i tworzenie instancji

Korzystanie z Worklet audio składa się z 2 części: AudioWorkletProcessor oraz AudioWorkletNode Wymaga to więcej pracy niż w przypadku ScriptProcessorNode. ale konieczne jest zapewnienie programistom niskopoziomowych możliwości o przetwarzaniu danych. AudioWorkletProcessor oznacza faktyczny procesor audio napisanych w kodzie JavaScriptu, a elementy te znajdują się w elemencie AudioWorkletGlobalScope. AudioWorkletNode to odpowiednik AudioWorkletProcessor i przyjmuje dba o połączenie z i od innych użytkowników AudioNodes w wątku głównym. it jest widoczny w głównym zakresie globalnym i działa jak zwykły AudioNode.

Oto 2 fragmenty kodu ilustrujące rejestrację oraz tworzenia instancji.

// 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);
});

Aby utworzyć AudioWorkletNode, musisz dodać kontekst AudioContext a nazwa procesora w formie ciągu znaków. Definicja procesora może być został wczytany i zarejestrowany przez wywołanie addModule() nowego obiektu Worklet Audio. Interfejsy API Worklet, w tym Worklet Audio, są dostępne tylko bezpieczny kontekst, strony używające ich muszą być wyświetlane przez HTTPS, podczas gdy http://localhost to uważanych za bezpieczne pod kątem testów lokalnych.

Możesz użyć podklasy AudioWorkletNode, aby zdefiniować w węźle niestandardowym obsługiwanym przez procesor działający w danym obszarze roboczym.

// 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);

Metoda registerProcessor() w AudioWorkletGlobalScope pobiera ciąg tekstowy zawierający nazwę procesora, który ma zostać zarejestrowany, oraz definicję klasy. Po zakończeniu oceny kodu skryptu w zakresie globalnym obietnica produktu AudioWorklet.addModule() zostanie rozwiązana powiadamianiem użytkowników że definicja klasy jest gotowa do użycia w głównym zakresie globalnym.

Niestandardowe parametry audio

Jedną z przydatnych funkcji AudioNodes jest parametr podlegający planowaniu. automatyzację za pomocą funkcji AudioParam. AudioWorkletNodes może ich użyć, aby uzyskać parametrów z grupy eksperymentalnej, które można automatycznie kontrolować, określając szybkość transmisji dźwięku.

Schemat węzła i procesora audio
Fig.2

Zdefiniowane przez użytkownika parametry audio można zadeklarować w polu AudioWorkletProcessor. definicji klasy przez skonfigurowanie zbioru AudioParamDescriptor. bazowy mechanizm WebAudio rejestruje te informacje podczas tworzenia węzła AudioWorkletNode, a następnie tworzy i łączy AudioParam obiektów do węzła.

/* 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() metoda

Rzeczywiste przetwarzanie dźwięku ma miejsce w metodzie wywołania zwrotnego process() w funkcji AudioWorkletProcessor Musi zostać wdrożony przez użytkownika w klasie definicji. Mechanizm WebAudio wywołuje tę funkcję w trybie izochronologicznym do przesyłania danych wejściowych i parametrów oraz pobierania danych wyjściowych.

/* 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;
}

Dodatkowo wartości zwracanej przez metodę process() można używać do kontrolować okres ważności AudioWorkletNode, aby deweloperzy mogli zarządzać wykorzystanie pamięci masowej. Zwracanie false ze znacznika process() metody procesor jest nieaktywny, a mechanizm WebAudio nie wywołuje już . Aby procesor był aktywny, metoda musi zwracać wartość true. W przeciwnym razie para węzła i procesora stanowi czyszczenie pamięci przez system w końcu.

Dwukierunkowa komunikacja z MessagePort

Czasami niestandardowy AudioWorkletNode chce udostępnić ustawienia, które nie są mapować na AudioParam, na przykład jako atrybut type oparty na ciągach znaków służy do sterowania filtrem niestandardowym. W tym celu i nie tylko, AudioWorkletNode i AudioWorkletProcessor mają: MessagePort dla komunikacji dwukierunkowej. Dowolne dane niestandardowe można wymieniać na tym kanale.

Fig.2
Fig.2

Dostęp do MessagePort można uzyskać za pomocą atrybutu .port zarówno w węźle, z procesorem. Metoda port.postMessage() węzła wysyła komunikat do w funkcji port.onmessage powiązanego procesora i odwrotnie.

/* 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 obsługuje przenoszenie, co pozwala pamięci masowej do przesyłania danych lub modułu WASM ponad granicę wątku. Spowoduje to otwarcie daje niezliczone możliwości wykorzystania systemu Audio Worklet.

Przewodnik: tworzenie węzła GainNode

Oto kompletny przykładowy interfejs GainNode oparty na AudioWorkletNode i AudioWorkletProcessor.

Plik 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>

Plik 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);

Omawiamy podstawy systemu Audio Worklet. Dostępne są prezentacje na żywo w repozytorium zespołu Chrome WebAudio na GitHubie.

Przejście funkcji: wersja eksperymentalna na stabilną

Worklet audio jest domyślnie włączony w Chrome 66 i nowszych wersjach. W Chrome 64 i 65 ta funkcja była dostępna za flagą eksperymentalnej.