이제 오디오 Worklet을 기본적으로 사용할 수 있습니다.

Hongchan Choi

Chrome 64에는 많은 기대를 받고 있는 Web Audio API의 새로운 기능이 포함되어 있습니다. AudioWorklet을 사용합니다. 여기에서는 Google 애널리틱스 4를 만드는 데 필요한 개념과 사용법을 커스텀 오디오 프로세서를 사용할 수 있습니다 자세한 내용은 라이브 데모를 살펴보세요. 시리즈의 다음 문서인 오디오 Worklet 디자인 패턴에서는 고급 오디오 앱을 빌드할 때 유용할 수 있습니다.

배경: ScriptProcessorNode

Web Audio API의 오디오 처리는 기본 오디오와 별도의 스레드에서 실행됩니다. 원활하게 실행되도록 합니다. 커스텀 오디오 처리를 사용 설정하려면 JavaScript, Web Audio API는 인코더-디코더 아키텍처에 사용되는 이벤트 핸들러를 사용하여 기본 UI 스레드에서 사용자 스크립트를 호출합니다.

이 설계에는 두 가지 문제가 있습니다. 이벤트 처리는 비동기식입니다. 코드 실행은 기본 스레드에서 이루어집니다. 이전 지연 시간이 발생하고 후자는 실행 중인 기본 스레드를 일반적으로 다양한 UI 및 DOM 관련 작업으로 혼잡하여 '버벅거림'이 오디오에서 '글리치'가 될 수도 있습니다. 이러한 기본적인 디자인 결함 때문에 ScriptProcessorNode는 사양에서 지원 중단되었으며 AudioWorklet으로 대체되었습니다.

개념

Audio Worklet은 사용자가 제공한 JavaScript 코드를 모두 오디오 처리 스레드입니다. 즉, 기본 화면으로 건너뛸 필요 없이 오디오를 처리해야 합니다. 즉, 사용자가 제공한 스크립트 코드가 다른 스레드 전문가와 함께 오디오 렌더링 스레드 (AudioWorkletGlobalScope)에서 기본 제공 AudioNodes: 추가 지연 시간과 동기식을 보장합니다. 있습니다.

<ph type="x-smartling-placeholder">
</ph> 기본 전역 범위 및 오디오 Worklet 범위 다이어그램 <ph type="x-smartling-placeholder">
</ph> Fig.1

등록 및 인스턴스화

오디오 Worklet 사용은 AudioWorkletProcessorAudioWorkletNode입니다. 이것은 ScriptProcessorNode를 사용하는 것보다 더 복잡합니다. 개발자에게 커스텀 오디오를 위한 하위 수준 기능을 제공해야 합니다. 가장 적합합니다 AudioWorkletProcessor는 실제 오디오 프로세서를 나타냅니다. JavaScript 코드로 작성되며 AudioWorkletGlobalScope에 있습니다. AudioWorkletNodeAudioWorkletProcessor의 대응 요소이며 기본 스레드의 다른 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를 추가해야 합니다. 객체 및 프로세서 이름을 문자열로 변환합니다. 프로세서 정의는 새 Audio Worklet 객체의 addModule() 호출을 통해 로드되고 등록됩니다. Audio Worklet을 포함한 Worklet API는 보안 컨텍스트가 존재하므로 http://localhost은(는) HTTPS를 통해 제공되어야 합니다. 로컬 테스트용으로 안전한 것으로 간주됩니다.

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

AudioWorkletGlobalScoperegisterProcessor() 메서드는 등록할 프로세서의 이름과 클래스 정의의 문자열입니다. 전역 범위에서 스크립트 코드 평가가 완료되면 사용자에게 알리는 AudioWorklet.addModule()의 약속이 해결됩니다. 클래스 정의를 기본 전역 범위에서 사용할 준비가 되었는지 확인합니다.

맞춤 오디오 매개변수

AudioNode의 유용한 기능 중 하나는 예약 가능한 매개변수입니다. AudioParam로 자동화합니다. AudioWorkletNodes는 이를 사용하여 오디오 레이트에서 자동으로 제어할 수 있는 노출된 매개변수가 포함됩니다.

<ph type="x-smartling-placeholder">
</ph> 오디오 워크렛 노드 및 프로세서 다이어그램 <ph type="x-smartling-placeholder">
</ph> Fig.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() 콜백 메서드에서 발생합니다. 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의 전체 기간을 제어하여 개발자가 메모리 공간을 줄일 수 있습니다 process() 메서드 표시에서 false 반환 비활성 상태이고 WebAudio 엔진은 더 이상 메서드를 사용하여 축소하도록 요청합니다. 프로세서의 활성 상태를 유지하려면 메서드가 true를 반환해야 합니다. 그 외의 경우에는 시스템에서 노드 및 프로세서 쌍이 가비지로 수집됨 결국에는 말이죠.

MessagePort를 사용한 양방향 통신

때로 맞춤 AudioWorkletNode는 빌드되지 않은 컨트롤을 노출하려고 할 수 있습니다. AudioParam에 매핑(예: 문자열 기반 type 속성) 사용됩니다. 이러한 목적과 그 이상을 위해 AudioWorkletNodeAudioWorkletProcessor에는 양방향 통신: MessagePort 모든 종류의 맞춤 데이터 이 채널을 통해 교환할 수 있습니다.

<ph type="x-smartling-placeholder">
</ph> Fig.2 <ph type="x-smartling-placeholder">
</ph> Fig.2

MessagePort는 노드 및 포트에서 모두 .port 속성을 사용하여 액세스할 수 있습니다. 프로세서에 위치합니다 노드의 port.postMessage() 메서드는 관련 프로세서의 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 모듈을 통해 전송할 수 있습니다. 그러면 오디오 Worklet 시스템을 사용할 수 있는 방법은 무수히 많습니다.

둘러보기: GETNode 빌드

다음은 AudioWorkletNodeAudioWorkletProcessor

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

오디오 Worklet 시스템의 기본 사항을 다룹니다. 라이브 데모를 사용할 수 있습니다. Chrome WebAudio팀의 GitHub 저장소에서 확인할 수 있습니다.

기능 전환: 실험용에서 안정화 버전으로

Audio Worklet은 Chrome 66 이상에서 기본적으로 사용 설정되어 있습니다. Chrome 64 및 65에서는 이 기능이 실험용 플래그 뒤에 있었습니다.