JavaScript
WebAudioAPI

Web Audio APIでカスタムAudioNodeを作る方法

More than 1 year has passed since last update.

Web Audio APIのノードをconnectで繋いで音を作る方法はわかりやすいのですが、ノードの種類自体は多くなく、複数のノードを組み合わせて一つのエフェクターなどを作っていく感じのようです。
そのため、既存ノードを組み合わせてエフェクターなどの単位で新しいAudioNodeを作ってconnectできるようにすれば便利なんじゃないかと思いました。

とりあえず、以下のようにAudioNodeを継承できるか試してみましたが、Uncaught TypeError: Illegal constructorというエラーがでてこれは無理そうです。

class MyAudioNode extends AudioNode {}
const myAudioNode = new MyAudioNode();

AudioNodeを継承したサブクラスならと思って以下も試してみましたが、これもUncaught TypeError: Failed to construct 'OscillatorNode': 1 argument required, but only 0 present.というエラーが出て駄目そうです。

class MyOscillatorNode extends OscillatorNode {}
const myOscillator = new MyOscillatorNode();

いろいろ調べた結果、直接AudioNodeを継承することはできないようです。
そのかわりに、以下のように入出力をGainNodeでラップして、内部で音を作ったり、加工したりしてあげることでほぼ既存のAudioNodeと同じように振る舞う自作ノードを作ることができます。
この方法だと他のノードを接続するときに、squareOscNode.connect(highpassFilter.in)というように自作ノードのinプロパティに接続しないといけないことが既存のノードに接続するときとは異なります。

class BaseCustomNode {
  constructor(context) {
    this._context = context;
    this.in = context.createGain();
    this._out = context.createGain();
  }

  connect(audioNode) {
    this._out.connect(audioNode);
  }
}

class SquareOscillatorNode extends BaseCustomNode {
  constructor(context) {
    super(context);
    this._osc = context.createOscillator();
    this._osc.type = 'square';
    this._osc.connect(this._out);
  }

  start(when) {
    this._osc.start(when);
  }

  stop(when) {
    this._osc.stop(when);
  }

  get frequency() {
    return this._osc.frequency;
  }
}

class HighpassFilter extends BaseCustomNode {
  constructor(context) {
    super(context);
    this._filter = context.createBiquadFilter();
    this._filter.type = 'highpass';
    this.in.connect(this._filter);
    this._filter.connect(this._out);
  }

  get frequency() {
    return this._filter.frequency;
  }
}

const context = new AudioContext(),
      squareOscNode = new SquareOscillatorNode(context),
      highpassFilter = new HighpassFilter(context);

squareOscNode.frequency.value = 880;
highpassFilter.frequency.value = 440;
squareOscNode.connect(highpassFilter.in); // 自作ノードに接続するときだけ、inプロパティに接続する
highpassFilter.connect(context.destination);

squareOscNode.start();

参考