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